Asymmetric Cryptography
The type of symmetric cryptography, that is also known as public-key cryptography, defines a security system that uses two different keys for each processing operation (encryption/decryption). The main reason why they are called asymmetric is the fact that the encryption key is different from the decryption key and the actual operation logic is totally different (in contrast to symmetric algorithms). The asymmetric ciphers are widely used for encryption/decryption of small amounts of data (like symmetrical algorithm keys), data signature implementations and key exchange protocols. This type of category has a slow processing speed and is limited to the size of the key (minus a few bytes).
This type of cryptographic system uses pairs of keys that are generated by cryptographic algorithms which are based on complex mathematical problems (termed as one-way functions). Each pair of keys consists of a public key, for encryption or verification purposes, and a private key, for decryption or signing type of processes. The private key must stay known only to the owner (or server), while the public key can be openly distributed to clients without compromising security. There are three main types of ciphers in this category:
- Encryption systems - Algorithms that are created to secure encrypt small chunks of data, like keys or passwords;
- Digital signatures - System that can provide a secure digital signature mechanism and a safe verification process;
- Key exchange protocols - Techniques to pick and exchange a secure cryptographic configuration between two parties over an insecure/public communication.
The CryptoMañana (CryptoManana) cryptography framework provides object-oriented components for each of the three types and has a huge set of available realizations. It is important to note, that the key exchange algorithms are added to the cryptographic protocol namespace because of their dependency on other cryptographic primitives to function correctly. They are described on the next page of this documentation’s tutorial.
Basic Methods
The provided software components for asymmetrical ciphers in the cryptography model always include:
setKeyPair() // set the two keys
getKeyPair() // get the two keys
setPrivateKey() // set a private key
getPrivateKey() // get the private key
setPublicKey() // set a public key
getPublicKey() // get the public key
checkIfThePrivateKeyIsSet() // is private key set
checkIfThePublicKeyIsSet() // is public key set
Note: It is important to note that the objects support working with just one key, because some often asymmetric primitives or protocols have that need and thus is the need of the last two methods above.
Key Pair Format
The key pair format uses a Base64 encoded value of the standard OpenSSL representation starting
with -----BEGIN {type in here} KEY-----
and ending with -----END {type in here} KEY-----
. This detail is actually
quite not important, because there is a created component in the CryptoMañana (CryptoManana) cryptography framework
that provides key pair generation (and more). Of course, you could always do more advanced manual key importing by doing
a Base64 encoding of the values of your keys. You can generate your key pairs with the
PHP OpenSSL functions
(like openssl_pkey_export()
) or even with a console-based
OpenSSL tool. In short, the key
format is the Base64 version of the standard format (base64_encode($key)
).
The simplest way to generate a key pair via the framework is by using the TokenGenerator
component (which will be examined in more detail in one of the next pages of this manual). This
component will return a data structure object of
type KeyPair
which will contain the two keys and can be used directly with the setKeyPair()
method of each
asymmetric object:
use CryptoManana\Utilities\TokenGenerator;
use CryptoManana\DataStructures\KeyPair;
$generator = new TokenGenerator();
$keyPair = $generator->getAsymmetricKeyPair(
$generator::KEY_PAIR_1024_BITS, // or 1024
$generator::RSA_KEY_PAIR_TYPE // $generator::DSA_KEY_PAIR_TYPE
);
if ($keyPair instanceof CryptoManana\DataStructures\KeyPair) {
echo 'The keypair is a valid framework object!';
} else {
echo 'The keypair is of invalid data format!';
}
$privateKey = $keyPair->private;
$publicKey = $keyPair->public;
echo 'Private Key: ' . $privateKey . '<br>';
echo 'Public Key: ' . $publicKey . '<br>';
// -----BEGIN PRIVATE KEY----- ..... -----END PRIVATE KEY
echo 'Raw Private Key: ' . base64_decode($privateKey) . '<br>';
// -----BEGIN PUBLIC KEY----- ..... -----END PUBLIC KEY
echo 'Raw Public Key: ' . base64_decode($publicKey) . '<br>';
Note: The supported types are RSA_KEY_PAIR_TYPE
and DSA_KEY_PAIR_TYPE
.
Key Pair Reuse
One of the best practices states that you should generate one key pair for your website (one website, multiple clients) or many for example when needing to implement multilevel signing/verification (multiple authority verification). As with other keys for cryptography primitive, try to avoid reuse or a too frequent period of generation for a new key pair. Key pair generation is slow and the key management can be frustrating, but ensure you are using the correct strategy for your system.
It is important to note that mathematically asymmetric algorithms may allow you to generate multiple public keys derived from one private key, but from a security point, this is forbidden. This is because having too many public keys can create the possibility of calculating or reverse-engineering the private key. The most frequently pointed example is the Chinese remainder theorem that can be used to exploit certain vulnerabilities in certain asymmetric systems.
Asymmetric Encryption
The first supported type is the data encryption cipher that is important for securing
cryptographic configurations (like secret keys). This type of encryption can handle only small chunks of data (smaller
than the key size) and also apply a padding operation to make the block filled (similar to symmetric block ciphers). The
supported algorithm/standard realizations at the \CryptoManana\AsymmetricEncryption
namespace are:
Rsa1024
- The RSA-1024 encryption algorithm primitive;Rsa2048
- The RSA-2048 encryption algorithm primitive;Rsa3072
- The RSA-3072 encryption algorithm primitive;Rsa4096
- The RSA-4096 encryption algorithm object.
It is important to mention that the asymmetric encryption cipher components have 13 other methods available:
encryptData() // encrypt a string of binary data
decryptData() // decrypt a ciphertext representation
encryptObject() // encrypt an object's serialization state
decryptObject() // decrypt an object's serialization state
encryptFile() // encrypt a file's content
decryptFile() // decrypt a file's content
setPaddingStandard() // set the padding standard
getPaddingStandard() // get the padding standard
getPaddingReservedSize() // get how many bytes are reserved
setCipherFormat() // set the ciphertext format
getCipherFormat() // get the ciphertext format
enableChunkProcessing() // enable chunk encryption
disableChunkProcessing() // disable chunk encryption
This type of component has 1 public constant available:
KEY_SIZE // The secret key size measured in bits
As previously discussed, these algorithms are meant to process a small amount of data that are
limited to the key size minus a few reserved bytes. This limitation can be removed via the usage of chunks, however,
this type of approach is insecure. The framework provides this feature but does not promote its usage, so it is
disabled by default. If you want to enable it, use the enableChunkProcessing()
/disableChunkProcessing()
methods.
The software framework supports a vast variety of well-known and frequently used data formats
for the input/output ciphertext value. Each asymmetrical encryption object provides 5 formats that can be easily
configured. The specific format can be enabled via the setCipherFormat()
setter method (the default setting
is ENCRYPTION_OUTPUT_BASE_64_URL
) and the available format setting constants per each object. The following options
are available for each component:
ENCRYPTION_OUTPUT_BASE_64_URL
- The raw byte string representation, used by PHP for working with byte arrays (for example�6�a
);ENCRYPTION_OUTPUT_HEX_UPPER
- This is the default format that uses the most popular uppercase HEX representation (for exampleAB45
);ENCRYPTION_OUTPUT_HEX_LOWER
- The lowercase HEX outputting format (for example7f39
);ENCRYPTION_OUTPUT_BASE_64
- The Base64 standard string representation (for exampleB3xGTA==
);ENCRYPTION_OUTPUT_BASE_64_URL
- The Base64 URL friendly string format (for exampleB3xGTA
).
The asymmetrical encryption object provides 2 padding standards that can be easily configured.
The specific padding mode can be enabled via the setPaddingStandard()
setter method (the default setting
is OAEP_PADDING
) and the available setting constants per each object. The framework supports the following final block
padding modes for encryption/decryption:
PKCS1_PADDING
- The PKCS#1 (v1.5, RSA Cryptography Standard) padding;OAEP_PADDING
- The OAEP (Optimal Asymmetric Encryption Padding) padding, this is the default mode.
Here is a simple example for the usage of this type of encryption component:
use CryptoManana\AsymmetricEncryption\Rsa1024;
use CryptoManana\Utilities\TokenGenerator;
use \stdClass as stdClass;
$generator = new TokenGenerator();
$keyPair = $generator->getAsymmetricKeyPair(
$generator::KEY_PAIR_1024_BITS, // or 1024
$generator::RSA_KEY_PAIR_TYPE
);
$crypter = new Rsa1024();
$crypter->setKeyPair($keyPair)
->setPaddingStandard($crypter::OAEP_PADDING); // or `Rsa1024::`
$data = 'testing information';
echo 'Data: ' . $data . '<br>';
$cipherData = $crypter->encryptData($data);
echo 'Cipher Data: ' . $cipherData . '<br>';
echo $data === $crypter->decryptData($cipherData) ?
'Data is decrypted successfully' : 'Wrong decryption!';
$object = new stdClass();
$object->data = $data;
$cipherObject = $crypter->encryptObject($object);
echo 'Cipher Object: ' . $cipherObject . '<br>';
$tmp = $crypter->decryptObject($cipherObject);
echo $object->data === $tmp->data ?
'Object is decrypted successfully' : 'Wrong decryption!';
$crypter->enableChunkProcessing(); // this is insecure usage
$encryptedContent = $crypter->encryptFile(__FILE__);
$fileName = trim(sys_get_temp_dir()) ?: (string)ini_get('upload_tmp_dir');
$fileName .= DIRECTORY_SEPARATOR . 'testing-file.encrypted';
file_put_contents($fileName, $encryptedContent);
$decryptedContent = $crypter->decryptFile($fileName);
echo file_get_contents(__FILE__) === $decryptedContent ?
'File is decrypted successfully' : 'Wrong decryption!';
@unlink($fileName);
Note: Please read more about the RSA algorithm before you use it!
Digital Signatures
The second supported type is the digital signature algorithm that is important for the data and
the owner verification process. They are widely used to sign valid computer documents under a valid authority and have
an actual legal value. It is important to note that in the next examples we will generate and use insecure self-signed
signatures to show how they can be used for internal security (but not as legal proof). Also, this type of system uses a
paired hash function inside, which is used to verify the integrity of the data being signed. The supported
algorithm/standard realizations at the \CryptoManana\AsymmetricEncryption
namespace are:
Dsa1024
- The DSA-1024 digital signature algorithm;Dsa2048
- The DSA-2048 digital signature algorithm;Dsa3072
- The DSA-3072 digital signature algorithm;Dsa4096
- The DSA-4096 digital signature algorithm.
It is important to mention that the asymmetric digital signature components have 13 other methods available:
signData() // sign and obtain a signature value
verifyDataSignature() // verify a signature against data
signObject() // sing an object's serialization state
verifyObjectSignature() // verify a signature against an object
signFile() // sing a file's content
verifyFileSignature() // verify a file's signature against the content
setSignatureFormat() // set the signature format
getSignatureFormat() // get the current signature format
setSignatureDigestion() // set the internal digestion algorithm
getSignatureDigestion() // set the current digestion algorithm
This type of component has 1 public constant available:
KEY_SIZE // The secret key size measured in bits
The software framework supports a vast variety of well-known and frequently used data formats
for the output digital signature value. Each asymmetrical signature object provides 5 formats that can be easily
configured. The specific format can be enabled via the setSignatureFormat()
setter method (the default setting
is SIGNATURE_OUTPUT_HEX_UPPER
) and the available format setting constants per each object. The following options are
available for each component:
SIGNATURE_OUTPUT_RAW
- The raw byte string representation, used by PHP for working with byte arrays (for example�6�a
);SIGNATURE_OUTPUT_HEX_UPPER
- This is the default format that uses the most popular uppercase HEX representation (for exampleAB45
);SIGNATURE_OUTPUT_HEX_LOWER
- The lowercase HEX outputting format (for example7f39
);SIGNATURE_OUTPUT_BASE_64
- The Base64 standard string representation (for exampleB3xGTA==
);SIGNATURE_OUTPUT_BASE_64_URL
- The Base64 URL friendly string format (for exampleB3xGTA
).
The implementation of the component supports all currently flagged hash functions used by the
digestion specification of digital signatures. This provides each asymmetrical signature object with 5 digestion
algorithms that can be easily chosen. The specific format can be enabled via the setSignatureDigestion()
setter
method (the default setting is SHA2_384_SIGNING
) and the available format setting constants per each object. The
following options are available for each component:
SHA1_SIGNING
- Use the SHA-1 algorithm for the signature digestion;SHA2_224_SIGNING
- Use the SHA-2 family SHA-224 algorithm for the signature digestion;SHA2_256_SIGNING
- Use the SHA-2 family SHA-256 algorithm for the signature digestion;SHA2_384_SIGNING
- Use the SHA-2 family SHA-384 algorithm for the signature digestion;SHA2_512_SIGNING
- Use the SHA-2 family SHA-512 algorithm for the signature digestion.
Note: These are the current available secure algorithms, that are used by standard, but in the future, these can become absolute.
Here is a simple example for the usage of this type of signature component:
use CryptoManana\AsymmetricEncryption\Dsa1024;
use CryptoManana\Utilities\TokenGenerator;
use \stdClass as stdClass;
$generator = new TokenGenerator();
// This signature is self-signed
$keyPair = $generator->getAsymmetricKeyPair(
$generator::KEY_PAIR_1024_BITS, // or 1024
$generator::DSA_KEY_PAIR_TYPE
);
$signer = new Dsa1024();
$signer->setKeyPair($keyPair)
->setSignatureFormat($signer::SIGNATURE_OUTPUT_HEX_LOWER) // or `Dsa1024::`
->setSignatureDigestion($signer::SHA2_512_SIGNING); // or `Dsa1024::`
$data = 'testing information';
$signature = $signer->signData($data);
echo 'Data: ' . $data . '<br>';
echo 'Signature: ' . $signature . '<br>';
if ($signer->verifyDataSignature($signature, $data)) {
echo "The signature is valid!";
} else {
echo "Invalid data or signature!";
}
$object = new stdClass();
$object->data = $data;
$objectSignature = $signer->signObject($object);
echo 'Object Signature: ' . $objectSignature . '<br>';
if ($signer->verifyObjectSignature($objectSignature, $object)) {
echo "The object signature is valid!";
} else {
echo "Invalid object data or signature!";
}
$fileSignature = $signer->signFile(__FILE__);
if ($signer->verifyFileSignature($fileSignature, __FILE__)) {
echo "The file signature is valid!";
} else {
echo "Invalid file data or signature!";
}
Note: Please read more about the DSA/DSS algorithm before you use it!
The Object Hierarchy
The internal components’ hierarchy is visualized as a technical diagram and can be seen in Figure 1.
Figure 1: The data asymmetric encryption components hierarchy.
For more information about the capabilities of the components, please see the technical
documentation for \CryptoManana\AsymmetricEncryption
namespace.
In addition, as previously stated the key exchange algorithms are described on the next page of this documentation’s tutorial.