Verify response with signature

For a response with a signature, verify the signature with the corresponding Google Pay public key.

  • If the verification succeeds, check that the virtual payment address, transaction ID and amount in the response are correct, and only then proceed based on the response status. Make sure that you use the tezResponse field, but not the deprecated fields to verify the response.
  • If the verification doesn't succeed, we recommend you consider this as a payment failure, and proceed accordingly.
  • For a response without a signature, we recommend that you treat this as a payment failure. Response without a signature might happen. One of the possible reasons is when user doesn't have the internet connectivity while making payment for the selected goods.

How to verify signature in Google Pay response

There are a few things that must be done to verify the signature in a Google Pay response.

  1. Get the public key that corresponds to the public key ID in the response.
  2. Hash the Google Pay response using SHA256 and get the UTF-8 message byte array.
  3. Decode the string signature using the base16 lowercase method to get the signature byte array.
  4. Verify the signature byte array with the message byte array and the public key using SHA256 with the ECDSA algorithm.

The current set of active public keys for signatureKeyId are available in google_pay_public_keys.json . Find the correct keyValue by matching the keyId you received in the response, the keyValue is a Base64 encoded version of the key which you'll see in a normal pem file. The public key will change every two years and you will be notified about the new key.

How to implement SignatureVerifier class

In this sample Java implementation, two open source libraries are used:

  • Bouncy Castle: a collection of APIs used in cryptography.
  • Guava: Google Core Libraries for Java.

The following code snippet illustrates how to implement the SignatureVerifier class.

