人民医院前端

rsa.js 12KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374
  1. // Depends on jsbn.js and rng.js
  2. // Version 1.1: support utf-8 encoding in pkcs1pad2
  3. // convert a (hex) string to a bignum object
  4. import { BigInteger, nbi, parseBigInt } from "./jsbn";
  5. import { SecureRandom } from "./rng";
  6. // function linebrk(s,n) {
  7. // var ret = "";
  8. // var i = 0;
  9. // while(i + n < s.length) {
  10. // ret += s.substring(i,i+n) + "\n";
  11. // i += n;
  12. // }
  13. // return ret + s.substring(i,s.length);
  14. // }
  15. // function byte2Hex(b) {
  16. // if(b < 0x10)
  17. // return "0" + b.toString(16);
  18. // else
  19. // return b.toString(16);
  20. // }
  21. function pkcs1pad1(s, n) {
  22. if (n < s.length + 22) {
  23. console.error("Message too long for RSA");
  24. return null;
  25. }
  26. var len = n - s.length - 6;
  27. var filler = "";
  28. for (var f = 0; f < len; f += 2) {
  29. filler += "ff";
  30. }
  31. var m = "0001" + filler + "00" + s;
  32. return parseBigInt(m, 16);
  33. }
  34. // PKCS#1 (type 2, random) pad input string s to n bytes, and return a bigint
  35. function pkcs1pad2(s, n) {
  36. if (n < s.length + 11) { // TODO: fix for utf-8
  37. console.error("Message too long for RSA");
  38. return null;
  39. }
  40. var ba = [];
  41. var i = s.length - 1;
  42. while (i >= 0 && n > 0) {
  43. var c = s.charCodeAt(i--);
  44. if (c < 128) { // encode using utf-8
  45. ba[--n] = c;
  46. }
  47. else if ((c > 127) && (c < 2048)) {
  48. ba[--n] = (c & 63) | 128;
  49. ba[--n] = (c >> 6) | 192;
  50. }
  51. else {
  52. ba[--n] = (c & 63) | 128;
  53. ba[--n] = ((c >> 6) & 63) | 128;
  54. ba[--n] = (c >> 12) | 224;
  55. }
  56. }
  57. ba[--n] = 0;
  58. var rng = new SecureRandom();
  59. var x = [];
  60. while (n > 2) { // random non-zero pad
  61. x[0] = 0;
  62. while (x[0] == 0) {
  63. rng.nextBytes(x);
  64. }
  65. ba[--n] = x[0];
  66. }
  67. ba[--n] = 2;
  68. ba[--n] = 0;
  69. return new BigInteger(ba);
  70. }
  71. // "empty" RSA key constructor
  72. var RSAKey = /** @class */ (function () {
  73. function RSAKey() {
  74. this.n = null;
  75. this.e = 0;
  76. this.d = null;
  77. this.p = null;
  78. this.q = null;
  79. this.dmp1 = null;
  80. this.dmq1 = null;
  81. this.coeff = null;
  82. }
  83. //#region PROTECTED
  84. // protected
  85. // RSAKey.prototype.doPublic = RSADoPublic;
  86. // Perform raw public operation on "x": return x^e (mod n)
  87. RSAKey.prototype.doPublic = function (x) {
  88. return x.modPowInt(this.e, this.n);
  89. };
  90. // RSAKey.prototype.doPrivate = RSADoPrivate;
  91. // Perform raw private operation on "x": return x^d (mod n)
  92. RSAKey.prototype.doPrivate = function (x) {
  93. if (this.p == null || this.q == null) {
  94. return x.modPow(this.d, this.n);
  95. }
  96. // TODO: re-calculate any missing CRT params
  97. var xp = x.mod(this.p).modPow(this.dmp1, this.p);
  98. var xq = x.mod(this.q).modPow(this.dmq1, this.q);
  99. while (xp.compareTo(xq) < 0) {
  100. xp = xp.add(this.p);
  101. }
  102. return xp.subtract(xq).multiply(this.coeff).mod(this.p).multiply(this.q).add(xq);
  103. };
  104. //#endregion PROTECTED
  105. //#region PUBLIC
  106. // RSAKey.prototype.setPublic = RSASetPublic;
  107. // Set the public key fields N and e from hex strings
  108. RSAKey.prototype.setPublic = function (N, E) {
  109. if (N != null && E != null && N.length > 0 && E.length > 0) {
  110. this.n = parseBigInt(N, 16);
  111. this.e = parseInt(E, 16);
  112. }
  113. else {
  114. console.error("Invalid RSA public key");
  115. }
  116. };
  117. // RSAKey.prototype.encrypt = RSAEncrypt;
  118. // Return the PKCS#1 RSA encryption of "text" as an even-length hex string
  119. RSAKey.prototype.encrypt = function (text) {
  120. var maxLength = (this.n.bitLength() + 7) >> 3;
  121. var m = pkcs1pad2(text, maxLength);
  122. if (m == null) {
  123. return null;
  124. }
  125. var c = this.doPublic(m);
  126. if (c == null) {
  127. return null;
  128. }
  129. var h = c.toString(16);
  130. var length = h.length;
  131. // fix zero before result
  132. for (var i = 0; i < maxLength * 2 - length; i++) {
  133. h = "0" + h;
  134. }
  135. return h;
  136. };
  137. // RSAKey.prototype.setPrivate = RSASetPrivate;
  138. // Set the private key fields N, e, and d from hex strings
  139. RSAKey.prototype.setPrivate = function (N, E, D) {
  140. if (N != null && E != null && N.length > 0 && E.length > 0) {
  141. this.n = parseBigInt(N, 16);
  142. this.e = parseInt(E, 16);
  143. this.d = parseBigInt(D, 16);
  144. }
  145. else {
  146. console.error("Invalid RSA private key");
  147. }
  148. };
  149. // RSAKey.prototype.setPrivateEx = RSASetPrivateEx;
  150. // Set the private key fields N, e, d and CRT params from hex strings
  151. RSAKey.prototype.setPrivateEx = function (N, E, D, P, Q, DP, DQ, C) {
  152. if (N != null && E != null && N.length > 0 && E.length > 0) {
  153. this.n = parseBigInt(N, 16);
  154. this.e = parseInt(E, 16);
  155. this.d = parseBigInt(D, 16);
  156. this.p = parseBigInt(P, 16);
  157. this.q = parseBigInt(Q, 16);
  158. this.dmp1 = parseBigInt(DP, 16);
  159. this.dmq1 = parseBigInt(DQ, 16);
  160. this.coeff = parseBigInt(C, 16);
  161. }
  162. else {
  163. console.error("Invalid RSA private key");
  164. }
  165. };
  166. // RSAKey.prototype.generate = RSAGenerate;
  167. // Generate a new random private key B bits long, using public expt E
  168. RSAKey.prototype.generate = function (B, E) {
  169. var rng = new SecureRandom();
  170. var qs = B >> 1;
  171. this.e = parseInt(E, 16);
  172. var ee = new BigInteger(E, 16);
  173. for (;;) {
  174. for (;;) {
  175. this.p = new BigInteger(B - qs, 1, rng);
  176. if (this.p.subtract(BigInteger.ONE).gcd(ee).compareTo(BigInteger.ONE) == 0 && this.p.isProbablePrime(10)) {
  177. break;
  178. }
  179. }
  180. for (;;) {
  181. this.q = new BigInteger(qs, 1, rng);
  182. if (this.q.subtract(BigInteger.ONE).gcd(ee).compareTo(BigInteger.ONE) == 0 && this.q.isProbablePrime(10)) {
  183. break;
  184. }
  185. }
  186. if (this.p.compareTo(this.q) <= 0) {
  187. var t = this.p;
  188. this.p = this.q;
  189. this.q = t;
  190. }
  191. var p1 = this.p.subtract(BigInteger.ONE);
  192. var q1 = this.q.subtract(BigInteger.ONE);
  193. var phi = p1.multiply(q1);
  194. if (phi.gcd(ee).compareTo(BigInteger.ONE) == 0) {
  195. this.n = this.p.multiply(this.q);
  196. this.d = ee.modInverse(phi);
  197. this.dmp1 = this.d.mod(p1);
  198. this.dmq1 = this.d.mod(q1);
  199. this.coeff = this.q.modInverse(this.p);
  200. break;
  201. }
  202. }
  203. };
  204. // RSAKey.prototype.decrypt = RSADecrypt;
  205. // Return the PKCS#1 RSA decryption of "ctext".
  206. // "ctext" is an even-length hex string and the output is a plain string.
  207. RSAKey.prototype.decrypt = function (ctext) {
  208. var c = parseBigInt(ctext, 16);
  209. var m = this.doPrivate(c);
  210. if (m == null) {
  211. return null;
  212. }
  213. return pkcs1unpad2(m, (this.n.bitLength() + 7) >> 3);
  214. };
  215. // Generate a new random private key B bits long, using public expt E
  216. RSAKey.prototype.generateAsync = function (B, E, callback) {
  217. var rng = new SecureRandom();
  218. var qs = B >> 1;
  219. this.e = parseInt(E, 16);
  220. var ee = new BigInteger(E, 16);
  221. var rsa = this;
  222. // These functions have non-descript names because they were originally for(;;) loops.
  223. // I don't know about cryptography to give them better names than loop1-4.
  224. var loop1 = function () {
  225. var loop4 = function () {
  226. if (rsa.p.compareTo(rsa.q) <= 0) {
  227. var t = rsa.p;
  228. rsa.p = rsa.q;
  229. rsa.q = t;
  230. }
  231. var p1 = rsa.p.subtract(BigInteger.ONE);
  232. var q1 = rsa.q.subtract(BigInteger.ONE);
  233. var phi = p1.multiply(q1);
  234. if (phi.gcd(ee).compareTo(BigInteger.ONE) == 0) {
  235. rsa.n = rsa.p.multiply(rsa.q);
  236. rsa.d = ee.modInverse(phi);
  237. rsa.dmp1 = rsa.d.mod(p1);
  238. rsa.dmq1 = rsa.d.mod(q1);
  239. rsa.coeff = rsa.q.modInverse(rsa.p);
  240. setTimeout(function () { callback(); }, 0); // escape
  241. }
  242. else {
  243. setTimeout(loop1, 0);
  244. }
  245. };
  246. var loop3 = function () {
  247. rsa.q = nbi();
  248. rsa.q.fromNumberAsync(qs, 1, rng, function () {
  249. rsa.q.subtract(BigInteger.ONE).gcda(ee, function (r) {
  250. if (r.compareTo(BigInteger.ONE) == 0 && rsa.q.isProbablePrime(10)) {
  251. setTimeout(loop4, 0);
  252. }
  253. else {
  254. setTimeout(loop3, 0);
  255. }
  256. });
  257. });
  258. };
  259. var loop2 = function () {
  260. rsa.p = nbi();
  261. rsa.p.fromNumberAsync(B - qs, 1, rng, function () {
  262. rsa.p.subtract(BigInteger.ONE).gcda(ee, function (r) {
  263. if (r.compareTo(BigInteger.ONE) == 0 && rsa.p.isProbablePrime(10)) {
  264. setTimeout(loop3, 0);
  265. }
  266. else {
  267. setTimeout(loop2, 0);
  268. }
  269. });
  270. });
  271. };
  272. setTimeout(loop2, 0);
  273. };
  274. setTimeout(loop1, 0);
  275. };
  276. RSAKey.prototype.sign = function (text, digestMethod, digestName) {
  277. var header = getDigestHeader(digestName);
  278. var digest = header + digestMethod(text).toString();
  279. var m = pkcs1pad1(digest, this.n.bitLength() / 4);
  280. if (m == null) {
  281. return null;
  282. }
  283. var c = this.doPrivate(m);
  284. if (c == null) {
  285. return null;
  286. }
  287. var h = c.toString(16);
  288. if ((h.length & 1) == 0) {
  289. return h;
  290. }
  291. else {
  292. return "0" + h;
  293. }
  294. };
  295. RSAKey.prototype.verify = function (text, signature, digestMethod) {
  296. var c = parseBigInt(signature, 16);
  297. var m = this.doPublic(c);
  298. if (m == null) {
  299. return null;
  300. }
  301. var unpadded = m.toString(16).replace(/^1f+00/, "");
  302. var digest = removeDigestHeader(unpadded);
  303. return digest == digestMethod(text).toString();
  304. };
  305. return RSAKey;
  306. }());
  307. export { RSAKey };
  308. // Undo PKCS#1 (type 2, random) padding and, if valid, return the plaintext
  309. function pkcs1unpad2(d, n) {
  310. var b = d.toByteArray();
  311. var i = 0;
  312. while (i < b.length && b[i] == 0) {
  313. ++i;
  314. }
  315. if (b.length - i != n - 1 || b[i] != 2) {
  316. return null;
  317. }
  318. ++i;
  319. while (b[i] != 0) {
  320. if (++i >= b.length) {
  321. return null;
  322. }
  323. }
  324. var ret = "";
  325. while (++i < b.length) {
  326. var c = b[i] & 255;
  327. if (c < 128) { // utf-8 decode
  328. ret += String.fromCharCode(c);
  329. }
  330. else if ((c > 191) && (c < 224)) {
  331. ret += String.fromCharCode(((c & 31) << 6) | (b[i + 1] & 63));
  332. ++i;
  333. }
  334. else {
  335. ret += String.fromCharCode(((c & 15) << 12) | ((b[i + 1] & 63) << 6) | (b[i + 2] & 63));
  336. i += 2;
  337. }
  338. }
  339. return ret;
  340. }
  341. // https://tools.ietf.org/html/rfc3447#page-43
  342. var DIGEST_HEADERS = {
  343. md2: "3020300c06082a864886f70d020205000410",
  344. md5: "3020300c06082a864886f70d020505000410",
  345. sha1: "3021300906052b0e03021a05000414",
  346. sha224: "302d300d06096086480165030402040500041c",
  347. sha256: "3031300d060960864801650304020105000420",
  348. sha384: "3041300d060960864801650304020205000430",
  349. sha512: "3051300d060960864801650304020305000440",
  350. ripemd160: "3021300906052b2403020105000414"
  351. };
  352. function getDigestHeader(name) {
  353. return DIGEST_HEADERS[name] || "";
  354. }
  355. function removeDigestHeader(str) {
  356. for (var name_1 in DIGEST_HEADERS) {
  357. if (DIGEST_HEADERS.hasOwnProperty(name_1)) {
  358. var header = DIGEST_HEADERS[name_1];
  359. var len = header.length;
  360. if (str.substr(0, len) == header) {
  361. return str.substr(len);
  362. }
  363. }
  364. }
  365. return str;
  366. }
  367. // Return the PKCS#1 RSA encryption of "text" as a Base64-encoded string
  368. // function RSAEncryptB64(text) {
  369. // var h = this.encrypt(text);
  370. // if(h) return hex2b64(h); else return null;
  371. // }
  372. // public
  373. // RSAKey.prototype.encrypt_b64 = RSAEncryptB64;