CONTENTS

NAME

Crypt::PK::DSA - Public key cryptography based on DSA

SYNOPSIS

### OO interface

my $message = 'hello world';
my $signer = Crypt::PK::DSA->new();
$signer->generate_key(30, 256);

my $signature = $signer->sign_message($message, 'SHA256');
my $public_der = $signer->export_key_der('public');
my $verifier = Crypt::PK::DSA->new(\$public_der);
$verifier->verify_message($signature, $message, 'SHA256') or die "ERROR";

my $ciphertext = $verifier->encrypt("secret message");
my $plaintext = $signer->decrypt($ciphertext);

my $private_der = $signer->export_key_der('private');
my $private_pem = $signer->export_key_pem('private');
my $public_pem = $verifier->export_key_pem('public');

DESCRIPTION

DSA is primarily a digital signature scheme. In this module, signing and verification are the most common operations and therefore the primary examples.

Legacy function-style wrappers still exist in code for backwards compatibility, but they are intentionally undocumented.

METHODS

new

my $source = Crypt::PK::DSA->new();
$source->generate_key(20, 128);

my $public_der = $source->export_key_der('public');
my $pub = Crypt::PK::DSA->new(\$public_der);

my $private_pem = $source->export_key_pem('private', 'secret', 'AES-256-CBC');
my $priv = Crypt::PK::DSA->new(\$private_pem, 'secret');

Passing $filename or \$buffer to new is equivalent: both forms immediately import the key material into the new object.

generate_key

Uses the bundled chacha20 PRNG via libtomcrypt's rng_make_prng. Returns the object itself (for chaining).

$pk->generate_key($group_size, $modulus_size);
# $group_size  ... [integer] size of the subgroup (q) in bytes; must satisfy: 15 < $group_size < 1024
# $modulus_size .. [integer] size of the prime (p) in bytes; must satisfy: ($modulus_size - $group_size) < 512
#                  $modulus_size must be >= $group_size
#
# The two-integer form uses Perl's usual numeric-to-integer coercion before
# the XS call. Callers should therefore pass exact integers; values like
# C<10.9> or C<"1e1"> will be coerced according to Perl's integer conversion.

### Common parameter pairs (group_size, modulus_size) => security level:
# generate_key(20, 128)   => 80-bit security  (1024-bit p, 160-bit q)
# generate_key(30, 256)   => 120-bit security (2048-bit p, 240-bit q)
# generate_key(32, 256)   => 128-bit security (2048-bit p, 256-bit q)
# generate_key(35, 384)   => ~140-bit security (3072-bit p, 280-bit q)
# 140 bits  => generate_key(35, 384)
# 160 bits  => generate_key(40, 512)

### Sizes according section 4.2 of FIPS 186-4
# (L and N are the bit lengths of p and q respectively)
# L = 1024, N = 160 => generate_key(20, 128)
# L = 2048, N = 224 => generate_key(28, 256)
# L = 2048, N = 256 => generate_key(32, 256)
# L = 3072, N = 256 => generate_key(32, 384)

$pk->generate_key($param_hash)
# $param_hash is { p => $p, q => $q, g => $g }
# where $p, $q, $g are hex strings

$pk->generate_key(\$dsa_param)
# $dsa_param is the content of DER or PEM file with DSA params
# e.g. openssl dsaparam 2048

import_key

Loads private or public key in DER or PEM format.

my $source = Crypt::PK::DSA->new();
$source->generate_key(20, 128);

my $public_der = $source->export_key_der('public');
my $pub = Crypt::PK::DSA->new();
$pub->import_key(\$public_der);

my $private_pem = $source->export_key_pem('private', 'secret', 'AES-256-CBC');
my $priv = Crypt::PK::DSA->new();
$priv->import_key(\$private_pem, 'secret');

The same method also accepts filenames instead of buffers.

Loading private or public keys from a Perl HASH:

$pk->import_key($hashref);

