Sign a request and validate the signature

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

Sign a request

The following graphic shows an overview of the signature creation process

image.png

Figure 1. Signature creation process 

Procedure

  1. Obtain your private key (privateKey) that is used to sign the 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

Step 1: Obtain your private key to sign the request

Get your private key ready, which is used to generate the signature later.

Step 2: Construct the content to be signed

Create the string to sign. For example, a request has the following properties:

  • 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: Used to identify a client, and this field is associated with the keys that are used for the signature. For example, SANDBOX_5Y0566SG25J004124. You can get this field from the request header.
  • Request-Time: Specifies the time when a request is sent, as defined by ISO 8601. 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.

Syntax of Content_To_Be_Signed

copy
<HTTP Method> <HTTP-URI-with-query-string>
<Client-Id>.<Request-Time>.<HTTP body>

Step 3: Calculate and generate the signature

Use the sha256withrsa method that involves the proper algorithm and private key to calculate and generate the signature. The following example assumes that the RSA256 algorithm is used to generate the signature.

copy
signature=base64UrlEncode(sha256withrsa(string-to-sign))
  • sha256withrsa: The algorithm to use, RSA256.
  • string-to-sign: The content to be signed that is obtained in step 2.

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);
        }
    }

Step 4: Add the generated signature to the request header

Add the signature to the request header. Assemble the signature algorithm, the key version used for the signature, and the signature into the Signature header. The following example shows a finished Signature header: 

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.

Validate a signature 

After you receive a response, you need to validate the signature of the response. The following graphic shows an overview of the signature validation process: 

图片1.png

Figure 2. Signature validation process 

Procedure

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

For details of each step, see the following examples. 

Example

Step 1: Obtain the platform public key

The Client-Id and algorithm properties can be obtained from the response header. You can create asymmetric keys in your own way or by using the tool provided in 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

Figure 3. Set public key

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

Step 2: Construct the content to be validated

Create the string to be validated. For example, a response has the following properties:

  • 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: Used to identify a client, and is associated with the keys that are used for the signature. You can get this field from the response header.
  • Response-Time: Specifies the time when a response is returned, as defined by ISO 8601. 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.

Syntax of Content_To_Be_Validated

copy
<HTTP Method> <HTTP-URI-with-query-string>
<Client-Id>.<Response-Time>.<HTTP body>

Step 3: Get the signature from the response header

The signature string (signature) can be extracted from the Signature header of the response. For details about the response header, see the Message structure chapter.

Step 4: 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. If the two digests match, the signature is verified. For example, if the RSA256 algorithm is used, base64url decodes the signature content to obtain the original signature and validates 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: The signature extracted from the response header, which is obtained from step 3.
  • content_to_be_verified: The content to be validated that is created from step 2.
  • serverPublicKey: The platform public key obtained from step 1.

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);
        }

    }