I am trying to verify a signature in php, and have exhausted myself trying every example I have found on the net. I've gone round in circles so much I have probably missed the solution now.
So I have test data which is
$msg = "test data";
which produced this signature using the private key from my key pair
$signature = "avALtk00btVyV74e5UdXJ/VClVV/fsuoLZpXQjiCrkVijsmMZsYWZujN56+Aa2CEQYkomDsm9CJ/Tue7lNP0tYVZz9Y0RngpcV9VT9V3i+3rbvbBEnuJuS/5e+PR7kQGMh8rVuCtHpAJhSePMyipC3kM90EQJ0jyY3rFaHDNpSzVBpOnRYLzqbsdy45v0bN78A2J/HaIhJy87Sh4X1a+WMg9PLkqSSYZnRYOB8XVDCYfyeeekcvI4rvP51wBQcaLwu7S0xPQA8yHfJqMXCqdmBVUQZrk/X+CujdXUyJItDWA8j2N8AHmcAD5oRaJ6bX3zCQFM1QnKMi1ETLudzIqfA==";
and this is the public key from the signing key pair
$key = "MIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEAxv4nCiH4vXvSLsvlceCOk3yfH1EQgNqNaVGdnFxdw9IIjSVZvTVH45NCodCJ0GlHoDwQM7DMV1+QrtF91cn44xg4Ys9zr1xkaT4jWBTe3YKoTqJoLHR4UU03F6Y1jTELhjY2a2Kt0ijyvAOKM4bm3gCItfMx59ETGInz7Oubb1T4IJ8TuWmZsh+X57c6fgv0B2+eTr/5FMK2VxXV5tHkB9UNLBgnbw0IZuC6izF4OFk9hxgh96i5wCf2HhHaNoEryx7ZV2ZG9a0OQnYZ+x1zaOIw6dJkV7rip3H57ksQfoQWM0GKMBB7cWIgWsf/GlbYTVgw26MvzEzGPb9uCfx8rwIDAQAB";
I have tried wrapping the key with this
$pubkey = "-----BEGIN RSA PUBLIC KEY-----" . $key . "-----END RSA PUBLIC KEY-----";
and with this
$pubkey = "-----BEGIN PUBLIC KEY-----" . $key . "-----END PUBLIC KEY-----";
I have tried creating a public key id with both wrappings and without any wrapping, like this
$pubkeyid = openssl_pkey_get_public($pubkey);
$pubkeyid = openssl_pkey_get_public($key);
and I have tried verifying the signature with $key and $pubkeyid, with various algorithms and with none, like this
openssl_verify($msg, base64_decode($signature), $pubkeyid);
openssl_verify($msg, base64_decode($signature), $key);
openssl_verify($msg, base64_decode($signature), $pubkeyid, "sha256withRSAEncryption");
openssl_verify($msg, base64_decode($signature), $key, "sha256withRSAEncryption");
openssl_verify($msg, base64_decode($signature), $pubkeyid, OPENSSL_ALGO_SHA256);
openssl_verify($msg, base64_decode($signature), $key, OPENSSL_ALGO_SHA256);
I probably tried some other permutations, but can't remember now. My head hurts.
No matter what I tried, I have not managed to verify the signature. I can verify the signature using the public key in java easily.
I loathe asking for a working php example because I have tried so many I have already found on the net and just can't get them to work. Unfortunately phpseclib is not an option for me, so I have to use openssl.
Where am I going wrong?
Thanks to #miken32 I have now finally fixed the code. Turns out all I was missing was a couple of line breaks when formatting the PEM key. So the final, and very simple code is:
// Get base64 encoded public key.
// NOTE: this is just for testing the code, final production code stores the public key in a db.
$pubkey = $_POST['pubkey'];
// Convert pubkey in to PEM format (don't forget the line breaks).
$pubkey_pem = "-----BEGIN PUBLIC KEY-----\n$pubkey\n-----END PUBLIC KEY-----";
// Get public key.
$key = openssl_pkey_get_public($pubkey_pem);
if ($key == 0)
{
$result = "Bad key zero.";
}
elseif ($key == false)
{
$result = "Bad key false.";
}
else
{
// Verify signature (use the same algorithm used to sign the msg).
$result = openssl_verify($msg, base64_decode($signature), $key, OPENSSL_ALGO_SHA256);
if ($result == 1)
{
$result = "Verified";
}
elseif ($result == 0)
{
$result = "Unverified";
}
else
{
$result = "Unknown verification response";
}
}
// do something with the result.
Related
I am working on a code
if (!function_exists('decrypt_password')) {
function decrypt_password( $iv, $value)
{
$key = config('services.decrypt.key');
$string_iv =hex_to_string($iv);
$encrypter = new Encrypter($key, 'AES-256-CBC');
$dec['iv'] = base64_encode($string_iv);
$dec['value'] = $value;
$dec['mac'] = hash_hmac('sha256', $dec['iv'] . $dec['value'], $key);
return ($encrypter->decrypt(base64_encode(json_encode($dec)),false));
}
}
this piece of code should make a layer of decrypting the password, and key is missing in config and I can't get one or generate a new one.
any help!
edit: I tried to set 32 char key like "22222222222222222222222222222222" and got a new error The payload is invalid
The problem was in decrypt the logic should encrypt.
I'm using openssl_verify() method to validate INAPP_PURCHASE_DATA and SIGNATURE using the public key from Google Developer Console.
I'm parsing the data and signature from my app to a url on which the following php code is running. While I make a test purchase from my android app, my script runs fine, however the return value is always zero.
Can anyone tell what is wrong with the code?
Is there an issue with the formats of the above two fields?
Also can anybody suggest a better way to parse the parameters to my php script?
Here's the code I'm using
<?php
$signed_data = $_REQUEST["signed_data"];
$signature = $_REQUEST["signature"];
$public_key_base64 = "......";
function verify_market_in_app($signed_data, $signature, $public_key_base64)
{
$key = "-----BEGIN PUBLIC KEY-----\n".
chunk_split($public_key_base64, 64,"\n").
'-----END PUBLIC KEY-----';
//using PHP to create an RSA key
$key = openssl_get_publickey($key);
//$signature should be in binary format, but it comes as BASE64.
//So, I'll convert it.
$signature = base64_decode($signature);
//using PHP's native support to verify the signature
$result = openssl_verify(
$signed_data,
$signature,
$key,
OPENSSL_ALGO_SHA1);
echo $result;
if (0 === $result)
{
echo "false";
return false;
}
else if (1 !== $result)
{
echo "false";
return false;
}
else
{
echo "true";
return true;
}
}
verify_market_in_app($signed_data, $signature, $public_key_base64);
?>
I'm trying to decode a string that was encoded with RSA private key at an Android device (as some kind of digital signature). I think I have a problem with my public key at server side.
if (openssl_public_decrypt($token, $decrypted, $pubKey) == FALSE)
{
while ($msg = openssl_error_string())
{
echo "ERROR: " . $msg;
}
return;
}
The function openssl_public_decrypt() returns false but openssl_error_string() does not return any errors. How can I find out what is going wrong here?
UPDATE:
This is how I create the encoded value in Android:
public static String Sign(byte[] text, RSAPrivateKey privateKey) throws Exception
{
// Encode the original data with RSA private key
byte[] encodedBytes = null;
try {
Cipher c = Cipher.getInstance("RSA/ECB/PKCS1Padding");
c.init(Cipher.ENCRYPT_MODE, privateKey);
encodedBytes = c.doFinal(text);
} catch (Exception e) {
Log.e("RSAHelper", "RSA encryption error");
throw e;
}
return Base64.encodeToString(encodedBytes, Base64.DEFAULT);
}
I'm trying to decode a string that was encoded with RSA private key at an Android device (as some kind of digital signature)
If its a digital signature, then the padding scheme is different. RSA encryption uses Type 2 padding, while RSA signatures uses Type 1 padding.
Since you believe its a digital signature, you should probably look for a openssl_public_verify function (or similar).
I didn't really find out why openssl_error_string() returns nothing but I could solve my problem with the not accepted PublicKey
Since the key-pair had been created by Android it could not be directly used by PHP. The following problems needed to be solved:
Missing header and footer
Wrong line breaks
// remove existing line breaks
$pubKey = str_replace ("\r\n" , "" , $pubKey);
$pubKey = str_replace ("\r" , "" , $pubKey);
$pubKey = str_replace ("\n" , "" , $pubKey);
// insert line breaks as expexted by PHP function
$pubKey = wordwrap($pubKey, 65, "\n", true);
// add header and footer
$pubKeyWithHeader = "-----BEGIN PUBLIC KEY----- \r\n" . $pubKey
. " \r\n-----END PUBLIC KEY-----";
// decode the key
openssl_pkey_get_public($pubKeyWithHeader);
if ($x == FALSE)
{
echo "error";
return;
}
// decrypt the message with the public key
if (openssl_public_decrypt($token, $decrypted, $pubKeyWithHeader) == FALSE)
{
echo "error";
return;
}
echo $decrypted;
I have seen many posts with this issue and tested many possible solutions but the verification was always false.
So, I am passing both of this parametters via POST to the PHP server:
String purchaseData = data.getStringExtra("INAPP_PURCHASE_DATA");<br>
String dataSignature = data.getStringExtra("INAPP_DATA_SIGNATURE");
This is the method I use:
$result = openssl_verify($responseData, base64_decode($signature),
key, OPENSSL_ALGO_SHA1);
I used the method json_decode to verify that purchaseData is correct.
I also use base64_decode for the dataSignature.
And this is how I form my key with the public key that I have with my Google Publisher account:
const KEY_PREFIX = "-----BEGIN PUBLIC KEY-----\n";<br>
const KEY_SUFFIX = '-----END PUBLIC KEY-----';
$key = self::KEY_PREFIX . chunk_split($publicKey, 64, "\n") . self::KEY_SUFFIX;
The tests are performed with an account test of a uploaded game in Google Play, so the In App Purchases are real but with no charges.
What I am missing here?
You have formatted the key but you are missing one step where you need to extract the public key from the PEM.
Here are the steps:
$key = self::KEY_PREFIX . chunk_split($publicKey, 64, "\n") . self::KEY_SUFFIX;
$key = openssl_get_publickey($key);
$result = openssl_verify($responseData, base64_decode($signature), $key, OPENSSL_ALGO_SHA1);
i'm trying android in app billing v3 verifying on my remote php server.
but, it seems something is wrong at my codes.
i think this openssl_verify function is problem.
result is always failed!
i can't find what first parameter to verify with openssl_verify. actually, i 'm confuse what's reasonable format to place at first parameter :(
could you help me to solve it?
$result = openssl_verify($data["purchaseToken"], base64_decode($signature), $key); // original // failed
belows full test codes.
<?php
$responseCode = 0;
$encoded='{
"orderId":"12999763169054705758.1111111111111",
"packageName":"com.xxx.yyy",
"productId":"test__100_c",
"purchaseTime":1368455064000,
"purchaseState":0,
"purchaseToken":"tcmggamllmgqiabymvcgtfsj.AO-J1OwoOzoFd-G-....."
}';
$data = json_decode($encoded,true);
$signature = "tKdvc42ujbYfLl+3sGdl7RAUPlNv.....";
$publicKey = "MIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEA2kMri6mE5+.....";
$key = "-----BEGIN PUBLIC KEY-----\n" . chunk_split($publicKey, 64, "\n") . "-----END PUBLIC KEY-----";
$key = openssl_get_publickey($key);
if (false === $key) {
exit("error openssl_get_publickey");
}
var_dump($key);
$result = openssl_verify($data["purchaseToken"], base64_decode($signature), $key); // original // failed
//$result = openssl_verify($data, base64_decode($signature), $key); // failed
//$result = openssl_verify($encoded, base64_decode($signature), $key); // failed
//$result = openssl_verify(base64_decode($data["purchaseToken"]), base64_decode($signature), $key); // failed
//$result = openssl_verify(base64_decode($signature),$data["purchaseToken"], $key,OPENSSL_ALGO_SHA512 ); // failed
if ($result == 1) {
echo "good";
} elseif ($result == 0) {
echo "bad";
} else {
echo "error";
}
echo($result);
thanks :)
You are passing the wrong $data value into openssl_verify(). This value should be the full JSON string you get from Google Play, not the purchase token inside it. It is important that the JSON string is untouched, as even if you were to add a space or newlines to it, the signature would no longer work.
All you need to do in your code above is to change this line:
$result = openssl_verify($data["purchaseToken"], base64_decode($signature), $key);
to
$result = openssl_verify($data, base64_decode($signature), $key);
And you should get a success, assuming you're using the correct public key and the JSON purchase string is valid. I'm pretty sure your JSON string is not the original string from Google however, as the ones from Google do not contain newlines. It will be one long line of JSON text. Make sure that's what you are passing to openssl_verify().