-
Notifications
You must be signed in to change notification settings - Fork 283
Expand file tree
/
Copy pathdrpyRsa.js
More file actions
304 lines (268 loc) · 10.8 KB
/
drpyRsa.js
File metadata and controls
304 lines (268 loc) · 10.8 KB
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
import crypto from 'crypto';
export const RSA = {
// 清理 PEM 格式,提取 base64 内容
cleanPEM: function (pem) {
return pem
.replace(/-----BEGIN[^-]+-----/g, '')
.replace(/-----END[^-]+-----/g, '')
.replace(/\s+/g, '');
},
// 格式化私钥为标准 PEM 格式
formatPrivateKey: function (pem) {
if (pem.includes('-----BEGIN')) {
return pem;
}
return `-----BEGIN RSA PRIVATE KEY-----\n${pem}\n-----END RSA PRIVATE KEY-----`;
},
// 格式化公钥为标准 PEM 格式
formatPublicKey: function (pem) {
if (pem.includes('-----BEGIN')) {
return pem;
}
return `-----BEGIN PUBLIC KEY-----\n${pem}\n-----END PUBLIC KEY-----`;
},
// 从私钥提取公钥
extractPublicKeyFromPrivate: function (privateKeyPem) {
const privateKey = this.formatPrivateKey(privateKeyPem);
try {
// 使用Node.js crypto模块从私钥创建公钥
const keyObject = crypto.createPrivateKey({
key: privateKey,
format: 'pem',
type: 'pkcs1'
});
const publicKey = crypto.createPublicKey(keyObject);
return publicKey.export({type: 'spki', format: 'pem'});
} catch (error) {
console.error("PKCS1格式失败:", error.message);
// 如果PKCS1格式失败,尝试PKCS8格式
try {
const keyObject = crypto.createPrivateKey({
key: privateKey,
format: 'pem',
type: 'pkcs8'
});
const publicKey = crypto.createPublicKey(keyObject);
return publicKey.export({type: 'spki', format: 'pem'});
} catch (error2) {
console.error("PKCS8格式也失败:", error2.message);
throw error;
}
}
},
// 手动移除 PKCS1 填充
removePKCS1Padding: function (buffer) {
// PKCS1 填充格式: 0x00 0x02 [随机字节] 0x00 [数据]
if (buffer[0] !== 0x00 || buffer[1] !== 0x02) {
throw new Error('Invalid PKCS1 padding');
}
// 找到第二个 0x00 字节,它标志着填充的结束
let dataStart = -1;
for (let i = 2; i < buffer.length; i++) {
if (buffer[i] === 0x00) {
dataStart = i + 1;
break;
}
}
if (dataStart === -1) {
throw new Error('Invalid PKCS1 padding: no data separator found');
}
return buffer.slice(dataStart);
},
// 分段解密长数据
decryptLongData: function (privateKey, encryptedData) {
const keySize = 256; // 2048 bits / 8 = 256 bytes
const chunks = [];
// 分段解密
for (let i = 0; i < encryptedData.length; i += keySize) {
const segment = encryptedData.slice(i, i + keySize);
try {
// 尝试不同的填充方式
let decryptedSegment;
try {
// 首先尝试 PKCS1 填充(与JSEncrypt兼容)
decryptedSegment = crypto.privateDecrypt({
key: privateKey,
padding: crypto.constants.RSA_PKCS1_PADDING
}, segment);
} catch (pkcs1Error) {
try {
// 如果 PKCS1 失败,尝试 OAEP(默认)
decryptedSegment = crypto.privateDecrypt(privateKey, segment);
} catch (oaepError) {
try {
// 如果 OAEP 失败,尝试 NO_PADDING
decryptedSegment = crypto.privateDecrypt({
key: privateKey,
padding: crypto.constants.RSA_NO_PADDING
}, segment);
// 手动移除 PKCS1 填充
decryptedSegment = this.removePKCS1Padding(decryptedSegment);
} catch (noPaddingError) {
console.error('PKCS1 错误:', pkcs1Error.message);
console.error('OAEP 错误:', oaepError.message);
console.error('NO_PADDING 错误:', noPaddingError.message);
throw pkcs1Error;
}
}
}
chunks.push(decryptedSegment);
} catch (error) {
console.error(`解密第 ${Math.floor(i / keySize) + 1} 段失败:`, error.message);
throw error;
}
}
// 合并所有解密段
return Buffer.concat(chunks);
},
// 分段加密长数据
encryptLongData: function (publicKey, data) {
// 动态获取密钥大小
let keySize;
try {
const keyObj = crypto.createPublicKey(publicKey);
const keyDetails = keyObj.asymmetricKeyDetails;
keySize = (keyDetails.modulusLength || 2048) / 8; // 默认2048位
} catch (error) {
console.warn('无法获取密钥大小,使用默认值2048位:', error.message);
keySize = 256; // 默认2048位 = 256字节
}
const maxLength = keySize - 11; // 使用JSEncrypt的标准PKCS1填充开销
const chunks = [];
try {
// 将Buffer转换为字符串进行Unicode边界分块
const string = data.toString('utf8');
let subStart = 0;
let subEnd = 0;
let bitLen = 0;
let tmpPoint = 0;
for (let i = 0, len = string.length; i < len; i++) {
const charCode = string.charCodeAt(i);
// 根据Unicode字符计算UTF-8字节数(与JSEncrypt相同的逻辑)
if (charCode <= 127) {
bitLen += 1;
} else if (charCode <= 2047) {
bitLen += 2;
} else if (charCode <= 65535) {
bitLen += 3;
} else {
bitLen += 4;
}
if (bitLen > maxLength) {
// 到达分块边界,加密当前段
const subStr = string.substring(subStart, subEnd);
const segment = Buffer.from(subStr, 'utf8');
try {
const encryptedSegment = crypto.publicEncrypt({
key: publicKey,
padding: crypto.constants.RSA_PKCS1_PADDING
}, segment);
chunks.push(encryptedSegment);
} catch (error) {
console.error(`加密第 ${chunks.length + 1} 段失败:`, error.message);
throw error;
}
// 重置为下一段
subStart = subEnd;
bitLen = bitLen - tmpPoint;
} else {
subEnd = i;
tmpPoint = bitLen;
}
}
// 加密最后一段
if (subStart < string.length) {
const subStr = string.substring(subStart, string.length);
const segment = Buffer.from(subStr, 'utf8');
try {
const encryptedSegment = crypto.publicEncrypt({
key: publicKey,
padding: crypto.constants.RSA_PKCS1_PADDING
}, segment);
chunks.push(encryptedSegment);
} catch (error) {
console.error(`加密最后一段失败:`, error.message);
throw error;
}
}
return Buffer.concat(chunks);
} catch (error) {
console.error('长数据加密失败:', error.message);
throw error;
}
},
// 解密方法
decode: function (data, privateKeyPem) {
try {
const encryptedBuffer = Buffer.from(data, 'base64');
const privateKey = this.formatPrivateKey(privateKeyPem);
console.time("RSA解密");
const decryptedData = this.decryptLongData(privateKey, encryptedBuffer);
console.timeEnd("RSA解密");
return decryptedData.toString('utf8');
} catch (error) {
console.error("解密过程中发生错误:", error);
throw error;
}
},
// 加密方法
encode: function (plainText, keyPem) {
try {
const dataBuffer = Buffer.from(plainText, 'utf8');
let publicKey;
// 判断输入的是私钥还是公钥
// 检查是否包含PRIVATE KEY标识,或者是否为base64格式的私钥
const isPrivateKey = keyPem.includes('PRIVATE KEY') || this.isBase64PrivateKey(keyPem);
if (isPrivateKey) {
// 如果是私钥,提取公钥
publicKey = this.extractPublicKeyFromPrivate(keyPem);
} else {
// 如果是公钥,直接使用
publicKey = this.formatPublicKey(keyPem);
}
console.time("RSA加密");
const encryptedData = this.encryptLongData(publicKey, dataBuffer);
console.timeEnd("RSA加密");
return encryptedData.toString('base64');
} catch (error) {
console.error("加密过程中发生错误:", error);
throw error;
}
},
// 检测是否为base64格式的私钥
isBase64PrivateKey: function (keyString) {
// 如果已经包含PEM头部,则不是纯base64
if (keyString.includes('-----')) {
return false;
}
// 检查是否为有效的base64字符串
const base64Regex = /^[A-Za-z0-9+/]*={0,2}$/;
if (!base64Regex.test(keyString)) {
return false;
}
// 尝试解码并检查是否为RSA私钥格式
try {
const formattedKey = this.formatPrivateKey(keyString);
// 尝试创建私钥对象来验证
const keyObject = crypto.createPrivateKey({
key: formattedKey,
format: 'pem',
type: 'pkcs1'
});
return true;
} catch (error) {
// 如果PKCS1失败,尝试PKCS8
try {
const formattedKey = this.formatPrivateKey(keyString);
const keyObject = crypto.createPrivateKey({
key: formattedKey,
format: 'pem',
type: 'pkcs8'
});
return true;
} catch (error2) {
return false;
}
}
}
};