Skip to content

Commit b15dfb5

Browse files
author
Neo
committed
fix decryptLong with Chinese and optimize encryptLong
1 parent 4da237e commit b15dfb5

File tree

8 files changed

+213
-67
lines changed

8 files changed

+213
-67
lines changed

README.md

+7-5
Original file line numberDiff line numberDiff line change
@@ -13,7 +13,7 @@ npm i wxmp-rsa -S
1313
```
1414

1515
### 3、使用方式
16-
小程序使用之前需先使用开发者工具构建npm
16+
小程序使用之前需先使用开发者工具[构建npm](https://developers.weixin.qq.com/miniprogram/dev/devtools/npm.html)
1717
```js
1818
// 导入包
1919
import WxmpRsa from 'wxmp-rsa'
@@ -62,14 +62,16 @@ rsa.setPrivateKey(privateKey)
6262
const originalStr = rsa.decryptLong(cryptStr)
6363
console.log('解密后的原始数据:', originalStr)
6464
```
65-
其他api参考jsencrypt插件:https://github.com/travist/jsencrypt
65+
其他api参考[jsencrypt](https://github.com/travist/jsencrypt)插件
6666

6767
### 4、注意事项
6868
+ 填空方式默认`pkcs1`,目前暂不支持其它填空方式。
69-
+ 之前版本中偶现的加密异常的问题已修复。
70-
+ v2.0.1版本的解密存在问题:使用本插件解密中英文混杂的数据时可能出现部分中文乱码的情况,尚待修复。
7169

72-
### 5、测试
70+
### 5、更新提示
71+
+ 之前偶现的加密异常的问题已于v2.0.0+版本中修复。
72+
+ 之前偶现的解密后部分中文乱码的问题已于v2.1.0+版本中修复。
73+
74+
### 6、测试对比
7375
推荐两个第三方rsa工具,仅供参考。
7476
+ 在线生成rsa公私钥:http://travistidwell.com/jsencrypt/demo/ (推荐1024长度的密钥)
7577
+ 在线rsa加解密:http://www.toolzl.com/tools/testrsa.html (117超长加密,128超长解密)

dist/JSEncrypt.js

+7-29
Original file line numberDiff line numberDiff line change
@@ -86,44 +86,22 @@ var JSEncrypt = /** @class */ (function () {
8686
}
8787
};
8888
// 超长文本加密
89-
JSEncrypt.prototype.encryptLong = function (text, count) {
90-
var _this = this;
91-
count = count || 0;
92-
var res = '';
93-
var maxLen = ((this.getKey().n.bitLength() + 7) >> 3) - 11;
94-
if (text.length > maxLen) {
95-
var textArr = text.match(new RegExp('.{1,' + Math.floor(maxLen / 3) + '}', 'g'));
96-
textArr.forEach(function (v) {
97-
res += _this.getKey().encrypt(v);
98-
});
99-
}
100-
else {
101-
res = this.getKey().encrypt(text);
102-
}
89+
JSEncrypt.prototype.encryptLong = function (str) {
10390
try {
104-
res = hex2b64(res);
91+
return hex2b64(this.getKey().encryptLong(str));
10592
}
10693
catch (ex) {
10794
return false;
10895
}
109-
return res;
11096
};
11197
// 超长文本解密
112-
JSEncrypt.prototype.decryptLong = function (text) {
113-
var _this = this;
114-
var res = '';
115-
var maxLen = (this.getKey().n.bitLength() + 7) >> 3;
116-
var hexText = b64tohex(text);
117-
if (hexText.length > maxLen) {
118-
var hexTextArr = hexText.match(new RegExp('.{1,' + maxLen * 2 + '}', 'g'));
119-
hexTextArr.forEach(function (v) {
120-
res += _this.getKey().decrypt(v);
121-
});
98+
JSEncrypt.prototype.decryptLong = function (str) {
99+
try {
100+
return this.getKey().decryptLong(b64tohex(str));
122101
}
123-
else {
124-
res = this.getKey().decrypt(hexText);
102+
catch (ex) {
103+
return false;
125104
}
126-
return res;
127105
};
128106
/**
129107
* Proxy method for RSAKey object's sign.

dist/lib/jsbn/rsa.js

+98
Original file line numberDiff line numberDiff line change
@@ -302,6 +302,65 @@ var RSAKey = /** @class */ (function () {
302302
var digest = removeDigestHeader(unpadded);
303303
return digest == digestMethod(text).toString();
304304
};
305+
RSAKey.prototype.encryptLong = function (text) {
306+
var _this = this;
307+
var res = '';
308+
var maxLen = ((this.n.bitLength() + 7) >> 3) - 11;
309+
var textArr = this.setSplitChn(text, maxLen);
310+
textArr.forEach(function (v) {
311+
res += _this.encrypt(v);
312+
});
313+
return res;
314+
};
315+
RSAKey.prototype.decryptLong = function (ctext) {
316+
var res = '';
317+
var maxLen = (this.n.bitLength() + 7) >> 3;
318+
var splitMaxLen = maxLen * 2;
319+
if (ctext.length > splitMaxLen) {
320+
var ctextArr = ctext.match(new RegExp('.{1,' + splitMaxLen + '}', 'g')) || [];
321+
var mArr = [];
322+
for (var i = 0; i < ctextArr.length; i++) {
323+
var c = parseBigInt(ctextArr[i], 16);
324+
var m = this.doPrivate(c);
325+
if (m == null) {
326+
return null;
327+
}
328+
mArr.push(m);
329+
}
330+
res = pkcs1unpad2Long(mArr, maxLen);
331+
}
332+
else {
333+
res = this.decrypt(ctext);
334+
}
335+
return res;
336+
};
337+
RSAKey.prototype.setSplitChn = function (str, maxLen, res) {
338+
if (res === void 0) { res = []; }
339+
var arr = str.split('');
340+
var len = 0;
341+
for (var i = 0; i < arr.length; i++) {
342+
var charCode = arr[i].charCodeAt(0);
343+
if (charCode <= 0x007f) {
344+
len += 1;
345+
}
346+
else if (charCode <= 0x07ff) {
347+
len += 2;
348+
}
349+
else if (charCode <= 0xffff) {
350+
len += 3;
351+
}
352+
else {
353+
len += 4;
354+
}
355+
if (len > maxLen) {
356+
var currentStr = str.substring(0, i);
357+
res.push(currentStr);
358+
return this.setSplitChn(str.substring(i), maxLen, res);
359+
}
360+
}
361+
res.push(str);
362+
return res;
363+
};
305364
return RSAKey;
306365
}());
307366
export { RSAKey };
@@ -338,6 +397,45 @@ function pkcs1unpad2(d, n) {
338397
}
339398
return ret;
340399
}
400+
function pkcs1unpad2Long(dArr, n) {
401+
var bArr = [];
402+
for (var j = 0; j < dArr.length; j++) {
403+
var d = dArr[j];
404+
var b_1 = d.toByteArray();
405+
var i_1 = 0;
406+
while (i_1 < b_1.length && b_1[i_1] == 0) {
407+
++i_1;
408+
}
409+
if (b_1.length - i_1 != n - 1 || b_1[i_1] != 2) {
410+
return null;
411+
}
412+
++i_1;
413+
while (b_1[i_1] != 0) {
414+
if (++i_1 >= b_1.length) {
415+
return null;
416+
}
417+
}
418+
bArr = bArr.concat(b_1.slice(i_1 + 1));
419+
}
420+
var b = bArr;
421+
var i = -1;
422+
var ret = "";
423+
while (++i < b.length) {
424+
var c = b[i] & 255;
425+
if (c < 128) { // utf-8 decode
426+
ret += String.fromCharCode(c);
427+
}
428+
else if ((c > 191) && (c < 224)) {
429+
ret += String.fromCharCode(((c & 31) << 6) | (b[i + 1] & 63));
430+
++i;
431+
}
432+
else {
433+
ret += String.fromCharCode(((c & 15) << 12) | ((b[i + 1] & 63) << 6) | (b[i + 2] & 63));
434+
i += 2;
435+
}
436+
}
437+
return ret;
438+
}
341439
// https://tools.ietf.org/html/rfc3447#page-43
342440
var DIGEST_HEADERS = {
343441
md2: "3020300c06082a864886f70d020205000410",

dist/version.json

+1-1
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,3 @@
11
{
2-
"version": ""
2+
"version": "3.2.1"
33
}

package-lock.json

+1-1
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

package.json

+1-1
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
{
22
"name": "wxmp-rsa",
3-
"version": "2.0.1",
3+
"version": "2.1.0",
44
"description": "兼容小程序环境的rsa加解密库,支持超长文本和中文字符",
55
"author": "neohan",
66
"main": "dist/index.js",

src/JSEncrypt.ts

+10-28
Original file line numberDiff line numberDiff line change
@@ -100,42 +100,24 @@ export class JSEncrypt {
100100
return false;
101101
}
102102
}
103+
103104
// 超长文本加密
104-
public encryptLong (text: string, count?: number) {
105-
count = count || 0
106-
let res: string = ''
107-
const maxLen = ((this.getKey().n.bitLength() + 7) >> 3) - 11
108-
const splitMax = Math.floor(maxLen / 3)
109-
if (text.length > splitMax) {
110-
const textArr = text.match(new RegExp('.{1,' + splitMax + '}', 'g'))
111-
textArr.forEach(v => {
112-
res += this.getKey().encrypt(v)
113-
})
114-
} else {
115-
res = this.getKey().encrypt(text)
116-
}
105+
public encryptLong (str: string) {
117106
try {
118-
res = hex2b64(res)
107+
return hex2b64(this.getKey().encryptLong(str));
119108
} catch (ex) {
120-
return false
109+
return false;
121110
}
122-
return res
123111
}
124112
// 超长文本解密
125-
public decryptLong (text: string) {
126-
let res: string = ''
127-
const maxLen = (this.getKey().n.bitLength() + 7) >> 3
128-
const hexText = b64tohex(text)
129-
if (hexText.length > maxLen) {
130-
const hexTextArr = hexText.match(new RegExp('.{1,' + maxLen * 2 + '}', 'g'))
131-
hexTextArr.forEach(v => {
132-
res += this.getKey().decrypt(v)
133-
})
134-
} else {
135-
res = this.getKey().decrypt(hexText)
113+
public decryptLong (str: string) {
114+
try {
115+
return this.getKey().decryptLong(b64tohex(str));
116+
} catch (ex) {
117+
return false;
136118
}
137-
return res
138119
}
120+
139121
/**
140122
* Proxy method for RSAKey object's sign.
141123
* @param {string} str the string to sign

src/lib/jsbn/rsa.ts

+88-2
Original file line numberDiff line numberDiff line change
@@ -322,11 +322,63 @@ export class RSAKey {
322322
return digest == digestMethod(text).toString();
323323
}
324324

325+
public encryptLong (text: string) {
326+
let res = ''
327+
const maxLen = ((this.n.bitLength() + 7) >> 3) - 11
328+
const textArr = this.setSplitChn(text, maxLen)
329+
textArr.forEach(v => {
330+
res += this.encrypt(v)
331+
})
332+
return res
333+
}
334+
public decryptLong (ctext: string) {
335+
let res = ''
336+
const maxLen = (this.n.bitLength() + 7) >> 3
337+
const splitMaxLen = maxLen * 2
338+
if (ctext.length > splitMaxLen) {
339+
const ctextArr = ctext.match(new RegExp('.{1,' + splitMaxLen + '}', 'g')) || []
340+
const mArr = []
341+
for (let i = 0; i < ctextArr.length; i++) {
342+
const c = parseBigInt(ctextArr[i], 16);
343+
const m = this.doPrivate(c);
344+
if (m == null) { return null; }
345+
mArr.push(m)
346+
}
347+
res = pkcs1unpad2Long(mArr, maxLen);
348+
} else {
349+
res = this.decrypt(ctext)
350+
}
351+
return res
352+
}
353+
private setSplitChn (str: string, maxLen: number, res: string[] = []): string[] {
354+
const arr = str.split('')
355+
let len = 0
356+
for (let i = 0; i < arr.length; i++) {
357+
const charCode = arr[i].charCodeAt(0)
358+
if (charCode <= 0x007f) {
359+
len += 1
360+
} else if(charCode <= 0x07ff){
361+
len += 2
362+
} else if(charCode <= 0xffff){
363+
len += 3
364+
} else{
365+
len += 4
366+
}
367+
if (len > maxLen) {
368+
const currentStr = str.substring(0, i)
369+
res.push(currentStr)
370+
return this.setSplitChn(str.substring(i), maxLen, res)
371+
}
372+
}
373+
res.push(str)
374+
return res
375+
}
376+
325377
//#endregion PUBLIC
326378

327-
public n:BigInteger;
379+
protected n:BigInteger;
328380
protected e:number;
329-
public d:BigInteger;
381+
protected d:BigInteger;
330382
protected p:BigInteger;
331383
protected q:BigInteger;
332384
protected dmp1:BigInteger;
@@ -364,6 +416,40 @@ function pkcs1unpad2(d:BigInteger, n:number):string {
364416
return ret;
365417
}
366418

419+
function pkcs1unpad2Long (dArr: BigInteger[], n: number): string {
420+
let bArr: number[] = []
421+
for (let j = 0; j < dArr.length; j++) {
422+
const d = dArr[j]
423+
const b = d.toByteArray();
424+
let i = 0;
425+
while (i < b.length && b[i] == 0) { ++i; }
426+
if (b.length - i != n - 1 || b[i] != 2) {
427+
return null;
428+
}
429+
++i;
430+
while (b[i] != 0) {
431+
if (++i >= b.length) { return null; }
432+
}
433+
bArr = bArr.concat(b.slice(i + 1))
434+
}
435+
const b = bArr
436+
let i = -1
437+
let ret = "";
438+
while (++i < b.length) {
439+
const c = b[i] & 255;
440+
if (c < 128) { // utf-8 decode
441+
ret += String.fromCharCode(c);
442+
} else if ((c > 191) && (c < 224)) {
443+
ret += String.fromCharCode(((c & 31) << 6) | (b[i + 1] & 63));
444+
++i;
445+
} else {
446+
ret += String.fromCharCode(((c & 15) << 12) | ((b[i + 1] & 63) << 6) | (b[i + 2] & 63));
447+
i += 2;
448+
}
449+
}
450+
return ret;
451+
}
452+
367453
// https://tools.ietf.org/html/rfc3447#page-43
368454
const DIGEST_HEADERS:{ [name:string]:string } = {
369455
md2: "3020300c06082a864886f70d020205000410",

0 commit comments

Comments
 (0)