dream-common 1.1.35 → 1.1.39
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/index.js +3 -1
- package/lib/CryptoJS/Base64Utils.js +123 -0
- package/lib/CryptoJS/HttpData.js +20 -21
- package/lib/CryptoJS/HttpRequest.js +150 -0
- package/package.json +4 -3
package/index.js
CHANGED
|
@@ -16,5 +16,7 @@ export default {
|
|
|
16
16
|
...require('./lib/json'),
|
|
17
17
|
...require('./lib/CryptoJS/Base58'),
|
|
18
18
|
...require('./lib/CryptoJS/HttpData'),
|
|
19
|
-
...require('./lib/CryptoJS/AesUtils')
|
|
19
|
+
...require('./lib/CryptoJS/AesUtils'),
|
|
20
|
+
...require('./lib/CryptoJS/Base64Utils'),
|
|
21
|
+
...require('./lib/CryptoJS/HttpRequest')
|
|
20
22
|
}
|
|
@@ -0,0 +1,123 @@
|
|
|
1
|
+
// utils/Base64Utils.js
|
|
2
|
+
|
|
3
|
+
/**
|
|
4
|
+
* Base64 编码/解码 & 二进制互转工具类
|
|
5
|
+
* 支持:String ↔ Base64 ↔ Uint8Array ↔ Hex
|
|
6
|
+
*/
|
|
7
|
+
class Base64Utils {
|
|
8
|
+
/**
|
|
9
|
+
* 字符串 → Base64
|
|
10
|
+
* @param {string} str - UTF-8 字符串
|
|
11
|
+
* @returns {string} Base64 编码结果
|
|
12
|
+
*/
|
|
13
|
+
static stringToBase64(str) {
|
|
14
|
+
if (typeof Buffer !== 'undefined') {
|
|
15
|
+
return Buffer.from(str, 'utf8').toString('base64');
|
|
16
|
+
}
|
|
17
|
+
// 浏览器环境
|
|
18
|
+
const encoder = new TextEncoder();
|
|
19
|
+
const bytes = encoder.encode(str);
|
|
20
|
+
let binary = '';
|
|
21
|
+
for (let i = 0; i < bytes.length; i++) {
|
|
22
|
+
binary += String.fromCharCode(bytes[i]);
|
|
23
|
+
}
|
|
24
|
+
return btoa(binary);
|
|
25
|
+
}
|
|
26
|
+
|
|
27
|
+
/**
|
|
28
|
+
* Base64 → 字符串
|
|
29
|
+
* @param {string} base64
|
|
30
|
+
* @returns {string} 原始 UTF-8 字符串
|
|
31
|
+
*/
|
|
32
|
+
static base64ToString(base64) {
|
|
33
|
+
if (typeof Buffer !== 'undefined') {
|
|
34
|
+
return Buffer.from(base64, 'base64').toString('utf8');
|
|
35
|
+
}
|
|
36
|
+
// 浏览器环境
|
|
37
|
+
const binary = atob(base64.replace(/[^A-Za-z0-9+/]/g, ''));
|
|
38
|
+
const bytes = new Uint8Array(binary.length);
|
|
39
|
+
for (let i = 0; i < binary.length; i++) {
|
|
40
|
+
bytes[i] = binary.charCodeAt(i);
|
|
41
|
+
}
|
|
42
|
+
const decoder = new TextDecoder();
|
|
43
|
+
return decoder.decode(bytes);
|
|
44
|
+
}
|
|
45
|
+
|
|
46
|
+
/**
|
|
47
|
+
* Uint8Array → Base64
|
|
48
|
+
* @param {Uint8Array} bytes
|
|
49
|
+
* @returns {string}
|
|
50
|
+
*/
|
|
51
|
+
static uint8ArrayToBase64(bytes) {
|
|
52
|
+
if (typeof Buffer !== 'undefined') {
|
|
53
|
+
return Buffer.from(bytes).toString('base64');
|
|
54
|
+
}
|
|
55
|
+
let binary = '';
|
|
56
|
+
for (let i = 0; i < bytes.length; i++) {
|
|
57
|
+
binary += String.fromCharCode(bytes[i]);
|
|
58
|
+
}
|
|
59
|
+
return btoa(binary);
|
|
60
|
+
}
|
|
61
|
+
|
|
62
|
+
/**
|
|
63
|
+
* Base64 → Uint8Array
|
|
64
|
+
* @param {string} base64
|
|
65
|
+
* @returns {Uint8Array}
|
|
66
|
+
*/
|
|
67
|
+
static base64ToUint8Array(base64) {
|
|
68
|
+
if (typeof Buffer !== 'undefined') {
|
|
69
|
+
return new Uint8Array(Buffer.from(base64, 'base64'));
|
|
70
|
+
}
|
|
71
|
+
const binary = atob(base64.replace(/[^A-Za-z0-9+/]/g, ''));
|
|
72
|
+
const bytes = new Uint8Array(binary.length);
|
|
73
|
+
for (let i = 0; i < binary.length; i++) {
|
|
74
|
+
bytes[i] = binary.charCodeAt(i);
|
|
75
|
+
}
|
|
76
|
+
return bytes;
|
|
77
|
+
}
|
|
78
|
+
|
|
79
|
+
/**
|
|
80
|
+
* Hex 字符串 → Uint8Array
|
|
81
|
+
* @param {string} hex - 如 'a1b2c3'
|
|
82
|
+
* @returns {Uint8Array}
|
|
83
|
+
*/
|
|
84
|
+
static hexToUint8Array(hex) {
|
|
85
|
+
if (hex.length % 2 !== 0) throw new Error('Invalid hex length');
|
|
86
|
+
const bytes = new Uint8Array(hex.length / 2);
|
|
87
|
+
for (let i = 0; i < bytes.length; i++) {
|
|
88
|
+
bytes[i] = parseInt(hex.substr(i * 2, 2), 16);
|
|
89
|
+
}
|
|
90
|
+
return bytes;
|
|
91
|
+
}
|
|
92
|
+
|
|
93
|
+
/**
|
|
94
|
+
* Uint8Array → 小写 Hex 字符串
|
|
95
|
+
* @param {Uint8Array} bytes
|
|
96
|
+
* @returns {string}
|
|
97
|
+
*/
|
|
98
|
+
static uint8ArrayToHex(bytes) {
|
|
99
|
+
return Array.from(bytes, byte => byte.toString(16).padStart(2, '0')).join('');
|
|
100
|
+
}
|
|
101
|
+
|
|
102
|
+
/**
|
|
103
|
+
* Hex → Base64
|
|
104
|
+
* @param {string} hex
|
|
105
|
+
* @returns {string}
|
|
106
|
+
*/
|
|
107
|
+
static hexToBase64(hex) {
|
|
108
|
+
const bytes = this.hexToUint8Array(hex);
|
|
109
|
+
return this.uint8ArrayToBase64(bytes);
|
|
110
|
+
}
|
|
111
|
+
|
|
112
|
+
/**
|
|
113
|
+
* Base64 → Hex
|
|
114
|
+
* @param {string} base64
|
|
115
|
+
* @returns {string}
|
|
116
|
+
*/
|
|
117
|
+
static base64ToHex(base64) {
|
|
118
|
+
const bytes = this.base64ToUint8Array(base64);
|
|
119
|
+
return this.uint8ArrayToHex(bytes);
|
|
120
|
+
}
|
|
121
|
+
}
|
|
122
|
+
|
|
123
|
+
export default Base64Utils;
|
package/lib/CryptoJS/HttpData.js
CHANGED
|
@@ -1,30 +1,29 @@
|
|
|
1
|
-
import {md5_48,md5_16} from "./md5";
|
|
2
|
-
import {aesEncrypt,aesDecrypt} from "./AesUtils";
|
|
3
|
-
import {decodeBase58,encodeBase58} from "./Base58";
|
|
1
|
+
import {md5_48, md5_16} from "./md5";
|
|
2
|
+
import {aesEncrypt, aesDecrypt} from "./AesUtils";
|
|
3
|
+
import {decodeBase58, encodeBase58} from "./Base58";
|
|
4
|
+
|
|
4
5
|
export const reqEncrypt = (orgId, content) => {
|
|
5
|
-
return reqDataEncrypt(orgId,"1.0.0","MD5","AES", content);
|
|
6
|
+
return reqDataEncrypt(orgId, "1.0.0", "MD5", "AES", content);
|
|
6
7
|
}
|
|
7
|
-
export const respDecrypt = (respData
|
|
8
|
+
export const respDecrypt = (respData = {}) => {
|
|
8
9
|
try {
|
|
9
10
|
if (respData.encType === "NONE") {
|
|
10
11
|
return respData.data;
|
|
11
12
|
} else {
|
|
12
13
|
let signData = respData.signData.toString();
|
|
13
|
-
let encData
|
|
14
|
-
let timestamp
|
|
15
|
-
let orgId
|
|
16
|
-
encData = aesDecrypt(md5_16(orgId + signData + timestamp),encData);
|
|
17
|
-
|
|
18
|
-
console.log(params);
|
|
14
|
+
let encData = respData.encData.toString();
|
|
15
|
+
let timestamp = respData.timestamp.toString();
|
|
16
|
+
let orgId = respData.orgId.toString();
|
|
17
|
+
encData = aesDecrypt(md5_16(orgId + signData + timestamp), encData);
|
|
18
|
+
const params = encData.split(/&(?=[^&]+=)/); // 智能分割,避免值中的 & 干扰
|
|
19
19
|
let obj = {}
|
|
20
20
|
// 遍历所有键值对
|
|
21
|
-
for (let
|
|
22
|
-
|
|
21
|
+
for (let pair of params) {
|
|
22
|
+
const [key, ...rest] = pair.split('=');
|
|
23
|
+
obj[key] = rest.join('=');
|
|
23
24
|
}
|
|
24
|
-
|
|
25
25
|
try {
|
|
26
|
-
|
|
27
|
-
return JSON.parse(data);
|
|
26
|
+
return JSON.parse(obj.data);
|
|
28
27
|
} catch (e) {
|
|
29
28
|
return obj.data || null;
|
|
30
29
|
}
|
|
@@ -38,13 +37,13 @@ export const respDecrypt = (respData = {}) => {
|
|
|
38
37
|
}
|
|
39
38
|
|
|
40
39
|
|
|
41
|
-
export const reqDataEncrypt = (orgId,version,signType,encType, content) => {
|
|
40
|
+
export const reqDataEncrypt = (orgId, version, signType, encType, content) => {
|
|
42
41
|
let timestamp = new Date().getTime().toString();
|
|
43
42
|
let obj = {
|
|
44
43
|
"orgId": orgId || "Client",
|
|
45
|
-
"version": version|| "1.0.0",
|
|
44
|
+
"version": version || "1.0.0",
|
|
46
45
|
"signType": signType || "NONE",
|
|
47
|
-
"encType": encType|| "NONE",
|
|
46
|
+
"encType": encType || "NONE",
|
|
48
47
|
};
|
|
49
48
|
if (encType === "NONE") {
|
|
50
49
|
obj.data = content;
|
|
@@ -82,8 +81,8 @@ export const reqDataEncrypt = (orgId,version,signType,encType, content) => {
|
|
|
82
81
|
return obj;
|
|
83
82
|
} catch (e) {
|
|
84
83
|
console.log(e);
|
|
85
|
-
obj.signType ="NONE";
|
|
86
|
-
obj.encType ="NONE";
|
|
84
|
+
obj.signType = "NONE";
|
|
85
|
+
obj.encType = "NONE";
|
|
87
86
|
obj.data = content;
|
|
88
87
|
obj.timestamp = timestamp;
|
|
89
88
|
return obj;
|
|
@@ -0,0 +1,150 @@
|
|
|
1
|
+
// utils/secureData.js
|
|
2
|
+
|
|
3
|
+
import {
|
|
4
|
+
sm2,
|
|
5
|
+
sm4
|
|
6
|
+
} from 'sm-crypto';
|
|
7
|
+
import Base64Utils from './Base64Utils.js';
|
|
8
|
+
import {md5} from './md5.js';
|
|
9
|
+
import {uuid} from '../uuid.js';
|
|
10
|
+
|
|
11
|
+
class HttpRequest {
|
|
12
|
+
|
|
13
|
+
/**
|
|
14
|
+
* 创建安全请求基础结构(支持传入部分字段进行覆盖)
|
|
15
|
+
* @param {Object} request - 可选:包含 appId、data 及其他可覆盖字段
|
|
16
|
+
* @returns {Object} 完整的 SecureBaseRequest 对象
|
|
17
|
+
*/
|
|
18
|
+
static createData(request = {}) {
|
|
19
|
+
const now = Date.now();
|
|
20
|
+
const defaults = {
|
|
21
|
+
version: '1.0.0',
|
|
22
|
+
signType: 'SM2',
|
|
23
|
+
encType: 'SM4',
|
|
24
|
+
timestamp: now,
|
|
25
|
+
nonceStr: uuid(),
|
|
26
|
+
encData: '',
|
|
27
|
+
signData: ''
|
|
28
|
+
};
|
|
29
|
+
|
|
30
|
+
return {
|
|
31
|
+
...defaults,
|
|
32
|
+
...request, // 👈 用传入的 request 覆盖默认值
|
|
33
|
+
};
|
|
34
|
+
}
|
|
35
|
+
|
|
36
|
+
/**
|
|
37
|
+
* 1️⃣ 打包安全数据:签名 + 加密
|
|
38
|
+
* @param request
|
|
39
|
+
* @param {string} privateKey - 64位十六进制 SM2 私钥
|
|
40
|
+
* @param {boolean} [useBase64=false] - encData 是否用 Base64 编码(否则为 hex)
|
|
41
|
+
* @returns {Object} 可直接发送的请求对象(不含 data 字段)
|
|
42
|
+
*/
|
|
43
|
+
static encryptData(request = {}, privateKey, useBase64 = true) {
|
|
44
|
+
// 1. 生成签名原文(排除 encData, signData, data)
|
|
45
|
+
const signSource = objectToSignString(request, 'encData', 'signData', 'data');
|
|
46
|
+
// 2. SM2 签名(DER 格式,与 Java Bouncy Castle 兼容)
|
|
47
|
+
const signValueStr = sm2.doSignature(signSource, privateKey, {
|
|
48
|
+
der: true
|
|
49
|
+
}).toLowerCase();
|
|
50
|
+
|
|
51
|
+
const signValue = useBase64 ? Base64Utils.hexToBase64(signValueStr) : signValueStr;
|
|
52
|
+
// 3. 生成 SM4 密钥和 IV(通过 MD5)
|
|
53
|
+
const keyHex = md5_32(signValue); // 32 hex → 16 bytes
|
|
54
|
+
const ivHex = md5_32(signSource); // 32 hex → 16 bytes
|
|
55
|
+
|
|
56
|
+
// 4. 序列化业务数据
|
|
57
|
+
const dataJson = typeof request.data === 'string' ? request.data : JSON.stringify(request.data);
|
|
58
|
+
|
|
59
|
+
// 5. SM4 加密(CBC + PKCS#7)
|
|
60
|
+
const encDataHex = sm4.encrypt(dataJson, keyHex, {
|
|
61
|
+
iv: ivHex,
|
|
62
|
+
mode: 'cbc',
|
|
63
|
+
padding: 'pkcs#7',
|
|
64
|
+
cipherType: 1, // hex output
|
|
65
|
+
});
|
|
66
|
+
|
|
67
|
+
// 6. 转换为最终格式(hex 或 base64)
|
|
68
|
+
const encData = useBase64 ? Base64Utils.hexToBase64(encDataHex) : encDataHex;
|
|
69
|
+
|
|
70
|
+
// 7. 返回可传输对象(移除 data)
|
|
71
|
+
const {
|
|
72
|
+
data: _,
|
|
73
|
+
...transmittable
|
|
74
|
+
} = {
|
|
75
|
+
...request,
|
|
76
|
+
signData: signValue,
|
|
77
|
+
encData,
|
|
78
|
+
};
|
|
79
|
+
return transmittable;
|
|
80
|
+
}
|
|
81
|
+
|
|
82
|
+
/**
|
|
83
|
+
* 2️⃣ 验证签名(用于调试)
|
|
84
|
+
* @param {Object} request - 接收到的请求对象(含 signData, encData 等)
|
|
85
|
+
* @param {string} publicKey - 130位十六进制 SM2 公钥(以 "04" 开头)
|
|
86
|
+
* @param useBase64
|
|
87
|
+
* @returns {boolean}
|
|
88
|
+
*/
|
|
89
|
+
static verifySignData(request, publicKey, useBase64 = true) {
|
|
90
|
+
const signSource = objectToSignString(request, 'encData', 'signData', 'data');
|
|
91
|
+
const signatureHex = useBase64 ? Base64Utils.base64ToHex(request.signData) : request.signData;
|
|
92
|
+
return sm2.doVerifySignature(signSource, signatureHex, publicKey, {
|
|
93
|
+
der: true
|
|
94
|
+
}) // 验签结果
|
|
95
|
+
|
|
96
|
+
}
|
|
97
|
+
|
|
98
|
+
/**
|
|
99
|
+
* 3️⃣ 解包安全数据:解密并返回原始 data
|
|
100
|
+
* @param {Object} request - 接收到的请求对象
|
|
101
|
+
* @param {string} privateKey - 64位十六进制 SM2 私钥(用于重算 key/iv)
|
|
102
|
+
* @param {boolean} [useBase64=false] - encData 是否为 Base64 编码
|
|
103
|
+
* @returns {any} 原始 data(自动 JSON.parse,若失败则返回字符串)
|
|
104
|
+
*/
|
|
105
|
+
static decryptData(request, privateKey, useBase64 = true) {
|
|
106
|
+
// 1. 重算签名原文(用于生成 key/iv)
|
|
107
|
+
const signSource = objectToSignString(request, 'encData', 'signData', 'data');
|
|
108
|
+
const lvHax = md5_32(signSource);
|
|
109
|
+
const keyHax = md5_32(request.signData);
|
|
110
|
+
|
|
111
|
+
// 3. 处理 encData(base64 → hex)
|
|
112
|
+
|
|
113
|
+
const encDataHex = useBase64 ? Base64Utils.base64ToHex(request.encData) : request.encData;
|
|
114
|
+
|
|
115
|
+
// 4. SM4 解密
|
|
116
|
+
const decrypted = sm4.decrypt(encDataHex, keyHax, {
|
|
117
|
+
iv: lvHax,
|
|
118
|
+
mode: 'cbc',
|
|
119
|
+
padding: 'pkcs#7',
|
|
120
|
+
cipherType: 1, // input is hex
|
|
121
|
+
});
|
|
122
|
+
// 5. 尝试解析 JSON
|
|
123
|
+
try {
|
|
124
|
+
return JSON.parse(decrypted);
|
|
125
|
+
} catch (e) {
|
|
126
|
+
return decrypted; // 原始字符串
|
|
127
|
+
}
|
|
128
|
+
}
|
|
129
|
+
}
|
|
130
|
+
|
|
131
|
+
|
|
132
|
+
/**
|
|
133
|
+
* 将对象转为签名字符串(排除指定字段 + 字典序)
|
|
134
|
+
*/
|
|
135
|
+
function objectToSignString(obj, ...excludeFields) {
|
|
136
|
+
const excludes = new Set(excludeFields);
|
|
137
|
+
return Object.keys(obj)
|
|
138
|
+
.filter(key => !excludes.has(key))
|
|
139
|
+
.sort()
|
|
140
|
+
.map(key => `${key}=${obj[key] != null ? String(obj[key]) : ''}`)
|
|
141
|
+
.join('&');
|
|
142
|
+
}
|
|
143
|
+
|
|
144
|
+
/**
|
|
145
|
+
* 生成 MD5(32位小写 hex)
|
|
146
|
+
*/
|
|
147
|
+
function md5_32(str) {
|
|
148
|
+
return md5(str);
|
|
149
|
+
}
|
|
150
|
+
export default HttpRequest;
|
package/package.json
CHANGED
|
@@ -1,10 +1,11 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "dream-common",
|
|
3
|
-
"version": "1.1.
|
|
3
|
+
"version": "1.1.39",
|
|
4
4
|
"description": "",
|
|
5
5
|
"main": "index.js",
|
|
6
6
|
"scripts": {
|
|
7
|
-
"test": "echo \"Error: no test specified\" && exit 1"
|
|
7
|
+
"test": "echo \"Error: no test specified\" && exit 1",
|
|
8
|
+
"check:updates": "npx npm-check-updates"
|
|
8
9
|
},
|
|
9
10
|
"repository": {
|
|
10
11
|
"type": "git",
|
|
@@ -15,6 +16,6 @@
|
|
|
15
16
|
"dependencies": {
|
|
16
17
|
"bs58": "^6.0.0",
|
|
17
18
|
"crypto-js": "^4.2.0",
|
|
18
|
-
"
|
|
19
|
+
"sm-crypto": "^0.4.0"
|
|
19
20
|
}
|
|
20
21
|
}
|