sm-crypto-wasm 0.3.1 → 0.7.0
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/README.md +70 -7
- package/dist/asn1/decode.d.ts +74 -0
- package/dist/asn1/decode.js +338 -0
- package/dist/asn1/encode.d.ts +68 -0
- package/dist/asn1/encode.js +275 -0
- package/dist/asn1/index.d.ts +11 -0
- package/dist/asn1/index.js +11 -0
- package/dist/asn1/oid.d.ts +22 -0
- package/dist/asn1/oid.js +107 -0
- package/dist/asn1/time.d.ts +39 -0
- package/dist/asn1/time.js +111 -0
- package/dist/asn1/types.d.ts +76 -0
- package/dist/asn1/types.js +71 -0
- package/dist/cms/errors.d.ts +13 -0
- package/dist/cms/errors.js +11 -0
- package/dist/cms/index.d.ts +13 -0
- package/dist/cms/index.js +10 -0
- package/dist/cms/oids.d.ts +17 -0
- package/dist/cms/oids.js +17 -0
- package/dist/cms/parse.d.ts +75 -0
- package/dist/cms/parse.js +326 -0
- package/dist/cms/sign.d.ts +43 -0
- package/dist/cms/sign.js +142 -0
- package/dist/cms/verify.d.ts +42 -0
- package/dist/cms/verify.js +173 -0
- package/dist/index.d.ts +3 -0
- package/dist/index.js +3 -0
- package/dist/pem/asn1.d.ts +10 -69
- package/dist/pem/asn1.js +10 -292
- package/dist/pem/pkcs8.js +9 -1
- package/dist/pem/sm2.d.ts +16 -0
- package/dist/pem/sm2.js +37 -5
- package/dist/sm2/index.d.ts +69 -0
- package/dist/sm2/index.js +188 -0
- package/dist/sm2/sm2.wasm +0 -0
- package/dist/webcrypto/algorithms.d.ts +42 -0
- package/dist/webcrypto/algorithms.js +120 -0
- package/dist/webcrypto/cryptoKey.d.ts +64 -0
- package/dist/webcrypto/cryptoKey.js +109 -0
- package/dist/webcrypto/errors.d.ts +19 -0
- package/dist/webcrypto/errors.js +45 -0
- package/dist/webcrypto/index.d.ts +14 -0
- package/dist/webcrypto/index.js +11 -0
- package/dist/webcrypto/subtle.d.ts +68 -0
- package/dist/webcrypto/subtle.js +358 -0
- package/dist/x509/encode.d.ts +59 -0
- package/dist/x509/encode.js +175 -0
- package/dist/x509/errors.d.ts +14 -0
- package/dist/x509/errors.js +14 -0
- package/dist/x509/extensions.d.ts +27 -0
- package/dist/x509/extensions.js +215 -0
- package/dist/x509/index.d.ts +14 -0
- package/dist/x509/index.js +11 -0
- package/dist/x509/ip.d.ts +12 -0
- package/dist/x509/ip.js +101 -0
- package/dist/x509/issue.d.ts +44 -0
- package/dist/x509/issue.js +414 -0
- package/dist/x509/oids.d.ts +40 -0
- package/dist/x509/oids.js +52 -0
- package/dist/x509/parse.d.ts +24 -0
- package/dist/x509/parse.js +436 -0
- package/dist/x509/sha1.d.ts +9 -0
- package/dist/x509/sha1.js +72 -0
- package/dist/x509/sha256.d.ts +9 -0
- package/dist/x509/sha256.js +84 -0
- package/dist/x509/sig.d.ts +20 -0
- package/dist/x509/sig.js +77 -0
- package/dist/x509/types.d.ts +235 -0
- package/dist/x509/types.js +7 -0
- package/dist/x509/verify.d.ts +44 -0
- package/dist/x509/verify.js +66 -0
- package/package.json +15 -3
package/dist/pem/asn1.js
CHANGED
|
@@ -1,297 +1,15 @@
|
|
|
1
1
|
/**
|
|
2
|
-
*
|
|
2
|
+
* 旧 ASN.1 入口(兼容层)。
|
|
3
3
|
*
|
|
4
|
-
*
|
|
5
|
-
*
|
|
6
|
-
* - OBJECT IDENTIFIER (0x06)、SEQUENCE (0x30, constructed)
|
|
4
|
+
* 实现已迁移到 `src/asn1/`(DER/BER 通用层)。本文件只保留历史导出名,
|
|
5
|
+
* 使 `src/pem/*` 与既有测试无需改动:
|
|
7
6
|
*
|
|
8
|
-
*
|
|
9
|
-
*
|
|
7
|
+
* - `parseDer` → `src/asn1/decode.ts`
|
|
8
|
+
* - `encodeDer` → `src/asn1/encode.ts` 的 `encode`
|
|
9
|
+
* - `asn1Int` / `asn1Octets` / `asn1Oid` / `asn1Null` / `asn1Seq` → 对应构造器
|
|
10
|
+
* - `encodeOid` / `decodeOid` / `asn1IntValue` → OID 与 INTEGER 解码
|
|
10
11
|
*
|
|
11
|
-
* spec/plan 对应:
|
|
12
|
+
* spec/plan 对应:X.509/CMS plan 阶段 0「迁移」。
|
|
12
13
|
*/
|
|
13
|
-
|
|
14
|
-
export
|
|
15
|
-
name = 'Asn1Error';
|
|
16
|
-
}
|
|
17
|
-
const TAG_INTEGER = 0x02;
|
|
18
|
-
const _TAG_BIT_STRING = 0x03;
|
|
19
|
-
const TAG_OCTET_STRING = 0x04;
|
|
20
|
-
const TAG_NULL = 0x05;
|
|
21
|
-
const TAG_OID = 0x06;
|
|
22
|
-
const TAG_SEQUENCE = 0x30; // constructed
|
|
23
|
-
/**
|
|
24
|
-
* 解析一段 DER 字节。
|
|
25
|
-
*
|
|
26
|
-
* @param buf DER 字节。
|
|
27
|
-
* @returns 解析后的根节点(通常为 SEQUENCE)。
|
|
28
|
-
* @throws Asn1Error 输入截断、tag/length 不合法、不支持的构造。
|
|
29
|
-
*/
|
|
30
|
-
export function parseDer(buf) {
|
|
31
|
-
const reader = new Reader(buf);
|
|
32
|
-
const node = reader.readNode();
|
|
33
|
-
// 允许根节点之后留 0 字节尾巴;多余字节视为错误。
|
|
34
|
-
if (reader.remaining() !== 0) {
|
|
35
|
-
throw new Asn1Error(`parseDer: ${reader.remaining()} trailing bytes after root node`);
|
|
36
|
-
}
|
|
37
|
-
return node;
|
|
38
|
-
}
|
|
39
|
-
/**
|
|
40
|
-
* 将 Asn1Node 编码回 DER 字节。**重新计算 length 与 raw**。
|
|
41
|
-
*
|
|
42
|
-
* @param node 待编码节点。
|
|
43
|
-
* @returns DER 字节。
|
|
44
|
-
*/
|
|
45
|
-
export function encodeDer(node) {
|
|
46
|
-
// 值字节:primitive 直接取 value;constructed 递归子节点。
|
|
47
|
-
const parts = [];
|
|
48
|
-
if (node.children) {
|
|
49
|
-
for (const child of node.children)
|
|
50
|
-
parts.push(encodeDer(child));
|
|
51
|
-
}
|
|
52
|
-
const value = node.children ? concat(parts) : node.value;
|
|
53
|
-
const lengthBytes = encodeLength(value.length);
|
|
54
|
-
const tagByte = node.tag & 0xff;
|
|
55
|
-
const out = new Uint8Array(1 + lengthBytes.length + value.length);
|
|
56
|
-
out[0] = tagByte;
|
|
57
|
-
for (let i = 0; i < lengthBytes.length; i++)
|
|
58
|
-
out[1 + i] = lengthBytes[i] ?? 0;
|
|
59
|
-
for (let i = 0; i < value.length; i++) {
|
|
60
|
-
out[1 + lengthBytes.length + i] = value[i] ?? 0;
|
|
61
|
-
}
|
|
62
|
-
return out;
|
|
63
|
-
}
|
|
64
|
-
/**
|
|
65
|
-
* 便捷构造函数:构造一个 INTEGER 节点。
|
|
66
|
-
*
|
|
67
|
-
* @param value 大端整数(可为负:负数用二进制补码表示)。
|
|
68
|
-
*/
|
|
69
|
-
export function asn1Int(value) {
|
|
70
|
-
const bytes = bigintToBytes(BigInt(value));
|
|
71
|
-
const raw = concat([new Uint8Array([TAG_INTEGER]), encodeLength(bytes.length), bytes]);
|
|
72
|
-
return {
|
|
73
|
-
cls: 0,
|
|
74
|
-
tag: TAG_INTEGER,
|
|
75
|
-
length: bytes.length,
|
|
76
|
-
value: bytes,
|
|
77
|
-
raw,
|
|
78
|
-
};
|
|
79
|
-
}
|
|
80
|
-
/** 便捷构造函数:构造一个 OCTET STRING 节点。 */
|
|
81
|
-
export function asn1Octets(value) {
|
|
82
|
-
const raw = concat([new Uint8Array([TAG_OCTET_STRING]), encodeLength(value.length), value]);
|
|
83
|
-
return {
|
|
84
|
-
cls: 0,
|
|
85
|
-
tag: TAG_OCTET_STRING,
|
|
86
|
-
length: value.length,
|
|
87
|
-
value,
|
|
88
|
-
raw,
|
|
89
|
-
};
|
|
90
|
-
}
|
|
91
|
-
/** 便捷构造函数:构造一个 OID 节点(输入如 `"1.2.156.10197.1.100"`)。 */
|
|
92
|
-
export function asn1Oid(oid) {
|
|
93
|
-
const bytes = encodeOid(oid);
|
|
94
|
-
const raw = concat([new Uint8Array([TAG_OID]), encodeLength(bytes.length), bytes]);
|
|
95
|
-
return { cls: 0, tag: TAG_OID, length: bytes.length, value: bytes, raw };
|
|
96
|
-
}
|
|
97
|
-
/** 便捷构造函数:构造一个 NULL 节点。 */
|
|
98
|
-
export function asn1Null() {
|
|
99
|
-
const raw = new Uint8Array([TAG_NULL, 0x00]);
|
|
100
|
-
return { cls: 0, tag: TAG_NULL, length: 0, value: new Uint8Array(0), raw };
|
|
101
|
-
}
|
|
102
|
-
/** 便捷构造函数:构造一个 SEQUENCE 节点。 */
|
|
103
|
-
export function asn1Seq(children) {
|
|
104
|
-
const parts = children.map((c) => c.raw);
|
|
105
|
-
const value = concat(parts);
|
|
106
|
-
const raw = concat([new Uint8Array([TAG_SEQUENCE]), encodeLength(value.length), value]);
|
|
107
|
-
// tag 字段统一存 UNIVERSAL 编号 16(0x10),与 Reader 输出一致;
|
|
108
|
-
// raw 首字节保留完整 0x30 以便序列化。
|
|
109
|
-
return {
|
|
110
|
-
cls: 0,
|
|
111
|
-
tag: 0x10,
|
|
112
|
-
length: value.length,
|
|
113
|
-
value,
|
|
114
|
-
children,
|
|
115
|
-
raw,
|
|
116
|
-
};
|
|
117
|
-
}
|
|
118
|
-
// ----------------- 内部:长度编码 -----------------
|
|
119
|
-
function encodeLength(len) {
|
|
120
|
-
if (len < 0 || !Number.isInteger(len)) {
|
|
121
|
-
throw new Asn1Error(`encodeLength: bad length ${len}`);
|
|
122
|
-
}
|
|
123
|
-
if (len < 0x80)
|
|
124
|
-
return new Uint8Array([len]);
|
|
125
|
-
// 长格式:首个字节 0x80|n(n = 后续字节数),后续 big-endian 长度。
|
|
126
|
-
const bytes = [];
|
|
127
|
-
let v = len;
|
|
128
|
-
while (v > 0) {
|
|
129
|
-
bytes.unshift(v & 0xff);
|
|
130
|
-
v >>= 8;
|
|
131
|
-
}
|
|
132
|
-
return new Uint8Array([0x80 | bytes.length, ...bytes]);
|
|
133
|
-
}
|
|
134
|
-
// ----------------- 内部:OID 编码 -----------------
|
|
135
|
-
/**
|
|
136
|
-
* OID 编码:第一个子标识符 = a*40 + b,后续每个子标识符按 7-bit 编码。
|
|
137
|
-
*
|
|
138
|
-
* 例:`1.2.156.10197.1.100` → [0x2a, 0x86, 0x48, 0xce, 0x3d, 0x01, 0x01, 0x64]
|
|
139
|
-
* 1*40+2 = 42 = 0x2a;156、10197、1、100 都用 base-128 编码。
|
|
140
|
-
*
|
|
141
|
-
* @param oid 点分 OID 字符串。
|
|
142
|
-
* @returns OID 内容字节(不含 tag 和 length)。
|
|
143
|
-
*/
|
|
144
|
-
export function encodeOid(oid) {
|
|
145
|
-
const parts = oid.split('.').map((s) => parseInt(s, 10));
|
|
146
|
-
if (parts.length < 2 || parts.some((n) => !Number.isFinite(n) || n < 0)) {
|
|
147
|
-
throw new Asn1Error(`encodeOid: bad oid "${oid}"`);
|
|
148
|
-
}
|
|
149
|
-
const first = (parts[0] ?? 0) * 40 + (parts[1] ?? 0);
|
|
150
|
-
const out = [first];
|
|
151
|
-
for (let i = 2; i < parts.length; i++) {
|
|
152
|
-
out.push(...encodeBase128(parts[i] ?? 0));
|
|
153
|
-
}
|
|
154
|
-
return new Uint8Array(out);
|
|
155
|
-
}
|
|
156
|
-
/** 反向解码。 */
|
|
157
|
-
export function decodeOid(bytes) {
|
|
158
|
-
if (bytes.length === 0)
|
|
159
|
-
throw new Asn1Error('decodeOid: empty');
|
|
160
|
-
const first = bytes[0] ?? 0;
|
|
161
|
-
const a = Math.floor(first / 40);
|
|
162
|
-
const b = first % 40;
|
|
163
|
-
const parts = [a, b];
|
|
164
|
-
let value = 0;
|
|
165
|
-
for (let i = 1; i < bytes.length; i++) {
|
|
166
|
-
const octet = bytes[i] ?? 0;
|
|
167
|
-
value = (value << 7) | (octet & 0x7f);
|
|
168
|
-
if ((octet & 0x80) === 0) {
|
|
169
|
-
parts.push(value);
|
|
170
|
-
value = 0;
|
|
171
|
-
}
|
|
172
|
-
}
|
|
173
|
-
if (value !== 0)
|
|
174
|
-
throw new Asn1Error('decodeOid: truncated');
|
|
175
|
-
return parts.join('.');
|
|
176
|
-
}
|
|
177
|
-
function encodeBase128(n) {
|
|
178
|
-
if (n < 0 || !Number.isInteger(n)) {
|
|
179
|
-
throw new Asn1Error(`encodeBase128: bad ${n}`);
|
|
180
|
-
}
|
|
181
|
-
if (n < 0x80)
|
|
182
|
-
return [n];
|
|
183
|
-
const out = [];
|
|
184
|
-
let v = n;
|
|
185
|
-
do {
|
|
186
|
-
out.unshift((v & 0x7f) | 0x80);
|
|
187
|
-
v >>= 7;
|
|
188
|
-
} while (v > 0);
|
|
189
|
-
out[out.length - 1] = (out[out.length - 1] ?? 0) & 0x7f;
|
|
190
|
-
return out;
|
|
191
|
-
}
|
|
192
|
-
// ----------------- 内部:大端整数 -----------------
|
|
193
|
-
function bigintToBytes(n) {
|
|
194
|
-
if (n === 0n)
|
|
195
|
-
return new Uint8Array([0]);
|
|
196
|
-
const negative = n < 0n;
|
|
197
|
-
let v = negative ? -n : n;
|
|
198
|
-
const tmp = [];
|
|
199
|
-
while (v > 0n) {
|
|
200
|
-
tmp.unshift(Number(v & 0xffn));
|
|
201
|
-
v >>= 8n;
|
|
202
|
-
}
|
|
203
|
-
// 负数采用补码:高位加 0xff 让最高位为 1。
|
|
204
|
-
if (negative) {
|
|
205
|
-
// 简化处理:本仓库不需要负 INTEGER。
|
|
206
|
-
throw new Asn1Error('bigintToBytes: negative integers not supported');
|
|
207
|
-
}
|
|
208
|
-
// 正数:补 0x00 前缀以免被当成负数(最高位为 1 时必须)。
|
|
209
|
-
if ((tmp[0] ?? 0) & 0x80)
|
|
210
|
-
tmp.unshift(0);
|
|
211
|
-
return new Uint8Array(tmp);
|
|
212
|
-
}
|
|
213
|
-
/** 读一个 INTEGER 返回 bigint。 */
|
|
214
|
-
export function asn1IntValue(node) {
|
|
215
|
-
if (node.tag !== TAG_INTEGER) {
|
|
216
|
-
throw new Asn1Error(`asn1IntValue: tag ${node.tag}, expected INTEGER`);
|
|
217
|
-
}
|
|
218
|
-
let v = 0n;
|
|
219
|
-
for (let i = 0; i < node.value.length; i++) {
|
|
220
|
-
v = (v << 8n) | BigInt(node.value[i] ?? 0);
|
|
221
|
-
}
|
|
222
|
-
return v;
|
|
223
|
-
}
|
|
224
|
-
// ----------------- 内部:Reader -----------------
|
|
225
|
-
class Reader {
|
|
226
|
-
buf;
|
|
227
|
-
pos = 0;
|
|
228
|
-
constructor(buf) {
|
|
229
|
-
this.buf = buf;
|
|
230
|
-
}
|
|
231
|
-
remaining() {
|
|
232
|
-
return this.buf.length - this.pos;
|
|
233
|
-
}
|
|
234
|
-
take(n) {
|
|
235
|
-
if (this.pos + n > this.buf.length) {
|
|
236
|
-
throw new Asn1Error(`Reader: truncated, need ${n} bytes at pos ${this.pos}`);
|
|
237
|
-
}
|
|
238
|
-
const out = this.buf.subarray(this.pos, this.pos + n);
|
|
239
|
-
this.pos += n;
|
|
240
|
-
return out;
|
|
241
|
-
}
|
|
242
|
-
readTag() {
|
|
243
|
-
if (this.remaining() < 1)
|
|
244
|
-
throw new Asn1Error('Reader: empty tag');
|
|
245
|
-
return this.buf[this.pos++] ?? 0;
|
|
246
|
-
}
|
|
247
|
-
readLength() {
|
|
248
|
-
if (this.remaining() < 1)
|
|
249
|
-
throw new Asn1Error('Reader: empty length');
|
|
250
|
-
const first = this.buf[this.pos++] ?? 0;
|
|
251
|
-
if ((first & 0x80) === 0)
|
|
252
|
-
return first;
|
|
253
|
-
const n = first & 0x7f;
|
|
254
|
-
if (n === 0)
|
|
255
|
-
throw new Asn1Error('Reader: indefinite length not supported');
|
|
256
|
-
if (this.remaining() < n)
|
|
257
|
-
throw new Asn1Error('Reader: truncated long length');
|
|
258
|
-
let len = 0;
|
|
259
|
-
for (let i = 0; i < n; i++) {
|
|
260
|
-
len = (len << 8) | (this.buf[this.pos++] ?? 0);
|
|
261
|
-
}
|
|
262
|
-
return len;
|
|
263
|
-
}
|
|
264
|
-
readNode() {
|
|
265
|
-
const tagStart = this.pos;
|
|
266
|
-
const tag = this.readTag();
|
|
267
|
-
const length = this.readLength();
|
|
268
|
-
const cls = (tag & 0xc0) >> 6;
|
|
269
|
-
const tagNum = tag & 0x1f;
|
|
270
|
-
const valueStart = this.pos;
|
|
271
|
-
const value = this.take(length);
|
|
272
|
-
const raw = this.buf.subarray(tagStart, valueStart + length);
|
|
273
|
-
const constructed = (tag & 0x20) !== 0;
|
|
274
|
-
if (constructed) {
|
|
275
|
-
// SEQUENCE 等:递归子节点。
|
|
276
|
-
const inner = new Reader(value);
|
|
277
|
-
const children = [];
|
|
278
|
-
while (inner.remaining() > 0) {
|
|
279
|
-
children.push(inner.readNode());
|
|
280
|
-
}
|
|
281
|
-
return { cls, tag: tagNum, length, value, children, raw };
|
|
282
|
-
}
|
|
283
|
-
return { cls, tag: tagNum, length, value, raw };
|
|
284
|
-
}
|
|
285
|
-
}
|
|
286
|
-
function concat(parts) {
|
|
287
|
-
let total = 0;
|
|
288
|
-
for (const p of parts)
|
|
289
|
-
total += p.length;
|
|
290
|
-
const out = new Uint8Array(total);
|
|
291
|
-
let off = 0;
|
|
292
|
-
for (const p of parts) {
|
|
293
|
-
out.set(p, off);
|
|
294
|
-
off += p.length;
|
|
295
|
-
}
|
|
296
|
-
return out;
|
|
297
|
-
}
|
|
14
|
+
export { decodeOid, encode as encodeDer, encodeOid, integer as asn1Int, nullValue as asn1Null, octetString as asn1Octets, oid as asn1Oid, parseDer, readInteger as asn1IntValue, seq as asn1Seq, } from '../asn1/index.js';
|
|
15
|
+
export { Asn1Error } from '../asn1/types.js';
|
package/dist/pem/pkcs8.js
CHANGED
|
@@ -155,7 +155,15 @@ function bitStringNode(value) {
|
|
|
155
155
|
for (let i = 0; i < value.length; i++) {
|
|
156
156
|
raw[1 + lengthBytes.length + i] = value[i] ?? 0;
|
|
157
157
|
}
|
|
158
|
-
return {
|
|
158
|
+
return {
|
|
159
|
+
cls: 0,
|
|
160
|
+
tag: 0x03,
|
|
161
|
+
constructed: false,
|
|
162
|
+
indefinite: false,
|
|
163
|
+
length: value.length,
|
|
164
|
+
value,
|
|
165
|
+
raw,
|
|
166
|
+
};
|
|
159
167
|
}
|
|
160
168
|
function encodeLen(n) {
|
|
161
169
|
if (n < 0x80)
|
package/dist/pem/sm2.d.ts
CHANGED
|
@@ -37,12 +37,24 @@ export declare const OID_SM2_CURVE = "1.2.156.10197.1.301";
|
|
|
37
37
|
* @throws InvalidKeyError PEM/DER 结构非法、或曲线不是 SM2。
|
|
38
38
|
*/
|
|
39
39
|
export declare function parseSm2PrivateKeyPem(pem: string): Uint8Array;
|
|
40
|
+
/**
|
|
41
|
+
* 解析 SM2 私钥 DER(PKCS#8 或 SEC1),返回 32 字节私钥 d。
|
|
42
|
+
*
|
|
43
|
+
* WebCrypto 的 `importKey('pkcs8', ...)` 使用这个入口。
|
|
44
|
+
*/
|
|
45
|
+
export declare function parseSm2PrivateKeyDer(der: Uint8Array): Uint8Array;
|
|
40
46
|
/**
|
|
41
47
|
* 解析 SM2 公钥 PEM,返回 65 字节未压缩点 `04 || X || Y`。
|
|
42
48
|
*
|
|
43
49
|
* @throws InvalidKeyError PEM/DER 结构非法、曲线不是 SM2、或点不是未压缩形式。
|
|
44
50
|
*/
|
|
45
51
|
export declare function parseSm2PublicKeyPem(pem: string): Uint8Array;
|
|
52
|
+
/**
|
|
53
|
+
* 解析 SM2 公钥 DER(SPKI),返回 65 字节未压缩点。
|
|
54
|
+
*
|
|
55
|
+
* WebCrypto 的 `importKey('spki', ...)` 使用这个入口。
|
|
56
|
+
*/
|
|
57
|
+
export declare function parseSm2PublicKeyDer(der: Uint8Array): Uint8Array;
|
|
46
58
|
/**
|
|
47
59
|
* 编码 SM2 私钥为 PKCS#8 PEM(`BEGIN PRIVATE KEY`)。
|
|
48
60
|
*
|
|
@@ -50,9 +62,13 @@ export declare function parseSm2PublicKeyPem(pem: string): Uint8Array;
|
|
|
50
62
|
* @param pub 可选公钥(64 或 65 字节);给出则内嵌到 ECPrivateKey 的 `[1]`。
|
|
51
63
|
*/
|
|
52
64
|
export declare function encodeSm2PrivateKeyPem(d: string | Uint8Array, pub?: string | Uint8Array): string;
|
|
65
|
+
/** 编码 SM2 私钥为 PKCS#8 DER。 */
|
|
66
|
+
export declare function encodeSm2PrivateKeyDer(d: string | Uint8Array, pub?: string | Uint8Array): Uint8Array;
|
|
53
67
|
/**
|
|
54
68
|
* 编码 SM2 公钥为 SPKI PEM(`BEGIN PUBLIC KEY`)。
|
|
55
69
|
*
|
|
56
70
|
* @param pub 64(X || Y)或 65(04 || X || Y)字节公钥。
|
|
57
71
|
*/
|
|
58
72
|
export declare function encodeSm2PublicKeyPem(pub: string | Uint8Array): string;
|
|
73
|
+
/** 编码 SM2 公钥为 SPKI DER。 */
|
|
74
|
+
export declare function encodeSm2PublicKeyDer(pub: string | Uint8Array): Uint8Array;
|
package/dist/pem/sm2.js
CHANGED
|
@@ -47,7 +47,14 @@ const CLS_CONTEXT = 2;
|
|
|
47
47
|
* @throws InvalidKeyError PEM/DER 结构非法、或曲线不是 SM2。
|
|
48
48
|
*/
|
|
49
49
|
export function parseSm2PrivateKeyPem(pem) {
|
|
50
|
-
|
|
50
|
+
return parseSm2PrivateKeyDer(pemToBytes(pem, 'sm2.privateKey'));
|
|
51
|
+
}
|
|
52
|
+
/**
|
|
53
|
+
* 解析 SM2 私钥 DER(PKCS#8 或 SEC1),返回 32 字节私钥 d。
|
|
54
|
+
*
|
|
55
|
+
* WebCrypto 的 `importKey('pkcs8', ...)` 使用这个入口。
|
|
56
|
+
*/
|
|
57
|
+
export function parseSm2PrivateKeyDer(der) {
|
|
51
58
|
const top = parseOrThrow(der, 'parseSm2PrivateKeyPem');
|
|
52
59
|
const kids = top.children;
|
|
53
60
|
if (top.tag !== TAG_SEQUENCE || !kids) {
|
|
@@ -92,7 +99,14 @@ export function parseSm2PrivateKeyPem(pem) {
|
|
|
92
99
|
* @throws InvalidKeyError PEM/DER 结构非法、曲线不是 SM2、或点不是未压缩形式。
|
|
93
100
|
*/
|
|
94
101
|
export function parseSm2PublicKeyPem(pem) {
|
|
95
|
-
|
|
102
|
+
return parseSm2PublicKeyDer(pemToBytes(pem, 'sm2.publicKey'));
|
|
103
|
+
}
|
|
104
|
+
/**
|
|
105
|
+
* 解析 SM2 公钥 DER(SPKI),返回 65 字节未压缩点。
|
|
106
|
+
*
|
|
107
|
+
* WebCrypto 的 `importKey('spki', ...)` 使用这个入口。
|
|
108
|
+
*/
|
|
109
|
+
export function parseSm2PublicKeyDer(der) {
|
|
96
110
|
const top = parseOrThrow(der, 'parseSm2PublicKeyPem');
|
|
97
111
|
const kids = top.children;
|
|
98
112
|
if (top.tag !== TAG_SEQUENCE || !kids || kids.length < 2) {
|
|
@@ -118,6 +132,10 @@ export function parseSm2PublicKeyPem(pem) {
|
|
|
118
132
|
* @param pub 可选公钥(64 或 65 字节);给出则内嵌到 ECPrivateKey 的 `[1]`。
|
|
119
133
|
*/
|
|
120
134
|
export function encodeSm2PrivateKeyPem(d, pub) {
|
|
135
|
+
return bytesToPem(encodeSm2PrivateKeyDer(d, pub), 'PRIVATE KEY');
|
|
136
|
+
}
|
|
137
|
+
/** 编码 SM2 私钥为 PKCS#8 DER。 */
|
|
138
|
+
export function encodeSm2PrivateKeyDer(d, pub) {
|
|
121
139
|
const scalar = normalizeScalar(d);
|
|
122
140
|
const ecChildren = [
|
|
123
141
|
asn1Int(1),
|
|
@@ -136,7 +154,7 @@ export function encodeSm2PrivateKeyPem(d, pub) {
|
|
|
136
154
|
asn1Seq([asn1Oid(OID_ID_EC_PUBLIC_KEY), asn1Oid(OID_SM2_CURVE)]),
|
|
137
155
|
asn1Octets(ec.raw),
|
|
138
156
|
]);
|
|
139
|
-
return
|
|
157
|
+
return pkcs8.raw;
|
|
140
158
|
}
|
|
141
159
|
/**
|
|
142
160
|
* 编码 SM2 公钥为 SPKI PEM(`BEGIN PUBLIC KEY`)。
|
|
@@ -144,6 +162,10 @@ export function encodeSm2PrivateKeyPem(d, pub) {
|
|
|
144
162
|
* @param pub 64(X || Y)或 65(04 || X || Y)字节公钥。
|
|
145
163
|
*/
|
|
146
164
|
export function encodeSm2PublicKeyPem(pub) {
|
|
165
|
+
return bytesToPem(encodeSm2PublicKeyDer(pub), 'PUBLIC KEY');
|
|
166
|
+
}
|
|
167
|
+
/** 编码 SM2 公钥为 SPKI DER。 */
|
|
168
|
+
export function encodeSm2PublicKeyDer(pub) {
|
|
147
169
|
const point = normalizeUncompressed(pub);
|
|
148
170
|
const bits = new Uint8Array(point.length + 1);
|
|
149
171
|
bits.set(point, 1);
|
|
@@ -151,7 +173,7 @@ export function encodeSm2PublicKeyPem(pub) {
|
|
|
151
173
|
asn1Seq([asn1Oid(OID_ID_EC_PUBLIC_KEY), asn1Oid(OID_SM2_CURVE)]),
|
|
152
174
|
bitStringNode(bits),
|
|
153
175
|
]);
|
|
154
|
-
return
|
|
176
|
+
return spki.raw;
|
|
155
177
|
}
|
|
156
178
|
// ---------- 内部 ----------
|
|
157
179
|
function parseOrThrow(der, where) {
|
|
@@ -229,7 +251,15 @@ function normalizeUncompressed(pub) {
|
|
|
229
251
|
/** 构造 BIT STRING 节点(value 需自带首字节「未使用位数」)。 */
|
|
230
252
|
function bitStringNode(value) {
|
|
231
253
|
const raw = concat([new Uint8Array([TAG_BIT_STRING]), encodeLen(value.length), value]);
|
|
232
|
-
return {
|
|
254
|
+
return {
|
|
255
|
+
cls: 0,
|
|
256
|
+
tag: TAG_BIT_STRING,
|
|
257
|
+
constructed: false,
|
|
258
|
+
indefinite: false,
|
|
259
|
+
length: value.length,
|
|
260
|
+
value,
|
|
261
|
+
raw,
|
|
262
|
+
};
|
|
233
263
|
}
|
|
234
264
|
/** 构造 [n] EXPLICIT 包装节点。 */
|
|
235
265
|
function explicit(tagNum, inner) {
|
|
@@ -237,6 +267,8 @@ function explicit(tagNum, inner) {
|
|
|
237
267
|
return {
|
|
238
268
|
cls: CLS_CONTEXT,
|
|
239
269
|
tag: tagNum,
|
|
270
|
+
constructed: true,
|
|
271
|
+
indefinite: false,
|
|
240
272
|
length: inner.raw.length,
|
|
241
273
|
value: inner.raw,
|
|
242
274
|
children: [inner],
|
package/dist/sm2/index.d.ts
CHANGED
|
@@ -120,12 +120,81 @@ export declare function createSignaturePointPool(n: number): Promise<SignaturePo
|
|
|
120
120
|
* @returns 64 字节签名(r || s)的 hex。
|
|
121
121
|
*/
|
|
122
122
|
export declare function sign(privateKey: string | Uint8Array, message: string | Uint8Array, opts?: SignOptions): string;
|
|
123
|
+
/**
|
|
124
|
+
* SM2 签名(自定义用户标识)。
|
|
125
|
+
*
|
|
126
|
+
* 与 `sign` 的区别仅在于 Z 值使用 `userID` 计算。X.509 签发需要它:
|
|
127
|
+
* OpenSSL 的 X.509 路径默认使用空标识,GM/T 证书使用 `1234567812345678`。
|
|
128
|
+
*
|
|
129
|
+
* 不支持 `pointPool`(签发场景不需要;需要时用 `sign` 的默认标识路径)。
|
|
130
|
+
*
|
|
131
|
+
* @param privateKey 32 字节私钥(hex / Uint8Array / PEM)。
|
|
132
|
+
* @param message 待签名消息。
|
|
133
|
+
* @param userID 用户标识(可为空数组)。
|
|
134
|
+
* @returns 64 字节签名 `r ‖ s` 的 hex。
|
|
135
|
+
*/
|
|
136
|
+
export declare function signWithID(privateKey: string | Uint8Array, message: string | Uint8Array, userID: Uint8Array): string;
|
|
123
137
|
/**
|
|
124
138
|
* SM2 验签。
|
|
125
139
|
*
|
|
126
140
|
* @returns true 有效。
|
|
127
141
|
*/
|
|
128
142
|
export declare function verify(publicKey: string | Uint8Array, message: string | Uint8Array, signature: string | Uint8Array): boolean;
|
|
143
|
+
/**
|
|
144
|
+
* SM2 验签(自定义用户标识)。
|
|
145
|
+
*
|
|
146
|
+
* 与 `verify` 的区别仅在于 Z 值使用 `userID` 计算。X.509 证书签名需要它:
|
|
147
|
+
* GM/T 证书使用默认标识 `1234567812345678`,而 OpenSSL 的 X.509 路径
|
|
148
|
+
* 默认使用空标识。
|
|
149
|
+
*
|
|
150
|
+
* @param publicKey 64/65 字节公钥。
|
|
151
|
+
* @param message 消息。
|
|
152
|
+
* @param signature 64 字节 `r ‖ s`。
|
|
153
|
+
* @param userID 用户标识(可为空数组)。
|
|
154
|
+
* @returns true 有效。
|
|
155
|
+
*/
|
|
156
|
+
export declare function verifyWithID(publicKey: string | Uint8Array, message: string | Uint8Array, signature: string | Uint8Array, userID: Uint8Array): boolean;
|
|
157
|
+
/** 多槽公钥缓存的配置。 */
|
|
158
|
+
export interface PreparedKeyCacheOptions {
|
|
159
|
+
/** 槽位上限,1..8,默认 8。 */
|
|
160
|
+
capacity?: number;
|
|
161
|
+
}
|
|
162
|
+
/**
|
|
163
|
+
* 配置多槽公钥缓存(默认 8 槽,满了按 LRU 淘汰)。
|
|
164
|
+
*
|
|
165
|
+
* 缩小容量不会立即清除高槽位,只是之后的查找与 `getPreparedPublicKeys` 不再考虑它们;
|
|
166
|
+
* 需要释放时调用 `clearPreparedPublicKeys()`。wasm 未加载时只记录,首次
|
|
167
|
+
* `preparePublicKey` 会应用。
|
|
168
|
+
*/
|
|
169
|
+
export declare function configurePreparedKeyCache(opts?: PreparedKeyCacheOptions): void;
|
|
170
|
+
/**
|
|
171
|
+
* 为一对**固定公钥**预计算 comb 表(默认 8 槽,满了 LRU 淘汰)。
|
|
172
|
+
*
|
|
173
|
+
* 之后 `encrypt` / `kemEncrypt` / `hybridEncrypt` 对该公钥会把 `k·P` 从 wNAF
|
|
174
|
+
* (256 次倍点)切到 comb(52 次倍点):本机端到端约 486 → 260 µs。建表一次性
|
|
175
|
+
* 约 280 µs,**约 1.2 次加密回本**。
|
|
176
|
+
*
|
|
177
|
+
* **只有显式 prepare 才会建表**:`encrypt` 命中就走 comb,不命中直接回退 wNAF,
|
|
178
|
+
* 绝不隐式建表 —— 否则外部可控的公钥能反复触发建表,形成放大面。
|
|
179
|
+
*
|
|
180
|
+
* 公钥是公开信息,缓存它不引入新的密钥材料驻留;8 槽共占 wasm 永久区固定 24 KiB。
|
|
181
|
+
*
|
|
182
|
+
* @param publicKey 64/65 字节公钥(hex / Uint8Array / PEM)。
|
|
183
|
+
* @throws {InvalidPublicKeyError} 公钥不在 SM2 曲线上。
|
|
184
|
+
*/
|
|
185
|
+
export declare function preparePublicKey(publicKey: string | Uint8Array): Promise<void>;
|
|
186
|
+
/** 清空所有公钥预计算槽,之后的 `encrypt` 全部回退到 wNAF。未加载 wasm 时也安全。 */
|
|
187
|
+
export declare function clearPreparedPublicKeys(): void;
|
|
188
|
+
/** @deprecated 用 `clearPreparedPublicKeys()`(保留单数别名)。 */
|
|
189
|
+
export declare function clearPreparedPublicKey(): void;
|
|
190
|
+
/**
|
|
191
|
+
* 当前槽位中的公钥(hex,按槽序)。
|
|
192
|
+
*
|
|
193
|
+
* wasm 未加载时返回空数组。
|
|
194
|
+
*/
|
|
195
|
+
export declare function getPreparedPublicKeys(): string[];
|
|
196
|
+
/** 最后一次 `preparePublicKey` 的公钥(hex);没有则为 `null`。 */
|
|
197
|
+
export declare function getPreparedPublicKey(): string | null;
|
|
129
198
|
/**
|
|
130
199
|
* SM2 加密。
|
|
131
200
|
*
|