Alipay, China's leading third-party online payment solutionAlipay, China's leading third-party online payment solution

Sign a request and validate the signature

Before calling an API, you must sign all HTTP or HTTPS API requests to ensure security and then need to validate the response signature accordingly.

Sign a request

See the following figure for an overview of the signature creation process


image

Figure 1. Signature creation process 

Procedure

  1. Obtain your private key, represented by privateKey , which is used to sign a request.
  2. Construct the content to be signed (Content_To_Be_Signed).
  3. Calculate and generate the signature.
  4. Add the generated signature to the request header.

For details of each step, see the following examples.

Example

1. Obtain your private key to sign the request

You can create asymmetric keys in your own way or by using the tool provided in the Alipay Developer Center. You can set up and then exchange keys for the sandbox and production environments respectively with the provided tool. The following figure shows where you can obtain the key generation tool and configure the key.

image

Besides using the tool to generate RSA2 key pair, you can also generate the RSA2 key pairs manually, for example:

copy
# 1. Generating the private key
openssl genrsa -out client_private_key_php_dotnet.pem

# 2. If you are a Java developer, convert the private key to PKCS8 format
openssl pkcs8 -topk8 -inform PEM -in client_private_key_php_dotnet.pem -outform PEM -nocrypt -out client_private_key_pkcs8.pem 

# 3. Generate the public key
openssl rsa -in client_private_key_php_dotnet.pem -pubout -out client_public_key_php_dotnet.pem 

# 4. Generate the private key that can be used in Java
cat client_private_key_pkcs8.pem | grep -v "^\-" | tr -d "\n" | sed 's/%$//' > client_private_key_java.pem

# 5. Generate the public key that can be used in Java
cat client_public_key_php_dotnet.pem | grep -v "^\-" | tr -d "\n" | sed 's/%$//' > client_public_key_java.pem

2. Construct the content to be signed

Create the string to sign. The string to be signed is:

copy
<HTTP Method> <HTTP-URI-with-query-string>
<Client-Id>.<Request-Time|Response-Time>.<HTTP body>
  • HTTP_method: POST
  • HTTP_URI_with_query_string: For example, if the HTTP URL is https://xxx/openapi/transfer/transfer, this field is /openapi/transfer/transfer.
  • client-Id: is used to identify a client, and is associated with the keys that are used for signature and encryption.  You can get this field from the request header.
  • request-time: Specifies the time when a request is sent, as defined by RFC3339. This field must be accurate to milliseconds. For example, 2019-05-28T12:12:12+08:00. You can get this field from the request header.
  • HTTP_BODY: the data body of the request.

3. Calculate and generate the signature

Generate the signature. Use the algorithm and private key obtained in step1 to generate the signature. The following example assumes that RSA256 algorithm is used to generate the signature, the string-to-sign var is obtained from step 2:

copy
signature=base64UrlEncode(sha256withrsa(<unsignedContent>), <privateKey>))

The content to be signed is <unsignedContent>, the algorithm used is RSA 256, and the private key value is <privateKey>.

Demo code:

copy
/**
     * 
     * @param requestURI // domain part excluded, sample: /ams/api/v1/payments/pay
     * @param clientId
     * @param requestTime
     * @param privateKey
     * @param requestBody
     * @return
     */
    public static String sign(String requestURI, String clientId, String requestTime,
                              String privateKey, String requestBody) {

        String content = String.format("POST %s\n%s.%s.%s", requestURI, clientId, requestTime,
            requestBody);

        try {
            java.security.Signature signature = java.security.Signature
                .getInstance("SHA256withRSA");

            PrivateKey priKey = KeyFactory.getInstance("RSA").generatePrivate(
                new PKCS8EncodedKeySpec(Base64.decodeBase64(privateKey.getBytes("UTF-8"))));

            signature.initSign(priKey);
            signature.update(content.getBytes("UTF-8"));

            byte[] signed = signature.sign();

            return URLEncoder.encode(new String(Base64.encodeBase64(signed), "UTF-8"), "UTF-8");
        } catch (Exception e) {
            throw new RuntimeException(e);
        }
    }