# where $hashref is a key exported via key2hash
$pk->import_key({
  p => "AAF839A764E04D80824B79FA1F0496C093...", #prime modulus
  q => "D05C4CB45F29D353442F1FEC43A6BE2BE8...", #prime divisor
  g => "847E8896D12C9BF18FE283AE7AD58ED7F3...", #generator of a subgroup of order q in GF(p)
  x => "6C801901AC74E2DC714D75A9F6969483CF...", #private key, random  0 < x < q
  y => "8F7604D77FA62C7539562458A63C7611B7...", #public key, where y = g^x mod p
});

Supported key formats:

export_key_der

Returns the key as a binary DER-encoded string.

my $private_der = $pk->export_key_der('private');
#or
my $public_der = $pk->export_key_der('public');

export_key_pem

Returns the key as a PEM-encoded string (ASCII).

my $private_pem = $pk->export_key_pem('private');
#or
my $public_pem = $pk->export_key_pem('public');
#or
my $public_pem = $pk->export_key_pem('public_x509');

With parameter 'public' uses header and footer lines:

-----BEGIN DSA PUBLIC KEY------
-----END DSA PUBLIC KEY------

With parameter 'public_x509' uses header and footer lines:

-----BEGIN PUBLIC KEY------
-----END PUBLIC KEY------

Support for password protected PEM keys

my $private_pem = $pk->export_key_pem('private', $password);
#or
my $private_pem = $pk->export_key_pem('private', $password, $cipher);

# supported ciphers: 'DES-CBC'
#                    'DES-EDE3-CBC'
#                    'SEED-CBC'
#                    'CAMELLIA-128-CBC'
#                    'CAMELLIA-192-CBC'
#                    'CAMELLIA-256-CBC'
#                    'AES-128-CBC'
#                    'AES-192-CBC'
#                    'AES-256-CBC' (DEFAULT)

encrypt

Returns the ciphertext as a binary string.

DSA is usually used for signatures. This helper is available because the underlying library exposes a DSA-based encryption primitive.

my $pk = Crypt::PK::DSA->new($pub_key_filename);
my $ct = $pk->encrypt($message);
#or
my $ct = $pk->encrypt($message, $hash_name);

# $hash_name .. [string] 'SHA1' (DEFAULT), 'SHA256' or any other hash supported by Crypt::Digest

decrypt

Returns the plaintext as a binary string.

my $pk = Crypt::PK::DSA->new($priv_key_filename);
my $pt = $pk->decrypt($ciphertext);

sign_message

Returns the signature as a binary string.

my $pk = Crypt::PK::DSA->new($priv_key_filename);
my $signature = $pk->sign_message($message);
#or
my $signature = $pk->sign_message($message, $hash_name);

# $hash_name .. [string] 'SHA1' (DEFAULT, INSECURE), 'SHA256' or any other hash supported by Crypt::Digest

BEWARE: The $hash_name default is 'SHA1' only for backward compatibility. SHA-1 is vulnerable to practical collision attacks and is not safe for signing messages whose content may be influenced by an attacker. Always pass an explicit hash name such as 'SHA256'. The same applies to "verify_message".

verify_message

Returns 1 if the signature is valid, 0 otherwise.

my $pk = Crypt::PK::DSA->new($pub_key_filename);
my $valid = $pk->verify_message($signature, $message);
#or
my $valid = $pk->verify_message($signature, $message, $hash_name);

# $hash_name .. [string] 'SHA1' (DEFAULT, INSECURE), 'SHA256' or any other hash supported by Crypt::Digest

sign_hash

Returns the signature as a binary string.

my $pk = Crypt::PK::DSA->new($priv_key_filename);
my $signature = $pk->sign_hash($message_hash);

verify_hash

Returns 1 if the signature is valid, 0 otherwise.

my $pk = Crypt::PK::DSA->new($pub_key_filename);
my $valid = $pk->verify_hash($signature, $message_hash);

is_private

my $rv = $pk->is_private;
# 1 .. private key loaded
# 0 .. public key loaded
# undef .. no key loaded

size

my $size = $pk->size;
# returns key size (length of the prime p) in bytes or undef if key not loaded

size_q

my $size = $pk->size_q;
# returns length of the prime q in bytes or undef if key not loaded

key2hash

Returns a hashref with the key components, or undef if no key is loaded.

my $hash = $pk->key2hash;

