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 10     10   775658 use strict;
  10         20  
  10         364  
4 10     10   85 use warnings;
  10         17  
  10         1616  
5             our $VERSION = '0.089';
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 10     10   67 use Carp;
  10         17  
  10         946  
13             $Carp::Internal{(__PACKAGE__)}++;
14 10     10   3367 use CryptX;
  10         22  
  10         438  
15 10     10   3325 use Crypt::Digest qw(digest_data digest_data_b64u);
  10         24  
  10         901  
16 10     10   3643 use Crypt::Misc qw(read_rawfile encode_b64u decode_b64u encode_b64 decode_b64 pem_to_der der_to_pem);
  10         34  
  10         931  
17 10     10   2979 use Crypt::PK;
  10         54  
  10         21242  
18              
19             sub new {
20 116     116 1 1812008 my $self = shift->_new();
21 116 100       71668 return @_ > 0 ? $self->import_key(@_) : $self;
22             }
23              
24             sub export_key_pem {
25 3     3 1 1086006 my ($self, $type, $password, $cipher) = @_;
26 3         14 local $SIG{__DIE__} = \&CryptX::_croak;
27             # public_x509 uses the same DER as public, just different PEM header
28 3 100 50     12 my $der_type = ($type || '') eq 'public_x509' ? 'public' : ($type || '');
      50        
29 3         144 my $key = $self->export_key_der($der_type);
30 3 50       6 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       6 return der_to_pem($key, "RSA PUBLIC KEY") if $type eq 'public';
39             # X.509 SubjectPublicKeyInfo** (PEM header: BEGIN PUBLIC KEY)
40 1 50       5 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 176     176 1 317375 my ($self, $key, $password) = @_;
90 176         1161 local $SIG{__DIE__} = \&CryptX::_croak;
91 176 50       583 croak "FATAL: undefined key" unless $key;
92              
93             # special case
94 176 100       604 if (ref($key) eq 'HASH') {
95 3 50 33     21 if ($key->{N} && $key->{e}) {
96             # hash exported via key2hash
97 3         1431 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 173         400 my $data;
112 173 100       3781 if (ref($key) eq 'SCALAR') {
    50          
113 72         139 $data = $$key;
114             }
115             elsif (-f $key) {
116 101         517 $data = read_rawfile($key);
117             }
118             else {
119 0         0 croak "FATAL: non-existing file '$key'";
120             }
121 173 50       669 croak "FATAL: invalid key data" unless $data;
122              
123 173 100       2308 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 55         70798 return $self->_import_pem($data, $password);
125             }
126             elsif ($data =~ /-----BEGIN CERTIFICATE-----(.+?)-----END CERTIFICATE-----/s) {
127 1         173 return $self->_import_pem($data, undef);
128             }
129             elsif ($data =~ /-----BEGIN OPENSSH PRIVATE KEY-----(.+?)-----END OPENSSH PRIVATE KEY-----/s) {
130 12         1327883 return $self->_import_openssh($data, $password);
131             }
132             elsif ($data =~ /---- BEGIN SSH2 PUBLIC KEY ----(.+?)---- END SSH2 PUBLIC KEY ----/s) {
133 6         824 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         106 $data = decode_b64("$1");
148 6         60 my ($typ, $e, $N) = Crypt::PK::_ssh_parse($data);
149 6 50 33     2634 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     150 my $rv = eval { $self->_import($data) } || eval { $self->_import_pkcs8($data, $password) } || eval { $self->_import_x509($data) };
154 93 50       944 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 2423220 my $key = shift;
164 2         15 local $SIG{__DIE__} = \&CryptX::_croak;
165 2 100       11 $key = __PACKAGE__->new($key) unless ref $key;
166 2 100       259 croak "FATAL: invalid 'key' param" unless ref($key) eq __PACKAGE__;
167 1         233 return $key->encrypt(@_);
168             }
169              
170             sub rsa_decrypt { # legacy/obsolete
171 1     1 0 351 my $key = shift;
172 1         5 local $SIG{__DIE__} = \&CryptX::_croak;
173 1 50       9 $key = __PACKAGE__->new($key) unless ref $key;
174 1 50       5 croak "FATAL: invalid 'key' param" unless ref($key) eq __PACKAGE__;
175 1         5066 return $key->decrypt(@_);
176             }
177              
178             sub rsa_sign_hash { # legacy/obsolete
179 1     1 0 375 my $key = shift;
180 1         8 local $SIG{__DIE__} = \&CryptX::_croak;
181 1 50       9 $key = __PACKAGE__->new($key) unless ref $key;
182 1 50       6 croak "FATAL: invalid 'key' param" unless ref($key) eq __PACKAGE__;
183 1         9013 return $key->sign_hash(@_);
184             }
185              
186             sub rsa_verify_hash { # legacy/obsolete
187 1     1 0 441 my $key = shift;
188 1         8 local $SIG{__DIE__} = \&CryptX::_croak;
189 1 50       12 $key = __PACKAGE__->new($key) unless ref $key;
190 1 50       7 croak "FATAL: invalid 'key' param" unless ref($key) eq __PACKAGE__;
191 1         423 return $key->verify_hash(@_);
192             }
193              
194             sub rsa_sign_message { # legacy/obsolete
195 2     2 0 1208 my $key = shift;
196 2         11 local $SIG{__DIE__} = \&CryptX::_croak;
197 2 100       13 $key = __PACKAGE__->new($key) unless ref $key;
198 2 100       187 croak "FATAL: invalid 'key' param" unless ref($key) eq __PACKAGE__;
199 1         8556 return $key->sign_message(@_);
200             }
201              
202             sub rsa_verify_message { # legacy/obsolete
203 1     1 0 434 my $key = shift;
204 1         7 local $SIG{__DIE__} = \&CryptX::_croak;
205 1 50       15 $key = __PACKAGE__->new($key) unless ref $key;
206 1 50       6 croak "FATAL: invalid 'key' param" unless ref($key) eq __PACKAGE__;
207 1         459 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 from a 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             kty => "RSA",
313             n => "0vx7agoebGcQSuuPiLJXZpt...eZu0fM4lFd2NcRwr3XPksINHaQ-G_xBniIqbw0Ls1jF44-csFCur-kEgU8awapJzKnqDKgw",
314             e => "AQAB",
315             d => "X4cTteJY_gn4FYPsXB8rdXi...FLN5EEaG6RoVH-HLKD9Mdx5ooGURknhnrRwUkC7h5fJLMWbFAKLWY2v7B6NqSzUvx0_YSf",
316             p => "83i-7IvMGXoMXCskv73TKr8...Z27zvoj6pbUQyLPBQxtPnwD20-60eTmD2ujMt5PoMrm8RmNhVWtjjMmMjOpSicFHjXOuVI",
317             q => "3dfOR9cuYq-0S-mkFLzgItg...q3hWeMuG0ouqnb3obLyuqjVZQ1dIrdgTnCdYzBcOW5r37AFXjift_NGiovonzhKpoVVS78",
318             dp => "G4sPXkc6Ya9y8oJW9_ILj4...zi_H7TkS8x5SdX3oE0oiYwxIiemTAu0UOa5pgFGyJ4c8t2VF40XRugKTP8akhFo5tA77Qe",
319             dq => "s9lAH9fggBsoFR8Oac2R_E...T2kGOhvIllTE1efA6huUvMfBcpn8lqW6vzzYY5SSF7pMd_agI3G8IbpBUb0JiraRNUfLhc",
320             qi => "GyM_p6JrXySiz1toFgKbWV...4ypu9bMWx3QJBfm0FoYzUIZEVEcOqwmRN81oDAaaBk0KWGDjJHDdDmFW3AN7I-pux_mHZG",
321             });
322              
323             Supported key formats:
324              
325             # all formats can be loaded from a file
326             my $pk = Crypt::PK::RSA->new($filename);
327              
328             # or from a buffer containing the key
329             my $pk = Crypt::PK::RSA->new(\$buffer_with_key);
330              
331             =over
332              
333             =item * RSA public keys
334              
335             -----BEGIN PUBLIC KEY-----
336             MIGfMA0GCSqGSIb3DQEBAQUAA4GNADCBiQKBgQDHlYKg9DeHB3/dY1D9WCyJTnl5
337             vEzAXpUOL9tDtdPUl96brIbbdMLooO1hKjsq98kLs1q4vOn/pxvzk0BRwhiu7Vvb
338             VUjAn/2HHDDL0U1utqqlMJhaffeLI3HEq5o/lSMFY7sSkZU/E4YX1yqAN0SE7xfK
339             B2uzcNq60sMIfp6siQIDAQAB
340             -----END PUBLIC KEY-----
341              
342             =item * RSA private keys
343              
344             -----BEGIN RSA PRIVATE KEY-----
345             MIICXQIBAAKBgQDHlYKg9DeHB3/dY1D9WCyJTnl5vEzAXpUOL9tDtdPUl96brIbb
346             dMLooO1hKjsq98kLs1q4vOn/pxvzk0BRwhiu7VvbVUjAn/2HHDDL0U1utqqlMJha
347             ffeLI3HEq5o/lSMFY7sSkZU/E4YX1yqAN0SE7xfKB2uzcNq60sMIfp6siQIDAQAB
348             AoGBAI5+GgNcGQDYw9uF+t7FwxZM5sGZRJrbbEPyuvL+sDxKKW6voKCyHi4EJzaF
349             9jRZMDqgVJcsmUwjPPuMGBHHJ+MI5Zb3L0jbZkyx8u+U5gf88oy9eZmfGOjmHcMB
350             oCgzyoLmJETuyADg2onLanuY3jggFb3tq/jimKjO8xM2R6zhAkEA7uXWWyJI9cCN
351             zrVt5R5v6oosjZ4r5VILGMqBRLrzfTvH+WDMK6Rl/2MHE+YDeLajzunaM8qY2456
352             GTYEXQsIdQJBANXfMEtXocSdPtoVj3ME8Do/0r+ApgTdcDPCwXOzkmkEJW/UFMSn
353             b8CYF5G6sZQN9L5z3s2nvi55PaFV8Q0LMUUCQBh9GvIQm6YFbQPpeTBpZFOIgnSp
354             6BoDxPtvlryy5U7LF/6qO4OlwIbjYdBaXbS8FCKbujBg7jZjboSzEtNu1BkCQDGT
355             w0Yz0jQZn3A+fzpScr2N/fSWheWqz0+wXdfMUKw3YdZCe236wlUK7KvDc1a2xX1A
356             ru1NbTCoujikC3TSm2ECQQDKQshchJlZJmFv9vCFQlGCA/EX+4406xvOOiixbPYC
357             pIB4Ee2cmvEdAqSaOjrvgs5zvaCCFBO0MecPStCAxUX6
358             -----END RSA PRIVATE KEY-----
359              
360             =item * RSA private keys in password protected PEM format
361              
362             -----BEGIN RSA PRIVATE KEY-----
363             Proc-Type: 4,ENCRYPTED
364             DEK-Info: DES-EDE3-CBC,4D697440FF5AEF18
365              
366             C09H49Gn99o8b8O2r4+Hqao4r3udvC+QSSfsk20sXatyuZSEmbhyqKAB+13NRj+3
367             KIsRTqnL9VkeibIGgLHuekOFKAqeSVZ0PmR4bGWEFxUPAYUvg9N9pIa6hGtNZG+y
368             TEpOAfFITb1pbHQhp3j8y7qmKc5kY5LrZSFE8WwA24NTG773E07wJgRxKDkXNGOl
369             kki6oYArNEps0DdtHFxzgdRg0+yaotXuFJRuC5V4YzKGG/oSRcgYyXKTwCndb3xt
370             aHgI2WprQAPg+qOpLABzoi7bEjCqbHWrwkvnAngylbim2Uyvw1e1xKnzlgIHU7pv
371             e/J+s00pTItfqW1IpY2mh4C9nkfkfVKBKaAv7jO0s6aPySATqsdlrzv2kpF6Ub4J
372             kgaZDOfZ4K3qkyAYVLWcQeDqg4glv9Ah2J05bTm4qrIMmthYnThyQlGvcjUfCMXs
373             0t+mEQbsRY7xKt0o6HzzvQlJ+JsFlLORoslAubJX9iLqpEdnlrj1lD9bo6uIClZ5
374             5+aoLcAyz1D4OsauuP5i8VFu+Is+QG4SN/vHVuArjkqi3VpLwSAjNDY+KWbq042l
375             CqlM2mwm6FIGUZQFxiLHJD7WDmk1xmae++m+XG9CEDTfrUQ5v+l0O6BTrl80XUfU
376             w3gzAWbSjz3UK0FpKeABVFPE9fjNP9fTcS6qL5YJWBPflwxCAbVgsBOW4bOMpDGK
377             BJDQTeShWn4BlYCe/vgThI9ERdgZhRz4NcFeDgVA/CqQzVqptvz4PSqH46fqUN2n
378             4PtJgKE5cASYUBuAjlD71FecSVVM/OTzL1uxYzXBilzvVn2vSHgo9g==
379             -----END RSA PRIVATE KEY-----
380              
381             =item * PKCS#8 encoded private keys
382              
383             -----BEGIN PRIVATE KEY-----
384             MIICdgIBADANBgkqhkiG9w0BAQEFAASCAmAwggJcAgEAAoGBANPN17xW4EkH5PXG
385             1i/i3rE1EXFcCHyxmz95VRBDs1p3MuYf9mxntbfYAmuzS3KrRWh3IyX/Eh80N/v9
386             OXPlwZbVqSTX+L3pCEJtRtsWn0zmswGThjMZiwle0oWuap63L35F1QN8EDaSPSBC
387             yGELNRr6rwVYq0w5b+LOcaCZ+/H1AgMBAAECgYEApfu3aGpww+rC3HUhX0+ckyTy
388             cXLdV9LbxidwqRlVEb0+DyfXNucjelp2sy5EHy3na9GJovo8mmWSxhCRGKliRkQ6
389             XgrEMZdCSaWI2AazuHAGlUJRFEVkvdla3AuBAn6y0YdDp/3kbg0yahmKyD8Gq74z
390             nUYbDL3R5JtR2Ad/KlUCQQDvSEICTHbO/BF7hVmlKRYZSNHKEPrv8X/OlppS14Kv
391             QRwc+CZ5+l6T1Y+l5cHJQUXrXZoWS1K741TXdUhjjUd7AkEA4pod804Ex8sttdWi
392             pHMfeyj+IbPAk5XnBc91jT7AYIeL8ccjtfl99xhMsGFaxrh3wA/4SGEvwzWkbxcq
393             H8G5TwJAKNG+0P2SVwURRm0dOdukdXPCtiHnbP9Zujhe4zr4hEUrMpXymmRntfh8
394             pORpBpgoAVraams3Fe5WDttnGfSD+QJAOOC6V9HjfUrQhG3FT0XeRwm5EDiQQ/tC
395             a8DxHqz7mL8tL1ju68ReC+G7jiJBqNOwqzLW/UP3uyYByiikWChGHQJAHUau7jIM
396             45ErO096n94Vh95p76ANxOroWszOt39TyvJOykIfoPwFagLrBWV9Jjos2/D54KE+
397             fyoy4t3yHT+/nw==
398             -----END PRIVATE KEY-----
399              
400             =item * PKCS#8 encrypted private keys - password protected keys (supported since: CryptX-0.062)
401              
402             -----BEGIN ENCRYPTED PRIVATE KEY-----
403             MIICojAcBgoqhkiG9w0BDAEDMA4ECCQk+Rr1yzzcAgIIAASCAoD/mgpUFjxxM/Ty
404             Yt+NeT0Fo4echgoGksqs6+rYhO16oshG664emZfkuNoFGGzJ38X6GVuqIXhlPnYQ
405             biKvL37dN/KnoGytFHq9Wnk8dDwjGHPtwajhW5WuIV3NuhW/AO1PF/cRZKFjWrPt
406             NWY5CrpfH6t6zojoe+5uyXpH29lQy4OqvSRdPIt/12UcB+tzV7XzSWEuXh8HAi8a
407             sYUu6tuCFnq4GrD2ffM4KWFmL5GqBAwN6m0KkyrNni9XT+RaA6zEhv/lVcwg2esa
408             4/EzRs0ixzzZDKaml8oCMl9RHtFAbQmdlfV7Ip4rGK9BwY6UFiDMIVru6HynOVQK
409             vvZ+j//bgO+3ubrv7psX+vC9Fy/MoH2Tc7MIwDN/QVTciPZlzjWBnBNxMfeFKtEn
410             d7NFiapgfLuRQIiDTMrW/clcqvO54NphxhrcgUEoxos4twKZARntqPZHtf8nEM2x
411             2sEF5kI65aEF/5Yy16qvP0vZAA2B1kcIdXZ8XLZCp4c3olhkIrmgUpo1gyFXdCoC
412             7dT5Cz7/YLkq5hkcFrtp4V9BZMR24fSttc4p24N5xuZ+JneGnGkLX6B+nJAtm9vw
413             bZA6P+23GI0qeMzL3HJXwCOTSsWfm/H9W5+2Zmw851aAmE+pZLni/pk3e3iNSWgs
414             946x/doA5O0uCFsU7oxme+WAIp2SjhxGoe808Lf1CCFMPboFi1O/E0NsX8SIEX+i
415             U+UHi4kxZqVkr3Q5SB/9kiSv8K1bE787yueQOT/dsTYYaMsjAbkEZo0o/47F32T6
416             A2ioXHOV/pr5zNHqE5tL+qKEcLYbAUF1O+WvmdqYz+vHQjRQBatAqTmncvLDYr/j
417             1HPwZX2d
418             -----END ENCRYPTED PRIVATE KEY-----
419              
420             =item * RSA public key from X509 certificate
421              
422             -----BEGIN CERTIFICATE-----
423             MIIC8zCCAdugAwIBAgIJAPi+LvMU3uGWMA0GCSqGSIb3DQEBCwUAMBAxDjAMBgNV
424             BAMMBXBva3VzMB4XDTE3MDcxNDE0MTAyMFoXDTIwMDQwOTE0MTAyMFowEDEOMAwG
425             A1UEAwwFcG9rdXMwggEiMA0GCSqGSIb3DQEBAQUAA4IBDwAwggEKAoIBAQDCQima
426             SUIMIdz5uVevzcScbcj06xs1OLaFKUoPJ8v+xP6Ut61BQhAvc8GYuw2uRx223hZC
427             r3HYLfSdWIfmOIAtlL8cPYPVoSivJtpSGE6fBG1tlBjVgXWRmJGR/oxx6Y5QDwcB
428             Q4GZKga8TtHQoY5idZuatYOFZGfMIcIUC0Uoda+YSypnw7A90F/JvlpcTUh3Fnem
429             VinqEA6XOegU9dCZk/29sXqauBjbdGihh8DvpklOhY16eQoiR3909AywQ0KUmI+R
430             Sa9E8oIsmUDetFuXEvana+sD3y42tU+cd2nhBPRETbSXPcum0B3uF4yKgweuJy5D
431             cvtVQIFVkkh4+AWNAgMBAAGjUDBOMB0GA1UdDgQWBBSS6V5PVGyN92NoB0AVLcOb
432             pzR3SzAfBgNVHSMEGDAWgBSS6V5PVGyN92NoB0AVLcObpzR3SzAMBgNVHRMEBTAD
433             AQH/MA0GCSqGSIb3DQEBCwUAA4IBAQBIszrBjoJ39axsS6Btbvwvo8vAmgiSWsav
434             7AmjXOAwknHPaCcDmrdOys5POD0DNRwNeRsnxFiZ/UL8Vmj2JGDLgAw+/v32MwfX
435             Ig7m+oIbO8KqDzlYvS5kd3suJ5C21hHy1/JUtfofZLovZH7ZRzhTAoRvCYaodW90
436             2o8ZqmyCdcXPzjFmoJ2xYzs/Sf8/E1cHfb+4HjOpeRnKxDvG0gwWzcsXpUrw2pNO
437             Oztj6Rd0THNrf/anIeYVtAHX4aqZA8Kbv2TyJd+9g78usFw1cn+8vfmilm6Pn0DQ
438             a+I5GyGd7BJI8wYuWqIStzvrJHbQQaNrSk7hgjWYiYlcsPh6w2QP
439             -----END CERTIFICATE-----
440              
441             =item * SSH public RSA keys
442              
443             ssh-rsa AAAAB3NzaC1yc2EAAAADAQA...6mdYs5iJNGu/ltUdc=
444              
445             =item * SSH public RSA keys (RFC-4716 format)
446              
447             ---- BEGIN SSH2 PUBLIC KEY ----
448             Comment: "768-bit RSA, converted from OpenSSH"
449             AAAAB3NzaC1yc2EAAAADAQABAAAAYQDYebeGQFCnlQiNRE7r9UEbjr+DQMTdw1ZHGB2w6x
450             D/DzKem8761GdCpqsLrGaw2D7aSIoP1B5Sz870YoVWHn6Ao7Hvm17V3Kxfn4B01GNQTM5+
451             L26mdYs5iJNGu/ltUdc=
452             ---- END SSH2 PUBLIC KEY ----
453              
454             =item * RSA private keys in JSON Web Key (JWK) format
455              
456             See L
457              
458             {
459             "kty":"RSA",
460             "n":"0vx7agoebGcQSuuPiLJXZpt...eZu0fM4lFd2NcRwr3XPksINHaQ-G_xBniIqbw0Ls1jF44-csFCur-kEgU8awapJzKnqDKgw",
461             "e":"AQAB",
462             "d":"X4cTteJY_gn4FYPsXB8rdXi...FLN5EEaG6RoVH-HLKD9Mdx5ooGURknhnrRwUkC7h5fJLMWbFAKLWY2v7B6NqSzUvx0_YSf",
463             "p":"83i-7IvMGXoMXCskv73TKr8...Z27zvoj6pbUQyLPBQxtPnwD20-60eTmD2ujMt5PoMrm8RmNhVWtjjMmMjOpSicFHjXOuVI",
464             "q":"3dfOR9cuYq-0S-mkFLzgItg...q3hWeMuG0ouqnb3obLyuqjVZQ1dIrdgTnCdYzBcOW5r37AFXjift_NGiovonzhKpoVVS78",
465             "dp":"G4sPXkc6Ya9y8oJW9_ILj4...zi_H7TkS8x5SdX3oE0oiYwxIiemTAu0UOa5pgFGyJ4c8t2VF40XRugKTP8akhFo5tA77Qe",
466             "dq":"s9lAH9fggBsoFR8Oac2R_E...T2kGOhvIllTE1efA6huUvMfBcpn8lqW6vzzYY5SSF7pMd_agI3G8IbpBUb0JiraRNUfLhc",
467             "qi":"GyM_p6JrXySiz1toFgKbWV...4ypu9bMWx3QJBfm0FoYzUIZEVEcOqwmRN81oDAaaBk0KWGDjJHDdDmFW3AN7I-pux_mHZG",
468             }
469              
470             B For JWK support you need to have L module installed.
471              
472             =item * RSA public keys in JSON Web Key (JWK) format
473              
474             {
475             "kty":"RSA",
476             "n": "0vx7agoebGcQSuuPiLJXZp...tN9nndrQmbXEps2aiAFbWhM78LhWx4cbbfAAtVT86zwu1RK7aPFFxuhDR1L6tSoc_BJECP",
477             "e":"AQAB",
478             }
479              
480             B For JWK support you need to have L module installed.
481              
482             =back
483              
484             =head2 export_key_der
485              
486             Returns the key as a binary DER-encoded string.
487              
488             my $private_der = $pk->export_key_der('private');
489             #or
490             my $public_der = $pk->export_key_der('public');
491              
492             =head2 export_key_pem
493              
494             Returns the key as a PEM-encoded string (ASCII).
495              
496             my $private_pem = $pk->export_key_pem('private');
497             #or
498             my $public_pem = $pk->export_key_pem('public');
499             #or
500             my $public_pem = $pk->export_key_pem('public_x509');
501              
502             With parameter C<'public'> uses header and footer lines:
503              
504             -----BEGIN RSA PUBLIC KEY------
505             -----END RSA PUBLIC KEY------
506              
507             With parameter C<'public_x509'> uses header and footer lines:
508              
509             -----BEGIN PUBLIC KEY------
510             -----END PUBLIC KEY------
511              
512             Support for password protected PEM keys
513              
514             my $private_pem = $pk->export_key_pem('private', $password);
515             #or
516             my $private_pem = $pk->export_key_pem('private', $password, $cipher);
517              
518             # supported ciphers: 'DES-CBC'
519             # 'DES-EDE3-CBC'
520             # 'SEED-CBC'
521             # 'CAMELLIA-128-CBC'
522             # 'CAMELLIA-192-CBC'
523             # 'CAMELLIA-256-CBC'
524             # 'AES-128-CBC'
525             # 'AES-192-CBC'
526             # 'AES-256-CBC' (DEFAULT)
527              
528             =head2 export_key_jwk
529              
530             I
531              
532             Returns a JSON string, or a hashref if the optional second argument is true.
533              
534             Exports public/private keys as a JSON Web Key (JWK).
535              
536             my $private_json_text = $pk->export_key_jwk('private');
537             #or
538             my $public_json_text = $pk->export_key_jwk('public');
539              
540             Also exports public/private keys as a Perl HASH with JWK structure.
541              
542             my $jwk_hash = $pk->export_key_jwk('private', 1);
543             #or
544             my $jwk_hash = $pk->export_key_jwk('public', 1);
545              
546             B For JWK support you need to have L module installed.
547              
548             =head2 export_key_jwk_thumbprint
549              
550             I
551              
552             Exports the key's JSON Web Key Thumbprint as a string.
553              
554             If you don't know what this is, see RFC 7638 L.
555              
556             my $thumbprint = $pk->export_key_jwk_thumbprint('SHA256');
557              
558             =head2 encrypt
559              
560             Returns the ciphertext as a binary string.
561              
562             my $pk = Crypt::PK::RSA->new($pub_key_filename);
563             my $ct = $pk->encrypt($message);
564             #or
565             my $ct = $pk->encrypt($message, $padding);
566             #or
567             my $ct = $pk->encrypt($message, 'oaep', $hash_name, $lparam);
568              
569             # $padding .................... [string] 'oaep' (DEFAULT), 'v1.5' or 'none' (INSECURE)
570             # $hash_name (only for oaep) .. [string] 'SHA1' (DEFAULT), 'SHA256' or any other hash supported by Crypt::Digest
571             # $lparam (only for oaep) ..... [binary string] DEFAULT is empty string
572              
573             =head2 decrypt
574              
575             Returns the plaintext as a binary string.
576              
577             my $pk = Crypt::PK::RSA->new($priv_key_filename);
578             my $pt = $pk->decrypt($ciphertext);
579             #or
580             my $pt = $pk->decrypt($ciphertext, $padding);
581             #or
582             my $pt = $pk->decrypt($ciphertext, 'oaep', $hash_name, $lparam);
583              
584             # $padding .................... [string] 'oaep' (DEFAULT), 'v1.5' or 'none' (INSECURE)
585             # $hash_name (only for oaep) .. [string] 'SHA1' (DEFAULT), 'SHA256' or any other hash supported by Crypt::Digest
586             # $lparam (only for oaep) ..... [binary string] DEFAULT is empty string
587              
588             =head2 sign_message
589              
590             Returns the signature as a binary string.
591              
592             my $pk = Crypt::PK::RSA->new($priv_key_filename);
593             my $signature = $pk->sign_message($message);
594             #or
595             my $signature = $pk->sign_message($message, $hash_name);
596             #or
597             my $signature = $pk->sign_message($message, $hash_name, $padding);
598             #or
599             my $signature = $pk->sign_message($message, $hash_name, 'pss', $saltlen);
600              
601             # $hash_name ............... [string] 'SHA1' (DEFAULT), 'SHA256' or any other hash supported by Crypt::Digest
602             # $padding ................. [string] 'pss' (DEFAULT) or 'v1.5' or 'none' (INSECURE)
603             # $saltlen (only for pss) .. [integer] DEFAULT is 12
604              
605             For RSASSA-PSS with SHAKE per L
606             use C<'SHAKE128'> (output 32 bytes, C<$saltlen> = 32) or C<'SHAKE256'> (output
607             64 bytes, C<$saltlen> = 64); both the message hash and the internal MGF use
608             SHAKE directly as defined by RFC 8702.
609              
610             my $sig = $pk->sign_message($message, 'SHAKE128', 'pss', 32); # id-RSASSA-PSS-SHAKE128
611             my $sig = $pk->sign_message($message, 'SHAKE256', 'pss', 64); # id-RSASSA-PSS-SHAKE256
612              
613             =head2 verify_message
614              
615             Returns C<1> if the signature is valid, C<0> otherwise.
616              
617             my $pk = Crypt::PK::RSA->new($pub_key_filename);
618             my $valid = $pk->verify_message($signature, $message);
619             #or
620             my $valid = $pk->verify_message($signature, $message, $hash_name);
621             #or
622             my $valid = $pk->verify_message($signature, $message, $hash_name, $padding);
623             #or
624             my $valid = $pk->verify_message($signature, $message, $hash_name, 'pss', $saltlen);
625              
626             # $hash_name ............... [string] 'SHA1' (DEFAULT), 'SHA256' or any other hash supported by Crypt::Digest
627             # $padding ................. [string] 'pss' (DEFAULT) or 'v1.5' or 'none' (INSECURE)
628             # $saltlen (only for pss) .. [integer] DEFAULT is 12
629              
630             =head2 sign_hash
631              
632             Returns the signature as a binary string.
633              
634             my $pk = Crypt::PK::RSA->new($priv_key_filename);
635             my $signature = $pk->sign_hash($message_hash);
636             #or
637             my $signature = $pk->sign_hash($message_hash, $hash_name);
638             #or
639             my $signature = $pk->sign_hash($message_hash, $hash_name, $padding);
640             #or
641             my $signature = $pk->sign_hash($message_hash, $hash_name, 'pss', $saltlen);
642             #or
643             my $signature = $pk->sign_hash($message_hash, $hash_name, 'pss', $saltlen, $mgf_hash_name);
644              
645             # $hash_name ................. [string] 'SHA1' (DEFAULT), 'SHA256' or any other hash supported by Crypt::Digest
646             # $padding ................... [string] 'pss' (DEFAULT) or 'v1.5' or 'none' (INSECURE)
647             # $saltlen (pss only) ........ [integer] DEFAULT is 12
648             # $mgf_hash_name (pss only) .. [string] MGF hash function name (DEFAULT: the $hash_name value)
649              
650             =head2 verify_hash
651              
652             Returns C<1> if the signature is valid, C<0> otherwise.
653              
654             my $pk = Crypt::PK::RSA->new($pub_key_filename);
655             my $valid = $pk->verify_hash($signature, $message_hash);
656             #or
657             my $valid = $pk->verify_hash($signature, $message_hash, $hash_name);
658             #or
659             my $valid = $pk->verify_hash($signature, $message_hash, $hash_name, $padding);
660             #or
661             my $valid = $pk->verify_hash($signature, $message_hash, $hash_name, 'pss', $saltlen);
662             #or
663             my $valid = $pk->verify_hash($signature, $message_hash, $hash_name, 'pss', $saltlen, $mgf_hash_name);
664              
665             # $hash_name ................. [string] 'SHA1' (DEFAULT), 'SHA256' or any other hash supported by Crypt::Digest
666             # $padding ................... [string] 'pss' (DEFAULT) or 'v1.5' or 'none' (INSECURE)
667             # $saltlen (pss only) ........ [integer] DEFAULT is 12
668             # $mgf_hash_name (pss only) .. [string] MGF hash function name (DEFAULT: the $hash_name value)
669              
670             =head2 is_private
671              
672             my $rv = $pk->is_private;
673             # 1 .. private key loaded
674             # 0 .. public key loaded
675             # undef .. no key loaded
676              
677             =head2 size
678              
679             my $size = $pk->size;
680             # returns key size in bytes or undef if no key loaded
681              
682             =head2 key2hash
683              
684             Returns a hashref with the key components, or C if no key is loaded.
685              
686             my $hash = $pk->key2hash;
687              
688             # returns hash like this (or undef if no key loaded):
689             {
690             type => 1, # integer: 1 .. private, 0 .. public
691             size => 256, # integer: key size in bytes
692             # all the rest are hex strings
693             e => "10001", #public exponent
694             d => "9ED5C3D3F866E06957CA0E9478A273C39BBDA4EEAC5B...", #private exponent
695             N => "D0A5CCCAE03DF9C2F5C4C8C0CE840D62CDE279990DC6...", #modulus
696             p => "D3EF0028FFAB508E2773C659E428A80FB0E9211346B4...", #p factor of N
697             q => "FC07E46B163CAB6A83B8E467D169534B2077DCDEECAE...", #q factor of N
698             qP => "88C6D406F833DF73C8B734548E0385261AD51F4187CF...", #1/q mod p CRT param
699             dP => "486F142FEF0A1F53269AC43D2EE4D263E2841B60DA36...", #d mod (p - 1) CRT param
700             dQ => "4597284B2968B72C4212DB7E8F24360B987B80514DA9...", #d mod (q - 1) CRT param
701             }
702              
703             =head1 OpenSSL interoperability
704              
705             ### let's have:
706             # RSA private key in PEM format - rsakey.priv.pem
707             # RSA public key in PEM format - rsakey.pub.pem
708             # data file to be signed or encrypted - input.data
709              
710             =head2 Encrypt by OpenSSL, decrypt by Crypt::PK::RSA
711              
712             Create encrypted file (from commandline):
713              
714             openssl rsautl -encrypt -inkey rsakey.pub.pem -pubin -out input.encrypted.rsa -in input.data
715              
716             Decrypt file (Perl code):
717              
718             use Crypt::PK::RSA;
719             use Crypt::Misc 'read_rawfile';
720              
721             my $pkrsa = Crypt::PK::RSA->new("rsakey.priv.pem");
722             my $encfile = read_rawfile("input.encrypted.rsa");
723             my $plaintext = $pkrsa->decrypt($encfile, 'v1.5');
724             print $plaintext;
725              
726             =head2 Encrypt by Crypt::PK::RSA, decrypt by OpenSSL
727              
728             Create encrypted file (Perl code):
729              
730             use Crypt::PK::RSA;
731             use Crypt::Misc 'write_rawfile';
732              
733             my $plaintext = 'secret message';
734             my $pkrsa = Crypt::PK::RSA->new("rsakey.pub.pem");
735             my $encrypted = $pkrsa->encrypt($plaintext, 'v1.5');
736             write_rawfile("input.encrypted.rsa", $encrypted);
737              
738             Decrypt file (from commandline):
739              
740             openssl rsautl -decrypt -inkey rsakey.priv.pem -in input.encrypted.rsa
741              
742             =head2 Sign by OpenSSL, verify by Crypt::PK::RSA
743              
744             Create signature (from commandline):
745              
746             openssl dgst -sha1 -sign rsakey.priv.pem -out input.sha1-rsa.sig input.data
747              
748             Verify signature (Perl code):
749              
750             use Crypt::PK::RSA;
751             use Crypt::Digest 'digest_file';
752             use Crypt::Misc 'read_rawfile';
753              
754             my $pkrsa = Crypt::PK::RSA->new("rsakey.pub.pem");
755             my $signature = read_rawfile("input.sha1-rsa.sig");
756             my $valid = $pkrsa->verify_hash($signature, digest_file("SHA1", "input.data"), "SHA1", "v1.5");
757             print $valid ? "SUCCESS" : "FAILURE";
758              
759             =head2 Sign by Crypt::PK::RSA, verify by OpenSSL
760              
761             Create signature (Perl code):
762              
763             use Crypt::PK::RSA;
764             use Crypt::Digest 'digest_file';
765             use Crypt::Misc 'write_rawfile';
766              
767             my $pkrsa = Crypt::PK::RSA->new("rsakey.priv.pem");
768             my $signature = $pkrsa->sign_hash(digest_file("SHA1", "input.data"), "SHA1", "v1.5");
769             write_rawfile("input.sha1-rsa.sig", $signature);
770              
771             Verify signature (from commandline):
772              
773             openssl dgst -sha1 -verify rsakey.pub.pem -signature input.sha1-rsa.sig input.data
774              
775             =head2 Keys generated by Crypt::PK::RSA
776              
777             Generate keys (Perl code):
778              
779             use Crypt::PK::RSA;
780             use Crypt::Misc 'write_rawfile';
781              
782             my $pkrsa = Crypt::PK::RSA->new;
783             $pkrsa->generate_key(256, 65537);
784             write_rawfile("rsakey.pub.der", $pkrsa->export_key_der('public'));
785             write_rawfile("rsakey.priv.der", $pkrsa->export_key_der('private'));
786             write_rawfile("rsakey.pub.pem", $pkrsa->export_key_pem('public_x509'));
787             write_rawfile("rsakey.priv.pem", $pkrsa->export_key_pem('private'));
788             write_rawfile("rsakey-passwd.priv.pem", $pkrsa->export_key_pem('private', 'secret'));
789              
790             Use keys by OpenSSL:
791              
792             openssl rsa -in rsakey.priv.der -text -inform der
793             openssl rsa -in rsakey.priv.pem -text
794             openssl rsa -in rsakey-passwd.priv.pem -text -inform pem -passin pass:secret
795             openssl rsa -in rsakey.pub.der -pubin -text -inform der
796             openssl rsa -in rsakey.pub.pem -pubin -text
797              
798             =head2 Keys generated by OpenSSL
799              
800             Generate keys:
801              
802             openssl genrsa -out rsakey.priv.pem 1024
803             openssl rsa -in rsakey.priv.pem -out rsakey.priv.der -outform der
804             openssl rsa -in rsakey.priv.pem -out rsakey.pub.pem -pubout
805             openssl rsa -in rsakey.priv.pem -out rsakey.pub.der -outform der -pubout
806             openssl rsa -in rsakey.priv.pem -passout pass:secret -des3 -out rsakey-passwd.priv.pem
807              
808             Load keys (Perl code):
809              
810             use Crypt::PK::RSA;
811              
812             my $pkrsa = Crypt::PK::RSA->new;
813             $pkrsa->import_key("rsakey.pub.der");
814             $pkrsa->import_key("rsakey.priv.der");
815             $pkrsa->import_key("rsakey.pub.pem");
816             $pkrsa->import_key("rsakey.priv.pem");
817             $pkrsa->import_key("rsakey-passwd.priv.pem", "secret");
818              
819             =head1 SEE ALSO
820              
821             =over
822              
823             =item * L
824              
825             =back
826              
827             =cut