PKCS12 file and Private key - PHP - php

I'm creating a an X509 certificate using phpseclib and all of that seems to be fine. Once I've created the certificate, I save it down as a pkcs12 file in PHP using the private key associated with my certificate. However, once I read that file, the private key I get back is different. Shouldn't the key be the same?
For instance, let's say I call:
openssl_pkcs12_export_to_file($cert , $write_loc, $priv_key , $pass);
Works great, now when I read the file with:
openssl_pkcs12_read($write_loc, $certs, $pass);
The output in $certs['pkey'] differs from the $priv_key I passed to the export_to_file method above.
Surely they must be the same, or am I mixing up 2 completely different things?
Thank you!

I think what's going is explained at PHP RSA key creation
Basically, you're using a key that starts off with -----BEGIN RSA PRIVATE KEY----- and the key you're getting back starts off with -----BEGIN PRIVATE KEY-----.
The former is a PKCS1 formatted private key and the latter is a PKCS8 formatted private key. The latter has the private key type embedded within the base64-encoded data itself whereas the former has the private key type embedded in the human readable string.
Some versions of PHP / OpenSSL output the PKCS8 key and others output the PKCS1 key..

Related

openssl_pkey_get_private returning false for one key

I am having two keys. For one it is working completely fine and giving the response as resource(368) of type (OpenSSL key) but for the other key it is just returning a false.
In both the cases I am not providing the path, but instead the string itself.
The key in which its working is like this :
-----BEGIN RSA PRIVATE KEY-----
MIIJKAIBAAKCAgEA74waKCtYqw7U7gE93BCn0CPmulFd1K8AaJqqBjOLe//okAuA
<...........................................................>
MGgCk1mrd65nf6JWsxb3RVcQ7jYxyFs3Pr4LFcgn1K0aTLnlwarBFjt+J3Y=
-----END RSA PRIVATE KEY-----
And the key in which it is not working is like this :
-----BEGIN RSA PRIVATE KEY-----
Proc-Type: 4,ENCRYPTED
DEK-Info: AES-128-CBC,14CA8(removing some values)2B6244
LwBvVX6TbdS9XYg0xywoLbhiaYK5UE6SQnATXJ8fXfJit+F4XcOfmWyQn+UsJRnM
<...........................................................>
s8M/Fe7YpkORsGSzQ6f5YslfmbmCsdCFDiBnTaIe45NP8IBULWY/vm45bYFteKUs
-----END RSA PRIVATE KEY-----
PS : I have hidden most part of keys for security.
Is there anything about the keys value also that affects the openssl to throw an error?
CODE :
$pkey = openssl_pkey_get_private(my_private_key_in_string_form);
EDIT :
What I on my research could find is that the second key is in an encrypted form whereas the first one is in the raw form.
But I am yet not able to find how to convert this encrypted private key to its raw form from an already created key.
Looking at the keys, the one which works is not protected with passpharase, the one that you say fails is protected with passpharase, also the call to openssl_pkey_get_private() , does not have second optional parameter for passpharese. I am referring the dcumentation from https://www.php.net/manual/en/function.openssl-pkey-get-private.php
So if your private key is encrypted, you have to specify the passpharase so that the key can be read properly by the openssl_pkey_get_private() API

python string format pkcs8 RSA signature

