Digest Signature Authentication

  • Last update: October 18, 2024
  • Overview

    Version

    FineDataLink VersionFunction Description

    4.0.29

    /

    Application Scenario

    You need a secure authentication method for the published API to ensure data security.

    Function Description

    FineDataLink supports AK/SK-based digest and signature authentication for APIs, which prevents the authentication information and the request from being intercepted and tampered with during transmission, ensuring authentication security.

    The API released by FineDataLink uses HMAC-SHA256 to calculate the signature, and the signature verification logic is:

    Request initiator (calculate the signature and attach it to the request) ---> Request recipient (calculate the signature according to the request parameters and verify the signature)

    Prerequisite

    You have published the API, bound the API to an application, and set App Authentication Method to Digest Signature.

    2024-10-16_11-37-20.png

    Signature Calculation Method

    Java Implementation of Signatures

    POST Request with Content-Type application/json

    Create a Java file according to the following sample code:

    package kk;

    import javax.crypto.Mac;
    import javax.crypto.spec.SecretKeySpec;
    import java.math.BigInteger;
    import java.nio.charset.StandardCharsets;
    import java.security.MessageDigest;
    import java.util.Base64;
    import java.util.UUID;

    public class k {
        public static void main(String[] args) {
            //Change it to the corresponding AppSecret.
            String secretKey = "1bbe91b1-a39c-4742-9694-e126bcf9a3bd";
            //Fill in the request method in all caps.
            String httpMethod = "POST";
            //For POST requests, specify the contentType. For GET requests, use an empty string here.
            String contentType = "application/json";
            //Fill in the API path. For GET requests, include the parameters, for example, xxx/xxx?a=1.
            String pathAndParameters = "a5ce6bb4-467b-46f2-8878-2132635973bb/87";
            //Fill in the content of the body request.
            String data = "{\"paging\":{\"pageSize\":10,\"pageNum\":1},\"params\":[]}";

            //The nonce is generated automatically.
            String nonce = String.valueOf(UUID.randomUUID());
            //The timestamp is generated automatically.
            String timestamp = String.valueOf(System.currentTimeMillis());
            //Fill in the string to be signed.
            String stringToSign = httpMethod +"\n"+
                    nonce +"\n"+
                    timestamp +"\n"+
                    pathAndParameters +"\n"+
                    contentType +"\n"+
                    md5(data);
            //Fill in the signature.
            String signature = hmacSHA256(secretKey,stringToSign);
            //Input the complete authorization.
            System.out.println("Authorization:\n"+"HMAC-SHA256 Signature="+signature+",Nonce="+nonce+",Timestamp="+timestamp);
        }

        /**
         * Create a Base64-encoded signature using the HMAC-SHA256 for the string data.
         *
         * @param secretKey Secret key
         * @param data      String to be signed
         */
        public static String hmacSHA256(String secretKey, String data) {
            try {
                Mac hmacSha256 = Mac.getInstance("HmacSHA256");
                SecretKeySpec secretKeySpec = new SecretKeySpec(secretKey.getBytes(StandardCharsets.UTF_8), "HmacSHA256");
                hmacSha256.init(secretKeySpec);

                byte[] hashBytes = hmacSha256.doFinal(data.getBytes(StandardCharsets.UTF_8));

                return Base64.getEncoder().encodeToString(hashBytes);
            } catch (Exception e) {
                throw new RuntimeException("HmacSha error", e);
            }
        }

        /**
         * Calculate the MD5 digest of the string data and perform Base64 encoding on the digest to get the encoded result.
         *
         * @param data String to be calculated
         */
        public static String md5(String data) {
            try {
                MessageDigest md = MessageDigest.getInstance("MD5");
                md.reset();
                md.update(data.getBytes(StandardCharsets.UTF_8));
                String md5Result = String.format("%032x", new BigInteger(1, md.digest()));
                return Base64.getEncoder().encodeToString(md5Result.getBytes(StandardCharsets.UTF_8));
            } catch (Exception e) {
                throw new RuntimeException("md5 error", e);
            }
        }
    }

    GET Request

    package kk;

    import javax.crypto.Mac;
    import javax.crypto.spec.SecretKeySpec;
    import java.math.BigInteger;
    import java.nio.charset.StandardCharsets;
    import java.security.MessageDigest;
    import java.util.Base64;
    import java.util.UUID;

    public class k {
        public static void main(String[] args) {
            //Change it to the corresponding AppSecret.
            String secretKey = "a07eefc1-4b29-469a-8cb1-f68e3532d3a2";
            //Fill in the request method in all caps.
            String httpMethod = "GET";
            //For POST requests, specify the contentType. For GET requests, use an empty string here.
            String contentType = "";
            //Fill in the API path. For GET requests, include the parameters, for example, xxx/xxx?a=1.
            String pathAndParameters = "a5ce6bb4-467b-46f2-8878-2132635973bb/dd?pageSize=10&pageNum=1";
            //Fill in the content of the body request.
            String data = "";

            //The nonce is generated automatically.
            String nonce = String.valueOf(UUID.randomUUID());
            //The timestamp is generated automatically.
            String timestamp = String.valueOf(System.currentTimeMillis());
            //String to be signed
            String stringToSign = httpMethod +"\n"+
                    nonce +"\n"+
                    timestamp +"\n"+
                    pathAndParameters +"\n"+
                    contentType +"\n"+
                    ("".equals(data)?"":md5(data));
            //Fill in the signature
            String signature = hmacSHA256(secretKey,stringToSign);
            //Input the complete authorization.
            System.out.println("Authorization:\n"+"HMAC-SHA256 Signature="+signature+",Nonce="+nonce+",Timestamp="+timestamp);
        }

        /**
         * Create a Base64-encoded signature using the HMAC-SHA256 for the string data.
         *
         * @param secretKey Secret key
         * @param data      String to be signed
         */
        public static String hmacSHA256(String secretKey, String data) {
            try {
                Mac hmacSha256 = Mac.getInstance("HmacSHA256");
                SecretKeySpec secretKeySpec = new SecretKeySpec(secretKey.getBytes(StandardCharsets.UTF_8), "HmacSHA256");
                hmacSha256.init(secretKeySpec);

                byte[] hashBytes = hmacSha256.doFinal(data.getBytes(StandardCharsets.UTF_8));

                return Base64.getEncoder().encodeToString(hashBytes);
            } catch (Exception e) {
                throw new RuntimeException("HmacSha error", e);
            }
        }

        /**
         * Calculate the MD5 digest of the string data and perform Base64 encoding on the digest to get the encoded result.
         *
         * @param data String to be calculated
         */
        public static String md5(String data) {
            try {
                MessageDigest md = MessageDigest.getInstance("MD5");
                md.reset();
                md.update(data.getBytes(StandardCharsets.UTF_8));
                String md5Result = String.format("%032x", new BigInteger(1, md.digest()));
                return Base64.getEncoder().encodeToString(md5Result.getBytes(StandardCharsets.UTF_8));
            } catch (Exception e) {
                throw new RuntimeException("md5 error", e);
            }
        }
    }

    POST Request with Content-Type application/x-www-form-urlencoded

    package kk;

    import javax.crypto.Mac;
    import javax.crypto.spec.SecretKeySpec;
    import java.math.BigInteger;
    import java.nio.charset.StandardCharsets;
    import java.security.MessageDigest;
    import java.util.Base64;
    import java.util.UUID;

    public class k {
        public static void main(String[] args) {
            //Change it to the corresponding AppSecret.
            String secretKey = "1bbe91b1-a39c-4742-9694-e126bcf9a3bd";
            //Fill in the request method in all caps.
            String httpMethod = "POST";
            //For POST requests, specify the contentType. For GET requests, use an empty string here.
            String contentType = "application/x-www-form-urlencoded";
            //Fill in the API path. For GET requests, include the parameters, for example, xxx/xxx?a=1.
            String pathAndParameters = "a5ce6bb4-467b-46f2-8878-2132635973bb/87";
            //Fill in the content of the body request. The key-value pairs in the body also need to be encoded.
            String data = "a=1&b=%E6%8C%AA%E5%A8%81";

            //The nonce is generated automatically.
            String nonce = String.valueOf(UUID.randomUUID());
            //The timestamp is generated automatically.
            String timestamp = String.valueOf(System.currentTimeMillis());
            //Fill in the string to be signed.
            String stringToSign = httpMethod +"\n"+
                    nonce +"\n"+
                    timestamp +"\n"+
                    pathAndParameters +"\n"+
                    contentType +"\n"+
                    md5(data);
            //Signature
            String signature = hmacSHA256(secretKey,stringToSign);
            //Input the complete authorization.
            System.out.println("Authorization:\n"+"HMAC-SHA256 Signature="+signature+",Nonce="+nonce+",Timestamp="+timestamp);
        }

        /**
         * Create a Base64-encoded signature using the HMAC-SHA256 for the string data.
         *
         * @param secretKey Secret key
         * @param data      String to be signed
         */
        public static String hmacSHA256(String secretKey, String data) {
            try {
                Mac hmacSha256 = Mac.getInstance("HmacSHA256");
                SecretKeySpec secretKeySpec = new SecretKeySpec(secretKey.getBytes(StandardCharsets.UTF_8), "HmacSHA256");
                hmacSha256.init(secretKeySpec);

                byte[] hashBytes = hmacSha256.doFinal(data.getBytes(StandardCharsets.UTF_8));

                return Base64.getEncoder().encodeToString(hashBytes);
            } catch (Exception e) {
                throw new RuntimeException("HmacSha error", e);
            }
        }

        /**
         * Calculate the MD5 digest of the string data and perform Base64 encoding on the digest to get the encoded result.
         *
         * @param data String to be calculated
         */
        public static String md5(String data) {
            try {
                MessageDigest md = MessageDigest.getInstance("MD5");
                md.reset();
                md.update(data.getBytes(StandardCharsets.UTF_8));
                String md5Result = String.format("%032x", new BigInteger(1, md.digest()));
                return Base64.getEncoder().encodeToString(md5Result.getBytes(StandardCharsets.UTF_8));
            } catch (Exception e) {
                throw new RuntimeException("md5 error", e);
            }
        }
    }

    Signature Algorithm Principle

    The Client Generating and Passing Parameters

    Pass the Authorization header and the Content-Type header at the client and send GET or POST requests to the server.

    Request HeaderContent

    Authorization

    Fill in the authorization header in the following format. Signature=Signature value, Nonce=Random number, Timestamp=Timestamp value

    For example,

    HMAC-SHA256 Signature=FiXSVh+oBzitw+HDypwlmB0PE+Z67pdXbQM7QyHA7qk=, Nonce=c967a237-cd6c-470e-906f-a8655461897, Timestamp=1686542039670.

    Content-Type

    Enter the Content-Type header according to the request's content type.

    You do not need to fill in the Content-Type header for GET requests.

    The following figure shows the required items for generating the signature as the string to be signed. You need to replace the value of parameters marked with letters with actual values.

    2024-10-16_11-45-05.png

    Letter
    ProjectDescription

    A

    AppSecret

    It is the app secret.

    Choose Data Service > Application List, set App Authentication Method to Digest Signature, and you can copy the app secret, as shown in the following figure.

    2024-10-16_11-37-20 copy.png

    B

    HTTPMethod

    It is the request method. GET and POST requests are supported currently. 


    iconNote:
    Fill in it in all caps.


    2024-10-16_11-49-38.png


    Nonce

    It is a UUID, which is generated automatically in the Java code for signature generation.

    During authentication, the server will use this UUID to check for duplicate requests within the last 5 minutes and intercept the duplicate request.


    Timestamp

    It is a 13-digit timestamp (the number of milliseconds since 1970-01-01 00:00:00 UTC), which is automatically generated in the Java code for signature generation.

    During authentication, the server will use this timestamp to check whether the request has expired and block the expired request.

    D

    PathAndParameters

    It is the API path configured on the API configuration page.

    Include the parameter for GET requests.

    For example, include the parameter <appid>/api01?a=1&b=2 in the API path: http://192.168.5.175:8089/webroot/service/publish/<appid>/api01?a=1&b=2

    You can copy the API path from Authorized App in the API List, as shown in the following figure.

    2024-10-16_11-51-04.png

    C

    Content-Type

    It is the MIME type of the request body.

    For POST requests, specify the contentType. For GET requests, use an empty string here.

    E

    String data

    It is the parameter string in the POST request body.

    • For requests with Content-Type application/json: Keep all strings as they are, including spaces, line breaks, and other symbols.

    • For requests with Content-Type application/x-www-form-urlencoded: Encode each key-value pair using URL encoding and join them with ampersands (&). Retain the equal sign (=) for keys without values and values without keys.

    For example, the request body in the JSON format {"paging":{"pageSize":10,"pageNum":1},"params":[]} first experiences MD5 processing to output a 32-character lowercase string and then experiences Base64 encoding to output the final Content-MD5 result.

    {"paging":{"pageSize":10,"pageNum":1},"params":[]}

    The way to construct the string to be signed is as follows:


    String to be signed = HTTPMethod value "\n"

    Nonce value "\n"

    Timestamp value "\n"

    PathAndParameters value "\n"

    Content-Type value "\n"

    Content-MD5 value

    iconNote:

    1. Each item is separated by a newline character (\n). If a particular item does not exist, it should be replaced by an empty string, while still preserving the newline character between items.

    2. If there is no request body, the Content-MD5 value will be an empty string. (In other words, for GET requests, both the Content-Type and Content-MD5 values will be empty strings.)

    Generate the signature from the string to be signed using the HMAC-SHA256 encryption algorithm.

    Mac hmacSha256 = Mac.getInstance("HmacSHA256");
    byte[] appSecretBytes = appSecret.getBytes(Charset.forName("UTF-8"));
    hmacSha256.init(new SecretKeySpec(appSecretBytes, 0, appSecretBytes.length, "HmacSHA256"));
    byte[] md5Result = hmacSha256.doFinal(stringToSign.getBytes(Charset.forName("UTF-8")));
    String signature = Base64.encodeBase64String(md5Result);

    The Server Receiving the Request

    The method for calculating the signature is the same as that used on the client side.

    Authentication ItemPassing Condition
    TimestampThe time difference is less than 5 minutes compared with the current time of the server.
    NonceNo duplicate Nonce values within 5 minutes at the server side.
    SignatureThe Signature value calculated by the server is identical to that in the request header.

    Examples of Signature Calculation Using Java

    The Client Generating Signature

    Modify the specific content in the code by referring to the section "Java Implementation of Signatures" of this document.

    For example, if you have published an API in FineDataLink with a request type of POST application/json, you need to prepare the following data to generate the string to be signed.

    Project
    DescriptionContent to Be Modified in the Java File

    AppSecret

    It is the app secret.

    Choose Data Service > Application List, set App Authentication Method to Digest Signature, and you can copy the app secret, as shown in the following figure.

    2024-10-16_11-37-20 copy.png

    2024-10-16_11-58-12.png

    HTTPMethod

    It is the request method. GET and POST requests are supported currently. 


    iconNote:
    Fill in it in all caps.


    2024-10-16_11-49-38.png

     2024-10-16_11-58-12 copy.png

    Nonce

    It is a UUID, which is generated automatically in the Java code for signature generation.

    During authentication, the server will use this UUID to check for duplicate requests within the last 5 minutes and intercept the duplicate request.


    Timestamp

    It is a 13-digit timestamp (the number of milliseconds since 1970-01-01 00:00:00 UTC), which is automatically generated in the Java code for signature generation.

    During authentication, the server will use this timestamp to check whether the request has expired and block the expired request.


    PathAndParameters

    It is the API path configured on the API configuration page.

    Include the parameter for GET requests.

    For example, include the parameter <appid>/api01?a=1&b=2 in the API path: http://192.168.5.175:8089/webroot/service/publish/<appid>/api01?a=1&b=2

    You can copy the API path from Authorized App in the API List, as shown in the following figure.

    2024-10-16_11-51-04.png

    2024-10-16_11-58-12 copy (1).png

    Content-Type

    It is the MIME type of the request body.

    For POST requests, specify the contentType. For GET requests, use an empty string here.


    Content-MD5

    It is the parameter string in the POST request body.

    • For requests with Content-Type application/json: Keep all strings as they are, including spaces, line breaks, and other symbols.

    • For requests with Content-Type application/x-www-form-urlencoded: Encode each key-value pair using URL encoding and join them with ampersands (&). Retain the equal sign (=) for keys without values and values without keys.

    For example, the request body in the JSON format {"paging":{"pageSize":10,"pageNum":1},"params":[]} first experiences MD5 processing to output a 32-character lowercase string and then experiences Base64 encoding to output the final Content-MD5 result.

    For the BODY in the JSON format, you can copy the BODY value on the API generation page.

    2024-10-16_11-56-15.png

    2024-10-16_11-58-12 copy (2).png

    Run the Java file to obtain Authorization, as shown in the following figure.

    The Server Receiving the Request

    The following takes a POST JSON request as an example. You can enter the Authorization value and the request body to fetch data from the published API, as shown in the following figure.

     

    附件列表


    主题: Data Service
    Previous
    Next
    • Helpful
    • Not helpful
    • Only read

    滑鼠選中內容,快速回饋問題

    滑鼠選中存在疑惑的內容,即可快速回饋問題,我們將會跟進處理。

    不再提示

    10s後關閉

    Get
    Help
    Online Support
    Professional technical support is provided to quickly help you solve problems.
    Online support is available from 9:00-12:00 and 13:30-17:30 on weekdays.
    Page Feedback
    You can provide suggestions and feedback for the current web page.
    Pre-Sales Consultation
    Business Consultation
    Business: international@fanruan.com
    Support: support@fanruan.com
    Page Feedback
    *Problem Type
    Cannot be empty
    Problem Description
    0/1000
    Cannot be empty

    Submitted successfully

    Network busy