I'm trying to get php to report the public key length of a certificate as a representation of bits.
e.g. 1024, 2048, 4096 etc.
I've trawled though countless functions on the PHP docs for answers or a steer in the right direction. Cant for the life of me work out a function that will provide this data.
http://php.net/manual/en/ref.openssl.php
openssl_pkey_get_details() seemed like the way to go. But cant find a way to give it the public key in the first place.
Currently i can parse the certificate. However this doesn't include the public key or bits information.
<?php
$cert = $_POST['cert_text'];
$ssl = openssl_x509_parse($cert);
echo json_encode($ssl);
?>
The $cert variable above is a PEM format certificate file. So in the format of
-----BEGIN CERTIFICATE-----
MIIGWTCCBUGgAwIBAgIKG6SqTwACAAAANzANBgkqhkiG.....etc..
-----END CERTIFICATE-----
i believe this
http://php.net/manual/en/function.openssl-pkey-get-details.php
will solve your problem
using
array openssl_pkey_get_details ( resource $key )
Returns an array with the key details in success or FALSE in failure. Returned array has indexes bits (number of bits), key (string representation of the public key) and type (type of the key which is one of OPENSSL_KEYTYPE_RSA, OPENSSL_KEYTYPE_DSA, OPENSSL_KEYTYPE_DH, OPENSSL_KEYTYPE_EC or -1 meaning unknown).
Related
I'm creating a program that, given a purely hexidecimal format of a cert, will extract and verify the digital signature.
I have the following example cert in hex format:
308206cd308205b5a003020102021001c3f8388b669f492cfac8731937fc8e300d06092a864886f70d01010b0500304f310b300906035504061302555331153013060355040a130c446967694365727420496e633129302706035504031320446967694365727420544c532052534120534841323536203230323020434131301e170d3232303132313030303030305a170d3233303230353233353935395a3079310b3009060355040613025553311330110603550408130a43616c69666f726e6961310f300d06035504071306497276696e6531253023060355040a131c426c697a7a61726420456e7465727461696e6d656e742c20496e632e311d301b0603550403131475732e61637475616c2e626174746c652e6e657430820122300d06092a864886f70d01010105000382010f003082010a0282010100b3838051fd24a13681e97e557476f22891b05355157c9ded1c7ca610f07f4d5a5c987e88318c213a62481fc0fd43ece409170cef6ac95ab106dd4cd046e4c3660097d2aeefffc7fe470307b17c9febd3799352a1971b0303360bd9c028c1939d9fbd0148eb0d4dbebfad23e5e3ab3b94943351cd8fe11556bf1ec7370a615ed74c5f5d66d62799f66bb1d1f3a4b661173cafed4b722b03f046572e73eae216b8e35e73add6cb86243fe4b457ffb8e6baf50af563110da121255c676248e8976ab1fe8c221a964cc550901b5191730aa6d616dc6aa9ee87fcaf7ac56e48f1592eeeb836065dd6c1078e724db7607677298169d9225eac5fdc974a21565621d4fb0203010001a382037930820375301f0603551d23041830168014b76ba2eaa8aa848c79eab4da0f98b2c59576b9f4301d0603551d0e04160414402287da2dd56eddfa85ccda1f946e493a08a99b301f0603551d1104183016821475732e61637475616c2e626174746c652e6e6574300e0603551d0f0101ff0404030205a0301d0603551d250416301406082b0601050507030106082b0601050507030230818f0603551d1f0481873081843040a03ea03c863a687474703a2f2f63726c332e64696769636572742e636f6d2f4469676943657274544c53525341534841323536323032304341312d342e63726c3040a03ea03c863a687474703a2f2f63726c342e64696769636572742e636f6d2f4469676943657274544c53525341534841323536323032304341312d342e63726c303e0603551d20043730353033060667810c0102023029302706082b06010505070201161b687474703a2f2f7777772e64696769636572742e636f6d2f435053307f06082b0601050507010104733071302406082b060105050730018618687474703a2f2f6f6373702e64696769636572742e636f6d304906082b06010505073002863d687474703a2f2f636163657274732e64696769636572742e636f6d2f4469676943657274544c53525341534841323536323032304341312d312e637274300c0603551d130101ff0402300030820180060a2b06010401d679020402048201700482016c016a007700e83ed0da3ef5063532e75728bc896bc903d3cbd1116beceb69e1777d6d06bd6e0000017e7eaf5af30000040300483046022100ce2d33ce8de3764ccc9d8ac03b936612fd10a4fc1815b3e092352643aa8d07e9022100e2a741a4c3ef5c90882f66951075be12ad54bdc573bd3ae3b17c662f63c6e29400760035cf191bbfb16c57bf0fad4c6d42cbbbb627202651ea3fe12aefa803c33bd64c0000017e7eaf5b14000004030047304502201ef42884353339d4921bf2e47af540b15af6bab94de74c3e3a1224418e9bc4ef0221008846deaa381d08b65607dc3290ba152f1c5aab5c7d03cf116682da8a45922f18007700b3737707e18450f86386d605a9dc11094a792db1670c0b87dcf0030e7936a59a0000017e7eaf5b4400000403004830460221008dc3b79af62dce53af04b8959afcb1f858bea16872eba97b5cc2c8f308b32d490221009b658042c5a0841d670ac1303c06b42e6a494596cb5e333fbdeddc9248e05dcd300d06092a864886f70d01010b0500038201010010170d137389daa010c477c0dde1af6529725489ad07822ace988cb78969683e1686a9fcde2d166b2c7ae5774e782ce7270904fb5abfdc0de25123d7cbcbd855598d35a027d1f5bdf3bd754eb3c6c9b7cc74de740b0b576c629dfd9ff5cca4d773f8bb499cc6f0aa39f269d219f019e62cbb354f32fa171226f58a4582b711e779268baa5d4bcc44f9dda3f2b867344ea29ecb6e28f4f818ef3594da16dc882cfd65cd2875a50d9ee9dff5d297135b5890ce4f583ab770c1d79bd5e7c4b2e1ae2ca0425cf63e12f151e8b6c6228ee53be299ee5ef09c85df0ad24b66e32b37f93193c2495f2de78d28126b3aa8a406b44d8469292fc242c5f39db44be56f4d91
From here, I am able to successfully derive the digital signature (this is located on the end of the cert hex above)
10170d137389daa010c477c0dde1af6529725489ad07822ace988cb78969683e1686a9fcde2d166b2c7ae5774e782ce7270904fb5abfdc0de25123d7cbcbd855598d35a027d1f5bdf3bd754eb3c6c9b7cc74de740b0b576c629dfd9ff5cca4d773f8bb499cc6f0aa39f269d219f019e62cbb354f32fa171226f58a4582b711e779268baa5d4bcc44f9dda3f2b867344ea29ecb6e28f4f818ef3594da16dc882cfd65cd2875a50d9ee9dff5d297135b5890ce4f583ab770c1d79bd5e7c4b2e1ae2ca0425cf63e12f151e8b6c6228ee53be299ee5ef09c85df0ad24b66e32b37f93193c2495f2de78d28126b3aa8a406b44d8469292fc242c5f39db44be56f4d91
I then take the public key that issued this cert, and use it to decrypt the Digital Signature Digest, which looks like this:
3031300d0609608648016503040201050004200bf3dcf2340b972e97fe3c8493e11eeee01f298939734690d0b4e79e1f5701b4
At this point is where I am trying to verify the Digital Signature by creating a hash of the entire SSL cert (this cert uses SHA256 Hash for digest):
hash('sha256', hex2bin($CertWithoutSignature))
Where $CertWithoutSignature is the same hex above (1st hex string above) WITHOUT the digital signature (2nd hex string above).
At this point I'm a bit confused, because not only does the length of the sha256 hash not match, neither does the data. I know I am decrypting the digest correctly, because otherwise I would get an error if the key were invalid, etc. But that extracted value is 102 characters long, and does not match what I will ever get from sha256 since the length of the string is totally different. Basically I know it is valid if I can get them to match because that means the document is the exact same and therefore will get the same hash. Any help is appreciated. Thanks.
You are forgetting that the hash is encoded and then padded in the signature, assuming that you are using PKCS#1 v1.5 padding within the certificate.
Hashing everything up to the signature is not correct either, you need to hash the TBSCertificate, where TBS means To Be Signed.
For more information, I would strongly recommend to read the X.509 specifications in RFC 5280. And, since ASN.1 encoders / decoders are rather complex you might want to use a library function instead of programming it yourself.
You're going to need to use an actual SSL library in order to validate the signature properly, and it's not simply comparing a couple hashes. You're going to also need the signing cert at the least.
To get you started, you're going to want to convert that cert to PEM format so that OpenSSL will like it.
// hex-encoded binary format
$cert_hex = '308206cd...';
// convert to PEM format
$cert_pem = sprintf(
"-----BEGIN CERTIFICATE-----\n%s\n-----END CERTIFICATE-----\n",
implode("\n", str_split(base64_encode(hex2bin($cert_hex)), 64))
);
// read in as an OpenSSL resource
$cert = openssl_x509_read($cert_pem);
// check the fingerprint just to verify it loaded
$fp = openssl_x509_fingerprint($cert, 'sha256');
var_dump($fp);
Output:
string(64) "d74157547fb287694b95b2533588c71f8706b0960e023fc4317f4f9a49ad2721"
After this you're likely going to want to load the signing cert in the same way and feed them both into openssl_x509_verify(), eg:
openssl_x509_verify($cert, $signing_cert);
I have a RSA Public Key in this format:
OpenSSLRSAPublicKey{modulus=9ee9f82dd8429d9fa7f091c1d375b9c289bcf2c39ec57e175a2998b4bdd083465ef0fe6c7955c821b7e883929d017a9164a60290f1622f664a72096f5d2ffda7c7825c3d657c2d13d177445fa6cdd5d68b96346006a96040f5b09baae56d0c3efeaa77d57602f69018f5cefd60cb5c71b6b6f8a4b0472e8740367266917d8c13,publicExponent=10001}
So, I have the modulus and the exponent.
How can I convert this in a format that is accepted by openssl_encrypt() in PHP?
I have searched on Google and didn't find anything good. Isn't there an easy way to format it?
Assuming you mean openssl_public_encrypt() instead of openssl_encrypt(), then try this (using phpseclib 1.0):
<?php
include('Crypt/RSA.php')
include('Math/BigInteger.php');
$rsa = new Crypt_RSA();
$rsa->loadKey(array(
'n' => new Math_BigInteger('9ee9f82dd8429d9fa7f091c1d375b9c289bcf2c39ec57e175a2998b4bdd083465ef0fe6c7955c821b7e883929d017a9164a60290f1622f664a72096f5d2ffda7c7825c3d657c2d13d177445fa6cdd5d68b96346006a96040f5b09baae56d0c3efeaa77d57602f69018f5cefd60cb5c71b6b6f8a4b0472e8740367266917d8c13', 16),
'e' => new Math_BigInteger('10001', 16)
));
echo $rsa;
Altho that said, if you're gonna use phpseclib to convert the key, why not use it to encrypt whatever it is that you're trying to encrypt as well?:
//$rsa->setEncryptionMode(CRYPT_RSA_ENCRYPTION_PKCS1);
$rsa->encrypt('plaintext');
No, there isn't any way to do this. Public/private keys can only be used in asymmetric cryptography. openssl_encrypt() requires symmetric cryptography.
openssl_public_encrypt() which you need for asymmetric cryptography wants a pubic key resource. This can be obtained using openssl_pkey_get_public().
Now, openssl_pkey_get_public() needs a PEM file. The docs describe it as:
an X.509 certificate resource
a string having the format file://path/to/file.pem. The named file must contain a PEM encoded certificate/public key (it may contain both).
A PEM formatted public key.
So you need to create a PEM file from the public key configuration you have in your question. The modulus and exponent you have are enough to do this.
There are several examples on the internet on how to archive this. Have a look at:
Generate .pem RSA public key file using base 10 modulus and exponent?
rawrsa (https://github.com/JonathonReinhart/rawrsa)
how to convert raw modulus & exponent to RSA public key (.pem format)
Basically you need to create a "ASN1-formatted text file" and employ some openssl commands.
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..
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.
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