# returns hash like this (or undef if no key loaded):
{
  type => 1,   # integer: 1 .. private, 0 .. public
  size => 256, # integer: key size in bytes
  # all the rest are hex strings
  p => "AAF839A764E04D80824B79FA1F0496C093...", #prime modulus
  q => "D05C4CB45F29D353442F1FEC43A6BE2BE8...", #prime divisor
  g => "847E8896D12C9BF18FE283AE7AD58ED7F3...", #generator of a subgroup of order q in GF(p)
  x => "6C801901AC74E2DC714D75A9F6969483CF...", #private key, random  0 < x < q
  y => "8F7604D77FA62C7539562458A63C7611B7...", #public key, where y = g^x mod p
}

OpenSSL interoperability

### let's have:
# DSA private key in PEM format - dsakey.priv.pem
# DSA public key in PEM format  - dsakey.pub.pem
# data file to be signed - input.data

Sign by OpenSSL, verify by Crypt::PK::DSA

Create signature (from commandline):

openssl dgst -sha256 -sign dsakey.priv.pem -out input.sha256-dsa.sig input.data

Verify signature (Perl code):

use Crypt::PK::DSA;
use Crypt::Digest 'digest_file';
use Crypt::Misc 'read_rawfile';

my $pkdsa = Crypt::PK::DSA->new("dsakey.pub.pem");
my $signature = read_rawfile("input.sha256-dsa.sig");
my $valid = $pkdsa->verify_hash($signature, digest_file("SHA256", "input.data"));
print $valid ? "SUCCESS" : "FAILURE";

Sign by Crypt::PK::DSA, verify by OpenSSL

Create signature (Perl code):

use Crypt::PK::DSA;
use Crypt::Digest 'digest_file';
use Crypt::Misc 'write_rawfile';

my $pkdsa = Crypt::PK::DSA->new("dsakey.priv.pem");
my $signature = $pkdsa->sign_hash(digest_file("SHA256", "input.data"));
write_rawfile("input.sha256-dsa.sig", $signature);

Verify signature (from commandline):

openssl dgst -sha256 -verify dsakey.pub.pem -signature input.sha256-dsa.sig input.data

Keys generated by Crypt::PK::DSA

Generate keys (Perl code):

use Crypt::PK::DSA;
use Crypt::Misc 'write_rawfile';

my $pkdsa = Crypt::PK::DSA->new;
$pkdsa->generate_key(20, 128);
write_rawfile("dsakey.pub.der",  $pkdsa->export_key_der('public'));
write_rawfile("dsakey.priv.der", $pkdsa->export_key_der('private'));
write_rawfile("dsakey.pub.pem",  $pkdsa->export_key_pem('public_x509'));
write_rawfile("dsakey.priv.pem", $pkdsa->export_key_pem('private'));
write_rawfile("dsakey-passwd.priv.pem", $pkdsa->export_key_pem('private', 'secret'));

Use keys by OpenSSL:

openssl dsa -in dsakey.priv.der -text -inform der
openssl dsa -in dsakey.priv.pem -text
openssl dsa -in dsakey-passwd.priv.pem -text -inform pem -passin pass:secret
openssl dsa -in dsakey.pub.der -pubin -text -inform der
openssl dsa -in dsakey.pub.pem -pubin -text

Keys generated by OpenSSL

Generate keys:

openssl dsaparam -genkey -out dsakey.priv.pem 1024
openssl dsa -in dsakey.priv.pem -out dsakey.priv.der -outform der
openssl dsa -in dsakey.priv.pem -out dsakey.pub.pem -pubout
openssl dsa -in dsakey.priv.pem -out dsakey.pub.der -outform der -pubout
openssl dsa -in dsakey.priv.pem -passout pass:secret -des3 -out dsakey-passwd.priv.pem

Load keys (Perl code):

use Crypt::PK::DSA;

my $pkdsa = Crypt::PK::DSA->new;
$pkdsa->import_key("dsakey.pub.der");
$pkdsa->import_key("dsakey.priv.der");
$pkdsa->import_key("dsakey.pub.pem");
$pkdsa->import_key("dsakey.priv.pem");
$pkdsa->import_key("dsakey-passwd.priv.pem", "secret");

SEE ALSO