文档管理中心

服务器端开发

校验密钥证明证书链

您的应用服务器接收到来自应用的请求,其中包含的密钥证明证书链采用X509标准格式,证书链(证书数组)中的第一本证书为密钥证明证书,最后一本证书为根CA证书,中间的为子CA证书。

在线密钥证明接口返回的证书链包含3本证书,其中1本密钥证明证书、1本子CA证书、1本根CA证书。

离线密钥证明接口返回的证书链包含4本证书,其中1本密钥证明证书、2本子CA证书、1本根CA证书。

应用服务器对密钥证明证书链的校验步骤如下:

  1. 使用官网提供的可信的根CA证书对证书链合法性进行校验。(根CA证书下载地址

    说明

    请勿在应用服务器中使用子CA证书对密钥证明证书链进行校验,子CA证书可能会因为有效期结束、证书被吊销等发生变化。

  2. 解析密钥证明证书获取应用公钥、挑战值Challenge、应用ID、密钥管理部件ID。

    其中应用公钥直接从密钥证明证书的subjectPublicKeyInfo字段获取;其他字段从密钥证明证书的扩展域段(Extensions)中获取,扩展域段的OID为1.3.6.1.4.1.2011.2.376.1.3(密钥证明扩展域段)。

  3. 校验挑战值Challenge是否与“创建密钥确立可信凭证流程”步骤3中应用服务器缓存的挑战值Challenge一致。

  4. 校验应用ID是否与预期的取值一致。根据应用ID判断请求是否来自预期的HarmonyOS应用。

  5. 校验密钥来源是否与预期的取值一致。

密钥证明证书格式说明:

密钥证明扩展域段为Asn.1 DER标准编码格式,数据结构定义如下:

收起
自动换行
深色代码主题
复制
  1. HuaweiAttestation ::= SEQUENCE {
  2. version AttestationVersion DEFAULT v1,
  3. claim1 AttestationClaim,
  4. claim2 AttestationClaim,
  5. claim3 AttestationClaim,
  6. ... ...
  7. }
  8. AttestationVersion ::= INTEGER { v1(0) }
  9. AttestationClaim ::= SEQUENCE {
  10. securityLevel SecurityLevel,
  11. type AttestationType,
  12. value AttestationValue
  13. }
  14. SecurityLevel ::= INTEGER
  15. AttestationType ::= OBJECT IDENTIFIER
  16. AttestationValue ::= ANY -- DEFINED BY AttestationType
  17. ApplicationIDType ::= SEQUENCE {
  18. type OBJECT IDENTIFIER,
  19. value OCT_STR
  20. }

AttestationClaim类型取值说明:

展开
序号 type(OID)取值 value的数据类型 securityLevel Claim说明
1 1.3.6.1.4.1.2011.2.376.2.1.4 OCT_STR 保留字段,暂不使用 应用输入的挑战值Challenge。
2 1.3.6.1.4.1.2011.2.376.2.1.3 ApplicationIDType 保留字段,暂不使用 应用ID。
3 1.3.6.1.4.1.2011.2.376.2.2.2.6 OCT_STR 保留字段,暂不使用 密钥管理部件ID。取值固定为:0x28c4fb4944afec11b9090242ac120002(Universal Keystore Kit的部件ID)。
4 1.3.6.1.4.1.2011.2.376.2.2.4.8 UTF8String 2 设备产品型号,从API version 20开始支持。设备产品型号一般应用在设备风控场景,识别黑灰产设备的行为,请勿使用该字段对设备身份进行认证。

securityLevel类型取值说明:

展开
securityLevel 说明
2 REE(Rich Execution Environment),数据来源于HarmonyOS系统层。
3 TEE(Trusted Execution Environment),数据通过TEE进行保护。

ApplicationIDType类型取值说明:

展开
type(OID)取值 value取值说明
1.3.6.1.4.1.2011.2.376.2.1.3.1

HarmonyOS Hap应用信息,包含如下字段:

1. appId:包含Hap应用的bundleName和签名证书公钥的hash;

2. bundleName:Hap应用的包名;

3. appIdentifier:AppGallery Connect网站上为Hap应用分配的统一APP ID字段,从API version 20开始支持;

4. appMode:Hap应用的状态,取值:debug或release,从API version 20开始支持。

value为json字符串,样例如下:

{

"appId":"com.example.attesthcts_BDmjsOezxRmguzlYRVhQavW22Eswi5sX61wOAysWPOGS8TZ5tY1u1A9EcvarzyrfOEj5zT8BCGkfFkVjn0m5wzo=",

"bundleName":"com.example.attesthcts",

"appIdentifier":"5765880207853009781",

"appMode":"release"

}

1.3.6.1.4.1.2011.2.376.2.1.3.2

系统服务(Service Ability)的ID,样例:

{processName: "huawei_share"}

示例:

收起
自动换行
深色代码主题
复制
  1. import org.bouncycastle.asn1.*;
  2. import org.bouncycastle.jce.provider.BouncyCastleProvider;
  3. import java.io.ByteArrayInputStream;
  4. import java.io.FileOutputStream;
  5. import java.security.PublicKey;
  6. import java.security.Security;
  7. import java.security.cert.CertPath;
  8. import java.security.cert.CertPathValidator;
  9. import java.security.cert.CertificateFactory;
  10. import java.security.cert.PKIXCertPathValidatorResult;
  11. import java.security.cert.PKIXParameters;
  12. import java.security.cert.TrustAnchor;
  13. import java.security.cert.X509Certificate;
  14. import java.util.ArrayList;
  15. import java.util.Date;
  16. import java.util.HashSet;
  17. import java.util.List;
  18. import com.alibaba.fastjson2.JSON;
  19. public class ParseAttestation {
  20. static {
  21. Security.addProvider(new BouncyCastleProvider());
  22. }
  23. //HarmonyOS Hap应用通过anonAttestKeyItem接口获取到的 “密钥证明证书链”数据
  24. //其中在线密钥证明接口返回的证书链包含3本证书,离线密钥证明接口返回的证书链包含4本证书。
  25. static String[] g_attestCertStr = new String[]{"-----BEGIN CERTIFICATE-----\nMIIEUDCCA/WgAwIBAgIOCfv5Xf9hjA2u32gjpG8wCgYIKoZIzj0EAwIwXTE5MDcGA1UEAwwwSHVhd2VpIENCRyBFQ0MgRGV2aWNlIEFub255bW91cyBBdHRlc3RhdGlvbiBDQSAxMRMwEQYDVQQKDApIdWF3ZWkgQ0JHMQswCQYDVQQGEwJDTjAeFw0yNTA1MTMwNjI3NDlaFw0yNTA1MjAwNjI3NDlaMCwxKjAoBgNVBAMMIURldmljZSBDZXJ0aWZpY2F0ZSBNYW5hZ2VtZW50IEtleTCCASIwDQYJKoZIhvcNAQEBBQADggEPADCCAQoCggEBALd0wgFgvDF5uPq2hh69LdRHnIX3+mzdAzf10L9Jk6bWPZqqTvz88ZX7e12Su1Myf5iyT3TMKjZ+Y2SnsWpHG/7Dpx990u7/CxeeRY0qziIqMTEbrLFaSHY++///9SxmEiM7a3z2Ged2FzDSvOTj1JVmm2hk+bUcceTVuHAmRwidFIQrL5h/lxaO3uPFqbTdiW6ocz06pEbi8mg6LAafik1pfsO30a3yIGiH1f4uZhCEFjHQxQdSFsRPh04Ehclx6lQ196tO0d3RHR8dxL7ghGNxs9rB1Sq/0TH2mK1vKAY/YgvBs5nypOnDY+0MXN7j5NucvJ32wssGI7CbmMxVeZMCAwEAAaOCAf0wggH5MB0GA1UdDgQWBBQA6HLpfdJvtiqPqQXenry8b6qjYzAMBgNVHRMBAf8EAjAAMAkGA1UdOAQCBQAwHwYDVR0jBBgwFoAU4yzL/3aHOxL7QyI/P/sCBoHfJ6cwggGcBgwrBgEEAY9bAoJ4AQMEggGKMIIBhgIBADCB/AIBAgYNKwYBBAGPWwKCeAIBAzCB5wYOKwYBBAGPWwKCeAIBAwEEgdR7ImFwcElkIjoiY29tLmV4YW1wbGUubXlhcHBsaWNhdGlvbl9CS3BOWWR2UU0yYkNLYklwRERuWmdkdGNYdEtnZUg5M2FwVm1aOWdpcTFoeUt2elNzVVNFZTFsT3VsK3N2bXhZS2ltb0dNWnF0U0o3eGxpRkVZd2NRK0E9IiwiYnVuZGxlTmFtZSI6ImNvbS5leGFtcGxlLm15YXBwbGljYXRpb24iLCJhcHBJZGVudGlmaWVyIjoiMTU3MzU0NjgiLCJhcHBNb2RlIjoiZGVidWcifTAiAgEABg0rBgEEAY9bAoJ4AgEEBA5jaGFsbGVuZ2VfZGF0YTAYAgEDBg0rBgEEAY9bAoJ4AgEFBAQCAAAAMB0CAQIGDisGAQQBj1sCgngCAgQIDAhDTFMtQUwwMDAlAgEDBg4rBgEEAY9bAoJ4AgICBgQQKMT7SUSv7BG5CQJCrBIAAjAKBggqhkjOPQQDAgNJADBGAiEAko1y6sf7Fg48oWZC8FoP5WtmzKiVk5AOOvuhwaK0CQcCIQD8HymOzkzmOOjUuz/rdVrTM4191dpGr3jfU1u5rBpNIw==\n-----END CERTIFICATE-----", "-----BEGIN CERTIFICATE-----\nMIICyjCCAlCgAwIBAgIREj5jzbLehL8yzkDm5uwcSJUwCgYIKoZIzj0EAwMwSzETMBEGA1UEChMKSHVhd2VpIENCRzE0MDIGA1UEAxMrSHVhd2VpIENCRyBFQ0MgRGV2aWNlIEF0dGVzdGF0aW9uIFJvb3QgQ0EgMTAeFw0yMzEyMDUwMzE4MDRaFw0zMzEyMDUwMzE4MDRaMF0xOTA3BgNVBAMMMEh1YXdlaSBDQkcgRUNDIERldmljZSBBbm9ueW1vdXMgQXR0ZXN0YXRpb24gQ0EgMTETMBEGA1UECgwKSHVhd2VpIENCRzELMAkGA1UEBhMCQ04wWTATBgcqhkjOPQIBBggqhkjOPQMBBwNCAATYjeQrfijuZ/9HJPLlsfJ4/wnXbQXaxy5f5fEcMN+pTZ5RekpY7PnDp2zEdibvkSzjv1MuRs8JzORyGatSOrYFo4IBATCB/jAfBgNVHSMEGDAWgBTaRGLD5yvof1E6XEuPQ3w5JMPOrDAdBgNVHQ4EFgQU4yzL/3aHOxL7QyI/P/sCBoHfJ6cwRgYDVR0gBD8wPTA7BgRVHSAAMDMwMQYIKwYBBQUHAgEWJWh0dHA6Ly9wa2kuY29uc3VtZXIuaHVhd2VpLmNvbS9jYS9jcHMwEgYDVR0TAQH/BAgwBgEB/wIBATAOBgNVHQ8BAf8EBAMCAQYwUAYDVR0fBEkwRzBFoEOgQYY/aHR0cDovL3BraS5jb25zdW1lci5odWF3ZWkuY29tL2NhL2NybC9yb290X2RldmljZUF0dGVzdF9jcmwuY3JsMAoGCCqGSM49BAMDA2gAMGUCMQCE9qqNREq3AvCuznKeBl8biwC5GpV/Z1B0rsU4RqeTqNJ0Gvyz3g8Noaf4SpWzsLUCMBm5nr39UEOq89kx7QQjgYWLEWKcuSsgw2/6MckKP/6zrxjVld2SMtqiphKnrv1EkA==\n-----END CERTIFICATE-----","-----BEGIN CERTIFICATE-----\nMIICCTCCAY6gAwIBAgIDVxAsMAoGCCqGSM49BAMDMEsxEzARBgNVBAoTCkh1YXdlaSBDQkcxNDAyBgNVBAMTK0h1YXdlaSBDQkcgRUNDIERldmljZSBBdHRlc3RhdGlvbiBSb290IENBIDEwIBcNMjMxMTMwMDIwNjU1WhgPMjA3MzExMzAwMjA2NTVaMEsxEzARBgNVBAoTCkh1YXdlaSBDQkcxNDAyBgNVBAMTK0h1YXdlaSBDQkcgRUNDIERldmljZSBBdHRlc3RhdGlvbiBSb290IENBIDEwdjAQBgcqhkjOPQIBBgUrgQQAIgNiAATDJzRdruaBeMoQBbdqCe51ezvkQn3OPYBoRmpL5KPktdFtD0b97FRp8jGLiUhPKyo8M15fxW5Ams4s80E8I1BSXoovDnkKllFfUadD8URgwEfOk5qttYNKzJcULavOhbijQjBAMA4GA1UdDwEB/wQEAwIBBjAPBgNVHRMBAf8EBTADAQH/MB0GA1UdDgQWBBTaRGLD5yvof1E6XEuPQ3w5JMPOrDAKBggqhkjOPQQDAwNpADBmAjEA2zDQREvORPqcZyjwKDltu0T9zN8Cd3/hi4DQZvuRJdJOY57yIIO/LKxezzEcGiMMAjEAkX7r0U4Mcaw4uURMh+7tLMyvyxnlW8yJqBEOnZfqS8I8t0bQIY2r/5TQAPC0JhBm\n-----END CERTIFICATE-----"};
  26. //从HarmonyOS官网下载的根CA证书
  27. static String g_rootCertStr = "-----BEGIN CERTIFICATE-----\n" +
  28. "MIICCTCCAY6gAwIBAgIDVxAsMAoGCCqGSM49BAMDMEsxEzARBgNVBAoTCkh1YXdl\n" +
  29. "aSBDQkcxNDAyBgNVBAMTK0h1YXdlaSBDQkcgRUNDIERldmljZSBBdHRlc3RhdGlv\n" +
  30. "biBSb290IENBIDEwIBcNMjMxMTMwMDIwNjU1WhgPMjA3MzExMzAwMjA2NTVaMEsx\n" +
  31. "EzARBgNVBAoTCkh1YXdlaSBDQkcxNDAyBgNVBAMTK0h1YXdlaSBDQkcgRUNDIERl\n" +
  32. "dmljZSBBdHRlc3RhdGlvbiBSb290IENBIDEwdjAQBgcqhkjOPQIBBgUrgQQAIgNi\n" +
  33. "AATDJzRdruaBeMoQBbdqCe51ezvkQn3OPYBoRmpL5KPktdFtD0b97FRp8jGLiUhP\n" +
  34. "Kyo8M15fxW5Ams4s80E8I1BSXoovDnkKllFfUadD8URgwEfOk5qttYNKzJcULavO\n" +
  35. "hbijQjBAMA4GA1UdDwEB/wQEAwIBBjAPBgNVHRMBAf8EBTADAQH/MB0GA1UdDgQW\n" +
  36. "BBTaRGLD5yvof1E6XEuPQ3w5JMPOrDAKBggqhkjOPQQDAwNpADBmAjEA2zDQREvO\n" +
  37. "RPqcZyjwKDltu0T9zN8Cd3/hi4DQZvuRJdJOY57yIIO/LKxezzEcGiMMAjEAkX7r\n" +
  38. "0U4Mcaw4uURMh+7tLMyvyxnlW8yJqBEOnZfqS8I8t0bQIY2r/5TQAPC0JhBm\n" +
  39. "-----END CERTIFICATE-----\n";
  40. //保存HarmonyOS Hap应用生成的应用公钥的文件名
  41. static String g_publicKeyFileName = "d:\\attestPublicKey.pem";
  42. public static void main(String[] args) {
  43. ParseAttestation parseAttestation = new ParseAttestation();
  44. parseAttestation.parseAndValidateAttestCertChain(g_attestCertStr, g_rootCertStr, g_publicKeyFileName);
  45. }
  46. void parseAndValidateAttestCertChain(String[] attestCertStr, String rootCertStr, String publicKeyFileName) {
  47. try {
  48. //解析密钥证明证书链
  49. List<X509Certificate> attestCerts = parseAttestationCerts(attestCertStr);
  50. //校验密钥证明证书链
  51. Date curDate = new Date();
  52. validateAttestationCertChain(attestCerts, rootCertStr, curDate);
  53. //解析密钥证明证书
  54. AttestationInfo attestInfo = extractAttestaionField(attestCerts.get(0));
  55. //校验密钥证明信息是否正确
  56. if (!checkAttestInfo(attestInfo)) {
  57. //todo: 进行异常处理
  58. }
  59. //保存HarmonyOS Hap应用生成的应用公钥
  60. saveAttestPublicKey(attestInfo.publicKey, publicKeyFileName);
  61. } catch (Exception e) {
  62. System.out.println(e);
  63. }
  64. }
  65. List<X509Certificate> parseAttestationCerts(String[] certStr) throws Exception {
  66. List<X509Certificate> certificateList = new ArrayList<>(certStr.length);
  67. CertificateFactory certificateFactory = CertificateFactory.getInstance("X.509", "BC");
  68. for (int i = 0; i < certStr.length; i++) {
  69. certificateList.add((X509Certificate) certificateFactory.generateCertificate(
  70. new ByteArrayInputStream(certStr[i].getBytes())));
  71. }
  72. return certificateList;
  73. }
  74. void validateAttestationCertChain(List<X509Certificate> certs, String trustCAStr, Date date) throws Exception {
  75. //构造证书链
  76. CertificateFactory factory = CertificateFactory.getInstance("X.509", "BC");
  77. CertPath certPath = factory.generateCertPath(certs);
  78. //读取信任根证书和构建trustAnchor对象
  79. X509Certificate trustCA = (X509Certificate) factory.generateCertificate(
  80. new ByteArrayInputStream(trustCAStr.getBytes()));
  81. TrustAnchor trustAnchor = new TrustAnchor(trustCA, null);
  82. HashSet trustAnchorSet = new HashSet<TrustAnchor>();
  83. trustAnchorSet.add(trustAnchor);
  84. //构建validator和对应的参数
  85. PKIXParameters params = new PKIXParameters(trustAnchorSet);
  86. params.setDate(date);
  87. //密钥证明证书有效期比较短,不需要进行证书的吊销验证。
  88. params.setRevocationEnabled(false);
  89. CertPathValidator validator = CertPathValidator.getInstance("PKIX", "BC");
  90. try {
  91. PKIXCertPathValidatorResult result = (PKIXCertPathValidatorResult) validator.validate(certPath, params);
  92. System.out.println("Cert Chain validate success!");
  93. } catch (Exception e) {
  94. System.out.println("Cert Chain validate fail!" + e.getMessage());
  95. }
  96. }
  97. int getInteger(ASN1Encodable value) {
  98. if (value instanceof ASN1Integer) {
  99. return ((ASN1Integer) value).getValue().intValue();
  100. } else if (value instanceof ASN1Enumerated) {
  101. return ((ASN1Enumerated) value).getValue().intValue();
  102. } else {
  103. throw new IllegalArgumentException(
  104. "expected Integer value ; found " + value.getClass().getName() + " instead.");
  105. }
  106. }
  107. byte[] getOctetString(ASN1Encodable value) {
  108. if (value instanceof ASN1OctetString) {
  109. return ((ASN1OctetString) value).getOctets();
  110. } else {
  111. throw new RuntimeException(
  112. "expected OctetString value ; found " + value.getClass().getName() + " instead.");
  113. }
  114. }
  115. void printBytes(byte[] byteArray) {
  116. if (byteArray == null) {
  117. System.out.println("null");
  118. }
  119. for (int i = 0; i < byteArray.length; i++) {
  120. System.out.printf("%02X ", byteArray[i]);
  121. }
  122. System.out.println();
  123. }
  124. AttestationInfo extractAttestaionField(X509Certificate certificate) {
  125. AttestationInfo attestInfo = new AttestationInfo();
  126. //获取应用公钥
  127. attestInfo.publicKey = certificate.getPublicKey();
  128. //从密钥证明证书中获取 “密钥证明扩展域段”
  129. byte[] attestationValue = certificate.getExtensionValue("1.3.6.1.4.1.2011.2.376.1.3");
  130. if (attestationValue == null || attestationValue.length == 0) {
  131. throw new IllegalArgumentException("Can't found the attestation extension!");
  132. }
  133. ASN1Sequence attestSequence = ASN1Sequence.getInstance(
  134. ASN1OctetString.getInstance(attestationValue).getOctets());
  135. //获取Attestation Version
  136. attestInfo.version = getInteger(attestSequence.getObjectAt(0));
  137. for (int i = 1; i < attestSequence.size(); i++) {
  138. ASN1Sequence attestClaim = ASN1Sequence.getInstance(attestSequence.getObjectAt(i));
  139. //获取Claim的oid
  140. ASN1ObjectIdentifier attestClaimOid = (ASN1ObjectIdentifier) attestClaim.getObjectAt(1);
  141. if ("1.3.6.1.4.1.2011.2.376.2.1.4".equalsIgnoreCase(attestClaimOid.getId())) {
  142. //读取Challenge
  143. attestInfo.challenge = getOctetString(attestClaim.getObjectAt(2));
  144. } else if ("1.3.6.1.4.1.2011.2.376.2.1.3".equalsIgnoreCase(attestClaimOid.getId())) {
  145. //读取appInfo
  146. ASN1Sequence appInfoAsn1 = (ASN1Sequence) attestClaim.getObjectAt(2);
  147. //获取appInfo的oid
  148. ASN1ObjectIdentifier appidOid = (ASN1ObjectIdentifier) appInfoAsn1.getObjectAt(0);
  149. if (!"1.3.6.1.4.1.2011.2.376.2.1.3.1".equalsIgnoreCase(appidOid.getId())) {
  150. continue;
  151. }
  152. //读取hap应用信息
  153. String appInfo = new String(getOctetString(appInfoAsn1.getObjectAt(1)));
  154. System.out.println("appInfo is:\n" + appInfo);
  155. attestInfo.appInfo = JSON.parseObject(appInfo, AppInfo.class);
  156. } else if ("1.3.6.1.4.1.2011.2.376.2.2.2.6".equalsIgnoreCase(attestClaimOid.getId())) {
  157. //读取密钥管理部件id,应该取值为0x28c4fb4944afec11b9090242ac120002
  158. attestInfo.keyManagerId = getOctetString(attestClaim.getObjectAt(2));
  159. } else if ("1.3.6.1.4.1.2011.2.376.2.2.4.8".equalsIgnoreCase(attestClaimOid.getId())) {
  160. //读取设备产品型号
  161. attestInfo.model = attestClaim.getObjectAt(2).toString();
  162. }
  163. }
  164. return attestInfo;
  165. }
  166. boolean checkAttestInfo(AttestationInfo attestInfo) {
  167. //todo: 校验Challenge
  168. System.out.println("challenge is:");
  169. printBytes(attestInfo.challenge);
  170. //todo: 校验appInfo中的字段信息
  171. System.out.println("appInfo.appId is:\n" + attestInfo.appInfo.appId);
  172. System.out.println("appInfo.bundleName is:\n" + attestInfo.appInfo.bundleName);
  173. System.out.println("appInfo.appIdentifier is:\n" + attestInfo.appInfo.appIdentifier);
  174. System.out.println("appInfo.appMode is:\n" + attestInfo.appInfo.appMode);
  175. //todo: 校验keyManagerId,固定为:0x28c4fb4944afec11b9090242ac120002。
  176. System.out.println("key manager id is:");
  177. printBytes(attestInfo.keyManagerId);
  178. System.out.println("device model is:");
  179. System.out.println(attestInfo.model);
  180. return true;
  181. }
  182. void saveAttestPublicKey(PublicKey publicKey, String publicKeyFileName) throws Exception {
  183. //todo: 把attestInfo.publicKey.getEncoded()获取到的应用公钥保存到服务器
  184. FileOutputStream file = new FileOutputStream(publicKeyFileName);
  185. file.write(publicKey.getEncoded());
  186. file.close();
  187. System.out.println("the app public key: \n" + publicKey);
  188. }
  189. class AttestationInfo {
  190. public PublicKey publicKey;
  191. public int version;
  192. public byte[] challenge;
  193. public AppInfo appInfo;
  194. public byte[] keyManagerId;
  195. public String model;
  196. }
  197. static class AppInfo {
  198. public String appId;
  199. public String bundleName;
  200. public String appIdentifier;
  201. public String appMode;
  202. }
  203. }

保存应用公钥

您的应用服务器对密钥证明证书链校验通过后,把密钥证明证书中的应用公钥保存到服务器中(“对密钥证明证书链进行校验”的样例代码中已包含公钥保存的示例代码),以便对后续的业务请求进行验证。在保存应用公钥前应确保公钥的唯一性,应用服务器中不应该存在多个相同的应用公钥。

说明

实现建议:在免密登录/支付等应用场景,需要为应用中的每个登录用户生成唯一的密钥对,并在应用服务器对用户与应用公钥进行关联。

实现提示: 对业务请求进行签名验签时需要先查找到应用公钥,建议为应用公钥生成一个唯一的应用公钥ID(如:对应用公钥计算Hash),并保存应用公钥ID与应用公钥的关系,通过应用公钥ID来查找应用公钥。

同时,应用服务器应该返回应用公钥ID给应用,并由应用存储应用ID。

搜索
请输入您想要搜索的关键词