crypto.dart 17 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582
  1. import 'dart:convert';
  2. import 'dart:math';
  3. import 'dart:typed_data';
  4. class SM3 {
  5. static const List<int> _iv = [
  6. 0x7380166F,
  7. 0x4914B2B9,
  8. 0x172442D7,
  9. 0xDA8A0600,
  10. 0xA96F30BC,
  11. 0x163138AA,
  12. 0xE38DEE4D,
  13. 0xB0FB0E4E
  14. ];
  15. static int _rotl(int x, int n) {
  16. assert(n >= 0 && n < 32);
  17. n = n & 31; // 保证 n 在 0~31 之间
  18. return ((x << n) | (x >> (32 - n))) & 0xFFFFFFFF;
  19. }
  20. static int _p0(int x) => x ^ _rotl(x, 9) ^ _rotl(x, 17);
  21. static int _p1(int x) => x ^ _rotl(x, 15) ^ _rotl(x, 23);
  22. static int _ff(int x, int y, int z, int j) =>
  23. (j < 16) ? (x ^ y ^ z) : ((x & y) | (x & z) | (y & z));
  24. static int _gg(int x, int y, int z, int j) =>
  25. (j < 16) ? (x ^ y ^ z) : ((x & y) | (~x & z));
  26. static List<int> digest(Uint8List data) {
  27. int len = data.length * 8;
  28. List<int> msg = List.from(data);
  29. msg.add(0x80);
  30. while ((msg.length * 8) % 512 != 448) {
  31. msg.add(0x00);
  32. }
  33. for (int i = 7; i >= 0; i--) {
  34. msg.add((len >> (i * 8)) & 0xFF);
  35. }
  36. List<int> v = List.from(_iv);
  37. for (int i = 0; i < msg.length; i += 64) {
  38. List<int> b = msg.sublist(i, i + 64);
  39. List<int> w = List.filled(68, 0);
  40. List<int> w1 = List.filled(64, 0);
  41. for (int j = 0; j < 16; j++) {
  42. w[j] = (b[j * 4] << 24) |
  43. (b[j * 4 + 1] << 16) |
  44. (b[j * 4 + 2] << 8) |
  45. (b[j * 4 + 3]);
  46. }
  47. for (int j = 16; j < 68; j++) {
  48. int rotl3 = _rotl(w[j - 3], 15);
  49. int rotl13 = _rotl(w[j - 13], 7);
  50. w[j] = _p1(w[j - 16] ^ w[j - 9] ^ rotl3) ^ rotl13 ^ w[j - 6];
  51. w[j] &= 0xFFFFFFFF;
  52. }
  53. for (int j = 0; j < 64; j++) {
  54. w1[j] = w[j] ^ w[j + 4];
  55. }
  56. int a = v[0], b_ = v[1], c = v[2], d = v[3];
  57. int e = v[4], f = v[5], g = v[6], h = v[7];
  58. for (int j = 0; j < 64; j++) {
  59. int tj = (j < 16) ? 0x79CC4519 : 0x7A879D8A;
  60. int rotlA12 = _rotl(a, 12);
  61. int rotlTj = _rotl(tj, j & 0x1F); // 保证位移参数在0~31
  62. int ss1 = _rotl((rotlA12 + e + rotlTj) & 0xFFFFFFFF, 7);
  63. int ss2 = ss1 ^ rotlA12;
  64. int tt1 = (_ff(a, b_, c, j) + d + ss2 + w1[j]) & 0xFFFFFFFF;
  65. int tt2 = (_gg(e, f, g, j) + h + ss1 + w[j]) & 0xFFFFFFFF;
  66. d = c;
  67. c = _rotl(b_, 9);
  68. b_ = a;
  69. a = tt1;
  70. h = g;
  71. g = _rotl(f, 19);
  72. f = e;
  73. e = _p0(tt2);
  74. }
  75. v[0] ^= a;
  76. v[1] ^= b_;
  77. v[2] ^= c;
  78. v[3] ^= d;
  79. v[4] ^= e;
  80. v[5] ^= f;
  81. v[6] ^= g;
  82. v[7] ^= h;
  83. }
  84. List<int> out = [];
  85. for (int i = 0; i < v.length; i++) {
  86. out.addAll([
  87. (v[i] >> 24) & 0xFF,
  88. (v[i] >> 16) & 0xFF,
  89. (v[i] >> 8) & 0xFF,
  90. v[i] & 0xFF
  91. ]);
  92. }
  93. return out;
  94. }
  95. }
  96. // SM2椭圆曲线参数
  97. class SM2Curve {
  98. static final BigInt p = BigInt.parse(
  99. 'FFFFFFFEFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF00000000FFFFFFFFFFFFFFFF',
  100. radix: 16);
  101. static final BigInt a = BigInt.parse(
  102. 'FFFFFFFEFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF00000000FFFFFFFFFFFFFFFC',
  103. radix: 16);
  104. static final BigInt b = BigInt.parse(
  105. '28E9FA9E9D9F5E344D5A9E4BCF6509A7F39789F515AB8F92DDBCBD414D940E93',
  106. radix: 16);
  107. static final BigInt n = BigInt.parse(
  108. 'FFFFFFFEFFFFFFFFFFFFFFFFFFFFFFFF7203DF6B61C6B3A2F4F6B7E4B8E5F5FF',
  109. radix: 16);
  110. static final BigInt gx = BigInt.parse(
  111. '32C4AE2C1F1981195F9904466A39C9948FE30BBFF2660BE1715A4589334C74C7',
  112. radix: 16);
  113. static final BigInt gy = BigInt.parse(
  114. 'BC3736A2F4F6779C59BDCEE36B692153D0A9877CC62A474002DF32E52139F0A0',
  115. radix: 16);
  116. }
  117. // 椭圆曲线点
  118. class ECPoint {
  119. BigInt? x;
  120. BigInt? y;
  121. bool isInfinity;
  122. ECPoint(this.x, this.y) : isInfinity = false;
  123. ECPoint.infinity()
  124. : isInfinity = true,
  125. x = null,
  126. y = null;
  127. @override
  128. String toString() {
  129. if (isInfinity) return 'O';
  130. return '(${x!.toRadixString(16)}, ${y!.toRadixString(16)})';
  131. }
  132. }
  133. // SM2加解密类
  134. class SM2Crypto {
  135. static final Random _random = Random.secure();
  136. // 模运算
  137. static BigInt _mod(BigInt a, BigInt m) {
  138. BigInt result = a % m;
  139. return result < BigInt.zero ? result + m : result;
  140. }
  141. // 模逆运算
  142. static BigInt _modInverse(BigInt a, BigInt m) {
  143. if (a < BigInt.zero) a = _mod(a, m);
  144. BigInt g = _gcd(a, m);
  145. if (g != BigInt.one) {
  146. throw Exception('Modular inverse does not exist');
  147. }
  148. return _mod(_extendedGcd(a, m)[0], m);
  149. }
  150. // 扩展欧几里德算法
  151. static List<BigInt> _extendedGcd(BigInt a, BigInt b) {
  152. if (a == BigInt.zero) {
  153. return [BigInt.zero, BigInt.one, b];
  154. }
  155. List<BigInt> result = _extendedGcd(b % a, a);
  156. BigInt x1 = result[0];
  157. BigInt y1 = result[1];
  158. BigInt gcd = result[2];
  159. BigInt x = y1 - (b ~/ a) * x1;
  160. BigInt y = x1;
  161. return [x, y, gcd];
  162. }
  163. // 最大公约数
  164. static BigInt _gcd(BigInt a, BigInt b) {
  165. while (b != BigInt.zero) {
  166. BigInt temp = b;
  167. b = a % b;
  168. a = temp;
  169. }
  170. return a;
  171. }
  172. // 椭圆曲线点加法
  173. static ECPoint _pointAdd(ECPoint p1, ECPoint p2) {
  174. if (p1.isInfinity) return p2;
  175. if (p2.isInfinity) return p1;
  176. if (p1.x == p2.x) {
  177. if (p1.y == p2.y) {
  178. return _pointDouble(p1);
  179. } else {
  180. return ECPoint.infinity();
  181. }
  182. }
  183. BigInt dx = _mod(p2.x! - p1.x!, SM2Curve.p);
  184. BigInt dy = _mod(p2.y! - p1.y!, SM2Curve.p);
  185. BigInt s = _mod(dy * _modInverse(dx, SM2Curve.p), SM2Curve.p);
  186. BigInt x3 = _mod(s * s - p1.x! - p2.x!, SM2Curve.p);
  187. BigInt y3 = _mod(s * (p1.x! - x3) - p1.y!, SM2Curve.p);
  188. return ECPoint(x3, y3);
  189. }
  190. // 椭圆曲线点倍乘
  191. static ECPoint _pointDouble(ECPoint p) {
  192. if (p.isInfinity) return p;
  193. BigInt s = _mod(
  194. (BigInt.from(3) * p.x! * p.x! + SM2Curve.a) *
  195. _modInverse(BigInt.from(2) * p.y!, SM2Curve.p),
  196. SM2Curve.p);
  197. BigInt x3 = _mod(s * s - BigInt.from(2) * p.x!, SM2Curve.p);
  198. BigInt y3 = _mod(s * (p.x! - x3) - p.y!, SM2Curve.p);
  199. return ECPoint(x3, y3);
  200. }
  201. // 椭圆曲线标量乘法
  202. static ECPoint _pointMultiply(BigInt k, ECPoint point) {
  203. if (k == BigInt.zero || point.isInfinity) {
  204. return ECPoint.infinity();
  205. }
  206. ECPoint result = ECPoint.infinity();
  207. ECPoint addend = point;
  208. while (k > BigInt.zero) {
  209. if (k.isOdd) {
  210. result = _pointAdd(result, addend);
  211. }
  212. addend = _pointDouble(addend);
  213. k = k >> 1;
  214. }
  215. return result;
  216. }
  217. // 生成随机数
  218. static BigInt _generateRandomBigInt(BigInt max) {
  219. int byteLength = (max.bitLength + 7) ~/ 8;
  220. BigInt randomNumber;
  221. do {
  222. List<int> bytes = List.generate(byteLength, (_) => _random.nextInt(256));
  223. randomNumber = BigInt.parse(
  224. bytes.map((b) => b.toRadixString(16).padLeft(2, '0')).join(),
  225. radix: 16);
  226. } while (randomNumber >= max || randomNumber == BigInt.zero);
  227. return randomNumber;
  228. }
  229. // KDF密钥派生函数
  230. static Uint8List _kdf(Uint8List z, int klen) {
  231. List<int> result = [];
  232. int ct = 1;
  233. while (result.length < klen) {
  234. List<int> input = [
  235. ...z,
  236. ...[(ct >> 24) & 0xFF, (ct >> 16) & 0xFF, (ct >> 8) & 0xFF, ct & 0xFF]
  237. ];
  238. List<int> hash = _sm3Hash(Uint8List.fromList(input));
  239. result.addAll(hash);
  240. ct++;
  241. }
  242. return Uint8List.fromList(result.take(klen).toList());
  243. }
  244. // SM3哈希函数(简化版本)
  245. static List<int> _sm3Hash(Uint8List data) {
  246. return SM3.digest(data);
  247. }
  248. // 字节数组转换为十六进制字符串
  249. static String _bytesToHex(Uint8List bytes) {
  250. return bytes.map((b) => b.toRadixString(16).padLeft(2, '0')).join();
  251. }
  252. // 十六进制字符串转换为字节数组
  253. static Uint8List _hexToBytes(String hex) {
  254. if (hex.length % 2 != 0) hex = '0' + hex;
  255. List<int> bytes = [];
  256. for (int i = 0; i < hex.length; i += 2) {
  257. bytes.add(int.parse(hex.substring(i, i + 2), radix: 16));
  258. }
  259. return Uint8List.fromList(bytes);
  260. }
  261. // BigInt转换为定长字节数组
  262. static Uint8List _bigIntToBytes(BigInt value, int length) {
  263. String hex = value.toRadixString(16).padLeft(length * 2, '0');
  264. return _hexToBytes(hex);
  265. }
  266. // 公钥压缩(与gm-crypto保持一致)
  267. static String compressPublicKey(String publicKey) {
  268. if (publicKey.length == 66) return publicKey; // 已经是压缩格式
  269. if (publicKey.startsWith('04')) {
  270. publicKey = publicKey.substring(2);
  271. }
  272. String x = publicKey.substring(0, 64);
  273. String y = publicKey.substring(64, 128);
  274. BigInt yBig = BigInt.parse(y, radix: 16);
  275. String prefix = yBig.isEven ? '02' : '03';
  276. return prefix + x;
  277. }
  278. // 公钥解压缩
  279. static String decompressPublicKey(String compressedKey) {
  280. if (compressedKey.length == 130) return compressedKey;
  281. if (compressedKey.length != 66) throw Exception('压缩公钥长度非法'); // 已经是非压缩格式
  282. String prefix = compressedKey.substring(0, 2);
  283. String x = compressedKey.substring(2);
  284. BigInt xBig = BigInt.parse(x, radix: 16);
  285. // 计算y²
  286. BigInt ySquared =
  287. _mod(xBig * xBig * xBig + SM2Curve.a * xBig + SM2Curve.b, SM2Curve.p);
  288. // 计算y
  289. BigInt y = _modSqrt(ySquared, SM2Curve.p);
  290. // 根据前缀选择正确的y值
  291. if ((prefix == '02' && y.isOdd) || (prefix == '03' && y.isEven)) {
  292. y = SM2Curve.p - y;
  293. }
  294. return '04' + x + y.toRadixString(16).padLeft(64, '0');
  295. }
  296. // 模平方根
  297. static BigInt _modSqrt(BigInt a, BigInt p) {
  298. // 使用Tonelli-Shanks算法或简化版本
  299. return a.modPow((p + BigInt.one) ~/ BigInt.from(4), p);
  300. }
  301. // SM2加密(支持C1C2C3和C1C3C2两种模式)
  302. static String encrypt(String message, String publicKey,
  303. {bool c1c3c2Mode = true}) {
  304. // 确保公钥是完整格式
  305. String fullPublicKey = decompressPublicKey(publicKey);
  306. if (fullPublicKey.startsWith('04')) {
  307. fullPublicKey = fullPublicKey.substring(2);
  308. }
  309. String xHex = fullPublicKey.substring(0, 64);
  310. String yHex = fullPublicKey.substring(64, 128);
  311. BigInt px = BigInt.parse(xHex, radix: 16);
  312. BigInt py = BigInt.parse(yHex, radix: 16);
  313. ECPoint publicKeyPoint = ECPoint(px, py);
  314. Uint8List messageBytes = utf8.encode(message);
  315. while (true) {
  316. // 生成随机数k
  317. BigInt k = _generateRandomBigInt(SM2Curve.n);
  318. // 计算C1 = k*G
  319. ECPoint c1Point = _pointMultiply(k, ECPoint(SM2Curve.gx, SM2Curve.gy));
  320. // 计算k*P
  321. ECPoint kPPoint = _pointMultiply(k, publicKeyPoint);
  322. // 计算x2||y2
  323. Uint8List x2Bytes = _bigIntToBytes(kPPoint.x!, 32);
  324. Uint8List y2Bytes = _bigIntToBytes(kPPoint.y!, 32);
  325. Uint8List x2y2 = Uint8List.fromList([...x2Bytes, ...y2Bytes]);
  326. // KDF生成密钥流
  327. Uint8List keyStream = _kdf(x2y2, messageBytes.length);
  328. // 检查密钥流是否全零
  329. bool allZero = keyStream.every((b) => b == 0);
  330. if (allZero) continue;
  331. // 计算C2 = M ⊕ t
  332. Uint8List c2 = Uint8List.fromList(List.generate(
  333. messageBytes.length, (i) => messageBytes[i] ^ keyStream[i]));
  334. // 计算C3 = Hash(x2||M||y2)
  335. Uint8List hashInput =
  336. Uint8List.fromList([...x2Bytes, ...messageBytes, ...y2Bytes]);
  337. List<int> c3 = _sm3Hash(hashInput);
  338. // 组装密文
  339. Uint8List c1x = _bigIntToBytes(c1Point.x!, 32);
  340. Uint8List c1y = _bigIntToBytes(c1Point.y!, 32);
  341. String result;
  342. if (c1c3c2Mode) {
  343. // C1||C3||C2模式(新标准,默认)
  344. result = '04' +
  345. _bytesToHex(c1x) +
  346. _bytesToHex(c1y) +
  347. _bytesToHex(Uint8List.fromList(c3)) +
  348. _bytesToHex(c2);
  349. } else {
  350. // C1||C2||C3模式(旧标准)
  351. result = '04' +
  352. _bytesToHex(c1x) +
  353. _bytesToHex(c1y) +
  354. _bytesToHex(c2) +
  355. _bytesToHex(Uint8List.fromList(c3));
  356. }
  357. return result;
  358. }
  359. }
  360. // SM2解密(自动检测C1C2C3和C1C3C2模式)
  361. static String decrypt(String ciphertext, String privateKey,
  362. {bool? c1c3c2Mode}) {
  363. // 解析密文
  364. if (ciphertext.startsWith('04')) {
  365. ciphertext = ciphertext.substring(2);
  366. }
  367. // C1点坐标(64字节)
  368. String c1x = ciphertext.substring(0, 64);
  369. String c1y = ciphertext.substring(64, 128);
  370. // 构造C1点
  371. BigInt c1xBig = BigInt.parse(c1x, radix: 16);
  372. BigInt c1yBig = BigInt.parse(c1y, radix: 16);
  373. ECPoint c1Point = ECPoint(c1xBig, c1yBig);
  374. // 私钥
  375. BigInt d = BigInt.parse(privateKey, radix: 16);
  376. // 计算d*C1
  377. ECPoint dC1Point = _pointMultiply(d, c1Point);
  378. // 计算x2||y2
  379. Uint8List x2Bytes = _bigIntToBytes(dC1Point.x!, 32);
  380. Uint8List y2Bytes = _bigIntToBytes(dC1Point.y!, 32);
  381. Uint8List x2y2 = Uint8List.fromList([...x2Bytes, ...y2Bytes]);
  382. String remainingCiphertext = ciphertext.substring(128);
  383. // 如果没有指定模式,先尝试C1C3C2模式,失败后尝试C1C2C3模式
  384. if (c1c3c2Mode == null) {
  385. try {
  386. return _decryptWithMode(
  387. remainingCiphertext, x2Bytes, y2Bytes, x2y2, true);
  388. } catch (e) {
  389. try {
  390. return _decryptWithMode(
  391. remainingCiphertext, x2Bytes, y2Bytes, x2y2, false);
  392. } catch (e2) {
  393. throw Exception(
  394. 'Decryption failed with both C1C3C2 and C1C2C3 modes');
  395. }
  396. }
  397. } else {
  398. return _decryptWithMode(
  399. remainingCiphertext, x2Bytes, y2Bytes, x2y2, c1c3c2Mode);
  400. }
  401. }
  402. // 辅助解密函数
  403. static String _decryptWithMode(String remainingCiphertext, Uint8List x2Bytes,
  404. Uint8List y2Bytes, Uint8List x2y2, bool c1c3c2Mode) {
  405. String c2, c3;
  406. if (c1c3c2Mode) {
  407. // C1C3C2模式:C3哈希值(64字节),然后是C2密文
  408. c3 = remainingCiphertext.substring(0, 64);
  409. c2 = remainingCiphertext.substring(64);
  410. } else {
  411. // C1C2C3模式:先是C2密文,最后64字节是C3哈希值
  412. c2 = remainingCiphertext.substring(0, remainingCiphertext.length - 64);
  413. c3 = remainingCiphertext.substring(remainingCiphertext.length - 64);
  414. }
  415. // 解析C2
  416. Uint8List c2Bytes = _hexToBytes(c2);
  417. // KDF生成密钥流
  418. Uint8List keyStream = _kdf(x2y2, c2Bytes.length);
  419. // 解密得到明文
  420. Uint8List messageBytes = Uint8List.fromList(
  421. List.generate(c2Bytes.length, (i) => c2Bytes[i] ^ keyStream[i]));
  422. // 验证C3
  423. Uint8List hashInput =
  424. Uint8List.fromList([...x2Bytes, ...messageBytes, ...y2Bytes]);
  425. List<int> computedC3 = _sm3Hash(hashInput);
  426. String computedC3Hex = _bytesToHex(Uint8List.fromList(computedC3));
  427. if (computedC3Hex != c3) {
  428. throw Exception('Hash verification failed');
  429. }
  430. return utf8.decode(messageBytes);
  431. }
  432. // 生成密钥对
  433. static Map<String, String> generateKeyPair() {
  434. BigInt privateKey = _generateRandomBigInt(SM2Curve.n);
  435. ECPoint publicKeyPoint =
  436. _pointMultiply(privateKey, ECPoint(SM2Curve.gx, SM2Curve.gy));
  437. String privateKeyHex = privateKey.toRadixString(16).padLeft(64, '0');
  438. String publicKeyHex = '04' +
  439. publicKeyPoint.x!.toRadixString(16).padLeft(64, '0') +
  440. publicKeyPoint.y!.toRadixString(16).padLeft(64, '0');
  441. return {
  442. 'privateKey': privateKeyHex,
  443. 'publicKey': publicKeyHex,
  444. 'compressedPublicKey': compressPublicKey(publicKeyHex),
  445. };
  446. }
  447. }
  448. // 使用示例
  449. // void main() {
  450. // // 生成密钥对
  451. // Map<String, String> keyPair = SM2Crypto.generateKeyPair();
  452. // print('Private Key: ${keyPair['privateKey']}');
  453. // print('Public Key: ${keyPair['publicKey']}');
  454. // print('Compressed Public Key: ${keyPair['compressedPublicKey']}');
  455. // String message = 'Hello SM2!';
  456. // // 测试C1C3C2模式(新标准,默认)
  457. // print('\n=== C1C3C2模式测试 ===');
  458. // String ciphertext1 = SM2Crypto.encrypt(message, keyPair['publicKey']!, c1c3c2Mode: true);
  459. // print('C1C3C2 Ciphertext: $ciphertext1');
  460. // String decrypted1 = SM2Crypto.decrypt(ciphertext1, keyPair['privateKey']!);
  461. // print('C1C3C2 Decrypted: $decrypted1');
  462. // // 测试C1C2C3模式(旧标准)
  463. // print('\n=== C1C2C3模式测试 ===');
  464. // String ciphertext2 = SM2Crypto.encrypt(message, keyPair['publicKey']!, c1c3c2Mode: false);
  465. // print('C1C2C3 Ciphertext: $ciphertext2');
  466. // String decrypted2 = SM2Crypto.decrypt(ciphertext2, keyPair['privateKey']!);
  467. // print('C1C2C3 Decrypted: $decrypted2');
  468. // // 测试自动模式检测
  469. // print('\n=== 自动模式检测测试 ===');
  470. // String autoDecrypted1 = SM2Crypto.decrypt(ciphertext1, keyPair['privateKey']!);
  471. // String autoDecrypted2 = SM2Crypto.decrypt(ciphertext2, keyPair['privateKey']!);
  472. // print('Auto detect C1C3C2: $autoDecrypted1');
  473. // print('Auto detect C1C2C3: $autoDecrypted2');
  474. // // 测试与压缩公钥的兼容性
  475. // print('\n=== 压缩公钥兼容性测试 ===');
  476. // String ciphertext3 = SM2Crypto.encrypt(message, keyPair['compressedPublicKey']!);
  477. // String decrypted3 = SM2Crypto.decrypt(ciphertext3, keyPair['privateKey']!);
  478. // print('Compressed key test: $decrypted3');
  479. // }