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