Sign .mobileconfig on a PHP server - php

Could anyone please tell me how to use openssl smime -sign -signer cert.pem -inkey key.pem -certfile ca-bundle.pem -nodetach -outform der -in profile-uns.mobileconfig -out profile-sig.mobileconfig this within PHP (this one worked properly!)?
I tried
$path = __DIR__ . DIRECTORY_SEPARATOR; // my actual directory
$infilename = $path . 'profile.mobileconfig'; // my unsigned profile
$outfilename = $path . 'profile-sig.mobileconfig'; // my signed profile
$signcert = file_get_contents($path . 'cert.pem'); // my certificate to sign
$privkey = file_get_contents($path . 'key.pem'); // my private key of the certificate
$extracerts = $path . 'ca-bundle.pem'; // the cert chain of my CA
echo openssl_pkcs7_sign($infilename, $outfilename , $signcert, $privkey, array(), PKCS7_NOATTR,$extracerts);
without success. I also tried all of the PKCS7 attributes...

Calling openssl smime with exec works fine:
exec('openssl smime -sign -signer cert.pem -inkey key.pem -certfile ca-bundle.pem -nodetach -outform der -in profile.mobileconfig -out profile-sig.mobileconfig');

Actually, there's an easy approach to solve this problem:
/**
* Sign MobileConfig
*
* #string $file_full_pathname e.g. /tmp/example.mobileconfig
* #string $certificate_pathname e.g. /etc/cert.d/apple_distribution.cert.pem
* #string $private_key_pathname e.g. /etc/cert.d/apple_distribution.key.pem
* #bool $remove_file Optional, default is true, if you want to keep your file then set to false.
*
* #return string
*/
function signMobileConfig (
string $file_full_pathname,
string $certificate_pathname,
string $private_key_pathname,
bool $remove_file = true
) {
openssl_pkcs7_sign(
$file_full_pathname,
$file_full_pathname.'.sig',
file_get_contents($certificate_pathname),
file_get_contents($private_key_pathname),
[], 0
);
$signed = file_get_contents($file_full_pathname.'.sig');
if ($remove_file) {
unlink($file_full_pathname.'.sig');
unlink($file_full_pathname);
}
$trimmed = preg_replace('/(.+\n)+\n/', '', $signed, 1);
return base64_decode($trimmed);
}
Feel free to modify the code above to fulfill your demands.

Related

openssl_private_decrypt() "RSA_padding_check_PKCS1_type_2:pkcs decoding error"

Problem Statement
I'm trying to decrypt data using private_key from PKCS12 formatted file by openssl_private_decrypt(). However I'm getting empty string in response.
Exception
[
0 => "error:0909006C:PEM routines:get_name:no start line"
1 => "error:0407109F:rsa routines:RSA_padding_check_PKCS1_type_2:pkcs decoding error"
2 => "error:04065072:rsa routines:rsa_ossl_private_decrypt:padding check failed"
]
Files & Configuration
$pkcs12 = file_get_contents('/path/server.p12');
openssl_pkcs12_read($pkcs12, $p12, '123456');
if (false === openssl_private_decrypt($encrypted, $decrypted, $p12['pkey'], OPENSSL_PKCS1_PADDING))
{
$e = [];
while ($msg = openssl_error_string())
array_push($e, $msg);
dd($e);
}
Edit 1:
I've run the following command to generate CSR as well as private key for SSL certificates
$ openssl req -new -newkey rsa:2048 -nodes -keyout example.key -out example.csr
example.key (Private key in .key format)
example.csr (CSR)
Got certificate & files from CA.
example.crt
intermediate.crt
example.pem
Run below command to convert private key and certificate into to PKCS12 file.
$ openssl pkcs12 -export -in example.crt -inkey example.key -out example.p12 -certfile intermediate.crt
example.p12
Edit 2:
I've example.key header
"""
-----BEGIN PRIVATE KEY-----\n
aasdasdddddddddddddadsssssssjhjjjjjjjjjj\n
.
.
asddddddddasdkjabshjdhajskdhajgggggggggg\n
TtasdhjaskjZPqD0UcJAcP\n
-----END PRIVATE KEY-----\n
"""
Edit 3:
I've converted private key from .key to .pem using below command but still getting same error.
openssl rsa -in example.key -text > example.key.pem
Edit 4:
After some research I found out that '\n' could be the cause of this error as stated in #derN3rd.
1. Declaring local variable
$pkcs8pem = "-----BEGIN PRIVATE KEY-----
AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA
.
.
ZZZZZZZZZZZZZZZZZZZZZZZZ
-----END PRIVATE KEY-----";
dd($pkcs8pem);
Output:
"""
-----BEGIN PRIVATE KEY-----\n
AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA\n
.
.
ZZZZZZZZZZZZZZZZZZZZZZZZ\n
-----END PRIVATE KEY-----\n
"""
2. Using str_replace()
$privateKey = $p12['pkey'];
$privateKeyClean = str_replace(array("\r", "\n"), '', $privateKey);
dd($privateKeyClean);
Output:
"-----BEGIN PRIVATE KEY-----
AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA..ZZZZZZZZZZZZZZZZ-----END PRIVATE KEY-----"

