Validate the Advanced Digital Signature
Learn how to validate the Advanced digital signature for Payment Pages 2.0, ensuring secure transmission of sensitive data.
For added security, you can use the Advanced digital signature for Payment Pages 2.0. For the Advanced signature, Zuora creates the signature with Tenant ID, Page ID, Token, Timestamp, as well as all pass-through URL parameters, encrypted Payment Method ID, and Error Code. Upon receiving the signature, the client verifies the signature using the public key, and comparing the values with the parameter values on the URL. Furthermore, the client will decrypt the Payment Method ID as well.
This approach will ensure the following:
-
The Payment Method ID will not be passed over HTTPS in plain text
-
Since pass-through parameters, Payment Method ID and Error Code are signed with the signature, once the signature is verified, the client can trust the values on the URL to be originated from Zuora.
The advanced signature will be generated using algorithm SHA512withRSA over a string that is concatenated with the following parts, delimited by the “#” character.
-
CallBack Path
-
Tenant ID
-
Host Page ID
-
Token (32 chars)
-
Timestamp (The time when we generate the signature)
-
Error Code
-
Passthrough parameters (field_passthrough[1,2,3,4,5]) Note: Though up to 15 passthrough parameters can be supported when passing in your client parameters, only the first 5 parameters are used for signature generation and validation.
-
Payment Method ID signed with Private Key
To validate the Advanced digital signature response for a Payment Page :
The following code shows a sample implementation of the verification steps. This sample code to validate the advanced signature can be found in the latest version of the Payment Pages 2.0 sample code suite on Zuora GitHub site.
//Step 1: Include the Zuora Security library 1.1 (zuora-rsa-security-1.1.jar)
com.zuora.rsa.security.decrypt.SignatureDecrypter;
com.zuora.rsa.security.decrypt.FieldDecrypter;
//Step 2: Get the signature from the callback parameter.
String signature = new String(request.getParameter("signature").getBytes("iso-8859-1"), "UTF-8"); //Make sure using correct character encoding standard.
//Step 3: Construct the string to be encrypted.
String DELIM="#";
String timestamp = request.getParameter("timestamp");
String pageId = FieldDecrypter.decrypt(request.getParameter("pageId"), publicKeyString );
String paymentMethodId = FieldDecrypter.decrypt(request.getParameter("refId"), publicKeyString);
StringBuilder encryptedString = new StringBuilder();
encryptedString.append( "/hpm2samplecodejsp/webcontent/callback.jsp"); //Use your callback url instead in your page configration
encryptedString.append( DELIM + request.getParameter("tenantId") );
encryptedString.append( DELIM + request.getParameter("token"));
encryptedString.append( DELIM + timestamp);
encryptedString.append( DELIM + pageId);
encryptedString.append( DELIM + (request.getParameter("errorCode") == null?"":request.getParameter("errorCode") ));
encryptedString.append( DELIM + (request.getParameter("field_passthrough1") == null? "":request.getParameter("field_passthrough1")));
encryptedString.append( DELIM + (request.getParameter("field_passthrough2") == null? "":request.getParameter("field_passthrough2")));
encryptedString.append( DELIM + (request.getParameter("field_passthrough3") == null? "":request.getParameter("field_passthrough3")));
encryptedString.append( DELIM + (request.getParameter("field_passthrough4") == null? "":request.getParameter("field_passthrough4")));
encryptedString.append( DELIM + (request.getParameter("field_passthrough5") == null? "":request.getParameter("field_passthrough5")));
encryptedString.append( DELIM + paymentMethodId );
//Step 4: Verify the signature. (Need zuora-rsa-security-1.1.jar )
boolean isSignatureValid = false;
isSignatureValid = SignatureDecrypter.verifyAdvancedSignature(signature, encryptedString.toString(), publicKeyString);
if( !isSignatureValid ){
// signature is invalid.
throw new Exception("Signature is invalid.");
}
//Step 5: Validate that the signature is not expired.
long expiredTime = 5 * 60 * 1000;
if((new Date()).getTime() > (Long.parseLong(timestamp) + expiredTime)) {
// signature is expired.
throw new Exception("Signature is expired.");
}