I need to use pkcs8 private key in my project as example below, I can't found any library or method relate to pkcs8 signature only pkcs1 are available for the Crypto.Signature.
My python code example:
from Crypto.Signature import PKCS1_v1_5
from Crypto.Hash import SHA
from Crypto.PublicKey import RSA
privatekey='''-----BEGIN PRIVATE KEY-----
MIICdgIBADANBgkqhkiG9w0BAQEFAASCAmAwggJcAgEAAoGBAOuxJs5RD6s2ECEV
C38Jfj6xBBrcX6W1Qy+GLiXhh+wKj650BPSerQNQI+XPx/+uUemk/dJM3RMm75Di
mXJlp7/5LY11VyKSqS+x6ELIvWz8lo2OhHW6/wU4jiQ1MlburYbn/pRRClGGA8i4
FjHMvMu9/QCNPlNTlf7qJDO6lDVlAgMBAAECgYB0iP76/DGXIgAPm2w3v+Xf8X5q
GZRhRqKVmO6wZDbkisRIKa1ZlitNfA6DzpzA2tw9fgrSNJcKpTHGnYPpgEHUQrcw
t471yuAvzAOBffaqVjIt7vKyL3QnkNfxWenliasacX3olwBpKPrkvG75H2KBe9S0
WKpju1Cm6MebAJ3z8QJBAP8gSx3l9nNTqJcBBmP+TGDcTL9Sif2nBZrU/pWnzzop
HolaFIQTYXPIj8rMoVT/c0gLo1gcdHoSUJTbB0WO7V8CQQDsf9FAWCI3fkWgS/fW
21ApO6B07/388CpNHOTjvfsdlent4yXLL+Guh5apMH4Oq0tkGkEij5YPG1EZSYXi
48+7AkEAiX3m3ZMMMXTZe5/CyOrIUL8I4WbjFP8JJzs4hICuTmLQoScZvWAQeeyR
ibKkE4GjqCUVf6u+Hfd20/ICRjtTswJANCjEh8JoWYDZ7k6S7KoV9eIWs3OyurRl
P/idarUdyxqjKzorvbJjvdBdpBbz1lxlFkDMGMk+OTq3GjKi+rVvvQJAGhBieR2Q
69JNeoOg7J8Xk7evNBJHwtJfRDQXKJpN5YZsKclf6oRaYFEQOM6ThuNoYHISfGKw
UbDoUsPZoQFPtQ==
-----END PRIVATE KEY-----'''
signstr = testdata123
key = RSA.importKey(privatekey)
h = SHA.new(signstr)
signer = PKCS1_v1_5.new(key)
signature = signer.sign(h)
sign = base64.b64encode(signature)
The signature generated by code above do not match the public key in the api system I am using.
For PHP side I am able to sign correctly. The working code for PHP as below:
$private_key= openssl_get_privatekey($private_key);
openssl_sign($signStr,$sign_info,$private_key,OPENSSL_ALGO_MD5);
$sign = base64_encode($sign_info);
Any advice on how to sign with RSA pkcs8 format private key using python is much appreciated.
Hash algorithm
Of course, it must be different, because you're using different hash algorithms.
Your Python code uses SHA-1 and your PHP code uses MD5. The default hash algorithm in PHP is OPENSSL_ALGO_SHA1 as the documentation describes:
if (!openssl_sign($signStr, $sign_info, $private_key)) {
echo "Failed to sign";
}
Key encoding
There are many ways of encoding a private key. Two of the most common ways are PKCS#1 and PKCS#8 encodings. See more information on that here.
The key encoding is not a concern here, because both pycrypto and openssl use the correct one here.
Padding
There are two common padding schemes that are used for signing: PKCS#1 v1.5 padding and RSA-PSS (technically RSASSA-PKCS1-V1_5 and RSASSA-PSS). Both of them are defined in PKCS#1, but the latter was introduced in PKCS#1 v2.x.
Pycrypto supports both padding schemes, but openssl only supports PKCS#1 v1.5 in PHP.
Compatibility
If you want to check for compatibility of the same algorithm between different languages, you need to sign in one using the private key and verify the signature in the other using the public key. After you've done that, you can try the other way around if you also need the other direction.
This is only relevant for RSA-PSS, because that is randomized.
If you're using PKCS#1 v1.5 padding which is not randomized, you should be able to create a signature in both Python and PHP and compare them directly to check for compatibility.

How to provide Privatekey for Omnipay

