全部產品
Search
文件中心

SuperApp:安全機制

更新時間:Nov 08, 2024

互連網並不是一個十分安全的網路環境,針對一些關鍵API,如支付、借貸等API,為了防止資料泄漏與篡改,需要使用資料加密與簽名的機制。

簽名機制

對所發送的內容進行加簽(簽名),而接受者需要對內容進行驗簽(簽名),證明內容沒有被篡改,以此來保障開發人員應用和開放平台互動的安全性。

一個完整的簽章要求流程如下:

image

請求結構

Request Structure

image

Request URL

請求地址為https://{domainName}/api/{version}/{restfulPath}

  • domainName:Superapp開放平台服務網域名稱。

  • restfulPath:OpenAPI介面路徑,例如:/v1/authorizations/applyToken/v1/users/inquiryUserInfo

  • version:OpenAPI版本。

Request Method

僅使用POST請求。

Request Header

要求標頭主要包含以下幾個Fields(Field是大小寫敏感的)。

Header

Required

Description

Code sample

X-Request-Sign

Yes

本次請求產生的簽名,預設為HmacSHA1。

X-Request-Sign:****

X-Access-Key

Yes

請求方的身份識別ID,AccessKey。

X-Access-Key:頒發給使用者的AccessKey。

X-Request-Timestamp

Yes

時間戳記

X-Timestamp:時間戳記。

Content-Type

No

內容格式

Content-Type: application/json; charset=UTF-8

Request Body

Body為JSON格式的詳細請求資訊,其中的欄位因介面而異。

Response Structure

image

Response Body

Response Body包含返回給用戶端的商務資訊,同時也包含請求的結果資訊resultCode、resultMessage。

Field

Data Type

Required

Description

resultCode

String

No

結果代碼.

最大長度:64個字元。

resultMessage

String

No

結果代碼的詳細描述。

最大長度:256個字元。

加簽/驗簽

1. 加簽請求

  1. 首先需要構造用於加簽的content,每次請求都會有以下屬性:

    • HTTP_URI : 例如 /v1/authorizations/applyToken

    • X-Access-Key:46b1cac78ed94ca4b99ad6de550a****

    • X-Request-Timestamp:1676904384074

    • HTTP_BODY : body 舉例如下:

      {
      	"appId": "223232323****",
      	"authClientId": "7230000****,
      	"grantType": "authorization_code",
      	"authCode": "1cc19911172e4f8aaa509c8fb5d1****"
      }
  2. 基於以上資訊,{Content_To_Be_Signed}的格式如下,我們可以構造出需要加簽的content。<HTTP_METHOD> <HTTP_URI> <Client-Id>.<Request-Time>.<Access-Key>

    POST /v1/authorizations/applyToken
    {
    	"appId": "223232323****",
    	"authClientId": "7230000****",
    	"grantType": "authorization_code",
    	"authCode": "1cc19911172e4f8aaa509c8fb5d1****"
    }.1676904384074.46b1cac78ed94ca4b99ad6de550afb68
    
  3. 使用HmacSHA1演算法,以下的程式碼範例展示了如何加簽一個請求。

    /**
     * 
     * @param requestURI
     * @param clientId
     * @param requestTime
     * @param privateKey
     * @param requestBody
     * @return
    */
    public static String sign(String requestURI, String accessKey, String secretKey, Long requestTime, String requestBody) {
    	String content = String.format("POST %s\n%s.%s.%s", requestURI, requestBody, requestTime, accessKey);
    	String signed = generateSign(content, secretKey, "HmacSHA1");
     	return Base64.getEncoder().encodeToString(signed.getBytes(StandardCharsets.UTF_8));
    }
    
    public static String generateSign(String content, String key, String algorithm) {
            if (!StringUtils.isEmpty(algorithm) && !StringUtils.isEmpty(content) && null != key) {
                try {
                    SecretKeySpec signinKey = new SecretKeySpec(key.getBytes(), algorithm);
                    Mac mac = Mac.getInstance(algorithm);
                    mac.init(signinKey);
                    byte[] rawHmac = mac.doFinal(content.getBytes());
                    return convertToHex(rawHmac);
                } catch (InvalidKeyException | NoSuchAlgorithmException var6) {
                    return "";
                }
            } else {
                return "";
            }
    }

2.發送請求

通過將Client-Id,、Request-Time和Signature添加到要求標頭,可以構造出如下請求:

curl -X POST \
  https://example.com/v1/authorizations/applyToken \
  -H 'Content-Type: application/json' \
  -H 'X-Access-Key: 46b1cac78ed94ca4b99ad6de550afb68' \
  -H 'X-Request-Timestamp: 1676904384074' \
  -H 'Signature: KrwDE9tAPJYBb4cUZU6ALJxGIZgwDXn5UkFPMip09n%2FkYKPhEIII%2Fki2rYY2lPtuKVgMNz%2BtuCU%2FjzRpohDbrOd8zYriiukpGAxBQDIVbatGI7WYOcc9YVQwdCR6ROuRQvr%2FD1AfdhHd6waAASu5Xugow9w1OW7Ti93LTd0tcyEWQYd2S7c3A73sHOJNYl8DC1PjasiBozZ%2FADgb7ONsqHo%2B8fKHsLygX9cuMkQYTGIRBQsvfgICnJhh%2BzXV8AQoecJBTrv6p%xxxx' \
  -d '{
	"appId": "223232323****",
	"authClientId": "7230000****",
	"grantType": "authorization_code",
	"authCode": "1cc19911172e4f8aaa509c8fb5d1****"
}'

3. 驗簽返回

當收到返回時,用戶端需要對返回做驗簽處理。通常返回包含response header與response body兩部分。舉例如下:

  • response header

Client-Id: TEST_3990000000x
Response-Time: 2022-12-19T12:12:12+08:00
Signature: algorithm=RSA256, keyVersion=0, signature=p9T2hXxIjek0UOLw3fwlthNsV6ATaioIvu8X1uFx8a9tE87d2XEhqylnf0KjifJ3WhCoMokl
GwwlDS3tsSenwnL0Ha6BsXbJvUHRC5qcVlNy5Oq%2FpNqx2%2BKdwbw4eY7tZBDQhMKoaMVSbqbCb3eRBxxxx
  • response body

{
	"resultCode":"SUCCESS",
	"resultMessage":"success"
	"accessToken": "201509BBeff9351ad1874306903e96b91d248A36",
  "refreshToken": "201509BBdcba1e3347de4e75ba3fed2c9abebE36",
	"tokenExpiryTime": "2022-12-15T12:12:12+08:00",
	"refreshTokenExpiryTime": "2022-12-30T12:12:12+08:00"
}

基於以上資訊,可以構造出需要加簽的content如下:

POST /v1/authorizations/applyToken
TEST_3990000000x.2022-12-19T12:12:12+08:00.{
	"resultCode":"SUCCESS",
	"resultMessage":"success"
	"accessToken": "201509BBeff9351ad1874306903e96b91d248A36",
  "refreshToken": "201509BBdcba1e3347de4e75ba3fed2c9abebE36",
	"tokenExpiryTime": "2022-12-15T12:12:12+08:00",
	"refreshTokenExpiryTime": "2022-12-30T12:12:12+08:00"
}

驗簽demo代碼如下:

/**
* 
* @param requestURI
* @param clientId
* @param reponseTime
* @param publicKey
* @param responseBody
* @param signatureToBeVerified
* @return
*/
public static boolean verify(String requestURI, String clientId, String reponseTime, String publicKey, String responseBody, String signatureToBeVerified) {
    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(publicKey.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);
    }

}