I have a code that makes the transformation but need to do it with native PHP functions because it is not activated support for running exec:
exec("openssl pkcs8 -inform DER -in 'archivo.key' -out 'archivo.key.pem' -passin pass:'lacontrasena'");
Someone can help me translate this into native PHP functions? It can be openssl or a library.
//Updated
This is my code using der2pem function:
function der2pem($der_data) {
$pem = chunk_split(base64_encode($der_data), 64, "\n");
$pem = "-----BEGIN PRIVATE KEY-----\n".$pem."-----END PRIVATE KEY-----\n";
return $pem;
}
$keyfile = 'myFileDER.key';
$keyFileContent = file_get_contents($keyfile);
$pemContent = der2pem($keyFileContent);
file_put_contents('llavetemp.pem', $pemContent);
$private_key1 = openssl_pkey_get_private($pemContent);
var_dump($private_key1);
The var_dump return boolean false
You can easily use uri2x's answer and a few informations from the first google result. PEM is just the base64-encoded form of the binary DER file.
Some Metadata is added and you can do everything with it.
so if you modify the function (posted by uri2x!) to the following:
function der2pem($der_data, $type='CERTIFICATE') {
$pem = chunk_split(base64_encode($der_data), 64, "\n");
$pem = "-----BEGIN ".$type."-----\n".$pem."-----END ".$type."-----\n";
return $pem;
}
you can now call it:
$private_key=file_get_contents('archivo.key');
file_put_contents('archivo.key.pem',der2pem($private_key,'PRIVATE KEY');
and you can transform nearly everything which needs to bee transferred in crypto-concerns:
//certificates
$private_key=file_get_contents('certificate');
echo der2pem($private_key,'CERTIFICATE');//here, certificate isn't even required because it's the default
//GPG/PGP Public Keys
$pgp_public_key=file_get_contents('pgp_public_key');
echo der2pem($private_key,'PGP PUBLIC KEY BLOCK');
//CSR
$certificate_signing_request=file_get_contents('csr');
echo der2pem($private_key,'CERTIFICATE REQUEST');
...and many others!
See dan's comment on php.net:
Use the following code to convert from DER to PEM and PEM to DER.
<?php
$pem_data = file_get_contents($cert_path.$pem_file);
$pem2der = pem2der($pem_data);
$der_data = file_get_contents($cert_path.$der_file);
$der2pem = der2pem($der_data);
function pem2der($pem_data) {
$begin = "CERTIFICATE-----";
$end = "-----END";
$pem_data = substr($pem_data, strpos($pem_data, $begin)+strlen($begin));
$pem_data = substr($pem_data, 0, strpos($pem_data, $end));
$der = base64_decode($pem_data);
return $der;
}
function der2pem($der_data) {
$pem = chunk_split(base64_encode($der_data), 64, "\n");
$pem = "-----BEGIN CERTIFICATE-----\n".$pem."-----END CERTIFICATE-----\n";
return $pem;
}
Related
I have now spent the last couple of days to find documentation about this..
I need to send a XML via SOAP with the WSSE security header, but don't know how to encrypt and store the encrypted keys
Here is an example
<?xml version="1.0" encoding="UTF-8" standalone="no"?>
<soap:Envelope xmlns:soap="http://schemas.xmlsoap.org/soap/envelope/">
<soap:Header>
<wsse:Security xmlns:wsse="http://docs.oasis-open.org/wss/2004/01/oasis-200401-wss-wssecurity-secext-1.0.xsd" xmlns:wsu="http://docs.oasis-open.org/wss/2004/01/oasis-200401-wss-wssecurity-utility-1.0.xsd" soap:mustUnderstand="1">
<xenc:EncryptedKey xmlns:xenc="http://www.w3.org/2001/04/xmlenc#" Id="EK-1B758D26C51BFCD86614340101135741">
<xenc:EncryptionMethod Algorithm="http://www.w3.org/2001/04/xmlenc#rsa-oaep-mgf1p"/>
<ds:KeyInfo xmlns:ds="http://www.w3.org/2000/09/xmldsig#">
<wsse:SecurityTokenReference>
<wsse:KeyIdentifier EncodingType="http://docs.oasis-open.org/wss/2004/01/oasis-200401-wss-soap-message-security-1.0#Base64Binary" ValueType="http://docs.oasis-open.org/wss/2004/01/oasis-200401-wss-x509-token-profile-1.0#X509v3">MIIDODCCAiCgAwIBAgIGAU0FlCVCMA0GCSqGSIb3DQEBCwUAMFoxCzAJBgNVBAYTAkRLMRUwEwYDVQQKEwxCYW5rIENvbm5lY3QxFTATBgNVBAsTDEJhbmsgQ29ubmVjdDEdMBsGA1UEAxMUQmFuayBDb25uZWN0IElBLXRlc3QwHhcNMTUwNDI5MTQyODI0WhcNMTgwNDI5MTQyODI0WjAcMRowGAYDVQQDExFiYW5rIGNvbm5lY3QtdGVzdDCCASIwDQYJKoZIhvcNAQEBBQADggEPADCCAQoCggEBAI23KdtaRKPTFTe/A1PnsF9dpSlTiXurKmio0OCgTP9wClHwync3JsInRwGTooA20P9zWobUnEFbEiAgRVYCxuYoldRE6NLhSC854/YTjMBeevH1TNa38lpavGiI4UwFhg70U9/JuYs21hoFyzVfaWlVfOkAMm1U/n4wHq6FZW461S5PY4A/UI1Mr8WgeIHU9GqMBtFvjynzq3SLenOPgdmKtyJ3V8EOU+DlgwKmDbxMVMtYNDZtoQvOWnuvlJ6ICDcqcW7OUkmwCKodjxxPvrdaPxyZDhT7h4FgRtrAOS8qR6L7x9D4ZIoxOMPudGvr99OSb4KVtaAEt/R7hKxG3OsCAwEAAaNCMEAwHwYDVR0jBBgwFoAU680YSkZnx1IaJAmI49LlTGiia0wwHQYDVR0OBBYEFMaWOY7Vf/iB3WVA96j5kRtbF8prMA0GCSqGSIb3DQEBCwUAA4IBAQAJ+bssSFWE6KsYT7HSDKag4Eot7yNGMY4Don/MilDnOREdu20QUS131DKrSkpBQiCXbyRUQjUoun4yue0EG+rlG3QUIlNNdJ4KZJB+dTYdLUV7XTYJNPimKAmoZ+PFNvT1eGgWcMT+MbTfpk0mw0V8IprYGa8UPchd6vtSVwpbTcPc/F4bgUTlm/V+FG4bQS61gF0koj0DEZjzat7CBHpozRgfRlXgwu26vnhWGc99uKH4GAKN4JpqPi/6Yz+7iQNJUC3yeezgBxFrIXuLpkBZSP4zunf9VxsICnxkFUXOTuYBdcbhPNzqMknD5ijFcFRZITwdv7x3uJGLkM7iUfBp</wsse:KeyIdentifier>
</wsse:SecurityTokenReference>
</ds:KeyInfo>
<xenc:CipherData>
<xenc:CipherValue>af9+FhA91ytLwjeRvTYJsRCkhjHmAQGwqYwMBoNZBn7BZhF/a6EUpM9ByarVhx1SRCpjW5fb8tBVuJO1ZkjfTUZ5EAh/oDLbkmwPdSAAVzmAURHwCq3XQgMZV3lAczlLnPamxjjZBCGqxvAmBo1CvFFPC4AcBedqY92mP8XGyVHpS7JYKOxqXK2vUA1by7371x+Mu0aoS2zJPyPLa1IPwOYgR9qicmWz1RNPiEVA8ZBCN0NRyg7FLJxdUcE81z+1SjButBo2j3qcwkNcecHzZAnweY+LSWp3H5JA3WNzUHUuvFHEaPzT5jd7fUI16xo8NLK8/Rd8Eq/zDD+T3baeVQ==</xenc:CipherValue>
</xenc:CipherData>
<xenc:ReferenceList>
<xenc:DataReference URI="#ED-1B758D26C51BFCD86614340101135852"/>
</xenc:ReferenceList>
</xenc:EncryptedKey>
</wsse:Security>
<technicalAddress xmlns="http://example.com/schema/2014" xmlns:ns2="http://www.w3.org/2000/09/xmldsig#"/>
<activationHeader xmlns="http://example.com/schema/2014" xmlns:ns2="http://www.w3.org/2000/09/xmldsig#">
<organisationIdentification>
<mainRegistrationNumber>8079</mainRegistrationNumber>
<isoCountryCode>DK</isoCountryCode>
</organisationIdentification>
<functionIdentification>112233445566778899</functionIdentification>
<erpInformation/>
<endToEndMessageId>d28b6a7dad414014a59029ef1a7e84d4</endToEndMessageId>
<createDateTime>2015-06-11T10:08:33.258+02:00</createDateTime>
</activationHeader>
</soap:Header>
<soap:Body>
<xenc:EncryptedData xmlns:xenc="http://www.w3.org/2001/04/xmlenc#" Id="ED-1B758D26C51BFCD86614340101135852" Type="http://www.w3.org/2001/04/xmlenc#Content">
<xenc:EncryptionMethod Algorithm="http://www.w3.org/2001/04/xmlenc#aes256-cbc"/>
<ds:KeyInfo xmlns:ds="http://www.w3.org/2000/09/xmldsig#">
<wsse:SecurityTokenReference xmlns:wsse="http://docs.oasis-open.org/wss/2004/01/oasis-200401-wss-wssecurity-secext-1.0.xsd" xmlns:wsse11="http://docs.oasis-open.org/wss/oasis-wss-wssecurity-secext-1.1.xsd" wsse11:TokenType="http://docs.oasis-open.org/wss/oasis-wss-soap-message-security-1.1#EncryptedKey">
<wsse:Reference URI="#EK-1B758D26C51BFCD86614340101135741"/>
</wsse:SecurityTokenReference>
</ds:KeyInfo>
<xenc:CipherData>
<xenc:CipherValue>dTSVuEJ90OYguQOsOz2ZtcE2mybwuvVl19pp7/e5yuvNygx3w5v+prpEvbjYLauiIAB3lrVDK2astJeYJGnDbaVJVeU0YqH5ItYVn7Wz36jJM52KB+UNbYo8EdTKYjsZuADzH+tAoA+pwYxGBXMEQctNI+C711HgP2hbpHNYOG7nAMOIrP/0B3FCy+st+9CbYlwAEENreTYunEEA41hciFnWCsIx0el7OeuiA6V51fAmvrF19RPNKwaptvbvmVdKj//RQ/0U1kRny16mDnFfX92bI3HBQm4XJA0nEfSvio7EUAAdhe77GMfu7+JELqXNowPGPLlvrbCFYnQhxGRITHtTIEbtJA6MKtBzHgjtw5pt7oWxKgGUnaJTfOPOSv43RLFGggkT/+gTjnZOagu8hhXp0x5HXJuZzw90aIS3jAfSPDc2ivct4WhWk0wcuQyC2rAh4I7gtiR+LqJJGqvucw4S+NR95FunKHKEW4yasKW1oU31/rRbp4Bmwo6BPsQlxnaSHPtk68IVkYDBslz1A5gOP+M/Iam2WI02y6sE/7aAH1ruN3pZlVuYFc3JDNHOPOvevP110d60lroknGdc9vxcFfj48OCKw/8Ed6tiXtAvk0Qu9Qt4ZyLUoPKIWEqjdLjwVadTDJQFAxRptNgiCos7s0czadUu7FNCRxfndjDxhA7trvys44ufEyK++YzZIgNu3r4dywNI22Nm+JZtLj+rX8ARE6FTPlxGBD0SSdXsfCfY2N1ytBBHQRnPsVaHK1p7KOhwQVbqEupcGyvaRolnymOzDLGFdS06OGYFrYXdgIbuqYtZP8QerXtUl0sWNAvvqHSPCQcpKecpMEecar+FUVwLEA+H1wzOprCMbRR+EgIboeDqQ7GxXqugkuFyvnlLDgxnaWhEhQb/5kAcQmnyUZ57MhDcUJqqQ4Cdmwrcxho1P+YqWY9yn0E86F+hl5976a/gH5KBobB84OWmgcX42eAmqpJf+8c8SuBv+7NctbQOk21aYlFEpkwSme/kG1/edtyoHQH/hF0RB1cT8g+u9S9AK2rs3s2G+Ap0U5oyY8pqJalGdZSBudE0sU4mhOV8trtx0FrN9A7pNkTcGPH25nCtyIz6rzR+DP8Mtgw5385s5ivVlDb+z74Wbh6iu7ZkVAogNTpUYU/1BxDXWJqFMkFmfziNxQ5AQqm1vGlBzXifoQkUFX1riutNphmu0Hs+7KMmMLvtW2cXmQDpkHFKVheeN4w7pBCEZ8KhZ0VTOwRZcdvrNcpYfXM13/QdTHQmCqqwgS/VvlUFz7PDn0/OKo6moUic8W6b1iEvd3kfc7QkunxoOUoJr4RwJ+PqCzN6PxQivAFA2tmDPc8qEa1PAdxTeNFoR/6dNQRojouuJq3C1LrbmGf6lQPvKi3KeKHXyjmDr7Tve+al2tcWJVr+1qEM3/XuthoiZbuTDxYUjZ2nf2fhHrmNcfvrfNxSNHVdQPp2R9Rf3eGxlRJsmRpef66VbYhOpmiH4xmq45EWiyBZmYm+tZtjsP51EDMIvdFbVRSGO/hMqURrDSsJXJeot27Iup2s0P2n/6a9k0c4SVvf/WXNN5x9JNvjU97bQNDQRfonJmo9pRYYHl1tSqNIYBK7KsMH+qr1vmiJuhrXUuL/RtOKvE9KXQ8kGoC9oF5rFn21z40ElxG5XRTASg==</xenc:CipherValue>
</xenc:CipherData>
</xenc:EncryptedData>
</soap:Body>
</soap:Envelope>
First of all I have never worked with SOAP before so chances I do things wrong has pretty good odds :)
Have found something here, but I need more details https://www.w3.org/TR/2002/REC-xmlenc-core-20021210/Overview.html#aes256-cbc
How are the iv and the key stored in CipherValue in the header?
When sending the XML request to the webservice I get this error
23-08-2018 12:50:02 General exception:Padding is invalid and cannot be removed.
23-08-2018 12:50:02 Stack trace: at System.Security.Cryptography.CapiSymmetricAlgorithm.DepadBlock(Byte[] block, Int32 offset, Int32 count)
at System.Security.Cryptography.CapiSymmetricAlgorithm.TransformFinalBlock(Byte[] inputBuffer, Int32 inputOffset, Int32 inputCount)
at System.Security.Cryptography.Xml.EncryptedXml.DecryptData(EncryptedData encryptedData, SymmetricAlgorithm symmetricAlgorithm)
at SomeClassCore.XmlSecurity.Decryptor.DecryptData(Byte[] symmetricKey)
at SomeClassCore.SecurityServiceImpl.UnwrapRequest(ServiceRequest serviceRequest)
at BD.BCA.MessageHandler.MessageHandler.ProcessRequest(HttpContext context)
Have searched a bit more.. Maybe the iv must be a part of the stored data. But it's still not working? Same error as above
class Encryption {
const AES256_CBC = 'AES-256-CBC';
public function data_encrypt(string $data, string $cipher): Array{
switch($cipher){
case self::AES256_CBC:
$key_length = 32;
$block_length = 16;
break;
}
$iv = openssl_random_pseudo_bytes(openssl_cipher_iv_length($cipher));
$key = openssl_random_pseudo_bytes($key_length);
$encrypted_data = $iv.openssl_encrypt($data, $cipher, $key, OPENSSL_RAW_DATA, $iv);
return [
'data' => base64_encode($this->pkcs7_padding($encrypted_data, $block_length)),
'key' => $key
];
}
public function key_encrypt(string $key): string{
$public_cert = openssl_pkey_get_public('contents of public cert');
openssl_public_encrypt($key, $data, $public_cert, OPENSSL_PKCS1_OAEP_PADDING);
openssl_free_key($public_cert);
return base64_encode($data);
}
private function pkcs7_padding(string $data, int $block_length): string{
$pad = $block_length - (strlen($data) % $block_length);
return $data.str_repeat(chr($pad), $pad);
}
}
$Enc = new Encryption;
$data_encrypted = $Enc->data_encrypt('The message I want to encrypt', Encryption::AES256_CBC);
// This base64 encoded string goes to <EncryptedData>
$data_encrypted['data'];
// This base64 encoded string goes to <EncryptedKey> in the header
$Enc->key_encrypt($data_encrypted['key']);
update
Have been in contact with the maintainer of the webservice and OAEP padding is used with the RSA encryption and PKCS7 padding is used with AES chipher..
As I can see this is also what I do?
*TESTED AND WORKING CODE *
I would suggest that you separate the different parts involved. The most likely cause to your problems is the order of execution (i.e. you should do padding before encryption). I am also surprised that there is no signature, but that might not be required in your case. However, I prepared the suggested code for you to test and also added decrypt/decode functions to make testing easier. Good luck.
<?php
class Encryption {
const AES256_CBC = 'AES-256-CBC';
const IV_BYTES = 16;
protected $binary_security_token = null;
protected $private_key = null;
protected $public_key = null;
public function data_encrypt(string $data, string $password): Array {
$key = hash('sha256', $password, true);
$iv = openssl_random_pseudo_bytes(self::IV_BYTES);
$padding = 16 - (strlen($data) % 16);
$data .= str_repeat(chr($padding), $padding);
$encrypted_data = openssl_encrypt($data, self::AES256_CBC, $key, OPENSSL_RAW_DATA, $iv);
$encoded_data = base64_encode($iv . $encrypted_data);
return [
'data' => $encoded_data,
'key' => $key
];
}
public function data_decrypt(string $data, string $password): Array {
$decoded_data = base64_decode($data);
$key = hash('sha256', $password, true);
$iv = substr($decoded_data, 0, self::IV_BYTES);
$encrypted_data = substr($decoded_data, self::IV_BYTES);
$decrypted_data = openssl_decrypt($encrypted_data, self::AES256_CBC, $key, OPENSSL_RAW_DATA, $iv);
$padding = ord($decrypted_data[strlen($decrypted_data) - 1]);
return [
'data' => substr($decrypted_data, 0, -$padding)
];
}
public function key_encrypt(string $key): ?string {
$encoded_data = null;
if ($this->public_key && openssl_public_encrypt($key, $data, $this->public_key, OPENSSL_PKCS1_OAEP_PADDING)) {
$encoded_data = base64_encode($data);
}
// openssl_free_key($this->public_key);
return $encoded_data;
}
public function key_decrypt(string $data): ?string {
$decrypted_data = null;
$decoded_data = base64_decode($data, true);
if ($this->private_key && openssl_private_decrypt($decoded_data, $decrypted, $this->private_key, OPENSSL_PKCS1_OAEP_PADDING)) {
$decrypted_data = $decrypted;
}
// openssl_free_key($decrypted);
return $decrypted_data;
}
public function generate_keys(): void {
$config = [ "private_key_bits" => 2048, "private_key_type" => OPENSSL_KEYTYPE_RSA ];
$resource = openssl_pkey_new($config);
if (openssl_pkey_export($resource, $this->private_key)) {
echo "private_key:\n" . $this->private_key . "\n";
$private_key_file = "private_key.pem";
file_put_contents("private_key.pem" , $this->private_key);
}
$this->public_key = openssl_pkey_get_details($resource);
$this->public_key = $this->public_key["key"];
$this->binary_security_token = preg_replace("#-.+-|[\r\n]| #", "", $this->public_key);
echo "public_key:\n" . $this->public_key . "\n";
file_put_contents("public_key.pem", $this->public_key);
}
public function load_keys(): void {
$private_key_path = realpath(dirname(__FILE__) . "/private_key.pem");
if (!$private_key_path) {
$this->generate_keys();
return;
}
$private_key_contents = file_get_contents($private_key_path);
if (!$private_key_contents) {
$this->generate_keys();
return;
}
$public_key_path = realpath(dirname(__FILE__) . "/public_key.pem");
if (!$public_key_path) {
$this->generate_keys();
return;
}
$public_key_contents = file_get_contents($public_key_path);
if (!$public_key_contents) {
$this->generate_keys();
return;
}
// Signature to see that data is not manipulated, could be performed on an encrypted body. The spec says you only make a signature for what you can see.
// Is it important to "hide data", "detect manipulated data" or both ...
$this->binary_security_token = preg_replace("#-.+-|[\r\n]| #", "", $public_key_contents); // BinarySecurityToken for securityToken in Security header
// ValueType: "http://docs.oasis-open.org/wss/2004/01/oasis-200401-wss-x509-token-profile-1.0#X509v3"
// EncodingType: "http://docs.oasis-open.org/wss/2004/01/oasis-200401-wss-soap-message-security-1.0#Base64Binary"
if (openssl_pkey_export($private_key_contents, $this->private_key)) {
echo "private_key:\n" . $this->private_key . "\n";
}
$public_resource = openssl_pkey_get_public($public_key_contents);
if ($public_resource) {
$this->public_key = openssl_pkey_get_details($public_resource);
$this->public_key = $this->public_key["key"];
echo "public_key:\n" . $this->public_key . "\n";
}
}
}
$enc = new Encryption();
$encrypted = $enc->data_encrypt("The message I want to encrypt", "password");
// This base64 encoded string goes to <EncryptedData>
// $encrypted['data']
// Test that data_encrypt / data_decrypt works (from a terminal)
echo "encrypted data:\n" . $encrypted["data"] . "\n";
$decrypted = $enc->data_decrypt($encrypted["data"], "password");
echo "decrypted data:\n" . $decrypted["data"] . "\n";
// This base64 encoded string goes to <EncryptedKey> in the header
// $enc->key_encrypt($encrypted['key']);
if (version_compare(phpversion(), "7.1.0", ">=")) {
$enc->load_keys();
$pwd_hash_pre = bin2hex($encrypted["key"]);
echo "hex key:" . $pwd_hash_pre . "\n";
$encrypted_key = $enc->key_encrypt($encrypted["key"]);
echo "\nencrypted and base64encoded key:" . $encrypted_key . "\n";
$decrypted_key = $enc->key_decrypt($encrypted_key);
$pwd_hash_post = bin2hex($decrypted_key);
echo "\ndecrypted and decoded key:" . $pwd_hash_post . "\n";
$equal_hashes = $pwd_hash_pre === $pwd_hash_post ? 'true' : 'false';
echo "password hashes equal:" . $equal_hashes . "\n";
}
Your error suggest that the API failed to decrypt the openssl AES-256-CBC data.
I think the reason is because in your class you are routing the encryption through your pkcs7_padding() function. I believe that by default, as long as you don't specify OPENSSL_ZERO_PADDING in your openssl_encrypt() function that the padding is pkcs7. The block size for all AES encryption is 128bits or 16 bytes.
So in essence you are padding your already padded encryption. So basically I just removed your pkcs7_padding() from your class.
I did test your public key encryption. I was able to use a 2048b rsa key from 2048b certificate and generate an encrypted public key using a PEM formatted certificate. Whether or not it's padded correctly I have no idea. But the OPENSSL_PKCS1_OAEP_PADDING is probably correct.
My guess is that the RSA encryption worked if the API made it to the AES portion.
As far as how you are assembling the data into the XML I don't have a clue.
But is seems reasonable that in the <xenc:EncryptedKey> tag in the cipher value would be the RSA encrypted key and for the <xenc:EncryptedData> tag the cipher value would be the AES ecrypted data. You just have to figure out how the API is getting the IV.
Read your API docs for how they are expecting the IV to be delivered. I will keep looking too if this does not work for you.
I will research on that later. But for know try without manually padding your encryption. Hope it helps.
Another thing to consider is that in your case example you don't need to use an IV. In your AES encryption you are generating a new key for every encryption and then encrypting the AES key via the RSA public key obtained by your certificate.
If you were using the same AES key I would see a need to implement an IV but in this case I don't. Regardless... We need to know if the API expects an IV and if it does, how it is expected to be sent?
class Encryption {
const AES256_CBC = 'AES-256-CBC';
public function __construct(){
}
public function data_encrypt($data, $cipher){
switch($cipher){
case self::AES256_CBC:
$key_length = 32;
$block_length = 16;
break;
}
$iv = openssl_random_pseudo_bytes(openssl_cipher_iv_length($cipher));
$key = openssl_random_pseudo_bytes($key_length);
$encrypted_data = $iv . openssl_encrypt($data, $cipher, $key, OPENSSL_RAW_DATA, $iv);
return [
'data' => base64_encode($encrypted_data),
'key' => $key //Does this need to be encoded?
];
}
//***Important***
//Make sure you certificate is 1.) a x.509 certificate resource, 2.)A file path that leads to a PEM encoded certificate, or 3.) a PEM formatted key.
public function key_encrypt($text){
$keyResource = openssl_pkey_get_public(file_get_contents('path/to/myCert.pem')); //This returns a resource or FALSE.
if(!$keyResource){
echo 'Something wrong with certificate.';
}
openssl_public_encrypt($text, $cipherText, $keyResource, OPENSSL_PKCS1_OAEP_PADDING);
openssl_free_key($keyResource);
return base64_encode($cipherText);
}
}
$Enc = new Encryption;
$cipherText = $Enc->data_encrypt('The message I want to encrypt', Encryption::AES256_CBC);
// This base64 encoded string goes to <EncryptedData>
echo 'AES Data: ' . $cipherText['data'] . '<br><br>';
echo 'AES Key: ' . $cipherText['key'] . '<br><br>';
// This base64 encoded string goes to <EncryptedKey> in the header
$key = $Enc->key_encrypt($cipherText['key']);
echo 'RSA OAEP Padded Key: ' . $key;
We migrated to PHPSecLib a while ago, but did not migrate our legacy data that was encrypted by the old PEAR\Crypt_RSA library. We've reached a point where we need to migrate that data into PHPSecLib's RSA format. While investigating this, I came across this old forum thread. I attempted to apply the suggestion in the response, but could not get it to successfully decrypt our data. It's not erroring out or anything, it just appears to still be encrypted or encoded. We're running PHPSecLib 2.0.6 currently, and I suspect the instructions were for 1.x.
Here's a roughed out version of my adapted decryption flow (based off the forum thread):
$rsaDecryptor = new RSA();
// The Private Key is encrypted based off a password
$mc = mcrypt_module_open(MCRYPT_RIJNDAEL_128, '', MCRYPT_MODE_ECB, '');
$iv = mcrypt_create_iv(mcrypt_enc_get_iv_size($mc), MCRYPT_DEV_URANDOM);
$keySize = mcrypt_enc_get_key_size($mc);
$key = substr($rsaDecryptor->password, 0, $keySize);
mcrypt_generic_init($mc, $key, $iv);
$privateKey = mdecrypt_generic($mc, base64_decode($privateKey));
mcrypt_generic_deinit($mc);
mcrypt_module_close($mc);
list($privateKeyModulus, $privateKeyExponent) = unserialize(base64_decode($privateKey));
$privateKeyExponent = new BigInteger(strrev($privateKeyExponent), 256);
$privateKeyModulus = new BigInteger(strrev($privateKeyModulus), 256);
$rsaDecryptor->modulus = $privateKeyModulus;
$rsaDecryptor->exponent = $privateKeyExponent;
$rsaDecryptor->publicExponent = $privateKeyExponent;
$rsaDecryptor->k = strlen($this->decRSA->modulus->toBytes());
// ciphertext is the raw encrypted string created by PEAR\Crypt_RSA
$value = base64_decode($ciphertext);
$value = new BigInteger($value, 256);
$value = $rsaDecryptor->_exponentiate($value)->toBytes();
$value = substr($value, 1);
Bugs In PEAR's Crypt_RSA
So I was playing around with this. There's a bug in PEAR's Crypt_RSA (latest version) that might prevent this from working at all. The following code demonstrates:
$key_pair = new Crypt_RSA_KeyPair(1024);
$privkey = $key_pair->getPrivateKey();
$pubkey = $key_pair->getPublicKey();
$a = $privkey->toString();
$b = $pubkey->toString();
echo $a == $b ? 'same' : 'different';
You'd expect $a and $b to be different, wouldn't you? Well they're not. This is because RSA/KeyPair.php does this:
$this->_public_key = &$obj;
...
$this->_private_key = &$obj;
If you remove the ampersands it works correctly but they're in the code by default.
It looks like this is an unresolved issue as of https://pear.php.net/bugs/bug.php?id=15900
Maybe it behaves differently on PHP4 but I have no idea.
Decrypting Data
Assuming the above bug isn't an issue for you then the following worked for me (using phpseclib 2.0):
function loadKey($key) // for keys genereated with $key->toString() vs $key->toPEMString()
{
if (!($key = base64_decode($key))) {
return false;
}
if (!($key = unserialize($key))) {
return false;
}
list($modulus, $exponent) = $key;
$modulus = new BigInteger(strrev($modulus), 256);
$exponent = new BigInteger(strrev($exponent), 256);
$rsa = new RSA();
$rsa->loadKey(compact('modulus', 'exponent'));
return $rsa;
}
function decrypt($key, $ciphertext)
{
if (!($ciphertext = base64_decode($ciphertext))) {
return false;
}
$key->setEncryptionMode(RSA::ENCRYPTION_NONE);
$ciphertext = strrev($ciphertext);
$plaintext = $key->decrypt($ciphertext);
$plaintext = strrev($plaintext);
$plaintext = substr($plaintext, 0, strpos($plaintext, "\0"));
return $plaintext[strlen($plaintext) - 1] == "\1" ?
substr($plaintext, 0, -1) : false;
}
$key = loadKey($private_key);
$plaintext = decrypt($key, $ciphertext);
echo $plaintext;
Private Keys Generated with toPEMString()
With PEAR's Crypt_RSA you can generate private keys an alternative way:
$key_pair->toPEMString();
This method works without code changes. If you used this approach to generate your private keys the private key starts off with -----BEGIN RSA PRIVATE KEY-----. If this is the case then you don't need to use the loadKey function I wrote. You can do this instead:
$key = new RSA();
$key->loadKey('...');
$plaintext = decrypt($key, $ciphertext);
echo $plaintext;
Sorry for this question. I've read all the previous questions, but my code still not work.
Thanks in advance to anyone that will help me in understanding where is the problem.
In android I use this code for reading the public key and produce the encrypted text:
public static PublicKey getPublicKeyFromString(String stringKey) throws Exception {
byte[] keyBytes = stringKey.getBytes();
byte[] decode = Base64.decode(keyBytes, Base64.DEFAULT);
KeyFactory fact = KeyFactory.getInstance("RSA");
X509EncodedKeySpec x509KeySpec = new X509EncodedKeySpec(decode);
return (PublicKey) fact.generatePublic(x509KeySpec);
}
public static String RSAEncrypt(final String plain, final PublicKey publicKey)
throws NoSuchAlgorithmException, NoSuchPaddingException,
InvalidKeyException, IllegalBlockSizeException, BadPaddingException {
byte[] encryptedBytes;
Cipher cipher;
cipher = Cipher.getInstance("RSA");
cipher.init(Cipher.ENCRYPT_MODE, publicKey);
encryptedBytes = cipher.doFinal(plain.getBytes());
return Base64.encodeToString(encryptedBytes, Base64.DEFAULT);
}
//I call these functions in this manner.
private final String pubKeyString =
//"-----BEGIN PUBLIC KEY-----" +
"MIG..." +
"...";
//"-----END PUBLIC KEY-----"
PublicKey pubKey = RSAFunctions.getPublicKeyFromString(pubKeyString);
String encData = RSAFunctions.RSAEncrypt("prova", pubKey);
The publickey.php and privatekey.php file are generated in php with this code:
<?php
include('./Crypt/RSA.php');
$rsa = new Crypt_RSA();
extract($rsa->createKey()); // == $rsa->createKey(1024) where 1024 is the key size
$File1 = "./privatekey.php";
$Handle = fopen($File1, 'w');
fwrite($Handle, "<?php \$privatekey=\"" . $privatekey . "\"?>");
fclose($Handle);
$File2 = "./publickey.php";
$Handle = fopen($File2, 'w');
fwrite($Handle, "<?php \$publickey=\"" . $publickey . "\"?>");
fclose($Handle);
?>
In php I use this code for decrypt data:
<?php
include('Crypt/RSA.php');
require('privatekey.php');
$rsa = new Crypt_RSA();
$rsa->loadKey($privatekey); // private key
$base64_string = $_GET["data"];
$base64_string = str_replace(' ', '+', $base64_string);
$ciphertext = base64_decode( $base64_string );
//$rsa->setEncryptionMode(CRYPT_RSA_ENCRYPTION_OAEP);
$rsa->setEncryptionMode(CRYPT_RSA_ENCRYPTION_PKCS1);
$plaintext = $rsa->decrypt($ciphertext);
echo $plaintext;
?>
I've also made a php crypt script for testing my decrypt php function. This is the code of my encrypt.php:
<?php
include('Crypt/RSA.php');
require('publickey.php');
$rsa = new Crypt_RSA();
$rsa->loadKey($publickey); // public key
$plaintext = $_GET["data"];
//$rsa->setEncryptionMode(CRYPT_RSA_ENCRYPTION_OAEP);
$ciphertext = $rsa->encrypt($plaintext);
echo base64_encode( $ciphertext );
?>
I've no problem when encrypt and decrypt text using only php, but if I use the encrypted data made with android app, php give me an error in decrypting.
Thanks for the attention.
In your php there is :
CRYPT_RSA_ENCRYPTION_PKCS1
but when you create your Cipher object on Android you omit the mode and padding
cipher = Cipher.getInstance("RSA");
you should try something like
Cipher c = Cipher.getInstance("AES/CBC/PKCS1Padding");
ref :
http://developer.android.com/reference/javax/crypto/Cipher.html
I want to exchange data with other businesses using PHP/OpenSSL. Each business creates public/private keys and publishes the public key. Then, I write code to manage all that. Here is a code in PHP (mostly from php.net):
<?php
$data = "secret message";
$key = file_get_contents("bus1.pub");
// get temp file w/ write access
$plaintextfile = tempnam(sys_get_temp_dir(), 'abc');
$ciphertextfile = tempnam(sys_get_temp_dir(), 'abc');
$fp = fopen($plaintextfile, "w");
fwrite($fp, $data);
fclose($fp);
// encrypt it
if (openssl_pkcs7_encrypt($plaintextfile, $ciphertextfile, $key,
array("To" => "nighthawk#example.com", // keyed syntax
"From: HQ <hq#example.com>", // indexed syntax
"Subject" => "Eyes only"))) {
echo "encryption ok<br>";
} else
echo "failure<br>";
?>
However, I get an error (failure). I suspect that I do not generate the keys correctly using OpenSSL. Please help in how to properly generate the keys so that they can be read by PHP functions above.
This is what I tried:
openssl genrsa -out bus1.pem 2048
openssl rsa -in bus1.pem -pubout > bus1.pub
Another suggestion from php docs https://www.php.net/manual/en/function.openssl-encrypt.php.
This Is The Most Secure Way To Encrypt And Decrypt Your Data,
It Is Almost Impossible To Crack Your Encryption.
--------------------------------------------------------
--- Create Two Random Keys And Save Them In Your Configuration File ---
<?php
// Create The First Key
echo base64_encode(openssl_random_pseudo_bytes(32));
// Create The Second Key
echo base64_encode(openssl_random_pseudo_bytes(64));
?>
--------------------------------------------------------
<?php
// Save The Keys In Your Configuration File
define('FIRSTKEY','Lk5Uz3slx3BrAghS1aaW5AYgWZRV0tIX5eI0yPchFz4=');
define('SECONDKEY','EZ44mFi3TlAey1b2w4Y7lVDuqO+SRxGXsa7nctnr/JmMrA2vN6EJhrvdVZbxaQs5jpSe34X3ejFK/o9+Y5c83w==');
?>
--------------------------------------------------------
<?php
function secured_encrypt($data)
{
$first_key = base64_decode(FIRSTKEY);
$second_key = base64_decode(SECONDKEY);
$method = "aes-256-cbc";
$iv_length = openssl_cipher_iv_length($method);
$iv = openssl_random_pseudo_bytes($iv_length);
$first_encrypted = openssl_encrypt($data,$method,$first_key, OPENSSL_RAW_DATA ,$iv);
$second_encrypted = hash_hmac('sha3-512', $first_encrypted, $second_key, TRUE);
$output = base64_encode($iv.$second_encrypted.$first_encrypted);
return $output;
}
?>
--------------------------------------------------------
<?php
function secured_decrypt($input)
{
$first_key = base64_decode(FIRSTKEY);
$second_key = base64_decode(SECONDKEY);
$mix = base64_decode($input);
$method = "aes-256-cbc";
$iv_length = openssl_cipher_iv_length($method);
$iv = substr($mix,0,$iv_length);
$second_encrypted = substr($mix,$iv_length,64);
$first_encrypted = substr($mix,$iv_length+64);
$data = openssl_decrypt($first_encrypted,$method,$first_key,OPENSSL_RAW_DATA,$iv);
$second_encrypted_new = hash_hmac('sha3-512', $first_encrypted, $second_key, TRUE);
if (hash_equals($second_encrypted,$second_encrypted_new))
return $data;
return false;
}
?>
OK. I figured out the mistake. Thanks to #towr for pointing out a debugging tool. For other readers, the solution is to generate the certificate as follows. Each business need to do this, keep the private key and publish the certificate:
openssl genrsa -out business1.pass.key 2048
openssl rsa -in business1.pass.key -out business1.key
openssl req -new -key business1.key -out business1.csr
openssl x509 -req -days 3650 -in business1.csr -signkey business1.key -out business1.crt
I want to generate ssh keypair from php can anyone please guide me how to do it? I have tried the shell_exec but the shell asks questions so that command does not work. I would like to specify filename and path in which to put the keys after generation.
This is based on Converting an OpenSSL generated RSA public key to OpenSSH format (PHP). Thanks shevron.
<?php
$rsaKey = openssl_pkey_new(array(
'private_key_bits' => 1024,
'private_key_type' => OPENSSL_KEYTYPE_RSA));
$privKey = openssl_pkey_get_private($rsaKey);
openssl_pkey_export($privKey, $pem); //Private Key
$pubKey = sshEncodePublicKey($rsaKey); //Public Key
$umask = umask(0066);
file_put_contents('/tmp/test.rsa', $pem); //save private key into file
file_put_contents('/tmp/test.rsa.pub', $pubKey); //save public key into file
print "Private Key:\n $pem \n\n";
echo "Public key:\n$pubKey\n\n";
function sshEncodePublicKey($privKey) {
$keyInfo = openssl_pkey_get_details($privKey);
$buffer = pack("N", 7) . "ssh-rsa" .
sshEncodeBuffer($keyInfo['rsa']['e']) .
sshEncodeBuffer($keyInfo['rsa']['n']);
return "ssh-rsa " . base64_encode($buffer);
}
function sshEncodeBuffer($buffer) {
$len = strlen($buffer);
if (ord($buffer[0]) & 0x80) {
$len++;
$buffer = "\x00" . $buffer;
}
return pack("Na*", $len, $buffer);
}
?>
I would use phpseclib, a pure PHP RSA implementation:
<?php
include('Crypt/RSA.php');
$rsa = new Crypt_RSA();
$rsa->setPublicKeyFormat(CRYPT_RSA_PUBLIC_FORMAT_OPENSSH);
extract($rsa->createKey());
echo "$publickey\r\n\r\n$privatekey";
?>
Sample output:
ssh-rsa AAAAB3NzaC1yc2EAAAADAQABAAAAgQCqGKukO1De7zhZj6+H0qtjTkVxwTCpvKe4eCZ0FPqri0cb2JZfXJ/DgYSF6vUpwmJG8wVQZKjeGcjDOL5UlsuusFncCzWBQ7RKNUSesmQRMSGkVb1/3j+skZ6UtW+5u09lHNsj6tQ51s1SPrCBkedbNf0Tp0GbMJDyR4e9T04ZZw== phpseclib-generated-key
-----BEGIN RSA PRIVATE KEY-----
MIICXAIBAAKBgQCqGKukO1De7zhZj6+H0qtjTkVxwTCpvKe4eCZ0FPqri0cb2JZfXJ/DgYSF6vUp
wmJG8wVQZKjeGcjDOL5UlsuusFncCzWBQ7RKNUSesmQRMSGkVb1/3j+skZ6UtW+5u09lHNsj6tQ5
1s1SPrCBkedbNf0Tp0GbMJDyR4e9T04ZZwIDAQABAoGAFijko56+qGyN8M0RVyaRAXz++xTqHBLh
3tx4VgMtrQ+WEgCjhoTwo23KMBAuJGSYnRmoBZM3lMfTKevIkAidPExvYCdm5dYq3XToLkkLv5L2
pIIVOFMDG+KESnAFV7l2c+cnzRMW0+b6f8mR1CJzZuxVLL6Q02fvLi55/mbSYxECQQDeAw6fiIQX
GukBI4eMZZt4nscy2o12KyYner3VpoeE+Np2q+Z3pvAMd/aNzQ/W9WaI+NRfcxUJrmfPwIGm63il
AkEAxCL5HQb2bQr4ByorcMWm/hEP2MZzROV73yF41hPsRC9m66KrheO9HPTJuo3/9s5p+sqGxOlF
L0NDt4SkosjgGwJAFklyR1uZ/wPJjj611cdBcztlPdqoxssQGnh85BzCj/u3WqBpE2vjvyyvyI5k
X6zk7S0ljKtt2jny2+00VsBerQJBAJGC1Mg5Oydo5NwD6BiROrPxGo2bpTbu/fhrT8ebHkTz2epl
U9VQQSQzY1oZMVX8i1m5WUTLPz2yLJIBQVdXqhMCQBGoiuSoSjafUhV7i1cEGpb88h5NBYZzWXGZ
37sJ5QsW+sJyoNde3xH8vdXhzU7eT82D6X/scw9RZz+/6rCJ4p0=
-----END RSA PRIVATE KEY-----
Source: http://phpseclib.sourceforge.net/rsa/examples.html#openssh
Has anybody tried this?
ssh-keygen -q -N place_your_passphrase_here -t rsa -f ~/.ssh/id_rsa
It's worked for me
Check the manpage of ssh-keygen to find out how to execute it without any prompts.
http://linux.die.net/man/1/ssh-keygen
This code uses only built-in PHP openssl functions.
It generates keys in a portable PEM format, suitable for use with most 3rd party software.
<?php
function generateRsaKeyPair(array $config = []): array
{
$config = array_merge([
'digest_alg' => 'sha512',
'private_key_bits' => 2048,
'private_key_type' => OPENSSL_KEYTYPE_RSA,
], $config);
// Create the keypair
$key = openssl_pkey_new($config);
if (!$key) {
throw new RuntimeException('Unable to generate RSA key pair');
}
// Get private key
openssl_pkey_export($key, $private);
if (!$private) {
throw new RuntimeException('Unable to extract private key');
}
// Get public key
$public = openssl_pkey_get_details($key)['key'] ?? null;
if (!$public) {
throw new RuntimeException('Unable to extract public key');
}
// Free key from memory
openssl_pkey_free($key);
// Return both keys
return compact('public', 'private');
}