PHP - read a private key file ".key" to use for JWT - php

I have a private key file $formatPrivateKey that I need to use as a variable
$privateKey = file_get_contents('27660275_website.com.key');
$signature = hash_hmac('sha256', $base64UrlHeader . "." . $base64UrlPayload, $privateKey, true);
I need this to generate a client-assertion JWT.
However, I'm getting the following error - {"error":"invalid_request","error_description":"The Token's Signature resulted invalid when verified using the Algorithm: SHA256withRSA"}
I guess I'm not reading the file_get_contents('27660275_website.com.key') properly.
My $privateKey file starts with the following -----BEGIN RSA PRIVATE KEY----- and finishes with -----END RSA PRIVATE KEY----. I tried to remove them but I still got the same output.
I tried to concatenate the alphanumeric sequence as suggested in another answer to a question in Stackoverflow. However, it didn't work.
Therefore my assumption that I'm not reading file_get_contents('27660275_website.com.key') properly.

In this context, the secret key is a password (a string) rather than a private key file.
Check out this for example, they simply use the password 'secret'. Any string should make your code work.
https://www.php.net/manual/en/function.hash-hmac.php

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

How to create signature using PHP

I need to use an API but first to login I need to create signature.
1. Concatenate the API key with the current timestamp in the format below:
<<APIKEY>>_<<timestamp(yyyy'-'MM'-'ddTHH:mm:ss.fffZ)>>
and this step is easy:
hash('sha256', $data);
result is:
9952375a30708b46739986482303cae30ad51fc9a362b5794d298dfc22f7ec02
and this is correct result
The next step is:
2. The combination of the created signature along with the provided API secret key will act as the
digital signature of the call.
I have API secret key like:
-----BEGIN PUBLIC KEY-----
9IGfMA0GCSqGSIb3DQEBAQUAA4GNADCBiQKBgQCgBSU90PX4WyXFAZ/+M84dJNEi
/0j5OermfydTU4g2JvnpO6BOQjNpb5+mOLjVoij7DWTdDtx1WThRm04N3DVuyh+a
5cledvMbcngvyiXpQCdskT9bVmI4QLbmRny46S7MER1jhziMQRfRw9gbmlB2iCEq
n21kDr842Q+WDtLE4QIDAQA9
-----END PUBLIC KEY-----
How I can get Digital signature with a combination of created signature and provided API secret key?
There is an Python example like:
key = api_key + '_' + timestamp
print "message", key
sha_hash = hashlib.sha256(key).hexdigest()
print "sha256 hash:", sha_hash
rsa_key = RSA.importKey(pub_key)
cipher = PKCS1_v1_5.new(rsa_key)
signature = base64.encodestring(cipher.encrypt(sha_hash))
but how I can get signature using PHP?
While there are numerous ways to accomplish this, I recommend leveraging the openssl_public_encrypt method. There are other crypt functions and even pure PHP implementations of RSA but they are likely not as current and well maintained as openSSL is going to be on a linux system. Don't forget all the disruption in SSL/TLS these last years. Everything from retiring older protocols and weaker cyphers to POODLE type exploits.
If for whatever reason that is not an option I would probably look into phpseclib. https://github.com/phpseclib/phpseclib
Really might come down to what makes the most sense for your project... OS, portability, speed, etc.
Here is that Python snippet converted to equivalent PHP code.
<?php
$key = $api_key . '_' . $timestamp;
echo "message:" . $key;
$sha_hash = hash('sha256', $key);
echo "sha256 hash:" . $sha_hash;
$rsa_key = "your public key goes in here"; //see https://www.php.net/manual/en/function.openssl-pkey-get-public.php
openssl_public_encrypt($sha_hash, $encrypted, $rsa_key);
$signature = base64_encode($encrypted);

Creating a JWT With ECDSA P-256 SHA-256 With PHP

