互連網並不是一個十分安全的網路環境,針對一些關鍵API,如支付、借貸等API,為了防止資料泄漏與篡改,需要使用資料加密與簽名的機制。
簽名機制
對所發送的內容進行加簽(簽名),而接受者需要對內容進行驗簽(簽名),證明內容沒有被篡改,以此來保障開發人員應用和開放平台互動的安全性。
一個完整的簽章要求流程如下:

請求結構
Request Structure

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

Response Body
Response Body包含返回給用戶端的商務資訊,同時也包含請求的結果資訊resultCode、resultMessage。
Field | Data Type | Required | Description |
resultCode | String | No | 結果代碼. 最大長度:64個字元。 |
resultMessage | String | No | 結果代碼的詳細描述。 最大長度:256個字元。 |
加簽/驗簽
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****" }
基於以上資訊,
{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使用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%2BKdwbw4eY7tZBDQhMKoaMVSbqbCb3eRBxxxxresponse 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);
}
}