File Coverage

blib/lib/Crypt/PK/RSA.pm
Criterion Covered Total %
statement 82 119 68.9
branch 47 90 52.2
condition 7 60 11.6
subroutine 16 19 84.2
pod 5 11 45.4
total 157 299 52.5


line stmt bran cond sub pod time code
1             package Crypt::PK::RSA;
2              
3 8     8   526881 use strict;
  8         10  
  8         228  
4 8     8   44 use warnings;
  8         11  
  8         988  
5             our $VERSION = '0.088_004';
6              
7             require Exporter; our @ISA = qw(Exporter); ### use Exporter 5.57 'import';
8             our %EXPORT_TAGS = ( all => [qw(rsa_encrypt rsa_decrypt rsa_sign_message rsa_verify_message rsa_sign_hash rsa_verify_hash)] );
9             our @EXPORT_OK = ( @{ $EXPORT_TAGS{'all'} } );
10             our @EXPORT = qw();
11              
12 8     8   39 use Carp;
  8         11  
  8         509  
13             $Carp::Internal{(__PACKAGE__)}++;
14 8     8   2283 use CryptX;
  8         14  
  8         276  
15 8     8   2258 use Crypt::Digest qw(digest_data digest_data_b64u);
  8         19  
  8         490  
16 8     8   2423 use Crypt::Misc qw(read_rawfile encode_b64u decode_b64u encode_b64 decode_b64 pem_to_der der_to_pem);
  8         19  
  8         593  
17 8     8   2265 use Crypt::PK;
  8         17  
  8         13189  
