Skip to content

Commit 0b340c7

Browse files
author
弩哥
committedAug 27, 2020
新增AES加解密功能
1 parent 3108aa3 commit 0b340c7

13 files changed

+562
-74
lines changed
 

‎.gitignore

+2-1
Original file line numberDiff line numberDiff line change
@@ -4,4 +4,5 @@ lib/*.js.map
44
coverage/
55
.vscode/
66
lib/*.d.ts
7-
.nyc_output
7+
.nyc_output
8+
.DS_Store

‎LEGAL.md

+7
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,7 @@
1+
Legal Disclaimer
2+
3+
Within this source code, the comments in Chinese shall be the original, governing version. Any comment in other languages are for reference only. In the event of any conflict between the Chinese language version comments and other language version comments, the Chinese language version shall prevail.
4+
5+
法律免责声明
6+
7+
关于代码注释部分,中文注释为官方版本,其它语言注释仅做参考。中文注释可能与其它语言注释存在不一致,当中文注释与其它语言注释存在不一致时,请以中文注释为准。

‎README.md

+22
Original file line numberDiff line numberDiff line change
@@ -23,18 +23,40 @@
2323
```
2424
// TypeScript
2525
import AlipaySdk from 'alipay-sdk';
26+
// 普通公钥模式
27+
const alipaySdk = new AlipaySdk({
28+
// 参考下方 SDK 配置
29+
appId: '2016123456789012',
30+
privateKey: fs.readFileSync('./private-key.pem', 'ascii'),
31+
//可设置AES密钥,调用AES加解密相关接口时需要(可选)
32+
encryptKey: '请填写您的AES密钥,例如:aa4BtZ4tspm2wnXLb1ThQA'
33+
});
2634
35+
// 证书模式
2736
const alipaySdk = new AlipaySdk({
2837
// 参考下方 SDK 配置
2938
appId: '2016123456789012',
3039
privateKey: fs.readFileSync('./private-key.pem', 'ascii'),
40+
alipayRootCertPath: path.join(__dirname,'../fixtures/alipayRootCert.crt'),
41+
appCertPath: path.join(__dirname,'../fixtures/appCertPublicKey.crt'),
42+
alipayPublicCertPath: path.join(__dirname,'../fixtures/alipayCertPublicKey_RSA2.crt'),
3143
});
3244
45+
// 无需加密的接口
3346
const result = await alipaySdk.exec('alipay.system.oauth.token', {
3447
grantType: 'authorization_code',
3548
code: 'code',
3649
refreshToken: 'token'
3750
});
51+
52+
// 需要AES加解密的接口
53+
await alipaySdk.exec('alipay.open.auth.app.aes.set', {
54+
bizContent: {
55+
merchantAppId: '2021001170662064'
56+
},
57+
// 自动AES加解密
58+
needEncrypt: true
59+
});
3860
```
3961

4062
## Demo:

‎lib/alipay.ts

+91-48
Original file line numberDiff line numberDiff line change
@@ -14,7 +14,8 @@ import * as camelcaseKeys from 'camelcase-keys';
1414
import * as snakeCaseKeys from 'snakecase-keys';
1515

1616
import AliPayForm from './form';
17-
import { sign, ALIPAY_ALGORITHM_MAPPING } from './util';
17+
import { sign, ALIPAY_ALGORITHM_MAPPING, aesDecrypt } from './util';
18+
import { getSNFromPath, getSN, loadPublicKey, loadPublicKeyFromPath } from './antcertutil';
1819

1920
const pkg = require('../package.json');
2021

@@ -43,6 +44,26 @@ export interface AlipaySdkConfig {
4344
urllib?: any;
4445
/** 指定private key类型, 默认: PKCS1, PKCS8: PRIVATE KEY, PKCS1: RSA PRIVATE KEY */
4546
keyType?: 'PKCS1' | 'PKCS8';
47+
/** 应用公钥证书文件路径 */
48+
appCertPath?: string;
49+
/** 应用公钥证书文件内容 */
50+
appCertContent?: string | Buffer;
51+
/** 应用公钥证书sn */
52+
appCertSn?: string;
53+
/** 支付宝根证书文件路径 */
54+
alipayRootCertPath?: string;
55+
/** 支付宝根证书文件内容 */
56+
alipayRootCertContent?: string | Buffer;
57+
/** 支付宝根证书sn */
58+
alipayRootCertSn?: string;
59+
/** 支付宝公钥证书文件路径 */
60+
alipayPublicCertPath?: string;
61+
/** 支付宝公钥证书文件内容 */
62+
alipayPublicCertContent?: string | Buffer;
63+
/** 支付宝公钥证书sn */
64+
alipayCertSn?: string;
65+
/** AES密钥,调用AES加解密相关接口时需要 */
66+
encryptKey?: string;
4667
}
4768