Java

 package 
  
 com.google.pay 
 ; 
 import 
  
 com.google.common.hash.Hashing 
 ; 
 import 
  
 com.google.common.io.BaseEncoding 
 ; 
 import 
  
 java.io.FileReader 
 ; 
 import 
  
 java.io.IOException 
 ; 
 import 
  
 java.io.UnsupportedEncodingException 
 ; 
 import 
  
 java.nio.charset.StandardCharsets 
 ; 
 import 
  
 java.security.InvalidKeyException 
 ; 
 import 
  
 java.security.KeyFactory 
 ; 
 import 
  
 java.security.NoSuchAlgorithmException 
 ; 
 import 
  
 java.security.PublicKey 
 ; 
 import 
  
 java.security.Signature 
 ; 
 import 
  
 java.security.SignatureException 
 ; 
 import 
  
 java.security.spec.InvalidKeySpecException 
 ; 
 import 
  
 java.security.spec.X509EncodedKeySpec 
 ; 
 import 
  
 org.bouncycastle.util.io.pem.PemReader 
 ; 
 /** Helper class to verify a signature returned in Google Pay response. */ 
 public 
  
 class 
 SignatureVerifier 
  
 { 
  
 private 
  
 static 
  
 final 
  
 String 
  
 CHARSET 
  
 = 
  
 "utf-8" 
 ; 
  
 private 
  
 static 
  
 final 
  
 String 
  
 ENCRYPTION_ALGORITHM 
  
 = 
  
 "EC" 
 ; 
  
 private 
  
 static 
  
 final 
  
 String 
  
 HASH_ENCRYPTION_ALGORITHM 
  
 = 
  
 "SHA256withECDSA" 
 ; 
  
 /** 
 * Verify the signature for the given signing message using the PEM format public key. 
 * 
 * @param message the signing message returned in Google Pay response. 
 * @param signature the signature returned in Google Pay response. 
 * @param publicKeyPemFilePath the path of the PEM format public key file corresponds to the 
 *     signature key ID returned in Google Pay response. 
 * @return true if the signature verification succeeds, false otherwise. 
 */ 
  
 public 
  
 static 
  
 boolean 
  
 verifySignature 
 ( 
  
 String 
  
 message 
 , 
  
 String 
  
 signature 
 , 
  
 String 
  
 publicKeyPemFilePath 
 ) 
  
 { 
  
 String 
  
 hashMessage 
  
 = 
  
 Hashing 
 . 
 sha256 
 (). 
 hashString 
 ( 
 message 
 , 
  
 StandardCharsets 
 . 
 UTF_8 
 ). 
 toString 
 (); 
  
 System 
 . 
 out 
 . 
 printf 
 ( 
 "Signing message: %s\n" 
 , 
  
 message 
 ); 
  
 System 
 . 
 out 
 . 
 printf 
 ( 
 "Hash of the signing message: %s\n" 
 , 
  
 hashMessage 
 ); 
  
 boolean 
  
 result 
  
 = 
  
 false 
 ; 
  
 try 
  
 { 
  
 byte 
 [] 
  
 hashMessageBytes 
  
 = 
  
 hashMessage 
 . 
 getBytes 
 ( 
 CHARSET 
 ); 
  
 byte 
 [] 
  
 signatureBytes 
  
 = 
  
 BaseEncoding 
 . 
 base16 
 (). 
 lowerCase 
 (). 
 decode 
 ( 
 signature 
 ); 
  
 PublicKey 
  
 publicKey 
  
 = 
  
 getPublicKeyFromPemFile 
 ( 
 publicKeyPemFilePath 
 ); 
  
 result 
  
 = 
  
 verify 
 ( 
 hashMessageBytes 
 , 
  
 signatureBytes 
 , 
  
 publicKey 
 ); 
  
 } 
  
 catch 
  
 ( 
 Exception 
  
 e 
 ) 
  
 { 
  
 e 
 . 
 printStackTrace 
 (); 
  
 } 
  
 System 
 . 
 out 
 . 
 printf 
 ( 
 "Signature verification result: %b\n" 
 , 
  
 result 
 ); 
  
 return 
  
 result 
 ; 
  
 } 
  
 /** 
 * Verify the signature for the given signing message using the given public key. 
 * 
 * @param messageBytes the byte array of the signing message. 
 * @param signatureBytes the byte array of the signature. 
 * @param publicKey the public key. 
 * @return true if the signature verification succeeds, false otherwise. 
 * @throws NoSuchAlgorithmException if the given algorithm is not available. 
 * @throws InvalidKeyException if the given key is invalid. 
 * @throws UnsupportedEncodingException if the given encoding is not supported. 
 * @throws SignatureException if exception occurs during signature verification . 
 */ 
  
 private 
  
 static 
  
 boolean 
  
 verify 
 ( 
 byte 
 [] 
  
 messageBytes 
 , 
  
 byte 
 [] 
  
 signatureBytes 
 , 
  
 PublicKey 
  
 publicKey 
 ) 
  
 throws 
  
 NoSuchAlgorithmException 
 , 
  
 InvalidKeyException 
 , 
  
 UnsupportedEncodingException 
 , 
  
 SignatureException 
  
 { 
  
 final 
  
 Signature 
  
 signature 
  
 = 
  
 Signature 
 . 
 getInstance 
 ( 
 HASH_ENCRYPTION_ALGORITHM 
 ); 
  
 signature 
 . 
 initVerify 
 ( 
 publicKey 
 ); 
  
 signature 
 . 
 update 
 ( 
 messageBytes 
 ); 
  
 return 
  
 signature 
 . 
 verify 
 ( 
 signatureBytes 
 ); 
  
 } 
  
 /** 
 * Read the public key from given public key file. 
 * 
 * @param publicKeyPemFilePath the path of a PEM format public key file. 
 * @return the generated public key. 
 * @throws NoSuchAlgorithmException if the given algorithm is not available. 
 * @throws IOException if the given file does not exists. 
 * @throws InvalidKeySpecException if the key spec is invalid. 
 */ 
  
 private 
  
 static 
  
 PublicKey 
  
 getPublicKeyFromPemFile 
 ( 
 String 
  
 publicKeyPemFilePath 
 ) 
  
 throws 
  
 NoSuchAlgorithmException 
 , 
  
 IOException 
 , 
  
 InvalidKeySpecException 
  
 { 
  
 final 
  
 KeyFactory 
  
 keyFactory 
  
 = 
  
 KeyFactory 
 . 
 getInstance 
 ( 
 ENCRYPTION_ALGORITHM 
 ); 
  
 final 
  
 PemReader 
  
 reader 
  
 = 
  
 new 
  
 PemReader 
 ( 
 new 
  
 FileReader 
 ( 
 publicKeyPemFilePath 
 )); 
  
 final 
  
 byte 
 [] 
  
 content 
  
 = 
  
 reader 
 . 
 readPemObject 
 (). 
 getContent 
 (); 
  
 final 
  
 X509EncodedKeySpec 
  
 publicKeySpec 
  
 = 
  
 new 
  
 X509EncodedKeySpec 
 ( 
 content 
 ); 
  
 reader 
 . 
 close 
 (); 
  
 return 
  
 keyFactory 
 . 
 generatePublic 
 ( 
 publicKeySpec 
 ); 
  
 } 
 } 