18              
19             sub new {
20 113     113 1 1182491 my $self = shift->_new();
21 113 100       1173 return @_ > 0 ? $self->import_key(@_) : $self;
22             }
23              
24             sub export_key_pem {
25 3     3 1 436503 my ($self, $type, $password, $cipher) = @_;
26 3         15 local $SIG{__DIE__} = \&CryptX::_croak;
27             # public_x509 uses the same DER as public, just different PEM header
28 3 100 50     33 my $der_type = ($type || '') eq 'public_x509' ? 'public' : ($type || '');
      50        
29 3         186 my $key = $self->export_key_der($der_type);
30 3 50       8 return unless $key;
31              
32             # PKCS#1 RSAPrivateKey** (PEM header: BEGIN RSA PRIVATE KEY)
33             # PKCS#8 PrivateKeyInfo* (PEM header: BEGIN PRIVATE KEY)
34             # PKCS#8 EncryptedPrivateKeyInfo** (PEM header: BEGIN ENCRYPTED PRIVATE KEY)
35 3 100       11 return der_to_pem($key, "RSA PRIVATE KEY", $password, $cipher) if $type eq 'private';
36              
37             # PKCS#1 RSAPublicKey* (PEM header: BEGIN RSA PUBLIC KEY)
38 2 100       7 return der_to_pem($key, "RSA PUBLIC KEY") if $type eq 'public';
39             # X.509 SubjectPublicKeyInfo** (PEM header: BEGIN PUBLIC KEY)
40 1 50       4 return der_to_pem($key, "PUBLIC KEY") if $type eq 'public_x509';
41             }
42              
43             sub export_key_jwk {
44 0     0 1 0 my ($self, $type, $wanthash) = @_;
45 0         0 local $SIG{__DIE__} = \&CryptX::_croak;
46 0         0 my $kh = $self->key2hash;
47 0 0       0 if ($type eq 'private') {
    0          
48 0 0 0     0 return unless $kh->{N} && $kh->{e} && $kh->{d} && $kh->{p} && $kh->{q} && $kh->{dP} && $kh->{dQ} && $kh->{qP};
      0        
      0        
      0        
      0        
      0        
      0        
49 0         0 for (qw/N e d p q dP dQ qP/) {
50 0 0       0 $kh->{$_} = "0$kh->{$_}" if length($kh->{$_}) % 2;
51             }
52             my $hash = {
53             kty => "RSA",
54             n => encode_b64u(pack("H*", $kh->{N})),
55             e => encode_b64u(pack("H*", $kh->{e})),
56             d => encode_b64u(pack("H*", $kh->{d})),
57             p => encode_b64u(pack("H*", $kh->{p})),
58             q => encode_b64u(pack("H*", $kh->{q})),
59             dp => encode_b64u(pack("H*", $kh->{dP})),
60             dq => encode_b64u(pack("H*", $kh->{dQ})),
61 0         0 qi => encode_b64u(pack("H*", $kh->{qP})),
62             };
63 0 0       0 return $wanthash ? $hash : CryptX::_encode_json($hash);
64             }
65             elsif ($type eq 'public') {
66 0 0 0     0 return unless $kh->{N} && $kh->{e};
67 0         0 for (qw/N e/) {
68 0 0       0 $kh->{$_} = "0$kh->{$_}" if length($kh->{$_}) % 2;
69             }
70             my $hash = {
71             kty => "RSA",
72             n => encode_b64u(pack("H*", $kh->{N})),
73 0         0 e => encode_b64u(pack("H*", $kh->{e})),
74             };
75 0 0       0 return $wanthash ? $hash : CryptX::_encode_json($hash);
76             }
77             }
78              
79             sub export_key_jwk_thumbprint {
80 0     0 1 0 my ($self, $hash_name) = @_;
81 0         0 local $SIG{__DIE__} = \&CryptX::_croak;
82 0   0     0 $hash_name ||= 'SHA256';
83 0         0 my $h = $self->export_key_jwk('public', 1);
84 0         0 my $json = CryptX::_encode_json({kty=>$h->{kty}, n=>$h->{n}, e=>$h->{e}});
85 0         0 return digest_data_b64u($hash_name, $json);
86             }
87              
88             sub import_key {
89 174     174 1 262856 my ($self, $key, $password) = @_;
90 174         876 local $SIG{__DIE__} = \&CryptX::_croak;
91 174 50       496 croak "FATAL: undefined key" unless $key;
92              
93             # special case
94 174 100       406 if (ref($key) eq 'HASH') {
95 3 50 33     12 if ($key->{N} && $key->{e}) {
96             # hash exported via key2hash
97 3         724 return $self->_import_hex($key->{N}, $key->{e}, $key->{d}, $key->{p}, $key->{q}, $key->{dP}, $key->{dQ}, $key->{qP});
98             }
99 0 0 0     0 if ($key->{n} && $key->{e} && $key->{kty} && $key->{kty} eq "RSA") {
      0        
      0        
100 0         0 $key = {%$key}; #make a copy so that the modifications below stay local
101              
102             # hash with items corresponding to JSON Web Key (JWK)
103 0         0 for (qw/n e d p q dp dq qi/) {
104 0 0       0 $key->{$_} = eval { unpack("H*", decode_b64u($key->{$_})) } if exists $key->{$_};
  0         0  
105             }
106 0         0 return $self->_import_hex($key->{n}, $key->{e}, $key->{d}, $key->{p}, $key->{q}, $key->{dp}, $key->{dq}, $key->{qi});
107             }
108 0         0 croak "FATAL: unexpected RSA key hash";
109             }
110              
111 171         231 my $data;
112 171 100       2852 if (ref($key) eq 'SCALAR') {
    50          
113 72         137 $data = $$key;
114             }
115             elsif (-f $key) {
116 99         407 $data = read_rawfile($key);
117             }
118             else {
119 0         0 croak "FATAL: non-existing file '$key'";
120             }
121 171 50       452 croak "FATAL: invalid key data" unless $data;
122              
123 171 100       1912 if ($data =~ /-----BEGIN (RSA PUBLIC|RSA PRIVATE|PUBLIC|PRIVATE|ENCRYPTED PRIVATE) KEY-----(.+?)-----END (RSA PUBLIC|RSA PRIVATE|PUBLIC|PRIVATE|ENCRYPTED PRIVATE) KEY-----/s) {
    100          
    100          
    100          
    50          
    100          
124 53         45192 return $self->_import_pem($data, $password);
125             }
126             elsif ($data =~ /-----BEGIN CERTIFICATE-----(.+?)-----END CERTIFICATE-----/s) {
127 1         133 return $self->_import_pem($data, undef);
128             }
129             elsif ($data =~ /-----BEGIN OPENSSH PRIVATE KEY-----(.+?)-----END OPENSSH PRIVATE KEY-----/s) {
130 12         1265107 return $self->_import_openssh($data, $password);
131             }
132             elsif ($data =~ /---- BEGIN SSH2 PUBLIC KEY ----(.+?)---- END SSH2 PUBLIC KEY ----/s) {
133 6         587 return $self->_import_openssh($data, undef);
134             }
135             elsif ($data =~ /^\s*(\{.*?\})\s*$/s) {
136             # JSON Web Key (JWK) - https://www.rfc-editor.org/rfc/rfc7517
137 0         0 my $json = "$1";
138 0         0 my $h = CryptX::_decode_json($json);
139 0 0 0     0 if ($h && $h->{kty} eq "RSA") {
140 0         0 for (qw/n e d p q dp dq qi/) {
141 0 0       0 $h->{$_} = eval { unpack("H*", decode_b64u($h->{$_})) } if exists $h->{$_};
  0         0  
142             }
143 0 0 0     0 return $self->_import_hex($h->{n}, $h->{e}, $h->{d}, $h->{p}, $h->{q}, $h->{dp}, $h->{dq}, $h->{qi}) if $h->{n} && $h->{e};
144             }
145             }
146             elsif ($data =~ /ssh-rsa\s+(\S+)/) {
147 6         82 $data = decode_b64("$1");
148 6         33 my ($typ, $e, $N) = Crypt::PK::_ssh_parse($data);
149 6 50 33     1577 return $self->_import_hex(unpack("H*", $N), unpack("H*", $e)) if $typ && $e && $N && $typ eq 'ssh-rsa';
      33        
      33        
150             }
151             else {
152             # DER format
153 93   33     113 my $rv = eval { $self->_import($data) } || eval { $self->_import_pkcs8($data, $password) } || eval { $self->_import_x509($data) };
154 93 50       661 return $rv if $rv;
155             }
156              
157 0         0 croak "FATAL: invalid or unsupported RSA key format";
158             }
159              
160             ### FUNCTIONS
161              
162             sub rsa_encrypt { # legacy/obsolete
163 2     2 0 2053787 my $key = shift;
164 2         11 local $SIG{__DIE__} = \&CryptX::_croak;
165 2 100       10 $key = __PACKAGE__->new($key) unless ref $key;
166 2 100       234 croak "FATAL: invalid 'key' param" unless ref($key) eq __PACKAGE__;
167 1         201 return $key->encrypt(@_);
168             }
169              
170             sub rsa_decrypt { # legacy/obsolete
171 1     1 0 284 my $key = shift;
172 1         4 local $SIG{__DIE__} = \&CryptX::_croak;
173 1 50       41 $key = __PACKAGE__->new($key) unless ref $key;
174 1 50       4 croak "FATAL: invalid 'key' param" unless ref($key) eq __PACKAGE__;
175 1         5116 return $key->decrypt(@_);
176             }
177              
178             sub rsa_sign_hash { # legacy/obsolete
179 1     1 0 263 my $key = shift;
180 1         5 local $SIG{__DIE__} = \&CryptX::_croak;
181 1 50       7 $key = __PACKAGE__->new($key) unless ref $key;
182 1 50       3 croak "FATAL: invalid 'key' param" unless ref($key) eq __PACKAGE__;
183 1         5101 return $key->sign_hash(@_);
184             }
185              
186             sub rsa_verify_hash { # legacy/obsolete
187 1     1 0 290 my $key = shift;
188 1         5 local $SIG{__DIE__} = \&CryptX::_croak;
189 1 50       8 $key = __PACKAGE__->new($key) unless ref $key;
190 1 50       5 croak "FATAL: invalid 'key' param" unless ref($key) eq __PACKAGE__;
191 1         246 return $key->verify_hash(@_);
192             }
193              
194             sub rsa_sign_message { # legacy/obsolete
195 2     2 0 761 my $key = shift;
196 2         9 local $SIG{__DIE__} = \&CryptX::_croak;
197 2 100       11 $key = __PACKAGE__->new($key) unless ref $key;
198 2 100       118 croak "FATAL: invalid 'key' param" unless ref($key) eq __PACKAGE__;
199 1         5098 return $key->sign_message(@_);
200             }
201              
202             sub rsa_verify_message { # legacy/obsolete
203 1     1 0 274 my $key = shift;
204 1         5 local $SIG{__DIE__} = \&CryptX::_croak;
205 1 50       8 $key = __PACKAGE__->new($key) unless ref $key;
206 1 50       3 croak "FATAL: invalid 'key' param" unless ref($key) eq __PACKAGE__;
207 1         322 return $key->verify_message(@_);
208             }
209              
210 0     0     sub CLONE_SKIP { 1 } # prevent cloning
211              
212             1;
213              
214             =pod
215              
216             =head1 NAME
217              
218             Crypt::PK::RSA - Public key cryptography based on RSA
219              
220             =head1 SYNOPSIS
221              
222             ### OO interface
223              
224             my $message = 'hello world';
225             my $private_key = Crypt::PK::RSA->new();
226             $private_key->generate_key(256, 65537);
227              
228             my $public_der = $private_key->export_key_der('public');
229             my $public_key = Crypt::PK::RSA->new(\$public_der);
230              
231             my $ciphertext = $public_key->encrypt($message);
232             my $plaintext = $private_key->decrypt($ciphertext);
233              
234             my $signature = $private_key->sign_message($message);
235             $public_key->verify_message($signature, $message) or die "ERROR";
236              
237             my $private_der = $private_key->export_key_der('private');
238             my $private_pem = $private_key->export_key_pem('private');
239             my $public_pem = $public_key->export_key_pem('public');
240              
241             =head1 DESCRIPTION
242              
243             The module provides a full featured RSA implementation.
244              
245             Legacy function-style wrappers still exist in code for backwards compatibility,
246             but they are intentionally undocumented.
247              
248             =head1 METHODS
249              
250             =head2 new
251              
252             my $source = Crypt::PK::RSA->new();
253             $source->generate_key(256, 65537);
254              
255             my $public_der = $source->export_key_der('public');
256             my $pub = Crypt::PK::RSA->new(\$public_der);
257              
258             my $private_pem = $source->export_key_pem('private', 'secret', 'AES-256-CBC');
259             my $priv = Crypt::PK::RSA->new(\$private_pem, 'secret');
260              
261             Passing C<$filename> or C<\$buffer> to C is equivalent: both forms
262             immediately import the key material into the new object.
263              
264             =head2 generate_key
265              
266             Uses the bundled C PRNG via libtomcrypt's C.
267             Returns the object itself (for chaining).
268              
269             $pk->generate_key($size, $e);
270             # $size .. [integer] key size: 128-512 bytes (DEFAULT is 256)
271             # $e ..... [integer] exponent: 3, 17, 257 or 65537 (DEFAULT is 65537)
272              
273             The C<$size> and C<$e> arguments use Perl's usual numeric-to-integer coercion
274             before reaching the XS layer. Pass exact integers; values like C<10.9> or
275             C<"1e2"> are coerced rather than rejected.
276              
277             =head2 import_key
278              
279             Loads private or public key in DER or PEM format.
280              
281             my $source = Crypt::PK::RSA->new();
282             $source->generate_key(256, 65537);
283              
284             my $public_der = $source->export_key_der('public');
285             my $pub = Crypt::PK::RSA->new();
286             $pub->import_key(\$public_der);
287              
288             my $private_pem = $source->export_key_pem('private', 'secret', 'AES-256-CBC');
289             my $priv = Crypt::PK::RSA->new();
290             $priv->import_key(\$private_pem, 'secret');
291              
292             The same method also accepts filenames instead of buffers.
293              
294             Loading private or public keys form perl hash:
295              
296             $pk->import_key($hashref);
297              
298             # the $hashref is either a key exported via key2hash
299             $pk->import_key({
300             e => "10001", #public exponent
301             d => "9ED5C3D3F866E06957CA0E9478A273C39BBDA4EEAC5B...", #private exponent
302             N => "D0A5CCCAE03DF9C2F5C4C8C0CE840D62CDE279990DC6...", #modulus
303             p => "D3EF0028FFAB508E2773C659E428A80FB0E9211346B4...", #p factor of N
304             q => "FC07E46B163CAB6A83B8E467D169534B2077DCDEECAE...", #q factor of N
305             qP => "88C6D406F833DF73C8B734548E0385261AD51F4187CF...", #1/q mod p CRT param
306             dP => "486F142FEF0A1F53269AC43D2EE4D263E2841B60DA36...", #d mod (p - 1) CRT param
307             dQ => "4597284B2968B72C4212DB7E8F24360B987B80514DA9...", #d mod (q - 1) CRT param
308             });
309              
310             # or a hash with items corresponding to JWK (JSON Web Key)
311             $pk->import_key({
312             {
313             kty => "RSA",
314             n => "0vx7agoebGcQSuuPiLJXZpt...eZu0fM4lFd2NcRwr3XPksINHaQ-G_xBniIqbw0Ls1jF44-csFCur-kEgU8awapJzKnqDKgw",
315             e => "AQAB",
316             d => "X4cTteJY_gn4FYPsXB8rdXi...FLN5EEaG6RoVH-HLKD9Mdx5ooGURknhnrRwUkC7h5fJLMWbFAKLWY2v7B6NqSzUvx0_YSf",
317             p => "83i-7IvMGXoMXCskv73TKr8...Z27zvoj6pbUQyLPBQxtPnwD20-60eTmD2ujMt5PoMrm8RmNhVWtjjMmMjOpSicFHjXOuVI",
318             q => "3dfOR9cuYq-0S-mkFLzgItg...q3hWeMuG0ouqnb3obLyuqjVZQ1dIrdgTnCdYzBcOW5r37AFXjift_NGiovonzhKpoVVS78",
319             dp => "G4sPXkc6Ya9y8oJW9_ILj4...zi_H7TkS8x5SdX3oE0oiYwxIiemTAu0UOa5pgFGyJ4c8t2VF40XRugKTP8akhFo5tA77Qe",
320             dq => "s9lAH9fggBsoFR8Oac2R_E...T2kGOhvIllTE1efA6huUvMfBcpn8lqW6vzzYY5SSF7pMd_agI3G8IbpBUb0JiraRNUfLhc",
321             qi => "GyM_p6JrXySiz1toFgKbWV...4ypu9bMWx3QJBfm0FoYzUIZEVEcOqwmRN81oDAaaBk0KWGDjJHDdDmFW3AN7I-pux_mHZG",
322             });
323              
324             Supported key formats:
325              
326             # all formats can be loaded from a file
327             my $pk = Crypt::PK::RSA->new($filename);
328              
329             # or from a buffer containing the key
330             my $pk = Crypt::PK::RSA->new(\$buffer_with_key);
331              
332             =over
333              
334             =item * RSA public keys
335              
336             -----BEGIN PUBLIC KEY-----
337             MIGfMA0GCSqGSIb3DQEBAQUAA4GNADCBiQKBgQDHlYKg9DeHB3/dY1D9WCyJTnl5
338             vEzAXpUOL9tDtdPUl96brIbbdMLooO1hKjsq98kLs1q4vOn/pxvzk0BRwhiu7Vvb
339             VUjAn/2HHDDL0U1utqqlMJhaffeLI3HEq5o/lSMFY7sSkZU/E4YX1yqAN0SE7xfK
340             B2uzcNq60sMIfp6siQIDAQAB
341             -----END PUBLIC KEY-----
342              
343             =item * RSA private keys
344              
345             -----BEGIN RSA PRIVATE KEY-----
346             MIICXQIBAAKBgQDHlYKg9DeHB3/dY1D9WCyJTnl5vEzAXpUOL9tDtdPUl96brIbb
347             dMLooO1hKjsq98kLs1q4vOn/pxvzk0BRwhiu7VvbVUjAn/2HHDDL0U1utqqlMJha
348             ffeLI3HEq5o/lSMFY7sSkZU/E4YX1yqAN0SE7xfKB2uzcNq60sMIfp6siQIDAQAB
349             AoGBAI5+GgNcGQDYw9uF+t7FwxZM5sGZRJrbbEPyuvL+sDxKKW6voKCyHi4EJzaF
350             9jRZMDqgVJcsmUwjPPuMGBHHJ+MI5Zb3L0jbZkyx8u+U5gf88oy9eZmfGOjmHcMB
351             oCgzyoLmJETuyADg2onLanuY3jggFb3tq/jimKjO8xM2R6zhAkEA7uXWWyJI9cCN
352             zrVt5R5v6oosjZ4r5VILGMqBRLrzfTvH+WDMK6Rl/2MHE+YDeLajzunaM8qY2456
353             GTYEXQsIdQJBANXfMEtXocSdPtoVj3ME8Do/0r+ApgTdcDPCwXOzkmkEJW/UFMSn
354             b8CYF5G6sZQN9L5z3s2nvi55PaFV8Q0LMUUCQBh9GvIQm6YFbQPpeTBpZFOIgnSp
355             6BoDxPtvlryy5U7LF/6qO4OlwIbjYdBaXbS8FCKbujBg7jZjboSzEtNu1BkCQDGT
356             w0Yz0jQZn3A+fzpScr2N/fSWheWqz0+wXdfMUKw3YdZCe236wlUK7KvDc1a2xX1A
357             ru1NbTCoujikC3TSm2ECQQDKQshchJlZJmFv9vCFQlGCA/EX+4406xvOOiixbPYC
358             pIB4Ee2cmvEdAqSaOjrvgs5zvaCCFBO0MecPStCAxUX6
359             -----END RSA PRIVATE KEY-----
360              
361             =item * RSA private keys in password protected PEM format
362              
363             -----BEGIN RSA PRIVATE KEY-----
364             Proc-Type: 4,ENCRYPTED
365             DEK-Info: DES-EDE3-CBC,4D697440FF5AEF18
366              
367             C09H49Gn99o8b8O2r4+Hqao4r3udvC+QSSfsk20sXatyuZSEmbhyqKAB+13NRj+3
368             KIsRTqnL9VkeibIGgLHuekOFKAqeSVZ0PmR4bGWEFxUPAYUvg9N9pIa6hGtNZG+y
369             TEpOAfFITb1pbHQhp3j8y7qmKc5kY5LrZSFE8WwA24NTG773E07wJgRxKDkXNGOl
370             kki6oYArNEps0DdtHFxzgdRg0+yaotXuFJRuC5V4YzKGG/oSRcgYyXKTwCndb3xt
371             aHgI2WprQAPg+qOpLABzoi7bEjCqbHWrwkvnAngylbim2Uyvw1e1xKnzlgIHU7pv
372             e/J+s00pTItfqW1IpY2mh4C9nkfkfVKBKaAv7jO0s6aPySATqsdlrzv2kpF6Ub4J
373             kgaZDOfZ4K3qkyAYVLWcQeDqg4glv9Ah2J05bTm4qrIMmthYnThyQlGvcjUfCMXs
374             0t+mEQbsRY7xKt0o6HzzvQlJ+JsFlLORoslAubJX9iLqpEdnlrj1lD9bo6uIClZ5
375             5+aoLcAyz1D4OsauuP5i8VFu+Is+QG4SN/vHVuArjkqi3VpLwSAjNDY+KWbq042l
376             CqlM2mwm6FIGUZQFxiLHJD7WDmk1xmae++m+XG9CEDTfrUQ5v+l0O6BTrl80XUfU
377             w3gzAWbSjz3UK0FpKeABVFPE9fjNP9fTcS6qL5YJWBPflwxCAbVgsBOW4bOMpDGK
378             BJDQTeShWn4BlYCe/vgThI9ERdgZhRz4NcFeDgVA/CqQzVqptvz4PSqH46fqUN2n
379             4PtJgKE5cASYUBuAjlD71FecSVVM/OTzL1uxYzXBilzvVn2vSHgo9g==
380             -----END RSA PRIVATE KEY-----
381              
382             =item * PKCS#8 encoded private keys
383              
384             -----BEGIN PRIVATE KEY-----
385             MIICdgIBADANBgkqhkiG9w0BAQEFAASCAmAwggJcAgEAAoGBANPN17xW4EkH5PXG
386             1i/i3rE1EXFcCHyxmz95VRBDs1p3MuYf9mxntbfYAmuzS3KrRWh3IyX/Eh80N/v9
387             OXPlwZbVqSTX+L3pCEJtRtsWn0zmswGThjMZiwle0oWuap63L35F1QN8EDaSPSBC
388             yGELNRr6rwVYq0w5b+LOcaCZ+/H1AgMBAAECgYEApfu3aGpww+rC3HUhX0+ckyTy
389             cXLdV9LbxidwqRlVEb0+DyfXNucjelp2sy5EHy3na9GJovo8mmWSxhCRGKliRkQ6
390             XgrEMZdCSaWI2AazuHAGlUJRFEVkvdla3AuBAn6y0YdDp/3kbg0yahmKyD8Gq74z
391             nUYbDL3R5JtR2Ad/KlUCQQDvSEICTHbO/BF7hVmlKRYZSNHKEPrv8X/OlppS14Kv
392             QRwc+CZ5+l6T1Y+l5cHJQUXrXZoWS1K741TXdUhjjUd7AkEA4pod804Ex8sttdWi
393             pHMfeyj+IbPAk5XnBc91jT7AYIeL8ccjtfl99xhMsGFaxrh3wA/4SGEvwzWkbxcq
394             H8G5TwJAKNG+0P2SVwURRm0dOdukdXPCtiHnbP9Zujhe4zr4hEUrMpXymmRntfh8
395             pORpBpgoAVraams3Fe5WDttnGfSD+QJAOOC6V9HjfUrQhG3FT0XeRwm5EDiQQ/tC
396             a8DxHqz7mL8tL1ju68ReC+G7jiJBqNOwqzLW/UP3uyYByiikWChGHQJAHUau7jIM
397             45ErO096n94Vh95p76ANxOroWszOt39TyvJOykIfoPwFagLrBWV9Jjos2/D54KE+
398             fyoy4t3yHT+/nw==
399             -----END PRIVATE KEY-----
400              
401             =item * PKCS#8 encrypted private keys - password protected keys (supported since: CryptX-0.062)
402              
403             -----BEGIN ENCRYPTED PRIVATE KEY-----
404             MIICojAcBgoqhkiG9w0BDAEDMA4ECCQk+Rr1yzzcAgIIAASCAoD/mgpUFjxxM/Ty
405             Yt+NeT0Fo4echgoGksqs6+rYhO16oshG664emZfkuNoFGGzJ38X6GVuqIXhlPnYQ
406             biKvL37dN/KnoGytFHq9Wnk8dDwjGHPtwajhW5WuIV3NuhW/AO1PF/cRZKFjWrPt
407             NWY5CrpfH6t6zojoe+5uyXpH29lQy4OqvSRdPIt/12UcB+tzV7XzSWEuXh8HAi8a
408             sYUu6tuCFnq4GrD2ffM4KWFmL5GqBAwN6m0KkyrNni9XT+RaA6zEhv/lVcwg2esa
409             4/EzRs0ixzzZDKaml8oCMl9RHtFAbQmdlfV7Ip4rGK9BwY6UFiDMIVru6HynOVQK
410             vvZ+j//bgO+3ubrv7psX+vC9Fy/MoH2Tc7MIwDN/QVTciPZlzjWBnBNxMfeFKtEn
411             d7NFiapgfLuRQIiDTMrW/clcqvO54NphxhrcgUEoxos4twKZARntqPZHtf8nEM2x
412             2sEF5kI65aEF/5Yy16qvP0vZAA2B1kcIdXZ8XLZCp4c3olhkIrmgUpo1gyFXdCoC
413             7dT5Cz7/YLkq5hkcFrtp4V9BZMR24fSttc4p24N5xuZ+JneGnGkLX6B+nJAtm9vw
414             bZA6P+23GI0qeMzL3HJXwCOTSsWfm/H9W5+2Zmw851aAmE+pZLni/pk3e3iNSWgs
415             946x/doA5O0uCFsU7oxme+WAIp2SjhxGoe808Lf1CCFMPboFi1O/E0NsX8SIEX+i
416             U+UHi4kxZqVkr3Q5SB/9kiSv8K1bE787yueQOT/dsTYYaMsjAbkEZo0o/47F32T6
417             A2ioXHOV/pr5zNHqE5tL+qKEcLYbAUF1O+WvmdqYz+vHQjRQBatAqTmncvLDYr/j
418             1HPwZX2d
419             -----END ENCRYPTED PRIVATE KEY-----
420              
421             =item * RSA public key from X509 certificate
422              
423             -----BEGIN CERTIFICATE-----
424             MIIC8zCCAdugAwIBAgIJAPi+LvMU3uGWMA0GCSqGSIb3DQEBCwUAMBAxDjAMBgNV
425             BAMMBXBva3VzMB4XDTE3MDcxNDE0MTAyMFoXDTIwMDQwOTE0MTAyMFowEDEOMAwG
426             A1UEAwwFcG9rdXMwggEiMA0GCSqGSIb3DQEBAQUAA4IBDwAwggEKAoIBAQDCQima
427             SUIMIdz5uVevzcScbcj06xs1OLaFKUoPJ8v+xP6Ut61BQhAvc8GYuw2uRx223hZC
428             r3HYLfSdWIfmOIAtlL8cPYPVoSivJtpSGE6fBG1tlBjVgXWRmJGR/oxx6Y5QDwcB
429             Q4GZKga8TtHQoY5idZuatYOFZGfMIcIUC0Uoda+YSypnw7A90F/JvlpcTUh3Fnem
430             VinqEA6XOegU9dCZk/29sXqauBjbdGihh8DvpklOhY16eQoiR3909AywQ0KUmI+R
431             Sa9E8oIsmUDetFuXEvana+sD3y42tU+cd2nhBPRETbSXPcum0B3uF4yKgweuJy5D
432             cvtVQIFVkkh4+AWNAgMBAAGjUDBOMB0GA1UdDgQWBBSS6V5PVGyN92NoB0AVLcOb
433             pzR3SzAfBgNVHSMEGDAWgBSS6V5PVGyN92NoB0AVLcObpzR3SzAMBgNVHRMEBTAD
434             AQH/MA0GCSqGSIb3DQEBCwUAA4IBAQBIszrBjoJ39axsS6Btbvwvo8vAmgiSWsav
435             7AmjXOAwknHPaCcDmrdOys5POD0DNRwNeRsnxFiZ/UL8Vmj2JGDLgAw+/v32MwfX
436             Ig7m+oIbO8KqDzlYvS5kd3suJ5C21hHy1/JUtfofZLovZH7ZRzhTAoRvCYaodW90
437             2o8ZqmyCdcXPzjFmoJ2xYzs/Sf8/E1cHfb+4HjOpeRnKxDvG0gwWzcsXpUrw2pNO
438             Oztj6Rd0THNrf/anIeYVtAHX4aqZA8Kbv2TyJd+9g78usFw1cn+8vfmilm6Pn0DQ
439             a+I5GyGd7BJI8wYuWqIStzvrJHbQQaNrSk7hgjWYiYlcsPh6w2QP
440             -----END CERTIFICATE-----
441              
442             =item * SSH public RSA keys
443              
444             ssh-rsa AAAAB3NzaC1yc2EAAAADAQA...6mdYs5iJNGu/ltUdc=
445              
446             =item * SSH public RSA keys (RFC-4716 format)
447              
448             ---- BEGIN SSH2 PUBLIC KEY ----
449             Comment: "768-bit RSA, converted from OpenSSH"
450             AAAAB3NzaC1yc2EAAAADAQABAAAAYQDYebeGQFCnlQiNRE7r9UEbjr+DQMTdw1ZHGB2w6x
451             D/DzKem8761GdCpqsLrGaw2D7aSIoP1B5Sz870YoVWHn6Ao7Hvm17V3Kxfn4B01GNQTM5+
452             L26mdYs5iJNGu/ltUdc=
453             ---- END SSH2 PUBLIC KEY ----
454              
455             =item * RSA private keys in JSON Web Key (JWK) format
456              
457             See L
458              
459             {
460             "kty":"RSA",
461             "n":"0vx7agoebGcQSuuPiLJXZpt...eZu0fM4lFd2NcRwr3XPksINHaQ-G_xBniIqbw0Ls1jF44-csFCur-kEgU8awapJzKnqDKgw",
462             "e":"AQAB",
463             "d":"X4cTteJY_gn4FYPsXB8rdXi...FLN5EEaG6RoVH-HLKD9Mdx5ooGURknhnrRwUkC7h5fJLMWbFAKLWY2v7B6NqSzUvx0_YSf",
464             "p":"83i-7IvMGXoMXCskv73TKr8...Z27zvoj6pbUQyLPBQxtPnwD20-60eTmD2ujMt5PoMrm8RmNhVWtjjMmMjOpSicFHjXOuVI",
465             "q":"3dfOR9cuYq-0S-mkFLzgItg...q3hWeMuG0ouqnb3obLyuqjVZQ1dIrdgTnCdYzBcOW5r37AFXjift_NGiovonzhKpoVVS78",
466             "dp":"G4sPXkc6Ya9y8oJW9_ILj4...zi_H7TkS8x5SdX3oE0oiYwxIiemTAu0UOa5pgFGyJ4c8t2VF40XRugKTP8akhFo5tA77Qe",
467             "dq":"s9lAH9fggBsoFR8Oac2R_E...T2kGOhvIllTE1efA6huUvMfBcpn8lqW6vzzYY5SSF7pMd_agI3G8IbpBUb0JiraRNUfLhc",
468             "qi":"GyM_p6JrXySiz1toFgKbWV...4ypu9bMWx3QJBfm0FoYzUIZEVEcOqwmRN81oDAaaBk0KWGDjJHDdDmFW3AN7I-pux_mHZG",
469             }
470              
471             B For JWK support you need to have L module installed.
472              
473             =item * RSA public keys in JSON Web Key (JWK) format
474              
475             {
476             "kty":"RSA",
477             "n": "0vx7agoebGcQSuuPiLJXZp...tN9nndrQmbXEps2aiAFbWhM78LhWx4cbbfAAtVT86zwu1RK7aPFFxuhDR1L6tSoc_BJECP",
478             "e":"AQAB",
479             }
480              
481             B For JWK support you need to have L module installed.
482              
483             =back
484              
485             =head2 export_key_der
486              
487             Returns the key as a binary DER-encoded string.
488              
489             my $private_der = $pk->export_key_der('private');
490             #or
491             my $public_der = $pk->export_key_der('public');
492              
493             =head2 export_key_pem
494              
495             Returns the key as a PEM-encoded string (ASCII).
496              
497             my $private_pem = $pk->export_key_pem('private');
498             #or
499             my $public_pem = $pk->export_key_pem('public');
500             #or
501             my $public_pem = $pk->export_key_pem('public_x509');
502              
503             With parameter C<'public'> uses header and footer lines:
504              
505             -----BEGIN RSA PUBLIC KEY------
506             -----END RSA PUBLIC KEY------
507              
508             With parameter C<'public_x509'> uses header and footer lines:
509              
510             -----BEGIN PUBLIC KEY------
511             -----END PUBLIC KEY------
512              
513             Support for password protected PEM keys
514              
515             my $private_pem = $pk->export_key_pem('private', $password);
516             #or
517             my $private_pem = $pk->export_key_pem('private', $password, $cipher);
518              
519             # supported ciphers: 'DES-CBC'
520             # 'DES-EDE3-CBC'
521             # 'SEED-CBC'
522             # 'CAMELLIA-128-CBC'
523             # 'CAMELLIA-192-CBC'
524             # 'CAMELLIA-256-CBC'
525             # 'AES-128-CBC'
526             # 'AES-192-CBC'
527             # 'AES-256-CBC' (DEFAULT)
528              
529             =head2 export_key_jwk
530              
531             I
532              
533             Returns a JSON string, or a hashref if the optional second argument is true.
534              
535             Exports public/private keys as a JSON Web Key (JWK).
536              
537             my $private_json_text = $pk->export_key_jwk('private');
538             #or
539             my $public_json_text = $pk->export_key_jwk('public');
540              
541             Also exports public/private keys as a perl HASH with JWK structure.
542              
543             my $jwk_hash = $pk->export_key_jwk('private', 1);
544             #or
545             my $jwk_hash = $pk->export_key_jwk('public', 1);
546              
547             B For JWK support you need to have L module installed.
548              
549             =head2 export_key_jwk_thumbprint
550              
551             I
552              
553             Exports the key's JSON Web Key Thumbprint as a string.
554              
555             If you don't know what this is, see RFC 7638 L.
556              
557             my $thumbprint = $pk->export_key_jwk_thumbprint('SHA256');
558              
559             =head2 encrypt
560              
561             Returns the ciphertext as a binary string.
562              
563             my $pk = Crypt::PK::RSA->new($pub_key_filename);
564             my $ct = $pk->encrypt($message);
565             #or
566             my $ct = $pk->encrypt($message, $padding);
567             #or
568             my $ct = $pk->encrypt($message, 'oaep', $hash_name, $lparam);
569              
570             # $padding .................... [string] 'oaep' (DEFAULT), 'v1.5' or 'none' (INSECURE)
571             # $hash_name (only for oaep) .. [string] 'SHA1' (DEFAULT), 'SHA256' or any other hash supported by Crypt::Digest
572             # $lparam (only for oaep) ..... [binary string] DEFAULT is empty string
573              
574             =head2 decrypt
575              
576             Returns the plaintext as a binary string.
577              
578             my $pk = Crypt::PK::RSA->new($priv_key_filename);
579             my $pt = $pk->decrypt($ciphertext);
580             #or
581             my $pt = $pk->decrypt($ciphertext, $padding);
582             #or
583             my $pt = $pk->decrypt($ciphertext, 'oaep', $hash_name, $lparam);
584              
585             # $padding .................... [string] 'oaep' (DEFAULT), 'v1.5' or 'none' (INSECURE)
586             # $hash_name (only for oaep) .. [string] 'SHA1' (DEFAULT), 'SHA256' or any other hash supported by Crypt::Digest
587             # $lparam (only for oaep) ..... [binary string] DEFAULT is empty string
588              
589             =head2 sign_message
590              
591             Returns the signature as a binary string.
592              
593             my $pk = Crypt::PK::RSA->new($priv_key_filename);
594             my $signature = $pk->sign_message($message);
595             #or
596             my $signature = $pk->sign_message($message, $hash_name);
597             #or
598             my $signature = $pk->sign_message($message, $hash_name, $padding);
599             #or
600             my $signature = $pk->sign_message($message, $hash_name, 'pss', $saltlen);
601              
602             # $hash_name ............... [string] 'SHA1' (DEFAULT), 'SHA256' or any other hash supported by Crypt::Digest
603             # $padding ................. [string] 'pss' (DEFAULT) or 'v1.5' or 'none' (INSECURE)
604             # $saltlen (only for pss) .. [integer] DEFAULT is 12
605              
606             =head2 verify_message
607              
608             Returns C<1> if the signature is valid, C<0> otherwise.
609              
610             my $pk = Crypt::PK::RSA->new($pub_key_filename);
611             my $valid = $pk->verify_message($signature, $message);
612             #or
613             my $valid = $pk->verify_message($signature, $message, $hash_name);
614             #or
615             my $valid = $pk->verify_message($signature, $message, $hash_name, $padding);
616             #or
617             my $valid = $pk->verify_message($signature, $message, $hash_name, 'pss', $saltlen);
618              
619             # $hash_name ............... [string] 'SHA1' (DEFAULT), 'SHA256' or any other hash supported by Crypt::Digest
620             # $padding ................. [string] 'pss' (DEFAULT) or 'v1.5' or 'none' (INSECURE)
621             # $saltlen (only for pss) .. [integer] DEFAULT is 12
622              
623             =head2 sign_hash
624              
625             Returns the signature as a binary string.
626              
627             my $pk = Crypt::PK::RSA->new($priv_key_filename);
628             my $signature = $pk->sign_hash($message_hash);
629             #or
630             my $signature = $pk->sign_hash($message_hash, $hash_name);
631             #or
632             my $signature = $pk->sign_hash($message_hash, $hash_name, $padding);
633             #or
634             my $signature = $pk->sign_hash($message_hash, $hash_name, 'pss', $saltlen);
635             #or
636             my $signature = $pk->sign_hash($message_hash, $hash_name, 'pss', $saltlen, $mgf_hash_name);
637              
638             # $hash_name ................. [string] 'SHA1' (DEFAULT), 'SHA256' or any other hash supported by Crypt::Digest
639             # $padding ................... [string] 'pss' (DEFAULT) or 'v1.5' or 'none' (INSECURE)
640             # $saltlen (pss only) ........ [integer] DEFAULT is 12
641             # $mgf_hash_name (pss only) .. [string] MGF hash function name (DEFAULT: the $hash_name value)
642              
643             =head2 verify_hash
644              
645             Returns C<1> if the signature is valid, C<0> otherwise.
646              
647             my $pk = Crypt::PK::RSA->new($pub_key_filename);
648             my $valid = $pk->verify_hash($signature, $message_hash);
649             #or
650             my $valid = $pk->verify_hash($signature, $message_hash, $hash_name);
651             #or
652             my $valid = $pk->verify_hash($signature, $message_hash, $hash_name, $padding);
653             #or
654             my $valid = $pk->verify_hash($signature, $message_hash, $hash_name, 'pss', $saltlen);
655             #or
656             my $valid = $pk->verify_hash($signature, $message_hash, $hash_name, 'pss', $saltlen, $mgf_hash_name);
657              
658             # $hash_name ................. [string] 'SHA1' (DEFAULT), 'SHA256' or any other hash supported by Crypt::Digest
659             # $padding ................... [string] 'pss' (DEFAULT) or 'v1.5' or 'none' (INSECURE)
660             # $saltlen (pss only) ........ [integer] DEFAULT is 12
661             # $mgf_hash_name (pss only) .. [string] MGF hash function name (DEFAULT: the $hash_name value)
662              
663             =head2 is_private
664              
665             my $rv = $pk->is_private;
666             # 1 .. private key loaded
667             # 0 .. public key loaded
668             # undef .. no key loaded
669              
670             =head2 size
671              
672             my $size = $pk->size;
673             # returns key size in bytes or undef if no key loaded
674              
675             =head2 key2hash
676              
677             Returns a hashref with the key components, or C if no key is loaded.
678              
679             my $hash = $pk->key2hash;
680              
681             # returns hash like this (or undef if no key loaded):
682             {
683             type => 1, # integer: 1 .. private, 0 .. public
684             size => 256, # integer: key size in bytes
685             # all the rest are hex strings
686             e => "10001", #public exponent
687             d => "9ED5C3D3F866E06957CA0E9478A273C39BBDA4EEAC5B...", #private exponent
688             N => "D0A5CCCAE03DF9C2F5C4C8C0CE840D62CDE279990DC6...", #modulus
689             p => "D3EF0028FFAB508E2773C659E428A80FB0E9211346B4...", #p factor of N
690             q => "FC07E46B163CAB6A83B8E467D169534B2077DCDEECAE...", #q factor of N
691             qP => "88C6D406F833DF73C8B734548E0385261AD51F4187CF...", #1/q mod p CRT param
692             dP => "486F142FEF0A1F53269AC43D2EE4D263E2841B60DA36...", #d mod (p - 1) CRT param
693             dQ => "4597284B2968B72C4212DB7E8F24360B987B80514DA9...", #d mod (q - 1) CRT param
694             }
695              
696             =head1 OpenSSL interoperability
697              
698             ### let's have:
699             # RSA private key in PEM format - rsakey.priv.pem
700             # RSA public key in PEM format - rsakey.pub.pem
701             # data file to be signed or encrypted - input.data
702              
703             =head2 Encrypt by OpenSSL, decrypt by Crypt::PK::RSA
704              
705             Create encrypted file (from commandline):
706              
707             openssl rsautl -encrypt -inkey rsakey.pub.pem -pubin -out input.encrypted.rsa -in input.data
708              
709             Decrypt file (Perl code):
710              
711             use Crypt::PK::RSA;
712             use Crypt::Misc 'read_rawfile';
713              
714             my $pkrsa = Crypt::PK::RSA->new("rsakey.priv.pem");
715             my $encfile = read_rawfile("input.encrypted.rsa");
716             my $plaintext = $pkrsa->decrypt($encfile, 'v1.5');
717             print $plaintext;
718              
719             =head2 Encrypt by Crypt::PK::RSA, decrypt by OpenSSL
720              
721             Create encrypted file (Perl code):
722              
723             use Crypt::PK::RSA;
724             use Crypt::Misc 'write_rawfile';
725              
726             my $plaintext = 'secret message';
727             my $pkrsa = Crypt::PK::RSA->new("rsakey.pub.pem");
728             my $encrypted = $pkrsa->encrypt($plaintext, 'v1.5');
729             write_rawfile("input.encrypted.rsa", $encrypted);
730              
731             Decrypt file (from commandline):
732              
733             openssl rsautl -decrypt -inkey rsakey.priv.pem -in input.encrypted.rsa
734              
735             =head2 Sign by OpenSSL, verify by Crypt::PK::RSA
736              
737             Create signature (from commandline):
738              
739             openssl dgst -sha1 -sign rsakey.priv.pem -out input.sha1-rsa.sig input.data
740              
741             Verify signature (Perl code):
742              
743             use Crypt::PK::RSA;
744             use Crypt::Digest 'digest_file';
745             use Crypt::Misc 'read_rawfile';
746              
747             my $pkrsa = Crypt::PK::RSA->new("rsakey.pub.pem");
748             my $signature = read_rawfile("input.sha1-rsa.sig");
749             my $valid = $pkrsa->verify_hash($signature, digest_file("SHA1", "input.data"), "SHA1", "v1.5");
750             print $valid ? "SUCCESS" : "FAILURE";
751              
752             =head2 Sign by Crypt::PK::RSA, verify by OpenSSL
753              
754             Create signature (Perl code):
755              
756             use Crypt::PK::RSA;
757             use Crypt::Digest 'digest_file';
758             use Crypt::Misc 'write_rawfile';
759              
760             my $pkrsa = Crypt::PK::RSA->new("rsakey.priv.pem");
761             my $signature = $pkrsa->sign_hash(digest_file("SHA1", "input.data"), "SHA1", "v1.5");
762             write_rawfile("input.sha1-rsa.sig", $signature);
763              
764             Verify signature (from commandline):
765              
766             openssl dgst -sha1 -verify rsakey.pub.pem -signature input.sha1-rsa.sig input.data
767              
768             =head2 Keys generated by Crypt::PK::RSA
769              
770             Generate keys (Perl code):
771              
772             use Crypt::PK::RSA;
773             use Crypt::Misc 'write_rawfile';
774              
775             my $pkrsa = Crypt::PK::RSA->new;
776             $pkrsa->generate_key(256, 65537);
777             write_rawfile("rsakey.pub.der", $pkrsa->export_key_der('public'));
778             write_rawfile("rsakey.priv.der", $pkrsa->export_key_der('private'));
779             write_rawfile("rsakey.pub.pem", $pkrsa->export_key_pem('public_x509'));
780             write_rawfile("rsakey.priv.pem", $pkrsa->export_key_pem('private'));
781             write_rawfile("rsakey-passwd.priv.pem", $pkrsa->export_key_pem('private', 'secret'));
782              
783             Use keys by OpenSSL:
784              
785             openssl rsa -in rsakey.priv.der -text -inform der
786             openssl rsa -in rsakey.priv.pem -text
787             openssl rsa -in rsakey-passwd.priv.pem -text -inform pem -passin pass:secret
788             openssl rsa -in rsakey.pub.der -pubin -text -inform der
789             openssl rsa -in rsakey.pub.pem -pubin -text
790              
791             =head2 Keys generated by OpenSSL
792              
793             Generate keys:
794              
795             openssl genrsa -out rsakey.priv.pem 1024
796             openssl rsa -in rsakey.priv.pem -out rsakey.priv.der -outform der
797             openssl rsa -in rsakey.priv.pem -out rsakey.pub.pem -pubout
798             openssl rsa -in rsakey.priv.pem -out rsakey.pub.der -outform der -pubout
799             openssl rsa -in rsakey.priv.pem -passout pass:secret -des3 -out rsakey-passwd.priv.pem
800              
801             Load keys (Perl code):
802              
803             use Crypt::PK::RSA;
804              
805             my $pkrsa = Crypt::PK::RSA->new;
806             $pkrsa->import_key("rsakey.pub.der");
807             $pkrsa->import_key("rsakey.priv.der");
808             $pkrsa->import_key("rsakey.pub.pem");
809             $pkrsa->import_key("rsakey.priv.pem");
810             $pkrsa->import_key("rsakey-passwd.priv.pem", "secret");
811              
812             =head1 SEE ALSO
813              
814             =over
815              
816             =item * L
817              
818             =back
819              
820             =cut