4869
export interface AlipaySdkCommonResult {
@@ -55,6 +76,8 @@ export interface AlipaySdkCommonResult {
5576
export interface IRequestParams {
5677
[key: string]: any;
5778
bizContent?: any;
79+
// 自动AES加解密
80+
needEncrypt?:boolean;
5881
}
5982

6083
export interface IRequestOption {
@@ -75,11 +98,26 @@ class AlipaySdk {
7598
if (!config.privateKey) { throw Error('config.privateKey is required'); }
7699

77100
const privateKeyType = config.keyType === 'PKCS8' ? 'PRIVATE KEY' : 'RSA PRIVATE KEY';
78-
config.privateKey = this.formatKey(config.privateKey, privateKeyType);
79-
if (config.alipayPublicKey) {
101+
config.privateKey = this.formatKey(config.privateKey, privateKeyType);
102+
// 普通公钥模式和证书模式二选其一,传入了证书路径或内容认为是证书模式
103+
if (config.appCertPath || config.appCertContent) {
104+
// 证书模式,优先处理传入了证书内容的情况,其次处理传入证书文件路径的情况
105+
// 应用公钥证书序列号提取
106+
config.appCertSn = is.empty(config.appCertContent) ? getSNFromPath(config.appCertPath, false)
107+
: getSN(config.appCertContent, false);
108+
// 支付宝公钥证书序列号提取
109+
config.alipayCertSn = is.empty(config.alipayPublicCertContent) ? getSNFromPath(config.alipayPublicCertPath, false)
110+
: getSN(config.alipayPublicCertContent, false);
111+
// 支付宝根证书序列号提取
112+
config.alipayRootCertSn = is.empty(config.alipayRootCertContent) ? getSNFromPath(config.alipayRootCertPath, true)
113+
: getSN(config.alipayRootCertContent, true);
114+
config.alipayPublicKey = is.empty(config.alipayPublicCertContent) ? loadPublicKeyFromPath(config.alipayPublicCertPath)
115+
: loadPublicKey(config.alipayPublicCertContent);
116+
config.alipayPublicKey = this.formatKey(config.alipayPublicKey, 'PUBLIC KEY');
117+
} else if (config.alipayPublicKey) {
118+
// 普通公钥模式,传入了支付宝公钥
80119
config.alipayPublicKey = this.formatKey(config.alipayPublicKey, 'PUBLIC KEY');
81120
}
82-
83121
this.config = Object.assign({
84122
urllib,
85123
gateway: 'https://openapi.alipay.com/gateway.do',
@@ -116,12 +154,13 @@ class AlipaySdk {
116154
'app_id', 'method', 'format', 'charset',
117155
'sign_type', 'sign', 'timestamp', 'version',
118156
'notify_url', 'return_url', 'auth_token', 'app_auth_token',
157+
'appCertSn', 'alipayRootCertSn',
119158
];
120159

121160
for (const key in params) {
122161
if (urlArgs.indexOf(key) > -1) {
123162
const val = encodeURIComponent(params[key]);
124-
requestUrl = `${requestUrl}${ requestUrl.includes('?') ? '&' : '?' }${key}=${val}`;
163+
requestUrl = `${requestUrl}${requestUrl.includes('?') ? '&' : '?'}${key}=${val}`;
125164
// 删除 postData 中对应的数据
126165
delete params[key];
127166
}
@@ -135,8 +174,8 @@ class AlipaySdk {
135174
const config = this.config;
136175
let signParams = {} as { [key: string]: string | Object };
137176
let formData = {} as { [key: string]: string | Object | fs.ReadStream };
138-
const infoLog = (option.log && is.fn(option.log.info)) ? option.log.info : null;
139-
const errorLog = (option.log && is.fn(option.log.error)) ? option.log.error : null;
177+
const infoLog = (option.log && is.fn(option.log.info)) ? option.log.info : null;
178+
const errorLog = (option.log && is.fn(option.log.error)) ? option.log.error : null;
140179

141180
option.formData.getFields().forEach((field) => {
142181
// 字段加入签名参数(文件不需要签名)
@@ -171,7 +210,7 @@ class AlipaySdk {
171210
json: false,
172211
timeout: config.timeout,
173212
headers: { 'user-agent': this.sdkVersion },
174-
}, (err, {}, body) => {
213+
}, (err, { }, body) => {
175214
if (err) {
176215
err.message = '[AlipaySdk]exec error';
177216
errorLog && errorLog(err);
@@ -204,7 +243,7 @@ class AlipaySdk {
204243
private pageExec(method: string, option: IRequestOption = {}): Promise<string> {
205244
let signParams = { alipaySdk: this.sdkVersion } as { [key: string]: string | Object };
206245
const config = this.config;
207-
const infoLog = (option.log && is.fn(option.log.info)) ? option.log.info : null;
246+
const infoLog = (option.log && is.fn(option.log.info)) ? option.log.info : null;
208247

209248
option.formData.getFields().forEach((field) => {
210249
signParams[field.name] = field.value;
@@ -248,7 +287,7 @@ class AlipaySdk {
248287

249288
// 消息验签
250289
private notifyRSACheck(signArgs: { [key: string]: any }, signStr: string, signType: 'RSA' | 'RSA2') {
251-
const signContent = Object.keys(signArgs).sort().filter(val => val).map((key) => {
290+
const signContent = Object.keys(signArgs).sort().filter(val => val).map((key) => {
252291
let value = signArgs[key];
253292

254293
if (Array.prototype.toString.call(value) !== '[object String]') {
@@ -334,8 +373,8 @@ class AlipaySdk {
334373
// 计算签名
335374
const signData = sign(method, params, config);
336375
const { url, execParams } = this.formatUrl(config.gateway, signData);
337-
const infoLog = (option.log && is.fn(option.log.info)) ? option.log.info : null;
338-
const errorLog = (option.log && is.fn(option.log.error)) ? option.log.error : null;
376+
const infoLog = (option.log && is.fn(option.log.info)) ? option.log.info : null;
377+
const errorLog = (option.log && is.fn(option.log.error)) ? option.log.error : null;
339378

340379
infoLog && infoLog('[AlipaySdk]start exec, url: %s, method: %s, params: %s',
341380
url, method, JSON.stringify(execParams));
@@ -349,46 +388,50 @@ class AlipaySdk {
349388
timeout: config.timeout,
350389
headers: { 'user-agent': this.sdkVersion },
351390
})
352-
.then((ret: { status: number, data: string }) => {
353-
infoLog && infoLog('[AlipaySdk]exec response: %s', ret);
354-
355-
if (ret.status === 200) {
356-
/**
357-
* 示例响应格式
358-
* {"alipay_trade_precreate_response":
359-
* {"code": "10000","msg": "Success","out_trade_no": "111111","qr_code": "https:\/\/"},
360-
* "sign": "abcde="
361-
* }
362-
* 或者
363-
* {"error_response":
364-
* {"code":"40002","msg":"Invalid Arguments","sub_code":"isv.code-invalid","sub_msg":"授权码code无效"},
365-
* }
366-
*/
367-
const result = JSON.parse(ret.data);
368-
const responseKey = `${method.replace(/\./g, '_')}_response`;
369-
const data = result[responseKey];
370-
371-
if (data) {
372-
// 按字符串验签
373-
const validateSuccess = option.validateSign ? this.checkResponseSign(ret.data, responseKey) : true;
374-
375-
if (validateSuccess) {
376-
resolve(config.camelcase ? camelcaseKeys(data, { deep: true }) : data);
377-
} else {
378-
reject({ serverResult: ret, errorMessage: '[AlipaySdk]验签失败' });
391+
.then((ret: { status: number, data: string }) => {
392+
infoLog && infoLog('[AlipaySdk]exec response: %s', ret);
393+
394+
if (ret.status === 200) {
395+
/**
396+
* 示例响应格式
397+
* {"alipay_trade_precreate_response":
398+
* {"code": "10000","msg": "Success","out_trade_no": "111111","qr_code": "https:\/\/"},
399+
* "sign": "abcde="
400+
* }
401+
* 或者
402+
* {"error_response":
403+
* {"code":"40002","msg":"Invalid Arguments","sub_code":"isv.code-invalid","sub_msg":"授权码code无效"},
404+
* }
405+
*/
406+
const result = JSON.parse(ret.data);
407+
const responseKey = `${method.replace(/\./g, '_')}_response`;
408+
let data = result[responseKey];
409+
410+
if (data) {
411+
if (params.needEncrypt) {
412+
data = aesDecrypt(data, config.encryptKey);
413+
}
414+
415+
// 按字符串验签
416+
const validateSuccess = option.validateSign ? this.checkResponseSign(ret.data, responseKey) : true;
417+
418+
if (validateSuccess) {
419+
resolve(config.camelcase ? camelcaseKeys(data, { deep: true }) : data);
420+
} else {
421+
reject({ serverResult: ret, errorMessage: '[AlipaySdk]验签失败' });
422+
}
379423
}
424+
425+
reject({ serverResult: ret, errorMessage: '[AlipaySdk]HTTP 请求错误' });
380426
}
381427

382428
reject({ serverResult: ret, errorMessage: '[AlipaySdk]HTTP 请求错误' });
383-
}
384-
385-
reject({ serverResult: ret, errorMessage: '[AlipaySdk]HTTP 请求错误' });
386-
})
387-
.catch((err) => {
388-
err.message = '[AlipaySdk]exec error';
389-
errorLog && errorLog(err);
390-
reject(err);
391-
});
429+
})
430+
.catch((err) => {
431+
err.message = '[AlipaySdk]exec error';
432+
errorLog && errorLog(err);
433+
reject(err);
434+
});
392435
});
393436
}
394437

‎lib/antcertutil.ts

+80
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,80 @@
1+
/**
2+
* @author yisheng.cl
3+
* @email [yisheng.cl@alibaba-inc.com]
4+
*/
5+
6+
import * as fs from 'fs';
7+
import bignumber_js_1 from 'bignumber.js';
8+
import * as crypto from 'crypto';
9+
const x509_1 = require('@fidm/x509');
10+
/** 从公钥证书文件里读取支付宝公钥 */
11+
function loadPublicKeyFromPath(filePath: string): string {
12+
const fileData = fs.readFileSync(filePath);
13+
const certificate = x509_1.Certificate.fromPEM(fileData);
14+
return certificate.publicKeyRaw.toString('base64');
15+
}
16+
/** 从公钥证书内容或buffer读取支付宝公钥 */
17+
function loadPublicKey(content: string|Buffer): string {
18+
if (typeof content == 'string') {
19+
content = Buffer.from(content);
20+
}
21+
const certificate = x509_1.Certificate.fromPEM(content);
22+
return certificate.publicKeyRaw.toString('base64');
23+
}
24+
/** 从证书文件里读取序列号 */
25+
function getSNFromPath(filePath: string, isRoot: boolean= false): string {
26+
const fileData = fs.readFileSync(filePath);
27+
return getSN(fileData, isRoot);
28+
}
29+
/** 从上传的证书内容或Buffer读取序列号 */
30+
function getSN(fileData: string|Buffer, isRoot: boolean= false): string {
31+
if (typeof fileData == 'string') {
32+
fileData = Buffer.from(fileData);
33+
}
34+
if (isRoot) {
35+
return getRootCertSN(fileData);
36+
}
37+
const certificate = x509_1.Certificate.fromPEM(fileData);
38+
return getCertSN(certificate);
39+
}
40+
/** 读取序列号 */
41+
function getCertSN(certificate: any): string {
42+
const { issuer, serialNumber } = certificate;
43+
const principalName = issuer.attributes
44+
.reduceRight((prev, curr) => {
45+
const { shortName, value } = curr;
46+
const result = `${prev}${shortName}=${value},`;
47+
return result;
48+
}, '')
49+
.slice(0, -1);
50+
const decimalNumber = new bignumber_js_1(serialNumber, 16).toString(10);
51+
const SN = crypto
52+
.createHash('md5')
53+
.update(principalName + decimalNumber, 'utf8')
54+
.digest('hex');
55+
return SN;
56+
}
57+
/** 读取根证书序列号 */
58+
function getRootCertSN(rootContent: Buffer): string {
59+
const certificates = x509_1.Certificate.fromPEMs(rootContent);
60+
let rootCertSN = '';
61+
certificates.forEach((item) => {
62+
if (item.signatureOID.startsWith('1.2.840.113549.1.1')) {
63+
const SN = getCertSN(item);
64+
if (rootCertSN.length === 0) {
65+
rootCertSN += SN;
66+
}
67+
else {
68+
rootCertSN += `_${SN}`;
69+
}
70+
}
71+
});
72+
return rootCertSN;
73+
}
74+
75+
export {
76+
getSN,
77+
getSNFromPath,
78+
loadPublicKeyFromPath,
79+
loadPublicKey,
80+
};

‎lib/util.ts

+62-5
Original file line numberDiff line numberDiff line change
@@ -7,6 +7,8 @@ import * as crypto from 'crypto';
77
import * as moment from 'moment';
88
import * as iconv from 'iconv-lite';
99
import * as snakeCaseKeys from 'snakecase-keys';
10+
import * as CryptoJS from 'crypto-js';
11+
import { omit, padEnd } from 'lodash';
1012

1113
import { AlipaySdkConfig } from './alipay';
1214

@@ -15,6 +17,40 @@ const ALIPAY_ALGORITHM_MAPPING = {
1517
RSA2: 'RSA-SHA256',
1618
};
1719

20+
function parseKey(aesKey) {
21+
return {
22+
iv: CryptoJS.enc.Hex.parse(padEnd('', 32, '0')),
23+
key: CryptoJS.enc.Base64.parse(aesKey),
24+
};
25+
}
26+
27+
// 先加密后加签
28+
function aesEncrypt(data, aesKey) {
29+
const {
30+
iv,
31+
key,
32+
} = parseKey(aesKey);
33+
const cipherText = CryptoJS.AES.encrypt(JSON.stringify(data), key, {
34+
iv,
35+
}).toString();
36+
37+
return cipherText;
38+
}
39+
40+
// 解密
41+
function aesDecrypt(data, aesKey) {
42+
const {
43+
iv,
44+
key,
45+
} = parseKey(aesKey);
46+
const bytes = CryptoJS.AES.decrypt(data, key, {
47+
iv,
48+
});
49+
const decryptedData = JSON.parse(bytes.toString(CryptoJS.enc.Utf8));
50+
51+
return decryptedData;
52+
}
53+
1854
/**
1955
* 签名
2056
* @param {string} method 调用接口方法名,比如 alipay.ebpp.bill.add
@@ -23,20 +59,40 @@ const ALIPAY_ALGORITHM_MAPPING = {
2359
* @param {object} config sdk 配置
2460
*/
2561
function sign(method: string, params: any = {}, config: AlipaySdkConfig): any {
26-
const bizContent = params.bizContent || null;
27-
delete params.bizContent;
2862

29-
const signParams = Object.assign({
63+
let signParams = Object.assign({
3064
method,
3165
appId: config.appId,
3266
charset: config.charset,
3367
version: config.version,
3468
signType: config.signType,
3569
timestamp: moment().format('YYYY-MM-DD HH:mm:ss'),
36-
}, params);
70+
}, omit(params, ['bizContent', 'needEncrypt']));
71+
72+
if (config.appCertSn && config.alipayRootCertSn) {
73+
signParams = Object.assign({
74+
appCertSn: config.appCertSn,
75+
alipayRootCertSn: config.alipayRootCertSn,
76+
}, signParams);
77+
}
78+
79+
const bizContent = params.bizContent;
3780

3881
if (bizContent) {
39-
signParams.bizContent = JSON.stringify(snakeCaseKeys(bizContent));
82+
// AES加密
83+
if (params.needEncrypt) {
84+
if (!config.encryptKey) {
85+
throw new Error('请设置encryptKey参数');
86+
}
87+
88+
signParams.encryptType = 'AES';
89+
signParams.bizContent = aesEncrypt(
90+
snakeCaseKeys(bizContent),
91+
config.encryptKey,
92+
);
93+
} else {
94+
signParams.bizContent = JSON.stringify(snakeCaseKeys(bizContent));
95+
}
4096
}
4197

4298
// params key 驼峰转下划线
@@ -61,4 +117,5 @@ function sign(method: string, params: any = {}, config: AlipaySdkConfig): any {
61117
export {
62118
sign,
63119
ALIPAY_ALGORITHM_MAPPING,
120+
aesDecrypt,
64121
};

‎package.json

+14-5
Original file line numberDiff line numberDiff line change
@@ -1,35 +1,44 @@
11
{
22
"name": "alipay-sdk",
3-
"version": "3.0.8",
3+
"version": "3.1.2",
44
"description": "蚂蚁金服开放平台 node sdk",
55
"main": "lib/alipay.js",
66
"scripts": {
7-
"tsc": "./node_modules/.bin/tsc -p ./tsconfig.json",
7+
"tsc": "tsc -p ./tsconfig.json",
8+
"tsc:watch": "tsc -w",
89
"lint": "tslint -p ./tsconfig.json --fix",
910
"lint:no-fix": "tslint -p ./tsconfig.json",
1011
"test": "mocha",
1112
"ci": "npm run tsc && npm run lint:no-fix && nyc mocha -t 6000"
1213
},
13-
"author": "tudou527<tudou527@gmail.com>",
14+
"author": "dersoncheng",
1415
"homepage": "https://github.com/ali-sdk/alipay-sdk",
1516
"bugs": "https://github.com/ali-sdk/alipay-sdk/issues",
1617
"license": "ISC",
1718
"dependencies": {
19+
"@fidm/x509": "^1.2.1",
1820
"@types/node": "^9.6.0",
21+
"bignumber.js": "^9.0.0",
1922
"camelcase-keys": "^4.2.0",
23+
"crypto-js": "^4.0.0",
2024
"decamelize": "^2.0.0",
2125
"is": "^3.2.1",
2226
"is-json": "^2.0.1",
2327
"isuri": "^2.0.3",
28+
"lodash": "^4.17.20",
2429
"moment": "^2.16.0",
2530
"request": "^2.86.0",
2631
"snakecase-keys": "^1.1.1",
2732
"urllib": "^2.17.0"
2833
},
2934
"nyc": {
3035
"extends": "@istanbuljs/nyc-config-typescript",
31-
"include": [ "lib" ],
32-
"extension": [ ".ts" ],
36+
"include": [
37+
"lib"
38+
],
39+
"extension": [
40+
".ts"
41+
],
3342
"check-coverage": true,
3443
"reporter": [
3544
"text-summary",

‎test/alipay.test.js

+50-15
Original file line numberDiff line numberDiff line change
@@ -51,7 +51,7 @@ describe('sdk', function() {
5151
(e.toString().indexOf('config.privateKey is required') > -1).should.eql(true);
5252
}
5353
});
54-
54+
5555
it('formatKey', function() {
5656
const noWrapperPrivateKey = fs.readFileSync(__dirname + '/fixtures/app-private-key-no-wrapper.pem', 'ascii');
5757
const noWrapperPublicKey = fs.readFileSync(__dirname + '/fixtures/alipay-public-key-no-wrapper.pem', 'ascii');
@@ -65,7 +65,7 @@ describe('sdk', function() {
6565
alipaySdk.config.privateKey.should.eql(`-----BEGIN RSA PRIVATE KEY-----\n${noWrapperPrivateKey}\n-----END RSA PRIVATE KEY-----`);
6666
alipaySdk.config.alipayPublicKey.should.eql(`-----BEGIN PUBLIC KEY-----\n${noWrapperPublicKey}\n-----END PUBLIC KEY-----`);
6767
});
68-
68+
6969
it('formatKey with pkcs8', function() {
7070
const pkcs8PrivateKey = fs.readFileSync(__dirname + '/fixtures/app-private-key-pkcs8.pem', 'ascii');
7171
const alipaySdk = new AlipaySdk({
@@ -92,6 +92,7 @@ describe('sdk', function() {
9292
alipayPublicKey,
9393
camelcase: true,
9494
timeout: 10000,
95+
encryptKey: 'aYA0GP8JEW+D7/UFaskCWA=='
9596
})
9697
});
9798

@@ -270,6 +271,40 @@ describe('sdk', function() {
270271
}).catch(done)
271272
});
272273

274+
it('execute needEncrypt', function (done) {
275+
sandbox.stub(urllib, "request", function () {
276+
return Promise.resolve({
277+
status: 200,
278+
data: JSON.stringify({
279+
alipay_open_auth_app_aes_set_response: '4AOYHE0rpPnRnghunsGo+mY02DzANFLwNJJCiHfrNh2oaB2pn33PwOEOvH8mjhkE3Wh/jR+3jHM9nvoFvOsY/SqZbZzamRg9Eh3VkRqOhSM=',
280+
sign: "abcde=",
281+
}),
282+
});
283+
});
284+
285+
var bizContent = {
286+
merchantAppId: '2021001170662064',
287+
}
288+
289+
sdk
290+
.exec('alipay.open.auth.app.aes.set', {
291+
bizContent,
292+
needEncrypt: true
293+
})
294+
.then(ret => {
295+
ret.should.eql({
296+
code: '10000',
297+
msg: 'Success',
298+
aesKey: 'cW8mcZgoMGUVp5g7uv7bHw=='
299+
})
300+
301+
done()
302+
}).catch((error) => {
303+
error.should.eql(false)
304+
done()
305+
})
306+
})
307+
273308
it('error log enable', function(done) {
274309
const infoLog = [];
275310
const errorLog = [];
@@ -595,7 +630,7 @@ describe('sdk', function() {
595630
}).catch(done)
596631
});
597632
});
598-
633+
599634
describe('pageExec', function() {
600635
let sdk;
601636

@@ -729,9 +764,9 @@ describe('sdk', function() {
729764

730765
it('include \\r\\n\\s', function() {
731766
const originStr = `{"alipay_offline_material_image_upload_response"
732-
:
767+
:
733768
{"code":"10000","msg":"Success","image_id":"Zp1Nm6FDTZaEuSSniGd5awAAACMAAQED","image_url":"http:\\/\\/oalipay-dl-django.alicdn.com\\/rest\\/1.0\\/image?fileIds=Zp1Nm6FDTZaEuSSniGd5awAAACMAAQED&zoom=original"},
734-
769+
735770
"sign" : "P8xrBWqZCUv11UrEBjhQ4Sk3hyj4607qehO2VbKIS0hWa4U+NeLlOftqTyhGv+x1lzfqN590Y/8CaNIzEEg06FiNWJlUFM/uEFJLzSKGse4MjHbblpiSzI3eCV5RzxH26wZbEd9wyVYYi0pHFBf35UrBva47g7b5EuKCHfoVA95/zin9fAyb3xhhiHhmfGaWIDV/1LmE2vtqtOHQnISbY/deC71U614ySZ3YB97ws8npCcCJ+tgZvhHPkMRGvmyYPCRDB/aIN/sKDSLtfPp0u8DxE8pHLvCHm3wR84MQxqNbKgpd8NTKNvH+obELsbCrqPhjW7qI48634qx6enDupw=="}`;
736771

737772
const signStr = sdk.getSignStr(originStr, 'alipay_offline_material_image_upload_response');
@@ -741,9 +776,9 @@ describe('sdk', function() {
741776

742777
it('include sign key in data', function() {
743778
const originStr = `{"alipay_offline_material_image_upload_response"
744-
:
779+
:
745780
{"code":"10000","sign":"xxx","msg":"Success","image_id":"Zp1Nm6FDTZaEuSSniGd5awAAACMAAQED","image_url":"http:\\/\\/oalipay-dl-django.alicdn.com\\/rest\\/1.0\\/image?fileIds=Zp1Nm6FDTZaEuSSniGd5awAAACMAAQED&zoom=original"},
746-
781+
747782
"sign" : "P8xrBWqZCUv11UrEBjhQ4Sk3hyj4607qehO2VbKIS0hWa4U+NeLlOftqTyhGv+x1lzfqN590Y/8CaNIzEEg06FiNWJlUFM/uEFJLzSKGse4MjHbblpiSzI3eCV5RzxH26wZbEd9wyVYYi0pHFBf35UrBva47g7b5EuKCHfoVA95/zin9fAyb3xhhiHhmfGaWIDV/1LmE2vtqtOHQnISbY/deC71U614ySZ3YB97ws8npCcCJ+tgZvhHPkMRGvmyYPCRDB/aIN/sKDSLtfPp0u8DxE8pHLvCHm3wR84MQxqNbKgpd8NTKNvH+obELsbCrqPhjW7qI48634qx6enDupw=="}`;
748783

749784
const signStr = sdk.getSignStr(originStr, 'alipay_offline_material_image_upload_response');
@@ -850,7 +885,7 @@ describe('sdk', function() {
850885
});
851886
});
852887

853-
it.only('with sign_type arguments verify success', function() {
888+
it('with sign_type arguments verify success', function() {
854889
const postData = {
855890
gmt_create: '2019-08-15 15:37:55',
856891
charset: 'utf-8',
@@ -910,7 +945,7 @@ describe('sdk', function() {
910945
auth_app_id: '2019073166072302',
911946
buyer_logon_id: 'xud***@126.com',
912947
point_amount: '0.00' };
913-
948+
914949
sdk.checkNotifySign(postData).should.eql(true);
915950
});
916951

@@ -942,7 +977,7 @@ describe('sdk', function() {
942977
trade_status: 'TRADE_SUCCESS',
943978
version: '1.0'
944979
};
945-
980+
946981
sdk.checkNotifySign(postData).should.eql(false);
947982
});
948983
});
@@ -973,10 +1008,10 @@ describe('sdk', function() {
9731008
total_price: '0.00',
9741009
version: '1.0',
9751010
};
976-
1011+
9771012
sdk.checkNotifySign(postData).should.eql(true);
9781013
});
979-
1014+
9801015
it('without sign_type arguments verify success', function() {
9811016
const postData = {
9821017
app_id: '2017122801303261',
@@ -1001,7 +1036,7 @@ describe('sdk', function() {
10011036
total_price: '0.00',
10021037
version: '1.0',
10031038
};
1004-
1039+
10051040
sdk.checkNotifySign(postData).should.eql(true);
10061041
});
10071042

@@ -1030,11 +1065,11 @@ describe('sdk', function() {
10301065
total_price: '0.00',
10311066
version: '1.0',
10321067
};
1033-
1068+
10341069
sdk.checkNotifySign(postData).should.eql(false);
10351070
});
10361071
});
1037-
1072+
10381073
});
10391074

10401075
describe('execute, pkcs8', function() {

‎test/antcertutil.test.js

+102
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,102 @@
1+
'use strict';
2+
require('should');
3+
4+
const https = require('https');
5+
const path = require('path');
6+
const { getSNFromPath, getSN, loadPublicKey, loadPublicKeyFromPath } = require('../lib/antcertutil');
7+
8+
const appCertPath = path.join(__dirname,'/fixtures/appCertPublicKey_2021001161683774.crt');
9+
const alipayPublicCertPath = path.join(__dirname,'/fixtures/alipayCertPublicKey_RSA2.crt');
10+
const rootCertPath = path.join(__dirname,'/fixtures/alipayRootCert.crt');
11+
12+
const appCertUrl = 'https://openhome-crt.oss-cn-beijing.aliyuncs.com/appCertPublicKey_2021001161683774.crt';
13+
const alipayPublicCertUrl = 'https://openhome-crt.oss-cn-beijing.aliyuncs.com/alipayCertPublicKey_RSA2.crt';
14+
const alipayRootCertUrl = 'https://openhome-crt.oss-cn-beijing.aliyuncs.com/alipayRootCert.crt';
15+
16+
const publicKeyVal = 'MIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEAhoVesfcGvUv1XvUndmX0rmSZ/2posJBCooySbSVFpV79RtHMzrVz2aKkC3WvOXeT5iNeQK4mK8gp3vNkWrHTkQGx5BcmkeO1WS384CQde7dAS0gmxeFs5bs+cCQqV2A2c2R9/5rJMtFtp1Ot/rIiMBUn6Ei0UoztM7AneavqQEzSwYlCKNhPFFtHCiz7u4O5R9CIyvUmYr+zpem2HXBN9ygPAZ0aXBQipGbc45+G07ZCNsmY4hV/Igya1aBf+Ye8p10Ew8uBBri0sIknhSC2LqKKy2IH1fO6q1d1jhN240QRHvbpRNv60kAfZsEulBASBrCMBi49NiJyr5nre7SNywIDAQAB';
17+
const appCertSnVal = '866efef280dec9137a87d047ac446315';
18+
const alipayPublicCertSnVal = '7513daaaa48aa3ba2e4018d84402479c';
19+
const rootCertSnVal = '687b59193f3f462dd5336e5abf83c5d8_02941eef3187dddf3d3b83462e1dfcf6';
20+
21+
describe('antcertutil', function() {
22+
it('loadPublicKeyFromPath', function() {
23+
const publicKey = loadPublicKeyFromPath(alipayPublicCertPath);
24+
(publicKey !== '').should.eql(true);
25+
(publicKey == publicKeyVal).should.eql(true);
26+
});
27+
});
28+
29+
describe('antcertutil', function() {
30+
it('loadPublicKey', async function() {
31+
function getRequest(url) {
32+
return new Promise(resolved => {
33+
https.get(url, (res) => {
34+
const { statusCode } = res;
35+
let rawData = '';
36+
res.on('data', (chunk) => { rawData += chunk; });
37+
res.on('end', () => {
38+
try {
39+
resolved(rawData);
40+
} catch (e) {
41+
console.error(e.message);
42+
}
43+
});
44+
});
45+
});
46+
}
47+
const alipayPublicCertContent = await getRequest(alipayPublicCertUrl);
48+
const publicKey = loadPublicKey(alipayPublicCertContent);
49+
(publicKey !== '').should.eql(true);
50+
(publicKey == publicKeyVal).should.eql(true);
51+
});
52+
});
53+
54+
describe('antcertutil', function() {
55+
it('getSNFromPath', function() {
56+
const appCertSn = getSNFromPath(appCertPath, false);
57+
(appCertSn !== '').should.eql(true);
58+
(appCertSn == appCertSnVal).should.eql(true);
59+
const alipayPublicCertSn = getSNFromPath(alipayPublicCertPath, false);
60+
(alipayPublicCertSn !== '').should.eql(true);
61+
(alipayPublicCertSn == alipayPublicCertSnVal).should.eql(true);
62+
const rootCertSn = getSNFromPath(rootCertPath, true);
63+
(rootCertSn !== '').should.eql(true);
64+
(rootCertSn == rootCertSnVal).should.eql(true);
65+
});
66+
});
67+
68+
describe('antcertutil', function() {
69+
it('getSN', async function() {
70+
function getRequest(url) {
71+
return new Promise(resolved => {
72+
https.get(url, (res) => {
73+
const { statusCode } = res;
74+
let rawData = '';
75+
res.on('data', (chunk) => { rawData += chunk; });
76+
res.on('end', () => {
77+
try {
78+
resolved(rawData);
79+
} catch (e) {
80+
console.error(e.message);
81+
}
82+
});
83+
});
84+
});
85+
}
86+
87+
const appCertContent = await getRequest(appCertUrl);
88+
const appCertSn = getSN(appCertContent, false);
89+
(appCertSn !== '').should.eql(true);
90+
(appCertSn == appCertSnVal).should.eql(true);
91+
92+
const alipayPublicCertContent = await getRequest(alipayPublicCertUrl);
93+
const alipayPublicCertSn = getSN(alipayPublicCertContent, false);
94+
(alipayPublicCertSn !== '').should.eql(true);
95+
(alipayPublicCertSn == alipayPublicCertSnVal).should.eql(true);
96+
97+
const rootCertContent = await getRequest(alipayRootCertUrl);
98+
const rootCertSn = getSN(rootCertContent, true);
99+
(rootCertSn !== '').should.eql(true);
100+
(rootCertSn == rootCertSnVal).should.eql(true);
101+
});
102+
});
+19
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,19 @@
1+
-----BEGIN CERTIFICATE-----
2+
MIIDsjCCApqgAwIBAgIQICAFFOY8L+BNU4cW/iE1izANBgkqhkiG9w0BAQsFADCBgjELMAkGA1UE
3+
BhMCQ04xFjAUBgNVBAoMDUFudCBGaW5hbmNpYWwxIDAeBgNVBAsMF0NlcnRpZmljYXRpb24gQXV0
4+
aG9yaXR5MTkwNwYDVQQDDDBBbnQgRmluYW5jaWFsIENlcnRpZmljYXRpb24gQXV0aG9yaXR5IENs
5+
YXNzIDIgUjEwHhcNMjAwNTE0MTQ0MjAxWhcNMjIwNTE0MTQ0MjAxWjCBkjELMAkGA1UEBhMCQ04x
6+
LTArBgNVBAoMJOadreW3nueEleaXreS/oeaBr+aKgOacr+aciemZkOWFrOWPuDEPMA0GA1UECwwG
7+
QWxpcGF5MUMwQQYDVQQDDDrmlK/ku5jlrp0o5Lit5Zu9Kee9kee7nOaKgOacr+aciemZkOWFrOWP
8+
uC0yMDg4ODMxMjA0NjU5MjE3MIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEAhoVesfcG
9+
vUv1XvUndmX0rmSZ/2posJBCooySbSVFpV79RtHMzrVz2aKkC3WvOXeT5iNeQK4mK8gp3vNkWrHT
10+
kQGx5BcmkeO1WS384CQde7dAS0gmxeFs5bs+cCQqV2A2c2R9/5rJMtFtp1Ot/rIiMBUn6Ei0Uozt
11+
M7AneavqQEzSwYlCKNhPFFtHCiz7u4O5R9CIyvUmYr+zpem2HXBN9ygPAZ0aXBQipGbc45+G07ZC
12+
NsmY4hV/Igya1aBf+Ye8p10Ew8uBBri0sIknhSC2LqKKy2IH1fO6q1d1jhN240QRHvbpRNv60kAf
13+
ZsEulBASBrCMBi49NiJyr5nre7SNywIDAQABoxIwEDAOBgNVHQ8BAf8EBAMCA/gwDQYJKoZIhvcN
14+
AQELBQADggEBAEx9T99X9NhDZYXDftc0wH0Tc5OVRIqWTYBN/KEP1df+Ix5djcDrUUcXJMvprMw3
15+
frUymgJsf7YUgZ1+dMz+Gup5dCH3qUu/5c9R3d8senb4wCiYR2FpJ3WGIONwInNQTs04a2wM4JIM
16+
CdCYWC/H1hdvNl9V4pYeMckTruSnPmJlL6E/pNF5AuIYP5AswZSOhASIQzt5Rr6QDz/YVGi2/6al
17+
5Bknm2f9dmsZbWDapKDrlJ3Ny2B0SZ0H47Y7lRlaVNe+Jj1jsrv16ONk1XLt71jyWyPAIlITLcyX
18+
mMtT1IympPFRxBIngEImjGt6d1VUtkf61Cjpr0bRRDoZXvWRv5U=
19+
-----END CERTIFICATE-----

‎test/fixtures/alipayRootCert.crt

+88
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,88 @@
1+
-----BEGIN CERTIFICATE-----
2+
MIIBszCCAVegAwIBAgIIaeL+wBcKxnswDAYIKoEcz1UBg3UFADAuMQswCQYDVQQG
3+
EwJDTjEOMAwGA1UECgwFTlJDQUMxDzANBgNVBAMMBlJPT1RDQTAeFw0xMjA3MTQw
4+
MzExNTlaFw00MjA3MDcwMzExNTlaMC4xCzAJBgNVBAYTAkNOMQ4wDAYDVQQKDAVO
5+
UkNBQzEPMA0GA1UEAwwGUk9PVENBMFkwEwYHKoZIzj0CAQYIKoEcz1UBgi0DQgAE
6+
MPCca6pmgcchsTf2UnBeL9rtp4nw+itk1Kzrmbnqo05lUwkwlWK+4OIrtFdAqnRT
7+
V7Q9v1htkv42TsIutzd126NdMFswHwYDVR0jBBgwFoAUTDKxl9kzG8SmBcHG5Yti
8+
W/CXdlgwDAYDVR0TBAUwAwEB/zALBgNVHQ8EBAMCAQYwHQYDVR0OBBYEFEwysZfZ
9+
MxvEpgXBxuWLYlvwl3ZYMAwGCCqBHM9VAYN1BQADSAAwRQIgG1bSLeOXp3oB8H7b
10+
53W+CKOPl2PknmWEq/lMhtn25HkCIQDaHDgWxWFtnCrBjH16/W3Ezn7/U/Vjo5xI
11+
pDoiVhsLwg==
12+
-----END CERTIFICATE-----
13+
14+
-----BEGIN CERTIFICATE-----
15+
MIIF0zCCA7ugAwIBAgIIH8+hjWpIDREwDQYJKoZIhvcNAQELBQAwejELMAkGA1UE
16+
BhMCQ04xFjAUBgNVBAoMDUFudCBGaW5hbmNpYWwxIDAeBgNVBAsMF0NlcnRpZmlj
17+
YXRpb24gQXV0aG9yaXR5MTEwLwYDVQQDDChBbnQgRmluYW5jaWFsIENlcnRpZmlj
18+
YXRpb24gQXV0aG9yaXR5IFIxMB4XDTE4MDMyMTEzNDg0MFoXDTM4MDIyODEzNDg0
19+
MFowejELMAkGA1UEBhMCQ04xFjAUBgNVBAoMDUFudCBGaW5hbmNpYWwxIDAeBgNV
20+
BAsMF0NlcnRpZmljYXRpb24gQXV0aG9yaXR5MTEwLwYDVQQDDChBbnQgRmluYW5j
21+
aWFsIENlcnRpZmljYXRpb24gQXV0aG9yaXR5IFIxMIICIjANBgkqhkiG9w0BAQEF
22+
AAOCAg8AMIICCgKCAgEAtytTRcBNuur5h8xuxnlKJetT65cHGemGi8oD+beHFPTk
23+
rUTlFt9Xn7fAVGo6QSsPb9uGLpUFGEdGmbsQ2q9cV4P89qkH04VzIPwT7AywJdt2
24+
xAvMs+MgHFJzOYfL1QkdOOVO7NwKxH8IvlQgFabWomWk2Ei9WfUyxFjVO1LVh0Bp
25+
dRBeWLMkdudx0tl3+21t1apnReFNQ5nfX29xeSxIhesaMHDZFViO/DXDNW2BcTs6
26+
vSWKyJ4YIIIzStumD8K1xMsoaZBMDxg4itjWFaKRgNuPiIn4kjDY3kC66Sl/6yTl
27+
YUz8AybbEsICZzssdZh7jcNb1VRfk79lgAprm/Ktl+mgrU1gaMGP1OE25JCbqli1
28+
Pbw/BpPynyP9+XulE+2mxFwTYhKAwpDIDKuYsFUXuo8t261pCovI1CXFzAQM2w7H
29+
DtA2nOXSW6q0jGDJ5+WauH+K8ZSvA6x4sFo4u0KNCx0ROTBpLif6GTngqo3sj+98
30+
SZiMNLFMQoQkjkdN5Q5g9N6CFZPVZ6QpO0JcIc7S1le/g9z5iBKnifrKxy0TQjtG
31+
PsDwc8ubPnRm/F82RReCoyNyx63indpgFfhN7+KxUIQ9cOwwTvemmor0A+ZQamRe
32+
9LMuiEfEaWUDK+6O0Gl8lO571uI5onYdN1VIgOmwFbe+D8TcuzVjIZ/zvHrAGUcC
33+
AwEAAaNdMFswCwYDVR0PBAQDAgEGMAwGA1UdEwQFMAMBAf8wHQYDVR0OBBYEFF90
34+
tATATwda6uWx2yKjh0GynOEBMB8GA1UdIwQYMBaAFF90tATATwda6uWx2yKjh0Gy
35+
nOEBMA0GCSqGSIb3DQEBCwUAA4ICAQCVYaOtqOLIpsrEikE5lb+UARNSFJg6tpkf
36+
tJ2U8QF/DejemEHx5IClQu6ajxjtu0Aie4/3UnIXop8nH/Q57l+Wyt9T7N2WPiNq
37+
JSlYKYbJpPF8LXbuKYG3BTFTdOVFIeRe2NUyYh/xs6bXGr4WKTXb3qBmzR02FSy3
38+
IODQw5Q6zpXj8prYqFHYsOvGCEc1CwJaSaYwRhTkFedJUxiyhyB5GQwoFfExCVHW
39+
05ZFCAVYFldCJvUzfzrWubN6wX0DD2dwultgmldOn/W/n8at52mpPNvIdbZb2F41
40+
T0YZeoWnCJrYXjq/32oc1cmifIHqySnyMnavi75DxPCdZsCOpSAT4j4lAQRGsfgI
41+
kkLPGQieMfNNkMCKh7qjwdXAVtdqhf0RVtFILH3OyEodlk1HYXqX5iE5wlaKzDop
42+
PKwf2Q3BErq1xChYGGVS+dEvyXc/2nIBlt7uLWKp4XFjqekKbaGaLJdjYP5b2s7N
43+
1dM0MXQ/f8XoXKBkJNzEiM3hfsU6DOREgMc1DIsFKxfuMwX3EkVQM1If8ghb6x5Y
44+
jXayv+NLbidOSzk4vl5QwngO/JYFMkoc6i9LNwEaEtR9PhnrdubxmrtM+RjfBm02
45+
77q3dSWFESFQ4QxYWew4pHE0DpWbWy/iMIKQ6UZ5RLvB8GEcgt8ON7BBJeMc+Dyi
46+
kT9qhqn+lw==
47+
-----END CERTIFICATE-----
48+
49+
-----BEGIN CERTIFICATE-----
50+
MIICiDCCAgygAwIBAgIIQX76UsB/30owDAYIKoZIzj0EAwMFADB6MQswCQYDVQQG
51+
EwJDTjEWMBQGA1UECgwNQW50IEZpbmFuY2lhbDEgMB4GA1UECwwXQ2VydGlmaWNh
52+
dGlvbiBBdXRob3JpdHkxMTAvBgNVBAMMKEFudCBGaW5hbmNpYWwgQ2VydGlmaWNh
53+
dGlvbiBBdXRob3JpdHkgRTEwHhcNMTkwNDI4MTYyMDQ0WhcNNDkwNDIwMTYyMDQ0
54+
WjB6MQswCQYDVQQGEwJDTjEWMBQGA1UECgwNQW50IEZpbmFuY2lhbDEgMB4GA1UE
55+
CwwXQ2VydGlmaWNhdGlvbiBBdXRob3JpdHkxMTAvBgNVBAMMKEFudCBGaW5hbmNp
56+
YWwgQ2VydGlmaWNhdGlvbiBBdXRob3JpdHkgRTEwdjAQBgcqhkjOPQIBBgUrgQQA
57+
IgNiAASCCRa94QI0vR5Up9Yr9HEupz6hSoyjySYqo7v837KnmjveUIUNiuC9pWAU
58+
WP3jwLX3HkzeiNdeg22a0IZPoSUCpasufiLAnfXh6NInLiWBrjLJXDSGaY7vaokt
59+
rpZvAdmjXTBbMAsGA1UdDwQEAwIBBjAMBgNVHRMEBTADAQH/MB0GA1UdDgQWBBRZ
60+
4ZTgDpksHL2qcpkFkxD2zVd16TAfBgNVHSMEGDAWgBRZ4ZTgDpksHL2qcpkFkxD2
61+
zVd16TAMBggqhkjOPQQDAwUAA2gAMGUCMQD4IoqT2hTUn0jt7oXLdMJ8q4vLp6sg
62+
wHfPiOr9gxreb+e6Oidwd2LDnC4OUqCWiF8CMAzwKs4SnDJYcMLf2vpkbuVE4dTH
63+
Rglz+HGcTLWsFs4KxLsq7MuU+vJTBUeDJeDjdA==
64+
-----END CERTIFICATE-----
65+
66+
-----BEGIN CERTIFICATE-----
67+
MIIDxTCCAq2gAwIBAgIUEMdk6dVgOEIS2cCP0Q43P90Ps5YwDQYJKoZIhvcNAQEF
68+
BQAwajELMAkGA1UEBhMCQ04xEzARBgNVBAoMCmlUcnVzQ2hpbmExHDAaBgNVBAsM
69+
E0NoaW5hIFRydXN0IE5ldHdvcmsxKDAmBgNVBAMMH2lUcnVzQ2hpbmEgQ2xhc3Mg
70+
MiBSb290IENBIC0gRzMwHhcNMTMwNDE4MDkzNjU2WhcNMzMwNDE4MDkzNjU2WjBq
71+
MQswCQYDVQQGEwJDTjETMBEGA1UECgwKaVRydXNDaGluYTEcMBoGA1UECwwTQ2hp
72+
bmEgVHJ1c3QgTmV0d29yazEoMCYGA1UEAwwfaVRydXNDaGluYSBDbGFzcyAyIFJv
73+
b3QgQ0EgLSBHMzCCASIwDQYJKoZIhvcNAQEBBQADggEPADCCAQoCggEBAOPPShpV
74+
nJbMqqCw6Bz1kehnoPst9pkr0V9idOwU2oyS47/HjJXk9Rd5a9xfwkPO88trUpz5
75+
4GmmwspDXjVFu9L0eFaRuH3KMha1Ak01citbF7cQLJlS7XI+tpkTGHEY5pt3EsQg
76+
wykfZl/A1jrnSkspMS997r2Gim54cwz+mTMgDRhZsKK/lbOeBPpWtcFizjXYCqhw
77+
WktvQfZBYi6o4sHCshnOswi4yV1p+LuFcQ2ciYdWvULh1eZhLxHbGXyznYHi0dGN
78+
z+I9H8aXxqAQfHVhbdHNzi77hCxFjOy+hHrGsyzjrd2swVQ2iUWP8BfEQqGLqM1g
79+
KgWKYfcTGdbPB1MCAwEAAaNjMGEwHQYDVR0OBBYEFG/oAMxTVe7y0+408CTAK8hA
80+
uTyRMB8GA1UdIwQYMBaAFG/oAMxTVe7y0+408CTAK8hAuTyRMA8GA1UdEwEB/wQF
81+
MAMBAf8wDgYDVR0PAQH/BAQDAgEGMA0GCSqGSIb3DQEBBQUAA4IBAQBLnUTfW7hp
82+
emMbuUGCk7RBswzOT83bDM6824EkUnf+X0iKS95SUNGeeSWK2o/3ALJo5hi7GZr3
83+
U8eLaWAcYizfO99UXMRBPw5PRR+gXGEronGUugLpxsjuynoLQu8GQAeysSXKbN1I
84+
UugDo9u8igJORYA+5ms0s5sCUySqbQ2R5z/GoceyI9LdxIVa1RjVX8pYOj8JFwtn
85+
DJN3ftSFvNMYwRuILKuqUYSHc2GPYiHVflDh5nDymCMOQFcFG3WsEuB+EYQPFgIU
86+
1DHmdZcz7Llx8UOZXX2JupWCYzK1XhJb+r4hK5ncf/w8qGtYlmyJpxk3hr1TfUJX
87+
Yf4Zr0fJsGuv
88+
-----END CERTIFICATE-----
Original file line numberDiff line numberDiff line change
@@ -0,0 +1 @@
1+
MIIEvQIBADANBgkqhkiG9w0BAQEFAASCBKcwggSjAgEAAoIBAQCcW+ewN/0Av9qAz3Ga6lij7P9qo1ZWP8dNLNzlmtAp4X7Tv+LZN93LlRMGkFu2/ym3sn5me6fEqMSfVAQwEgwAquExoEdIRRSCE9RQo3lHvKMF8mkLI2yi/+sKFyhxzgGjQmONnfbimdGQMnsl+IBfnhFlZqG5A41AGIuhXS/88qIG6yq2BYm5im047laHL5WTdtkeghCRwkp3N5cPmGvTVXuww8dOQfDFEDDwWXvWYb0G9g++W54ldYuHJb/5XsynVsk//3f3SKRlceLbfdmzMLf/1HhRtoy01iRuofW7+kQqeDuzRcBOCFTLCYaBU0ChGrQcecLH22FAtyL7qNvzAgMBAAECggEASOfZiiMxBw5t9a3ETPWESzsQhUqP0Tt/1I5cmXqsITsRhe5s+dfAxVJHQsJA4XzsmMjTgzmZKmt+8sOcCEYB7rW+twMC+cGsJFLWppryy+47HWTAcf8cgoxNrbDKnPXvcY5dGQVUvqf2PQQsw7GxOeRcPCm0sq3jSMWpJZ5r58cGqEp0XdjCVhBf/Qqh5OnhOCyygb3e6290GKnBtXl69S1pkXgKAkcEk4Hw+klio62aW0FeD9hzSFHhJc+pIH5l1AIaDtHoRFGU+M7sIGFur+KVf2SQV904jW+wSivmGDG6HARcDJ0V1Iz05hUGXzSITpWP+fV0hXILd+OOHhX2AQKBgQDiGxFAw3b4ljiaC4KbuNxMNTJJnh6qpO+5KzS4C3ggq1JQd4gdYxiKqYkWNPRJza+6A7gJeezRCZWrhQtD7xYm7OXrilkipve2L/GcypocAlRedLU5b/AiYrwW0K+vz/Y94wpWkV//m8Wo8aftVUUAm5IJjTwm3kqGSbftqR4YcwKBgQCxCCQiEwTJ8oXmKm7nw3QGWQHnQroEHXWQeZYLTluRuRMDSfmApfi8MMCFEwEX2ozMbzJ8aeBBu/E4j6Sjeu6JQhLJOipe2G9p8Qp6YYkFNP843xBvuCAva9orKEHsnewLqNbMNaRjPWeA4WyejGxIimKx1dFM8DrG8XFCembOgQKBgQCjNv7BTVf4IhhBusVRO6RqrovacHWaoO0bMYEgycaxbnpA88ghzoLMtYzrptYg4IVVNZIH5Sd5zf1ojuWO6GuJNcDSwk+NoBF4RiuXDvkn9taISJ75E0C40aUMox/vPaN3EQlJbC9sCDoqtiaOP9tLkQFeO8GMHTKWtWg7+rRq2QKBgFDGhS4i/WljxmLJ88a+MN+Al1S85oFgK7dbMvI/s5pfFuTGSRjBIihEGmPd0v7eUqYpCqEn8hIsPhlvuVB6lAJ+6nF0n8ac/OEbAYvrmwiMMPPzG93N5qmcjXRprwLW39BGHpQY2cQKTYLJ2LTTH8lNk1DPXeu8JSWHHwWVMfmBAoGAQNGGKhA10ARN9piNSHEnBra/vd1yyDmc6xEgLgavAbxALMI3SYWpy8kU4hAWme4zBNeF89huTkXopjXYOcInuij7+T/ZBlk53kXbYSHJJhaIKO0N6n9o00zWiop6XvgDL4sdQjkx9y7jsCem1BVQzutPLPeJebQVDjnsIHTBNCw=
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,24 @@
1+
-----BEGIN CERTIFICATE-----
2+
MIIEsTCCA5mgAwIBAgIQICAFGGIDnUCQiXuk1RwhJjANBgkqhkiG9w0BAQsFADCBgjELMAkGA1UE
3+
BhMCQ04xFjAUBgNVBAoMDUFudCBGaW5hbmNpYWwxIDAeBgNVBAsMF0NlcnRpZmljYXRpb24gQXV0
4+
aG9yaXR5MTkwNwYDVQQDDDBBbnQgRmluYW5jaWFsIENlcnRpZmljYXRpb24gQXV0aG9yaXR5IENs
5+
YXNzIDEgUjEwHhcNMjAwNTE4MDUyODQ3WhcNMjIwNTE4MDUyODQ3WjB5MQswCQYDVQQGEwJDTjEt
6+
MCsGA1UECgwk5p2t5bee54SV5pet5L+h5oGv5oqA5pyv5pyJ6ZmQ5YWs5Y+4MQ8wDQYDVQQLDAZB
7+
bGlwYXkxKjAoBgNVBAMMITIwODg4MzEyMDQ2NTkyMTctMjAyMTAwMTE2MTY4Mzc3NDCCASIwDQYJ
8+
KoZIhvcNAQEBBQADggEPADCCAQoCggEBAJxb57A3/QC/2oDPcZrqWKPs/2qjVlY/x00s3OWa0Cnh
9+
ftO/4tk33cuVEwaQW7b/KbeyfmZ7p8SoxJ9UBDASDACq4TGgR0hFFIIT1FCjeUe8owXyaQsjbKL/
10+
6woXKHHOAaNCY42d9uKZ0ZAyeyX4gF+eEWVmobkDjUAYi6FdL/zyogbrKrYFibmKbTjuVocvlZN2
11+
2R6CEJHCSnc3lw+Ya9NVe7DDx05B8MUQMPBZe9ZhvQb2D75bniV1i4clv/lezKdWyT//d/dIpGVx
12+
4tt92bMwt//UeFG2jLTWJG6h9bv6RCp4O7NFwE4IVMsJhoFTQKEatBx5wsfbYUC3Ivuo2/MCAwEA
13+
AaOCASkwggElMB8GA1UdIwQYMBaAFHEH4gRhFuTl8mXrMQ/J4PQ8mtWRMB0GA1UdDgQWBBSXl4+T
14+
1U+ja4hCST3DlTQAAKQ/tTBABgNVHSAEOTA3MDUGB2CBHAFuAQEwKjAoBggrBgEFBQcCARYcaHR0
15+
cDovL2NhLmFsaXBheS5jb20vY3BzLnBkZjAOBgNVHQ8BAf8EBAMCBsAwLwYDVR0fBCgwJjAkoCKg
16+
IIYeaHR0cDovL2NhLmFsaXBheS5jb20vY3JsNDEuY3JsMGAGCCsGAQUFBwEBBFQwUjAoBggrBgEF
17+
BQcwAoYcaHR0cDovL2NhLmFsaXBheS5jb20vY2E2LmNlcjAmBggrBgEFBQcwAYYaaHR0cDovL2Nh
18+
LmFsaXBheS5jb206ODM0MC8wDQYJKoZIhvcNAQELBQADggEBALeeTrnXKUYapQ/ig9MB4h9KoP3g
19+
27BVJwOxYaIqWxm6iDvOr/Ju+/8QKygnH6dM3+6+pShCdHsCFaSAQM4gUwxS4F1PcISjKs3LQ6Q8
20+
eWHZMV9w292DOzFRNUoMmgI2yAGOW6DxmnNDFxbVjzcABsrjOmoqso5c4y82K7cl9AfvTm+ThOC3
21+
wPQqTiLOZ6xoSL8pO4V+BkPrJTKJNqX0cVPm+QLP+TC411GTjrmz2nv1OPEVSTey5HaB6HWQeDz7
22+
hGnbipZswfCRHRiu42+4pQZw+3rQ6uRzSPMpxgk7RaBWexybrqRk6ZfyXMcxJvNDWWk/EQlzLL8S
23+
RuJzW8kcFrE=
24+
-----END CERTIFICATE-----

0 commit comments

Comments
 (0)
Please sign in to comment.