NAME
Data::Tools::Crypto::RSA - hybrid public key encryption and digital signatures with an RSA key pair
SYNOPSIS
use Data::Tools qw( file_text_load );
use Data::Tools::Crypto::RSA;
use Exception::Sink;
# the key is always the PEM text itself, never a file name
my $pem = file_text_load( 'private.pem' );
my $crypto = Data::Tools::Crypto::RSA->new( $pem );
# --------------------------------------------------------------------------
# anyone with the public key can encrypt, only the private key can decrypt
my $ctext = $crypto->encrypt( $ptext );
my $ptext = $crypto->decrypt( $ctext );
boom( "data cannot be decrypted" ) unless defined $ptext;
# --------------------------------------------------------------------------
# only the private key can sign, anyone with the public key can verify
my $sig = $crypto->sign( $message );
boom( "message is not authentic" ) unless $crypto->verify( $message, $sig );
# the same, with the signature in text-safe encodings
my $sig_hex = $crypto->sign_hex( $message );
my $sig_b64 = $crypto->sign_base64( $message );
my $sig_b64u = $crypto->sign_base64url( $message );
boom( "message is not authentic" ) unless $crypto->verify_base64url( $message, $sig_b64u );
# --------------------------------------------------------------------------
# a different hash can be selected, it applies to both wrapping and signing
my $crypto = Data::Tools::Crypto::RSA->new( $pem, HASH => 'SHA512' );
# --------------------------------------------------------------------------
# the whole encoding and freeze/thaw API comes from
# Data::Tools::Crypto::Base, see there for the full list
my $sealed = $crypto->freeze_base64url( { name => 'test' } );
my $data = $crypto->thaw_base64url( $sealed );
# --------------------------------------------------------------------------
DESCRIPTION
Data::Tools::Crypto::RSA encrypts with RSA-OAEP and signs with RSA-PSS.
Encryption is hybrid. RSA by itself can only encrypt a few hundred bytes, so it is not used on the data at all: a fresh symmetric key is generated for each message, the message is encrypted with Data::Tools::Crypto::Symmetric and only that key is wrapped with RSA. There is no limit on the size of the data, and the payload is authenticated exactly as it is there.
PUBLIC AND PRIVATE KEYS
A private key contains the public key, so an object holding a private key can do everything. A public key can only do the two public operations:
private key public key
encrypt() yes yes
verify() yes yes
decrypt() yes no
sign() yes no
This is why a service which only sends encrypted data, or only verifies signatures, should be given the public key alone -- it works exactly the same and the private key never reaches that host.
Asking a public key object to decrypt() or sign() is a configuration error, not a data error, and raises an exception saying so.
SIGNATURES
Encryption does not prove who sent anything. Anyone holding the public key can produce a well formed message, because that is precisely what the public key is for. Only sign()/verify() establish origin, and they are independent of the encrypt/decrypt pair -- use both when both properties are needed.
METHODS
new( $pem_text, %options )
Returns a new object. $pem_text is the PEM text of a public or a private key. It is never taken for a file name, reading a key from a file is left to the calling code. Options:
HASH => 'SHA256' hash for OAEP wrapping and PSS signatures, default SHA256
Unknown options are refused rather than silently ignored, as is a key too small to be usable -- see KEY SIZE below.
reinit( $pem_text, %options )
Sets a new key on an existing object. All previous state is cleared first, so if the key or an option is rejected the object is left without a key and is not usable until a later reinit() succeeds. Catching the exception and handling the unusable object is left to the caller.
encrypt( $plaintext )
Returns the cryptotext, which is always binary. There is no limit on the size of the plaintext, the cryptotext is the key modulus size plus 29 bytes longer than the plaintext.
decrypt( $cryptotext )
Returns the plaintext, or undef if the cryptotext cannot be decrypted, which covers a wrong key, modified data, truncated data and random input alike. Raises an exception if the object holds a public key.
sign( $message )
Returns an RSA-PSS signature, which is always binary and is the key modulus size. Raises an exception if the object holds a public key.
verify( $message, $signature )
Returns true if the signature was made over this exact message by the private key matching this object's key, false otherwise. An unusable or malformed signature is simply false, it does not raise an exception.
sign_hex(), sign_base64(), sign_base64url() and their verify pairs
As sign()/verify() but with the signature HEX, BASE64 or URL-safe BASE64 encoded, so it can be stored or passed as plain text. BASE64 is on a single line with no newlines added. Only the signature is encoded, the message is signed and verified exactly as with sign()/verify():
my $sig = $crypto->sign_hex( $message );
my $ok = $crypto->verify_hex( $message, $sig );
An encoded signature which does not decode to a valid signature is simply false, as in verify().
is_private()
True if the object holds a private key, so the caller can tell in advance whether decrypt() and sign() are available to it. Like every other method it raises an exception on an object whose reinit() failed, rather than answering false.
KEY SIZE
OAEP can carry keysize minus twice the hash size minus two bytes, and that must fit the 32 byte symmetric key, so the key modulus must be at least
32 + 2 * hash size + 2 bytes
PSS signatures need less than that, so the OAEP limit is the one which decides. The minimum key size for each HASH option:
HASH hash size min modulus smallest usual key
SHA1 20 bytes 74 bytes ( 592 bits) 1024 bits
SHA224 28 bytes 90 bytes ( 720 bits) 1024 bits
SHA256 (default) 32 bytes 98 bytes ( 784 bits) 1024 bits
SHA3_256 32 bytes 98 bytes ( 784 bits) 1024 bits
SHA384 48 bytes 130 bytes (1040 bits) 1536 bits
SHA512 64 bytes 162 bytes (1296 bits) 1536 bits
SHA3_512 64 bytes 162 bytes (1296 bits) 1536 bits
So a 1024 bit key works with SHA256 but is refused with SHA384 or SHA512. A key too small for the chosen hash is refused when it is loaded, rather than failing on every encrypt() later.
These are the smallest keys which work at all, not a recommendation: 1024 bit RSA is no longer considered secure, use 2048 bits or more.
UTF8 HANDLING
encrypt()/decrypt() are transparent, exactly as described in Data::Tools::Crypto::Symmetric, which does the work.
sign()/verify() cover the same distinction, so a character string and its utf8 byte form do not share a signature. Signatures are always binary, passing a character string to verify() raises an exception.
ERRORS
The two kinds of failure are reported differently and never confused:
* a caller or configuration error raises an exception with boom(), i.e. a
key given as a reference instead of PEM text, malformed or unsupported
key material, a key too small, an invalid
or unknown hash, an unknown option, an undefined argument, a private
operation on a public key, or any use of an object whose reinit() failed
* data which simply does not decrypt or verify returns undef from decrypt()
and false from verify(), which are normal results the caller must check
DATA FORMAT
RSA wrapped symmetric key (key modulus size) . Crypto::Symmetric cryptotext
REQUIRED MODULES
Data::Tools::Crypto::RSA uses:
* Data::Tools::Crypto::Base
* Data::Tools::Crypto::Symmetric
* Crypt::PK::RSA (CryptX)
* Crypt::Digest (CryptX)
* Crypt::PRNG (CryptX)
* Data::Tools
* Encode
* Exception::Sink
* MIME::Base64
GITHUB REPOSITORY
https://github.com/cade-vs/perl-data-tools-crypto
git clone https://github.com/cade-vs/perl-data-tools-crypto.git
AUTHOR
Vladi Belperchinov-Shabanski "Cade"
<cade@noxrun.com> <cade@bis.bg> <cade@cpan.org>
http://cade.noxrun.com/