SSL: match private key with certificate using PHP (without phpseclib)

I have tried to use this PHP code script to check SSL private key with SSL certificate match or not the result is match every time.
error_reporting(E_ALL & ~E_NOTICE);
if (!extension_loaded('OpenSSL')) {
$this->markTestSkipped("Need OpenSSL extension");
}
$pkey = "-----BEGIN PRIVATE KEY-----
MIIEvgIBADANBgkqhkiG9w0BAQEFAASCBKgwggSkAgEAAoIBAQDvwT54v2kQTRP3
ZnJepfuBgEUfrEqBZ7zLm87s1NHwwJNNbwqGCYTIoCv4xDgRCK7X7NVmMyV2OWIn
...
-----END PRIVATE KEY-----";
$cert = "-----BEGIN CERTIFICATE-----
MIIGRTCCBS2gAwIBAgIQVWcnF+whEw+mvnBlp/JMCzANBgkqhkiG9w0BAQsFADCB
kDELMAkGA1UEBhMCR0IxGzAZBgNVBAgTEkdyZWF0ZXIgTWFuY2hlc3RlcjEQMA4G
...
-----END CERTIFICATE-----";
$check_result = check_pkey_cert_match($pkey, $cert);
if($check_result == true) {
echo "Match";
} else {
echo "Not Match";
}
this function use openssl by shell_exec it can export files server.crt, server.key, server.csr
function check_pkey_cert_match($Private_Key, $Certificate) {
//checks if Private Key match Certificate
$random_blurp = rand(10,99999);
$tmp_dir = "/tmp/";
if(openssl_x509_export_to_file($Certificate, $tmp_dir.$random_blurp.'.server.crt')) {
echo "Export Cert OK = ".$tmp_dir.$random_blurp.".server.crt";
} else {
echo "Export Crt Error";
}
if(openssl_pkey_export_to_file($Private_Key, $tmp_dir.$random_blurp.'.server.key')) {
echo "Export Pkey OK = ".$tmp_dir.$random_blurp.".server.key";
} else {
echo "Export Pkey Error";
}
but when i use this shell_exec for check $pkey_check & $cert_check match or not it still result match every time. Because $pkey_check & $cert_check = null
$pkey_check = shell_exec('openssl pkey -in
'.$tmp_dir.$random_blurp.'.server.key -pubout -outform pem | sha256sum');
$cert_check = shell_exec('openssl x509 -in
'.$tmp_dir.$random_blurp.'.server.crt -pubout -outform pem | sha256sum');
// $csr_check = shell_exec('openssl req -in '.$tmp_dir.$random_blurp.'.server.csr -pubout -outform pem | sha256sum');
//remove those temp files.
unlink($tmp_dir.'server.crt');
unlink($tmp_dir.'server_key');
//unlink($tmp_dir.'server.csr');
//Check for match
if ( $cert_check == $pkey_check ) {
return true;
} else {
return false;
}
Result of above script
Export Cert OK = /tmp/41893.server.crt
Export Pkey OK = /tmp/41893.server.key
cert_check =
pkey_check =
Match
I have try another shell_exec but the same resutl
/*
$pkey_check = shell_exec('openssl rsa -noout -modulus -in server.key | openssl md5');
$cert_check = shell_exec('openssl x509 -noout -modulus -in server.crt | openssl md5');
$csr_check = shell_exec('openssl req -noout -modulus -in server.csr | openssl md5');
*/
/*
$pkey_check = shell_exec('openssl rsa -modulus -in '.$tmp_dir.$random_blurp.'.server.key | openssl md5 2>&1');
$cert_check = shell_exec('openssl x509 -modulus -in '.$tmp_dir.$random_blurp.'.server.crt | openssl md5 2>&1');
$csr_check = shell_exec('openssl req -noout -modulus -in '.$tmp_dir.$random_blurp.'.server.csr | openssl md5 2>&1');
*/
$pkey_check = shell_exec('openssl pkey -in '.$tmp_dir.$random_blurp.'.server.key -pubout -outform pem | sha256sum');
$cert_check = shell_exec('openssl x509 -in '.$tmp_dir.$random_blurp.'.server.crt -pubout -outform pem | sha256sum');
// $csr_check = shell_exec('openssl req -in '.$tmp_dir.$random_blurp.'.server.csr -pubout -outform pem | sha256sum');
(Posted on behalf of the question author).
This simple script use to check private key & certificate match or not.
error_reporting(E_ALL & ~E_NOTICE);
if (!extension_loaded('OpenSSL')) {
$this->markTestSkipped("Need OpenSSL extension");
}
Define $cert and $pkey (or use $_POST[$cert] and $_POST[$pkey] instead)
$pkey = "-----BEGIN PRIVATE KEY-----
MIIEvgIBADANBgkqhkiG9w0BAQEFAASCBKgwggSkAgEAAoIBAQDvwT54v2kQTRP3
ZnJepfuBgEUfrEqBZ7zLm87s1NHwwJNNbwqGCYTIoCv4xDgRCK7X7NVmMyV2OWIn
...
-----END PRIVATE KEY-----";
$cert = "-----BEGIN CERTIFICATE-----
MIIGRTCCBS2gAwIBAgIQVWcnF+whEw+mvnBlp/JMCzANBgkqhkiG9w0BAQsFADCB
kDELMAkGA1UEBhMCR0IxGzAZBgNVBAgTEkdyZWF0ZXIgTWFuY2hlc3RlcjEQMA4G
...
-----END CERTIFICATE-----";
Call function check_pkey_cert_match() and result.
$check_result = check_pkey_cert_match($pkey, $cert);
if($check_result == true) {
echo "Match";
} else {
echo "Not Match";
}
Just use Function openssl_x509_check_private_key()
function check_pkey_cert_match($Private_Key, $Certificate) {
//Check for match
if(openssl_x509_check_private_key ( $Certificate , $Private_Key )) {
return true;
} else {
return false;
}
}

How to decrypt PHP Openssl encryption with BASH command

I am encrypting a password in PHP, and want to decrypt it on a different box. I am having no luck and I would prefer to be able to decrypt it right from bash and echo it. Below is a snippet of a test in PHP.
$textToEncrypt = "My super secret information.";
$encryptionMethod = "AES-256-CBC";
$secretHash = "Testkey";
//To encrypt
$encryptedMessage = openssl_encrypt($textToEncrypt, $encryptionMethod, $secretHash);
//To Decrypt
$decryptedMessage = openssl_decrypt($encryptedMessage, $encryptionMethod, $secretHash);
//Result
echo "Encrypted: $encryptedMessage <br>Decrypted: $decryptedMessage";
I have tried numerous methods to decrypt it on Ubuntu, even storing the data to a file and outputting it to a file. Command tried was:
openssl aes-256-cbc -a -d -k Testkey -in foo.txt -out secrets.txt
Where foo.txt is the value returned from the PHP encryption, and secrets.txt is the output. How can I do this?
It bears repeating, as in the comments, that encryption without an IV is dangerous. In fact, the current version of PHP will issue a warning about it. IVs can be randomly generated using the openssl_random_pseudo_bytes() function, and transmitted in the clear along with the encrypted text. They don't have to be secret, the important thing is not to reuse the same key and IV combination, and have a random IV.
So, with that out of the way, if you take a look at the source for the function, it's not passing the password argument as a passphrase, but rather as the key. So for using openssl on the command line, it needs to be in hex and passed to the -K option, not the -k option. But then, you'll get an error back saying "iv undefined" so your PHP needs to be adjusted to include one:
<?php
$textToEncrypt = "My super secret information.\n";
$encryptionMethod = "AES-256-CBC";
$key = "Testkey";
$iv = openssl_random_pseudo_bytes(
openssl_cipher_iv_length($encryptionMethod)
);
$keyHex = bin2hex($key);
$ivHex = bin2hex($iv);
//To encrypt
$encryptedMessage = openssl_encrypt($textToEncrypt, $encryptionMethod, $key, 0, $iv);
//To Decrypt
$decryptedMessage = openssl_decrypt($encryptedMessage, $encryptionMethod, $key, 0, $iv);
//Result
printf(
"Decrypted message: %s\n\nkeyHex=%s\nivHex=%s\nencryptedMessage=%s\n",
$decryptedMessage,
escapeshellarg($keyHex),
escapeshellarg($ivHex),
escapeshellarg($encryptedMessage)
);
Once you have these details, you can decrypt from command line (re-using PHP variable names here):
echo -n "$encryptedMessage" | openssl aes-256-cbc -d -a -A -K "$keyHex" -iv "$ivHex"
The other way around
#!/bin/bash
# create in bash keys
echo "generating private key"
openssl genrsa -out privkey.pem 2048
echo "signing private key"
openssl req -new -key privkey.pem -out certreq.csr -subj "/C=RO/ST=AB L=AB/O=None/OU=Department/CN=someweb.com"
echo "create a sign request"
openssl x509 -req -in certreq.csr -signkey privkey.pem -out newcert.pem
# end-of-bash-script
cp ./privkey.pem /path/to/apache/root/<some>
Encrypt some json file
openssl smime -encrypt -aes256 -in ./json.txt -binary -outform DER -out ./json.xxx newcert.pem
# test decrypt here in bash
# openssl smime -decrypt -in json.xxx -inform DER -inkey privkey.pem -out json.dec
Post it as binary to php
curl --request POST --data-binary #./json.xxx http://localhost/<some/>json.php
Then json.php script # apache root
<?php
$rkey = file_get_contents("/var/www/html/privkey.pem");
$pkey = file_get_contents("/var/www/html/newcert.pem");
$data = file_get_contents("php://input");
$fenc = tempnam("", "enc");
$fdec = tempnam("", "dec");
file_put_contents($fenc,$data);
// openssl_pkcs7_decrypt ($fenc , $fdec , $pkey, $rkey ); unable to coerce parameter 3 to x509 cert
system("openssl smime -decrypt -in ${fenc} -inform DER -inkey privkey.pem -out ${fdec}");
echo file_get_contents($fdec);
?>

How to get the same output SMIME signing using PHP openssl commands (DER Format?)

I've been trying to duplicate this command using PHP's built in openssl functions with no luck. I've tried variations of openssl_pkcs7_sign and openssl_pkcs7_encrypt. I believe the issue is that there is no flag to indicate the DER format output.
Here is the openssl command I am trying to replicate:
openssl smime -sign -signer mycert.pem -certfile mybundle.crt -inkey mykey.pem -nodetach -outform der -in file_in -out file_out
openssl_pkcs7_sign indeed signs the data in PEM format but you can just take the base64 chunk of the PEM data and convert it to DER by using base64_decode().
function get_base64($file_name) {
$content = file($file_name, FILE_IGNORE_NEW_LINES);
$base64_data = "";
for ($i=5; $i<sizeof($content); $i++){ // take only the base64 chunk
$base64_data .= $content[$i];
}
return $base64_data;
}
function pem2der($base64_data) {
$der = base64_decode($base64_data);
return $der;
}
if (openssl_pkcs7_sign( // Signs file_in and saves as file_out in PEM format
"file_in", // Input file
"file_out", // Output file (PEM format)
"file://../.pki/company.crt", // Certificate (mycert.pem)
"file://../.pki/company.key", // Private key (mykey.pem)
array(),
PKCS7_NOATTR,
"../.pki/company.cacrt" // Intermediate certificate (mybundle.crt)
)) {
$data = pem2der(get_base64("file_out")); // converts content of file_out to DER format
$out = fopen("file_out", "w") or die("Unable to open file!");
fwrite($out,$data); // output file (DER format)
fclose($out);
echo("File signed successfully!")
}
?>

Specifying input format type when calling openssl_pkcs7_verify in PHP

I have a crypto/php question, I was hoping someone could help me with.
My issue is that I have a signed PKCS7 block that I am trying to verify in PHP.
However, when I run the following PHP command:
openssl_pkcs7_verify($myfile, PKCS7_BINARY | PKCS7_NOVERIFY, $signers_file);
I get the following error:
PKCS7 routines:SMIME_read_PKCS7:no content type
If I do it using ruby like so:
p7container = OpenSSL::PKCS7.new(file_contents);
mystore = OpenSSL::X509::Store.new
p7container.verify(nil, store, nil, OpenSSL::PKCS7::NOVERIFY)
It works.
Also, if I run it through the OpenSSL commandline:
openssl smime -verify -inform der -in my_data_file -noverify
It also works. However, if I run the following:
openssl smime -verify -in my_data_file -noverify
Which is the same command, but without specifying the inform parameter, it fails with the same error message specified before, regarding the "no content type", which makes it seem I need to specify the input file format. Any ideas how I can do that through PHP?
Thanks in advance for your help,
I got around that problem by calling openssl directly from PHP (using the exec function). Be sure to add 2>&1 to the command to redirect stderr to stdout as the message "Verification successful" is sent to stderr.
function verify($signedData, &$data = null) {
#mkdir("tmp");
$random = randomString(32);
$signedFile = "tmp/" . $random . ".pem";
$file = "tmp/" . $random . ".dat";
file_put_contents($signedFile, $signedData);
$output = exec("openssl smime -verify -in $signedFile -inform DER -noverify -out $file 2>&1");
if ($output == "Verification successful") {
$data = file_get_contents($file);
$result = true;
} else {
$result = false;
}
#unlink($signedFile);
#unlink($file);
return $result;
}

Categories