I'm wondering if it's possible to properly create a signature using the P256 curve and PHP. OpenSSL in PHP has support for creating the key and getting the proper things in order.
According to this documentation - http://self-issued.info/docs/draft-jones-json-web-token-01.html#DefiningECDSA - Section 8.3 states:
A JWT is signed with an ECDSA P-256 SHA-256 signature as follows:
Generate a digital signature of the UTF-8 representation of the JWT Signing Input using ECDSA P-256 SHA-256 with the desired private key. The output will be the EC point (R, S), where R and S are unsigned integers.
Turn R and S into byte arrays in big endian order. Each array will be 32 bytes long.
Concatenate the two byte arrays in the order R and then S.
Base64url encode the 64 byte array as defined in this specification.
Herein the problem lies with getting the R and S byte arrays.
Here is an example of what I'm trying to do.
//Create Array Configuration
$config = array(
"curve_name" => "prime256v1",
"private_key_type" => OPENSSL_KEYTYPE_EC,
);
$ourkey = openssl_pkey_new($config);
/* We would get the key details to help extract out other information such as x/y coord
of curve and private key but it's not necessary to show for this part*/
// Extract the private key from $res to $privKey
openssl_pkey_export($ourkey, $privKey);
$data = "Example data we will be using to sign";
$data = hash("sha256", $data);
$signature = "";
openssl_sign($data, $signature, $privKey); // Should I include the digest algo in this call as well?
The problem here is that this signature is not R and S that I can use to concatenate together to make the real signature I need... I think.
So ultimately, is there any way I can get the R and S values from a openssl function in php?
Any help is greatly appreciated!
If you need something like JWT but not JWT in particular, consider PASETO.
Assuming you opt for v2, PASETO doesn't use ECDSA, it uses the more secure EdDSA over Ed25519. Furthermore, the standard goes out of its way to be boring so that implementations will be obviously secure.
(If you opt for v1, PASETO uses RSASSA-PSS.)
If you do need JWT in particular, look no further than lcobucci/jwt, which in turn uses PHPECC for ECDSA.
Once upon a time, PHPECC was riddled with side-channel vulnerabilities which made it a bad choice to use in production systems. These days, it's about as secure as a PHP implementation of ECDSA could be. I wrote a usability wrapper called easy-ecc if anyone wants to use PHPECC in only the safest configuration.
Luís Cobucci's JWT library is the only PHP implementation of JWT that has, to my knowledge, been formally audited by a security company. The report is public and lives here.
these functions have helped me to encode and decode signatures generated with curve, i hope this help.
to encode:
function encode_signature() {
$data = "HOLA";
$private_key = <<<EOD
-----BEGIN PRIVATE KEY-----
MEECAQAwEwYHKoZIzj0CAQYIKoZIzj0DAQcEJzAlAgEBBCDKuYJPma+sA2svl02CPCJECESuBrW2nExuUR1vtHgozw==
-----END PRIVATE KEY-----
EOD;
$binary_signature = "";
openssl_sign($data, $binary_signature, $private_key, OPENSSL_ALGO_SHA256);
$valid_signature = base64_encode($binary_signature);
return $valid_signature;
}
to decode:
function decode_signature($signature) {
$data = "HOLA";
$public_key = <<<EOD
-----BEGIN PUBLIC KEY-----
MFkwEwYHKoZIzj0CAQYIKoZIzj0DAQcDQgAE/kmRxyn5LUQdC3owkWDp6DkLnTlBN2VPd86FS5WAqQJA4y9oaowYYVSu0A7kv8tUa9FLAqb7UPfUNeh5zDnrFQ==
-----END PUBLIC KEY-----
EOD;
$publicKey = openssl_pkey_get_public($public_key);
$verify = openssl_verify($data, base64_decode($signature), $publicKey, OPENSSL_ALGO_SHA256);
return (bool) $verify;
}

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().

PKCS12 file and Private key - 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..

Categories