C#

The following C# example uses the Bouncy Castle library:

 using 
  
 Org.BouncyCastle.Crypto 
 ; 
 using 
  
 Org.BouncyCastle.OpenSsl 
 ; 
 using 
  
 Org.BouncyCastle.Security 
 ; 
 using 
  
 System 
 ; 
 using 
  
 System.IO 
 ; 
 using 
  
 System.Security.Cryptography 
 ; 
 using 
  
 System.Text 
 ; 
 namespace 
  
 Google.Google 
  
 Pay 
 . 
 Samples 
  
 { 
  
 /// 
   
 /// Helper class to verify the signature returned in Google Pay response 
  
 /// 
 
  
 class 
  
 SignatureVerifier 
  
 { 
  
 private 
  
 const 
  
 int 
  
 FROM_BASE16 
  
 = 
  
 16 
 ; 
  
 private 
  
 const 
  
 string 
  
 LOWERCASE_HEX_FORMAT 
  
 = 
  
 "x2" 
 ; 
  
 private 
  
 const 
  
 string 
  
 HASH_ENCRYPTION_ALGORITHM 
  
 = 
  
 "SHA256withECDSA" 
 ; 
  
 /// 
   
 /// Verify  signature for the given message using PEM format public key. 
  
 /// 
 
  
 /// true if the signature verification succeeds, false otherwise. 
 
  
 /// 
  
 /// 
  
 /// 
  
 /// signature key ID returned in Google Pay response. 
  
 public 
  
 static 
  
 bool 
  
 verifySignature 
 ( 
 string 
  
 message 
 , 
  
 string 
  
 signature 
 , 
  
 string 
  
 publicKeyPemFilePath 
 ) 
  
 { 
  
 string 
  
 hashMessage 
 ; 
  
 using 
  
 ( 
 SHA256 
  
 sha256 
  
 = 
  
 SHA256 
 . 
 Create 
 ()) 
  
 { 
  
 byte 
 [] 
  
 data 
  
 = 
  
 sha256 
 . 
 ComputeHash 
 ( 
 Encoding 
 . 
 Default 
 . 
 GetBytes 
 ( 
 message 
 )); 
  
 hashMessage 
  
 = 
  
 convertByteArrayToHexString 
 ( 
 data 
 ); 
  
 Console 
 . 
 WriteLine 
 ( 
 "Signing message: {0}" 
 , 
  
 message 
 ); 
  
 Console 
 . 
 WriteLine 
 ( 
 "Hash of the signing message: {0}" 
 , 
  
 hashMessage 
 ); 
  
 } 
  
 bool 
  
 result 
  
 = 
  
 false 
 ; 
  
 byte 
 [] 
  
 hashMessageBytes 
  
 = 
  
 Encoding 
 . 
 UTF8 
 . 
 GetBytes 
 ( 
 hashMessage 
 ); 
  
 byte 
 [] 
  
 signatureBytes 
  
 = 
  
 convertHexStringToByteArray 
 ( 
 signature 
 ); 
  
  
 AsymmetricKeyParameter 
  
 publicKey 
  
 = 
  
 getPublicKeyFromPemFile 
 ( 
 publicKeyPemFilePath 
 ); 
  
 result 
  
 = 
  
 verify 
 ( 
 hashMessageBytes 
 , 
  
 signatureBytes 
 , 
  
 publicKey 
 ); 
  
 return 
  
 result 
 ; 
  
 } 
  
 /// 
   
 ///  Verify signature for the given message using given public key 
  
 /// 
 
  
 /// true if the signature verification succeeds, false otherwise. 
 
  
 /// 
  
 /// 
  
 /// 
  
 public 
  
 static 
  
 bool 
  
 verify 
 ( 
 byte 
 [] 
  
 messageBytes 
 , 
  
 byte 
 [] 
  
 signatureBytes 
 , 
  
 AsymmetricKeyParameter 
  
 publicKey 
 ) 
  
 { 
  
 ISigner 
  
 signer 
  
 = 
  
 SignerUtilities 
 . 
 GetSigner 
 ( 
 HASH_ENCRYPTION_ALGORITHM 
 ); 
  
 signer 
 . 
 Init 
 ( 
 false 
 , 
  
 publicKey 
 ); 
  
 signer 
 . 
 BlockUpdate 
 ( 
 messageBytes 
 , 
  
 0 
 , 
  
 messageBytes 
 . 
 Length 
 ); 
  
 return 
  
 signer 
 . 
 VerifySignature 
 ( 
 signatureBytes 
 ); 
  
 } 
  
 /// 
   
 /// Converts the byte array to hex string. 
  
 /// 
 
  
 /// The byte array to hex string. 
 
  
 /// 
  
 private 
  
 static 
  
 string 
  
 convertByteArrayToHexString 
 ( 
 byte 
 [] 
  
 data 
 ) 
  
 { 
  
 if 
  
 ( 
 data 
  
 == 
  
 null 
 ) 
  
 { 
  
 throw 
  
 new 
  
 ArgumentNullException 
 ( 
 nameof 
 ( 
 data 
 )); 
  
 } 
  
 if 
  
 ( 
 data 
 . 
 Length 
  
 == 
  
 0 
 ) 
  
 { 
  
 throw 
  
 new 
  
 ArgumentException 
 ( 
 "{0} array cannot be empty" 
 , 
  
 nameof 
 ( 
 data 
 )); 
  
 } 
  
 StringBuilder 
  
 sBuilder 
  
 = 
  
 new 
  
 StringBuilder 
 (); 
  
 for 
  
 ( 
 int 
  
 i 
  
 = 
  
 0 
 ; 
  
 i 
  
 < 
  
 data 
 . 
 Length 
 ; 
  
 i 
 ++ 
 ) 
  
 { 
  
 sBuilder 
 . 
 Append 
 ( 
 data 
 [ 
 i 
 ]. 
 ToString 
 ( 
 LOWERCASE_HEX_FORMAT 
 )); 
  
 } 
  
 return 
  
 sBuilder 
 . 
 ToString 
 (); 
  
 } 
  
 /// 
   
 /// Converts the hex string to bytes. 
  
 /// 
 
  
 /// The string to bytes. 
 
  
 /// 
  
 public 
  
 static 
  
 byte 
 [] 
  
 convertHexStringToByteArray 
 ( 
 string 
  
 hexString 
 ) 
  
 { 
  
 if 
  
 ( 
 hexString 
  
 == 
  
 null 
 ) 
  
 { 
  
 throw 
  
 new 
  
 ArgumentNullException 
 ( 
 nameof 
 ( 
 hexString 
 )); 
  
 } 
  
 if 
  
 ( 
 hexString 
 . 
 Length 
  
 % 
  
 2 
  
 != 
  
 0 
 ) 
  
 { 
  
 throw 
  
 new 
  
 ArgumentException 
 ( 
 "{0} must have an even length" 
 , 
  
 nameof 
 ( 
 hexString 
 )); 
  
 } 
  
 byte 
 [] 
  
 bytes 
  
 = 
  
 new 
  
 byte 
 [ 
 hexString 
 . 
 Length 
  
 / 
  
 2 
 ]; 
  
 for 
  
 ( 
 int 
  
 i 
  
 = 
  
 0 
 ; 
  
 i 
  
 < 
  
 bytes 
 . 
 Length 
 ; 
  
 i 
 ++ 
 ) 
  
 { 
  
 string 
  
 currentHex 
  
 = 
  
 hexString 
 . 
 Substring 
 ( 
 i 
  
 * 
  
 2 
 , 
  
 2 
 ); 
  
 bytes 
 [ 
 i 
 ] 
  
 = 
  
 Convert 
 . 
 ToByte 
 ( 
 currentHex 
 , 
  
 FROM_BASE16 
 ); 
  
 } 
  
 return 
  
 bytes 
 ; 
  
 } 
  
 /// 
   
 ///  Read the public key from given pem file. 
  
 /// 
 
  
 /// The asymmetric key parameter. 
 
  
 /// 
  
 private 
  
 static 
  
 AsymmetricKeyParameter 
  
 getPublicKeyFromPemFile 
 ( 
 string 
  
 pemFilename 
 ) 
  
 { 
  
 StreamReader 
  
 fileStream 
  
 = 
  
 System 
 . 
 IO 
 . 
 File 
 . 
 OpenText 
 ( 
 pemFilename 
 ); 
  
 PemReader 
  
 pemReader 
  
 = 
  
 new 
  
 PemReader 
 ( 
 fileStream 
 ); 
  
 AsymmetricKeyParameter 
  
 keyParameter 
  
 = 
  
 ( 
 AsymmetricKeyParameter 
 ) 
 pemReader 
 . 
 ReadObject 
 (); 
  
 return 
  
 keyParameter 
 ; 
  
 } 
  
 } 
 } 