4. Add the generated signature to the request header

Add the signature to header. Assemble the signature algorithm, the key version used for the signature, and the signature into Signature header. The following example shows a finished Signature header, the signature var is obtained from step 2

copy
key: Signature ;
value:algorithm=<algorithm>,keyVersion=<key-version>,signature=<signature>
  • algorithm , keyVersion : see the header of the Message structure chapter.
  • signature : the signature that is generated in step 3.

Handle a response

See the following figure for an overview of the signature validation process: 

image

Figure 2. Signature validation process 

Validate a signature 

  1. Obtain the platform public key.
  2. Construct the content to be validated (Content_To_Be_Validated).
  3. Extract the signature from the response header.
  4. Validate the signature.

For details of each step, see the following examples.

Example

1. Obtain the platform public key

Obtain the public key, see Obtain your private key to sign the request for details. Obtain Client-Id and algorithm from header.

2. Construct the content to be validated

Create the string to be validated. The string to be signed is:

copy
<HTTP Method> <HTTP-URI-with-query-string>
<Client-Id>.<Request-Time|Response-Time>.<HTTP body>
  • HTTP_METHOD: POST
  • HTTP_URI_WITH_QUERY_STRING: For example, if the HTTP URL is https://xxx/openapi/transfer/transfer, this field is /openapi/transfer/transfer.
  • Client-Id: is used to identify a client, and is associated with the keys that are used for signature and encryption.  You can get this field from the request header.
  • response-Time: Specifies the time when a request is sent, as defined by RFC3339. This field must be accurate to milliseconds. For example, 2019-05-28T12:12:14+08:00. You can get this field from the response header.
  • HTTP_BODY: the data body of the response.

3. Get the signature from the response header and validate the signature

Use the algorithm obtained in step 1 to calculate a digest of the string you created in step 2. Then, decrypt the signature by using the public key to get a digest. Compare two digests, if the digests match the signature is verified. For example, assume RSA256 algorithm is used, base64url decode the signature content to obtain the original signature, and then validate the signature by using the sender's public key and sha256withrsa algorithm. 

copy
sha256withrsa_verify(base64UrlDecode(<signature>), <content_to_be_verified>, <serverPublicKey>)
  • sha256withrsa_verify: the method to verify the signature.
  • base64UrlDecode: the method to decode the signature.
  • signature: Signature string from the response.
  • content_to_be_verified: The content to be validated that is created from step2.
  • serverPublicKey: the platform public key that is obtained from step1.

Demo code:

copy
/**
     * 
     * @param requestURI // domain part excluded, sample: /ams/api/v1/payments/pay
     * @param clientId
     * @param reponseTime
     * @param alipayPublicKey
     * @param responseBody
     * @param signatureToBeVerified
     * @return
     */
    public static boolean verify(String requestURI, String clientId, String reponseTime,
                                 String alipayPublicKey, String responseBody,
                                 String signatureToBeVerified) {

        //signatureToBeVerified would not be present in the response when AMS returns a SIGNATURE_INVALID
        if (StringUtil.isBlank(signatureToBeVerified)) {
            return false;
        }

        String content = String.format("POST %s\n%s.%s.%s", requestURI, clientId, reponseTime,
            responseBody);

        try {
            java.security.Signature signature = java.security.Signature
                .getInstance("SHA256withRSA");

            PublicKey pubKey = KeyFactory.getInstance("RSA").generatePublic(
                new X509EncodedKeySpec(Base64.decodeBase64(alipayPublicKey.getBytes("UTF-8"))));

            signature.initVerify(pubKey);
            signature.update(content.getBytes("UTF-8"));

            return signature.verify(Base64.decodeBase64(URLDecoder.decode(signatureToBeVerified,
                "UTF-8").getBytes("UTF-8")));

        } catch (Exception e) {
            throw new RuntimeException(e);
        }

    }