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 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


      图片1.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.


      #Step 1: Obtain your private key to sign the request

      Get your private key ready, which is used to generate the signature later. For example, you can generate the RSA2 key pairs in the following way:

      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 signed

      The syntax of Content_To_Be_Signed is as below:

      copy
      <HTTP-Method> <HTTP-URI>
      <Client-Id>.<Request-Time>.<Request-Body>
      • HTTP-Method: POST
      • HTTP-URI: For example, if the HTTP URL is https://open-na.alipay.com/ams/api/v1/payments/refund, this field is /ams/api/v1/payments/refund.
      • Client-Id: Used to identify a client. 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.
      • Request-Body: The data body of the request. For example:
      copy
      {
       "order":{
          "orderId":"OrderID_0101010101",
          "orderDescription":"sample_order",
          "orderAmount":{
             "value":"100",
             "currency":"JPY"
          },
       },
       "paymentAmount":{
          "value":"100",
          "currency":"JPY"
       },
       "paymentFactor": {
           "isInStorePayment": "true"
       } 
      }


      By complying with the syntax of Content_To_Be_Signed, the content to be signed (Content_To_Be_Signed) is created as follows:

      copy
      POST /ams/api/v1/payments/pay
      TEST_5X00000000000000.2019-05-28T12:12:12+08:00.{
      "order":{
          "orderId":"OrderID_0101010101",
          "orderDescription":"sample_order",
          "orderAmount":{
             "value":"100",
             "currency":"JPY"
          },
       },
       "paymentAmount":{
          "value":"100",
          "currency":"JPY"
       },
       "paymentFactor": {
           "isInStorePayment": "true"
       } 
      }


      #Step 3: Calculate and generate the signature

      Use the base64UrlEncode and sha256withrsa methods that involve the proper algorithm and private key to calculate and generate the signature.

      copy
      generatedSignature=base64UrlEncode(sha256withrsa(<Content_To_Be_Signed>), <privateKey>))


      Methods used:

      • sha256withrsa: The method to generate a digital signature for the content provided.
      • base64UrlEncode : The method to encode the generated digital signature.


      Input parameters:

      • Content_To_Be_Signed: The content to be signed that is obtained in step 2.
      • privateKey: The private key value that is obtained in step 1.


      For example, the generated signature generatedSignature looks as follows:

      copy
      KrwDE9tAPJYBb4cUZU6ALJxGIZgwDXn5UkFPMip09n%2FkYKPhEIII%2Fki2rYY2lPtuKVgMNz%2BtuCU%
      2FjzRpohDbrOd8zYriiukpGAxBQDIVbatGI7WYOcc9YVQwdCR6ROuRQvr%2FD1AfdhHd6waAASu5Xugow9
      w1OW7Ti93LTd0tcyEWQYd2S7c3A73sHOJNYl8DC1PjasiBozZ%2FADgb7ONsqHo%2B8fKHsLygX9cuMkQY
      TGIRBQsvfgICnJhh%2BzXV8AQoecJBTrv6p%xxxx


      The following demo code shows how to use java to sign the request:

      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

      a. Assemble a signature string based on the following syntax:

      copy
      'Signature: algorithm=<algorithm>, keyVersion=<key-version>, signature=<generatedSignature>'
      • algorithm, keyVersion: See the header of the Message structure chapter.
      • generatedSignature: The signature that is generated in step 3.


      For example:

      copy
      'Signature: algorithm=RSA256, keyVersion=1, signature=KrwDE9tAPJYBb4cUZU6ALJxGIZgwDXn5UkFPMip09n%2FkYKPhEIII%2Fki2rYY2lPtuKVgMNz%2BtuCU%2FjzRpohDbrOd8zYriiukpGAxBQDIVbatGI7WYOcc9YVQwdCR6ROuRQvr%2FD1AfdhHd6waAASu5Xugow9w1OW7Ti93LTd0tcyEWQYd2S7c3A73sHOJNYl8DC1PjasiBozZ%2FADgb7ONsqHo%2B8fKHsLygX9cuMkQYTGIRBQsvfgICnJhh%2BzXV8AQoecJBTrv6p%****'


      b. Add the signature string to the request header. For details about the request header, see the Message structure chapter


      #Send a request

      Construct a request by adding the Client-Id, Request-Time, and Signature properties to the request header. After a request is constructed, you can use common tools, like cURL or Postman to send the request. In the following example, cURL is used.

      copy
      curl -X POST \
        https://www.example.com/ams/api/v1/payments/pay \
        -H 'Content-Type: application/json' \
        -H 'Client-Id: TEST_5X00000000000000' \
        -H 'Request-Time: 2019-05-28T12:12:12+08:00' \
        -H 'Signature: algorithm=RSA256, keyVersion=0, signature=KrwDE9tAPJYBb4cUZU6ALJxGIZgwDXn5UkFPMip09n%2FkYKPhEIII%2Fki2rYY2lPtuKVgMNz%2BtuCU%2FjzRpohDbrOd8zYriiukpGAxBQDIVbatGI7WYOcc9YVQwdCR6ROuRQvr%2FD1AfdhHd6waAASu5Xugow9w1OW7Ti93LTd0tcyEWQYd2S7c3A73sHOJNYl8DC1PjasiBozZ%2FADgb7ONsqHo%2B8fKHsLygX9cuMkQYTGIRBQsvfgICnJhh%2BzXV8AQoecJBTrv6p%xxxx' \
        -d '{
            "order":{
                "orderId":"OrderID_0101010101",
                "orderDescription":"sample_order",
                "orderAmount":{
                   "value":"100",
                   "currency":"JPY"
                },
             },
             "paymentAmount":{
                "value":"100",
                "currency":"JPY"
             },
             "paymentFactor": {
                 "isInStorePayment": "true"
             } 
      }'


      #Handle a response

      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: 


      图片2.png


      Figure 2. Signature validation process 


      A response consists of the response header and the response body. For example:

      • Code sample of the response header:
      copy
      Client-Id: 5X00000000000000
      Response-Time: 2019-05-28T12:12:14+08:00
      signature: algorithm=RSA256, keyVersion=0, signature=p9T2hXxIjek0UOLw3fwlthNsV6ATaioIvu8X1uFx8a9tE87d2XEhqylnf0KjifJ3WhCoMokl
      GwwlDS3tsSenwnL0Ha6BsXbJvUHRC5qcVlNy5Oq%2FpNqx2%2BKdwbw4eY7tZBDQhMKoaMVSbqbCb3eRBX
      sw9ZwOO%2FFCyq1zICzllOd4pbhpvES3gcw2X%2B0Ye4hQJBghcLCJxCizSv9lMyTmV%2BYA39B9gRouha
      N0dM2aeAXMlVJAWtJdcL%2Bdub%2F3LrzxBnY%****


      • Code sample of the response body:
      copy
      {
      "result": {
          "resultCode":"SUCCESS",
          "resultStatus":"S",
          "resultMessage":"success"
      },
      "paymentTime": "2019-05-28T12:12:13+08:00",
      "paymentId":"1234567"
      }


      #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. 


      #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 by using the tool provided in Alipay Developer Center or in your own way. You can set up and then exchange keys for the sandbox and production environments respectively with the provided tool.


      The following examples show two ways of creating asymmetric keys:

      • Use the tool provided in Alipay Developer Center. The key generation tool and the space to configure the key are as below:


      image

      Figure 3. Set public key


      • 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

      By complying with the Syntax of Content_To_Be_Validated, construct the content to be validated (Content_To_Be_Validated) as follows:

      copy
      POST /ams/api/v1/payments/pay
      TEST_5X00000000000000.2019-05-28T12:12:14+08:00.{
       "result": {
          "resultCode":"SUCCESS",
          "resultStatus":"S",
          "resultMessage":"success"
       },
       "paymentTime": "2019-05-28T12:12:13+08:00",
       "paymentId":"1234567"
      }


      #Syntax of Content_To_Be_Validated

      copy
      <HTTP-Method> <HTTP-URI>
      <Client-Id>.<Response-Time>.<Response-Body>

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

      • HTTP-Method: POST
      • HTTP-URI: For example, if the HTTP URL is https://open-na.alipay.com/ams/api/v1/payments/refund, this field is /ams/api/v1/payments/refund.
      • Client-Id: Used to identify a client. 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.
      • Response-Body: The data body of the response.


      #Step 3: Get the signature from the response header

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


      Code sample of signature:

      Signature: algorithm=RSA256, signature=<target_signature>


      #Step 4: Validate the signature

      Use the sha256withrsa_verify method to validate the signature of the response.

      The syntax of the sha256withrsa_verify method is as follows:

      copy
      IS_SIGNATURE_VALID=sha256withrsa_verify(base64UrlDecode(<target_signature>), <Content_To_Be_Validated>, <serverPublicKey>)


      Methods used:

      • sha256withrsa_verify: The method to verify the signature.
      • base64UrlDecode: The method to decode the signature.


      Input parameters:

      • target_signature: The target signature that 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.


      Output parameters:

      • IS_SIGNATURE_VALID: a boolean value that specifies whether the signature is valid.
        • true: The signature is valid.
        • false: The signature is not valid. The causes can be that the private key and the public key do not match, or Content_To_Be_Validated is not correctly constructed.


      The following demo code shows how to use java to validate the signature:

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