PHP

The following example uses the OpenSSL library to validate the digital signature:

  
 $message = "{\"Status\":\"SUCCESS\",\"amount\":\"10.01\",\"txnRef\":\"test reference      id\",\"toVpa\":\"merchant3@icici\",\"txnId\":\"ICI6a88c3ae581649f7b0e2157504358ead\",\"responseCode\":\"0\"}"; 
 $publicKey = file_get_contents("/var/www/html/php/googlepayPublicKeyV1.pem"); // a valid path to public key 
 $signature = "30450221008f32b3ac01a00e5b3c1f53e71e6f111546b60a9f6911df2ad75af5921f8681d002203b090e25a1b6dc7132530116d03024f8ab25795931f34a1724bed8f7389909cc"; 
 $alg = OPENSSL_ALGO_SHA256; 
 // verify using openssl library 
 $success = openssl_verify(hash("sha256", $message), hex2bin($signature), $publicKey, $alg); 
 if ($success === -1) { 
 echo "openssl_verify() failed with error. " . openssl_error_string() . "\n"; 
 } elseif ($success === 1) { 
 echo "signature verification was successful\n"; 
 } else { 
 echo "signature verification failed. \n"; 
 } 
 ?> 
  

How to use the signature verifier class

The following examples illustrate how to use the signatureVerifier class in Java and C# implemtation.