I am developing an wap app with laravel and omnipay-alipay (https://github.com/lokielse/omnipay-alipay/wiki/Aop-WAP-Gateway) I followed the instructions:
$gateway = Omnipay::create('Alipay_AopWap');
$gateway->setAppId('201610100207.....');
$gateway->setPrivateKey('MIICXAIBAAKBgQDH8RiuZfAi1Lm+81GTopr9Ttg/NL6CJ4vtQcKkCkj0DCdL4DVo6V2mBFp9aWsC1KmbphEvOCBotwfdBnEXeSSpGaGi8DNR95za+C027YluN6uxrSRQh3Enk16oEf4QIzezn91+aZgS2innm6RqDOkx/7qWQVAeQPtrPUOQdAJgQwIDAQABAoGAL9x+1ACC41OYwyTrujOKdmiRS1AM6osc68Z6GVp87C6cmYUqQ9rZGAyivLKncx4FG8U9B9ifqXFm6HnKSJwvMf6jKeMM5sn+ez3Ixc3MuaQyyPkAOeU/jdOYAtFHU6slbZTSaofgGaJ0CZCSkdQ+rMaAoJm3EcOjmAVpMvn0IgECQQD5gBJn4Tz0twIIS+VukJ0tQA/BpvuJU/CoHj/28EH6X1oHGvz4Se7Hdgx+TDY99akq24StyVwqLgp3OmuGYp7jAkEAzSaESlTLI/huFMJSCp1QLVWdz6nubMsNF82Na7I2S2v8OElerhi5HVCTXq4xWO4I8V9o34JktGn5GpwAdFyXIQJAWxBTp3aeOPNS2pRY+THvLETle1jnFgh9Hd9smUS30BpdUZqYGkdhz4tWpAJNCfBP/kSA+K015m9HgpzgAfyc4QJAfGRQbqm/iw4F4Xx6Nolwpix1xgcp1LnCNJ6kk5q5pT3S72Y9jJ7dD9NdqFlC/sNGlOTfODdeTK69Js9UzzmdQQJBAI99imtOqFPssADFHQg+w7EYH3tdX+YN7guiOaurb2r2P4a3S6DOq5GtFWG/ffM10q7gbXVS1KOWOFiMUCF/Ac0=');
//$gateway->setPrivateKey('-----BEGIN RSA PRIVATE KEY-----MIICXAIBAAKBgQDH8RiuZfAi1Lm+81GTopr9Ttg/NL6CJ4vtQcKkCkj0DCdL4DVo6V2mBFp9aWsC1KmbphEvOCBotwfdBnEXeSSpGaGi8DNR95za+C027YluN6uxrSRQh3Enk16oEf4QIzezn91+aZgS2innm6RqDOkx/7qWQVAeQPtrPUOQdAJgQwIDAQABAoGAL9x+1ACC41OYwyTrujOKdmiRS1AM6osc68Z6GVp87C6cmYUqQ9rZGAyivLKncx4FG8U9B9ifqXFm6HnKSJwvMf6jKeMM5sn+ez3Ixc3MuaQyyPkAOeU/jdOYAtFHU6slbZTSaofgGaJ0CZCSkdQ+rMaAoJm3EcOjmAVpMvn0IgECQQD5gBJn4Tz0twIIS+VukJ0tQA/BpvuJU/CoHj/28EH6X1oHGvz4Se7Hdgx+TDY99akq24StyVwqLgp3OmuGYp7jAkEAzSaESlTLI/huFMJSCp1QLVWdz6nubMsNF82Na7I2S2v8OElerhi5HVCTXq4xWO4I8V9o34JktGn5GpwAdFyXIQJAWxBTp3aeOPNS2pRY+THvLETle1jnFgh9Hd9smUS30BpdUZqYGkdhz4tWpAJNCfBP/kSA+K015m9HgpzgAfyc4QJAfGRQbqm/iw4F4Xx6Nolwpix1xgcp1LnCNJ6kk5q5pT3S72Y9jJ7dD9NdqFlC/sNGlOTfODdeTK69Js9UzzmdQQJBAI99imtOqFPssADFHQg+w7EYH3tdX+YN7guiOaurb2r2P4a3S6DOq5GtFWG/ffM10q7gbXVS1KOWOFiMUCF/Ac0=-----END RSA PRIVATE KEY-----');
but I kept getting the error msg:
openssl_sign(): supplied key param cannot be coerced into a private key.
I've tried different formats but in vain. How should I set the private key properly ? Thanks in advance.
According to my reading of the code, the private_key parameter is passed to PHP's openssl_pkey_get_private() function, i.e. this one:
http://php.net/manual/en/function.openssl-pkey-get-private.php
By looking at your code above it seems that you need to use something like the line that you have commented out:
$gateway->setPrivateKey('-----BEGIN RSA PRIVATE KEY-----MIICXAI...
however each line of the private key file must be terminated with a new line (\n) character.
Use OpenSSL to create a PEM formatted private key file and then read that in using file_get_contents().

Encrypt password (RSA) with public key

I'm having serious problems with what I was hoping would be a simple call to PHP's
openssl_public_encrypt();
Sadly, I get an invalid public key warning.
My goal is to simply RSA encrypt a users password using public key provided by their API which currently looks like this:
+Tir6+unMOaQ5tHqjjjwnlAMhccnCSMFEi3a0mhIxbW+O/GukjomGyzckQT2h0Ys70JezHbNq5YS3sYkNF29kCkz4HuNfy9eEjE/clA9/zyfT8ZcbnusLcLz2xNgbTp62fQdzBnReI5+dpj/N24krYvHaYIr8ACxDqBv2TR3E9M=AQAB
Apparently a working implementation using the same service I'm trying to use found a solution with these steps:
Extract the modulus and exponent from the public key
Re-constructs the key in the MS PUBLICKEYBLOB format
Call OpenSSL to convert the key to PEM format
Load the PEM public key from the converted file
Encrypts the password (after converting to UTF-16LE)
However not only that I dont know if its possible to do in PHP , I think there Must be an easier way!
One post I saw hinted that the exponent may come after the last = sign (so AQAB) but I don't know if this is reliable.
Something like this do what you want?:
<?php
$key = '+Tir6+unMOaQ5tHqjjjwnlAMhccnCSMFEi3a0mhIxbW+O/GukjomGyzckQT2h0Ys70JezHbNq5YS3sYkNF29kCkz4HuNfy9eEjE/clA9/zyfT8ZcbnusLcLz2xNgbTp62fQdzBnReI5+dpj/N24krYvHaYIr8ACxDqBv2TR3E9M=AQAB';
include('Crypt/RSA.php');
$rsa = new Crypt_RSA();
$rsa->loadKey(array(
'e' => new Math_BigInteger(65537),
'n' => new Math_BigInteger(substr($key, 0, -4), -256)
));
$ciphertext = $rsa->encrypt('password');
echo bin2hex($ciphertext);
?>
This example uses phpseclib, a pure PHP RSA implementation. Although phpseclib does not support the format of the key you posted it does support raw public keys and as such converting to PKCS1 style keys is unnecessar. And the ciphertext's produced by phpseclib are perfectly interoperable with OpenSSL.
OK, so what you need to do:
remove the AQAB part and use the value 65537 for the public exponent
base 64 decode the modulus
create a DER encoding of the public key in PKCS#1 format (see below)
base64 encode the DER encoding using 64 character line length and DOS line endings
add PEM header & footer
use the PEM encoded string to create a public key and finally
encrypt the password
The following ASN.1 spec defines the public key in PKCS#1 format:
RSAPublicKey ::= SEQUENCE {
modulus INTEGER, -- n
publicExponent INTEGER -- e
}
Now the identifier octet or tag of SEQUENCE is 30 in hex, INTEGER is 02 and the length should be specified like this. So you get something like:
30818902818100F938ABEBEBA730E690E6D1EA8E38F09E500C85C727092305122DDAD26848C5B5BE3BF1AE923A261B2CDC9104F687462CEF425ECC76CDAB9612DEC624345DBD902933E07B8D7F2F5E12313F72503DFF3C9F4FC65C6E7BAC2DC2F3DB13606D3A7AD9F41DCC19D1788E7E7698FF376E24AD8BC769822BF000B10EA06FD9347713D30203010001
in hexadecimals. So after base 64 encoding and adding the header and footer line you should get:
-----BEGIN RSA PUBLIC KEY-----
MIIBCgKCAQH5OKvr66cw5pDm0eqOOPCeUAyFxycJIwUSLdrSaEjFtb478a6SOiYbLNyRBPaHRizvQl7Mds2rlhLexiQ0Xb2QKTPge41/L14SMT9yUD3/PJ9Pxlxue6wt
wvPbE2BtOnrZ9B3MGdF4jn52mP83biSti8dpgivwALEOoG/ZNHcT0wIDAQAB
-----END RSA PUBLIC KEY-----
Happy coding.

Get RSA Private Key in RAW format

I was working on a Project which purely involves PROVEN Mathematical equations, One of step requires value of private key, not for decryption or encryption.
In RSA we do have (e,n) as public key, (d,n) as private key. I am using phpseclib RSA implementation, there's option for us to get public key in RAW format, but there's none for private key. I came to know from documentation that phpseclib's RSA pads & then encrypts (Which I do not want, as application is purely a mathematical).
Solution for me would be to get either of following two
(a) Get Private Key in Raw Format.
(b) Use some other implementation of RSA which gives me RAW values of both Private Key & Public Key or if there's none implement my own RSA algorithm.
I did go through documentation of phpseclib, no useful solution was found. My question may not be properly formatted, but any help in this matter is welcome. Thanks in advance...
After you load the key you could probably do something like this:
$rsa = new Crypt_RSA();
$rsa->loadKey('...');
echo $rsa->modulus;
echo $rsa->exponent; // private exponent
echo $rsa->publicExponent;
echo $rsa->exponents[1]; // dp
echo $rsa->exponents[2]; // dq
echo $rsa->coefficients[2]; // inverseq
echo $rsa->primes[1]; // p
echo $rsa->primes[2]; // q

Categories