Java

  
 public 
  
 static 
  
 void 
  
 main 
 ( 
 String 
 [] 
  
 args 
 ) 
  
 { 
  
 String 
  
 signingMessage 
  
 = 
  
 "{\"Status\":\"SUCCESS\",\"amount\":\"10.01\",\"txnRef\":\"test reference id\",\"toVpa\":\"merchant3@icici\",\"txnId\":\"ICI6a88c3ae581649f7b0e2157504358ead\",\"responseCode\":\"0\"}" 
 ; 
  
 String 
  
 signature 
  
 = 
  
 "30450221008f32b3ac01a00e5b3c1f53e71e6f111546b60a9f6911df2ad75af5921f8681d002203b090e25a1b6dc7132530116d03024f8ab25795931f34a1724bed8f7389909cc" 
 ; 
  
 String 
  
 publicKeyPath 
  
 = 
  
 "./googlepayPublicKeyV1.pem" 
 ; 
  
 SignatureVerifier 
 . 
 verifySignature 
 ( 
 signingMessage 
 , 
  
 signature 
 , 
  
 publicKeyPath 
 ); 
 } 

C#

 static 
  
 void 
  
 Main 
 ( 
 string 
 [] 
  
 args 
 ) 
  
 { 
  
 string 
  
 signingMessage 
  
 = 
  
 "{\"Status\":\"SUCCESS\",\"amount\":\"10.01\",\"txnRef\":\"test reference id\"," 
  
 + 
  
 "\"toVpa\":\"merchant3@icici\",\"txnId\":\"ICI6a88c3ae581649f7b0e2157504358ead\",\"responseCode\":\"0\"}" 
 ; 
  
 string 
  
 signature 
  
 = 
  
 "30450221008f32b3ac01a00e5b3c1f53e71e6f111546b60a9f6911df" 
  
 + 
  
 "2ad75af5921f8681d002203b090e25a1b6dc7132530116d03024f8ab25795931f34a1724bed8f7389909cc" 
 ; 
  
 string 
  
 publicKeyPath 
  
 = 
  
 "./googlepayPublicKeyV1.pem" 
 ; 
  
 SignatureVerifier 
 . 
 verifySignature 
 ( 
 signingMessage 
 , 
  
 signature 
 , 
  
 publicKeyPath 
 ); 
 } 
Create a Mobile Website
View Site in Mobile | Classic
Share by: