Encrypted messaging app
You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

979 lines
31 KiB

  1. import 'dart:convert';
  2. import 'dart:math';
  3. import 'dart:typed_data';
  4. import 'package:pointycastle/asn1/object_identifiers.dart';
  5. import 'package:pointycastle/export.dart';
  6. import 'package:pointycastle/pointycastle.dart';
  7. import 'package:pointycastle/src/utils.dart' as thing;
  8. import 'package:pointycastle/ecc/ecc_fp.dart' as ecc_fp;
  9. import './string_utils.dart';
  10. ///
  11. /// Helper class for cryptographic operations
  12. ///
  13. class CryptoUtils {
  14. static const BEGIN_PRIVATE_KEY = '-----BEGIN PRIVATE KEY-----';
  15. static const END_PRIVATE_KEY = '-----END PRIVATE KEY-----';
  16. static const BEGIN_PUBLIC_KEY = '-----BEGIN PUBLIC KEY-----';
  17. static const END_PUBLIC_KEY = '-----END PUBLIC KEY-----';
  18. static const BEGIN_EC_PRIVATE_KEY = '-----BEGIN EC PRIVATE KEY-----';
  19. static const END_EC_PRIVATE_KEY = '-----END EC PRIVATE KEY-----';
  20. static const BEGIN_EC_PUBLIC_KEY = '-----BEGIN EC PUBLIC KEY-----';
  21. static const END_EC_PUBLIC_KEY = '-----END EC PUBLIC KEY-----';
  22. static const BEGIN_RSA_PRIVATE_KEY = '-----BEGIN RSA PRIVATE KEY-----';
  23. static const END_RSA_PRIVATE_KEY = '-----END RSA PRIVATE KEY-----';
  24. static const BEGIN_RSA_PUBLIC_KEY = '-----BEGIN RSA PUBLIC KEY-----';
  25. static const END_RSA_PUBLIC_KEY = '-----END RSA PUBLIC KEY-----';
  26. ///
  27. /// Converts the [RSAPublicKey.modulus] from the given [publicKey] to a [Uint8List].
  28. ///
  29. static Uint8List rsaPublicKeyModulusToBytes(RSAPublicKey publicKey) =>
  30. thing.encodeBigInt(publicKey.modulus);
  31. ///
  32. /// Converts the [RSAPublicKey.exponent] from the given [publicKey] to a [Uint8List].
  33. ///
  34. static Uint8List rsaPublicKeyExponentToBytes(RSAPublicKey publicKey) =>
  35. thing.encodeBigInt(publicKey.exponent);
  36. ///
  37. /// Converts the [RSAPrivateKey.modulus] from the given [privateKey] to a [Uint8List].
  38. ///
  39. static Uint8List rsaPrivateKeyModulusToBytes(RSAPrivateKey privateKey) =>
  40. thing.encodeBigInt(privateKey.modulus);
  41. ///
  42. /// Converts the [RSAPrivateKey.exponent] from the given [privateKey] to a [Uint8List].
  43. ///
  44. static Uint8List rsaPrivateKeyExponentToBytes(RSAPrivateKey privateKey) =>
  45. thing.encodeBigInt(privateKey.exponent);
  46. ///
  47. /// Get a SHA1 Thumbprint for the given [bytes].
  48. ///
  49. @Deprecated('Use [getHash]')
  50. static String getSha1ThumbprintFromBytes(Uint8List bytes) {
  51. return getHash(bytes, algorithmName: 'SHA-1');
  52. }
  53. ///
  54. /// Get a SHA256 Thumbprint for the given [bytes].
  55. ///
  56. @Deprecated('Use [getHash]')
  57. static String getSha256ThumbprintFromBytes(Uint8List bytes) {
  58. return getHash(bytes, algorithmName: 'SHA-256');
  59. }
  60. ///
  61. /// Get a MD5 Thumbprint for the given [bytes].
  62. ///
  63. @Deprecated('Use [getHash]')
  64. static String getMd5ThumbprintFromBytes(Uint8List bytes) {
  65. return getHash(bytes, algorithmName: 'MD5');
  66. }
  67. ///
  68. /// Get a hash for the given [bytes] using the given [algorithm]
  69. ///
  70. /// The default [algorithm] used is **SHA-256**. All supported algorihms are :
  71. ///
  72. /// * SHA-1
  73. /// * SHA-224
  74. /// * SHA-256
  75. /// * SHA-384
  76. /// * SHA-512
  77. /// * SHA-512/224
  78. /// * SHA-512/256
  79. /// * MD5
  80. ///
  81. static String getHash(Uint8List bytes, {String algorithmName = 'SHA-256'}) {
  82. var hash = getHashPlain(bytes, algorithmName: algorithmName);
  83. const hexDigits = '0123456789abcdef';
  84. var charCodes = Uint8List(hash.length * 2);
  85. for (var i = 0, j = 0; i < hash.length; i++) {
  86. var byte = hash[i];
  87. charCodes[j++] = hexDigits.codeUnitAt((byte >> 4) & 0xF);
  88. charCodes[j++] = hexDigits.codeUnitAt(byte & 0xF);
  89. }
  90. return String.fromCharCodes(charCodes).toUpperCase();
  91. }
  92. ///
  93. /// Get a hash for the given [bytes] using the given [algorithm]
  94. ///
  95. /// The default [algorithm] used is **SHA-256**. All supported algorihms are :
  96. ///
  97. /// * SHA-1
  98. /// * SHA-224
  99. /// * SHA-256
  100. /// * SHA-384
  101. /// * SHA-512
  102. /// * SHA-512/224
  103. /// * SHA-512/256
  104. /// * MD5
  105. ///
  106. static Uint8List getHashPlain(Uint8List bytes,
  107. {String algorithmName = 'SHA-256'}) {
  108. Uint8List hash;
  109. switch (algorithmName) {
  110. case 'SHA-1':
  111. hash = Digest('SHA-1').process(bytes);
  112. break;
  113. case 'SHA-224':
  114. hash = Digest('SHA-224').process(bytes);
  115. break;
  116. case 'SHA-256':
  117. hash = Digest('SHA-256').process(bytes);
  118. break;
  119. case 'SHA-384':
  120. hash = Digest('SHA-384').process(bytes);
  121. break;
  122. case 'SHA-512':
  123. hash = Digest('SHA-512').process(bytes);
  124. break;
  125. case 'SHA-512/224':
  126. hash = Digest('SHA-512/224').process(bytes);
  127. break;
  128. case 'SHA-512/256':
  129. hash = Digest('SHA-512/256').process(bytes);
  130. break;
  131. case 'MD5':
  132. hash = Digest('MD5').process(bytes);
  133. break;
  134. default:
  135. throw ArgumentError('Hash not supported');
  136. }
  137. return hash;
  138. }
  139. ///
  140. /// Generates a RSA [AsymmetricKeyPair] with the given [keySize].
  141. /// The default value for the [keySize] is 2048 bits.
  142. ///
  143. /// The following keySize is supported:
  144. /// * 1024
  145. /// * 2048
  146. /// * 4096
  147. /// * 8192
  148. ///
  149. static AsymmetricKeyPair<RSAPublicKey, RSAPrivateKey> generateRSAKeyPair({int keySize = 2048}) {
  150. var keyParams =
  151. RSAKeyGeneratorParameters(BigInt.parse('65537'), keySize, 12);
  152. var secureRandom = _getSecureRandom();
  153. var rngParams = ParametersWithRandom(keyParams, secureRandom);
  154. var generator = RSAKeyGenerator();
  155. generator.init(rngParams);
  156. final pair = generator.generateKeyPair();
  157. final myPublic = pair.publicKey as RSAPublicKey;
  158. final myPrivate = pair.privateKey as RSAPrivateKey;
  159. return AsymmetricKeyPair<RSAPublicKey, RSAPrivateKey>(myPublic, myPrivate);
  160. }
  161. ///
  162. /// Generates a elliptic curve [AsymmetricKeyPair].
  163. ///
  164. /// The default curve is **prime256v1**
  165. ///
  166. /// The following curves are supported:
  167. ///
  168. /// * brainpoolp160r1
  169. /// * brainpoolp160t1
  170. /// * brainpoolp192r1
  171. /// * brainpoolp192t1
  172. /// * brainpoolp224r1
  173. /// * brainpoolp224t1
  174. /// * brainpoolp256r1
  175. /// * brainpoolp256t1
  176. /// * brainpoolp320r1
  177. /// * brainpoolp320t1
  178. /// * brainpoolp384r1
  179. /// * brainpoolp384t1
  180. /// * brainpoolp512r1
  181. /// * brainpoolp512t1
  182. /// * GostR3410-2001-CryptoPro-A
  183. /// * GostR3410-2001-CryptoPro-B
  184. /// * GostR3410-2001-CryptoPro-C
  185. /// * GostR3410-2001-CryptoPro-XchA
  186. /// * GostR3410-2001-CryptoPro-XchB
  187. /// * prime192v1
  188. /// * prime192v2
  189. /// * prime192v3
  190. /// * prime239v1
  191. /// * prime239v2
  192. /// * prime239v3
  193. /// * prime256v1
  194. /// * secp112r1
  195. /// * secp112r2
  196. /// * secp128r1
  197. /// * secp128r2
  198. /// * secp160k1
  199. /// * secp160r1
  200. /// * secp160r2
  201. /// * secp192k1
  202. /// * secp192r1
  203. /// * secp224k1
  204. /// * secp224r1
  205. /// * secp256k1
  206. /// * secp256r1
  207. /// * secp384r1
  208. /// * secp521r1
  209. ///
  210. static AsymmetricKeyPair generateEcKeyPair({String curve = 'prime256v1'}) {
  211. var ecDomainParameters = ECDomainParameters(curve);
  212. var keyParams = ECKeyGeneratorParameters(ecDomainParameters);
  213. var secureRandom = _getSecureRandom();
  214. var rngParams = ParametersWithRandom(keyParams, secureRandom);
  215. var generator = ECKeyGenerator();
  216. generator.init(rngParams);
  217. return generator.generateKeyPair();
  218. }
  219. ///
  220. /// Generates a secure [FortunaRandom]
  221. ///
  222. static SecureRandom _getSecureRandom() {
  223. var secureRandom = FortunaRandom();
  224. var random = Random.secure();
  225. var seeds = <int>[];
  226. for (var i = 0; i < 32; i++) {
  227. seeds.add(random.nextInt(255));
  228. }
  229. secureRandom.seed(KeyParameter(Uint8List.fromList(seeds)));
  230. return secureRandom;
  231. }
  232. ///
  233. /// Enode the given [publicKey] to PEM format using the PKCS#8 standard.
  234. ///
  235. static String encodeRSAPublicKeyToPem(RSAPublicKey publicKey) {
  236. var algorithmSeq = ASN1Sequence();
  237. var paramsAsn1Obj = ASN1Object.fromBytes(Uint8List.fromList([0x5, 0x0]));
  238. algorithmSeq.add(ASN1ObjectIdentifier.fromName('rsaEncryption'));
  239. algorithmSeq.add(paramsAsn1Obj);
  240. var publicKeySeq = ASN1Sequence();
  241. publicKeySeq.add(ASN1Integer(publicKey.modulus));
  242. publicKeySeq.add(ASN1Integer(publicKey.exponent));
  243. var publicKeySeqBitString =
  244. ASN1BitString(stringValues: Uint8List.fromList(publicKeySeq.encode()));
  245. var topLevelSeq = ASN1Sequence();
  246. topLevelSeq.add(algorithmSeq);
  247. topLevelSeq.add(publicKeySeqBitString);
  248. var dataBase64 = base64.encode(topLevelSeq.encode());
  249. var chunks = StringUtils.chunk(dataBase64, 64);
  250. return '$BEGIN_PUBLIC_KEY\n${chunks.join('\n')}\n$END_PUBLIC_KEY';
  251. }
  252. ///
  253. /// Enode the given [rsaPublicKey] to PEM format using the PKCS#1 standard.
  254. ///
  255. /// The ASN1 structure is decripted at <https://tools.ietf.org/html/rfc8017#page-53>.
  256. ///
  257. /// ```
  258. /// RSAPublicKey ::= SEQUENCE {
  259. /// modulus INTEGER, -- n
  260. /// publicExponent INTEGER -- e
  261. /// }
  262. /// ```
  263. ///
  264. static String encodeRSAPublicKeyToPemPkcs1(RSAPublicKey rsaPublicKey) {
  265. var topLevelSeq = ASN1Sequence();
  266. topLevelSeq.add(ASN1Integer(rsaPublicKey.modulus));
  267. topLevelSeq.add(ASN1Integer(rsaPublicKey.exponent));
  268. var dataBase64 = base64.encode(topLevelSeq.encode());
  269. var chunks = StringUtils.chunk(dataBase64, 64);
  270. return '$BEGIN_RSA_PUBLIC_KEY\n${chunks.join('\n')}\n$END_RSA_PUBLIC_KEY';
  271. }
  272. ///
  273. /// Enode the given [rsaPrivateKey] to PEM format using the PKCS#1 standard.
  274. ///
  275. /// The ASN1 structure is decripted at <https://tools.ietf.org/html/rfc8017#page-54>.
  276. ///
  277. /// ```
  278. /// RSAPrivateKey ::= SEQUENCE {
  279. /// version Version,
  280. /// modulus INTEGER, -- n
  281. /// publicExponent INTEGER, -- e
  282. /// privateExponent INTEGER, -- d
  283. /// prime1 INTEGER, -- p
  284. /// prime2 INTEGER, -- q
  285. /// exponent1 INTEGER, -- d mod (p-1)
  286. /// exponent2 INTEGER, -- d mod (q-1)
  287. /// coefficient INTEGER, -- (inverse of q) mod p
  288. /// otherPrimeInfos OtherPrimeInfos OPTIONAL
  289. /// }
  290. /// ```
  291. static String encodeRSAPrivateKeyToPemPkcs1(RSAPrivateKey rsaPrivateKey) {
  292. var version = ASN1Integer(BigInt.from(0));
  293. var modulus = ASN1Integer(rsaPrivateKey.n);
  294. var publicExponent = ASN1Integer(BigInt.parse('65537'));
  295. var privateExponent = ASN1Integer(rsaPrivateKey.privateExponent);
  296. var p = ASN1Integer(rsaPrivateKey.p);
  297. var q = ASN1Integer(rsaPrivateKey.q);
  298. var dP =
  299. rsaPrivateKey.privateExponent! % (rsaPrivateKey.p! - BigInt.from(1));
  300. var exp1 = ASN1Integer(dP);
  301. var dQ =
  302. rsaPrivateKey.privateExponent! % (rsaPrivateKey.q! - BigInt.from(1));
  303. var exp2 = ASN1Integer(dQ);
  304. var iQ = rsaPrivateKey.q!.modInverse(rsaPrivateKey.p!);
  305. var co = ASN1Integer(iQ);
  306. var topLevelSeq = ASN1Sequence();
  307. topLevelSeq.add(version);
  308. topLevelSeq.add(modulus);
  309. topLevelSeq.add(publicExponent);
  310. topLevelSeq.add(privateExponent);
  311. topLevelSeq.add(p);
  312. topLevelSeq.add(q);
  313. topLevelSeq.add(exp1);
  314. topLevelSeq.add(exp2);
  315. topLevelSeq.add(co);
  316. var dataBase64 = base64.encode(topLevelSeq.encode());
  317. var chunks = StringUtils.chunk(dataBase64, 64);
  318. return '$BEGIN_RSA_PRIVATE_KEY\n${chunks.join('\n')}\n$END_RSA_PRIVATE_KEY';
  319. }
  320. ///
  321. /// Enode the given [rsaPrivateKey] to PEM format using the PKCS#8 standard.
  322. ///
  323. /// The ASN1 structure is decripted at <https://tools.ietf.org/html/rfc5208>.
  324. /// ```
  325. /// PrivateKeyInfo ::= SEQUENCE {
  326. /// version Version,
  327. /// algorithm AlgorithmIdentifier,
  328. /// PrivateKey BIT STRING
  329. /// }
  330. /// ```
  331. ///
  332. static String encodeRSAPrivateKeyToPem(RSAPrivateKey rsaPrivateKey) {
  333. var version = ASN1Integer(BigInt.from(0));
  334. var algorithmSeq = ASN1Sequence();
  335. var algorithmAsn1Obj = ASN1Object.fromBytes(Uint8List.fromList(
  336. [0x6, 0x9, 0x2a, 0x86, 0x48, 0x86, 0xf7, 0xd, 0x1, 0x1, 0x1]));
  337. var paramsAsn1Obj = ASN1Object.fromBytes(Uint8List.fromList([0x5, 0x0]));
  338. algorithmSeq.add(algorithmAsn1Obj);
  339. algorithmSeq.add(paramsAsn1Obj);
  340. var privateKeySeq = ASN1Sequence();
  341. var modulus = ASN1Integer(rsaPrivateKey.n);
  342. var publicExponent = ASN1Integer(BigInt.parse('65537'));
  343. var privateExponent = ASN1Integer(rsaPrivateKey.privateExponent);
  344. var p = ASN1Integer(rsaPrivateKey.p);
  345. var q = ASN1Integer(rsaPrivateKey.q);
  346. var dP =
  347. rsaPrivateKey.privateExponent! % (rsaPrivateKey.p! - BigInt.from(1));
  348. var exp1 = ASN1Integer(dP);
  349. var dQ =
  350. rsaPrivateKey.privateExponent! % (rsaPrivateKey.q! - BigInt.from(1));
  351. var exp2 = ASN1Integer(dQ);
  352. var iQ = rsaPrivateKey.q!.modInverse(rsaPrivateKey.p!);
  353. var co = ASN1Integer(iQ);
  354. privateKeySeq.add(version);
  355. privateKeySeq.add(modulus);
  356. privateKeySeq.add(publicExponent);
  357. privateKeySeq.add(privateExponent);
  358. privateKeySeq.add(p);
  359. privateKeySeq.add(q);
  360. privateKeySeq.add(exp1);
  361. privateKeySeq.add(exp2);
  362. privateKeySeq.add(co);
  363. var publicKeySeqOctetString =
  364. ASN1OctetString(octets: Uint8List.fromList(privateKeySeq.encode()));
  365. var topLevelSeq = ASN1Sequence();
  366. topLevelSeq.add(version);
  367. topLevelSeq.add(algorithmSeq);
  368. topLevelSeq.add(publicKeySeqOctetString);
  369. var dataBase64 = base64.encode(topLevelSeq.encode());
  370. var chunks = StringUtils.chunk(dataBase64, 64);
  371. return '$BEGIN_PRIVATE_KEY\n${chunks.join('\n')}\n$END_PRIVATE_KEY';
  372. }
  373. ///
  374. /// Decode a [RSAPrivateKey] from the given [pem] String.
  375. ///
  376. static RSAPrivateKey rsaPrivateKeyFromPem(String pem) {
  377. var bytes = getBytesFromPEMString(pem);
  378. return rsaPrivateKeyFromDERBytes(bytes);
  379. }
  380. ///
  381. /// Decode the given [bytes] into an [RSAPrivateKey].
  382. ///
  383. static RSAPrivateKey rsaPrivateKeyFromDERBytes(Uint8List bytes) {
  384. var asn1Parser = ASN1Parser(bytes);
  385. var topLevelSeq = asn1Parser.nextObject() as ASN1Sequence;
  386. //ASN1Object version = topLevelSeq.elements[0];
  387. //ASN1Object algorithm = topLevelSeq.elements[1];
  388. var privateKey = topLevelSeq.elements![2];
  389. asn1Parser = ASN1Parser(privateKey.valueBytes);
  390. var pkSeq = asn1Parser.nextObject() as ASN1Sequence;
  391. var modulus = pkSeq.elements![1] as ASN1Integer;
  392. //ASN1Integer publicExponent = pkSeq.elements[2] as ASN1Integer;
  393. var privateExponent = pkSeq.elements![3] as ASN1Integer;
  394. var p = pkSeq.elements![4] as ASN1Integer;
  395. var q = pkSeq.elements![5] as ASN1Integer;
  396. //ASN1Integer exp1 = pkSeq.elements[6] as ASN1Integer;
  397. //ASN1Integer exp2 = pkSeq.elements[7] as ASN1Integer;
  398. //ASN1Integer co = pkSeq.elements[8] as ASN1Integer;
  399. var rsaPrivateKey = RSAPrivateKey(
  400. modulus.integer!, privateExponent.integer!, p.integer, q.integer);
  401. return rsaPrivateKey;
  402. }
  403. ///
  404. /// Decode a [RSAPrivateKey] from the given [pem] string formated in the pkcs1 standard.
  405. ///
  406. static RSAPrivateKey rsaPrivateKeyFromPemPkcs1(String pem) {
  407. var bytes = getBytesFromPEMString(pem);
  408. return rsaPrivateKeyFromDERBytesPkcs1(bytes);
  409. }
  410. ///
  411. /// Decode the given [bytes] into an [RSAPrivateKey].
  412. ///
  413. /// The [bytes] need to follow the the pkcs1 standard
  414. ///
  415. static RSAPrivateKey rsaPrivateKeyFromDERBytesPkcs1(Uint8List bytes) {
  416. var asn1Parser = ASN1Parser(bytes);
  417. var pkSeq = asn1Parser.nextObject() as ASN1Sequence;
  418. var modulus = pkSeq.elements![1] as ASN1Integer;
  419. //ASN1Integer publicExponent = pkSeq.elements[2] as ASN1Integer;
  420. var privateExponent = pkSeq.elements![3] as ASN1Integer;
  421. var p = pkSeq.elements![4] as ASN1Integer;
  422. var q = pkSeq.elements![5] as ASN1Integer;
  423. //ASN1Integer exp1 = pkSeq.elements[6] as ASN1Integer;
  424. //ASN1Integer exp2 = pkSeq.elements[7] as ASN1Integer;
  425. //ASN1Integer co = pkSeq.elements[8] as ASN1Integer;
  426. var rsaPrivateKey = RSAPrivateKey(
  427. modulus.integer!, privateExponent.integer!, p.integer, q.integer);
  428. return rsaPrivateKey;
  429. }
  430. ///
  431. /// Helper function for decoding the base64 in [pem].
  432. ///
  433. /// Throws an ArgumentError if the given [pem] is not sourounded by begin marker -----BEGIN and
  434. /// endmarker -----END or the [pem] consists of less than two lines.
  435. ///
  436. /// The PEM header check can be skipped by setting the optional paramter [checkHeader] to false.
  437. ///
  438. static Uint8List getBytesFromPEMString(String pem,
  439. {bool checkHeader = true}) {
  440. var lines = LineSplitter.split(pem)
  441. .map((line) => line.trim())
  442. .where((line) => line.isNotEmpty)
  443. .toList();
  444. var base64;
  445. if (checkHeader) {
  446. if (lines.length < 2 ||
  447. !lines.first.startsWith('-----BEGIN') ||
  448. !lines.last.startsWith('-----END')) {
  449. throw ArgumentError('The given string does not have the correct '
  450. 'begin/end markers expected in a PEM file.');
  451. }
  452. base64 = lines.sublist(1, lines.length - 1).join('');
  453. } else {
  454. base64 = lines.join('');
  455. }
  456. return Uint8List.fromList(base64Decode(base64));
  457. }
  458. ///
  459. /// Decode a [RSAPublicKey] from the given [pem] String.
  460. ///
  461. static RSAPublicKey rsaPublicKeyFromPem(String pem) {
  462. var bytes = CryptoUtils.getBytesFromPEMString(pem);
  463. return rsaPublicKeyFromDERBytes(bytes);
  464. }
  465. ///
  466. /// Decode the given [bytes] into an [RSAPublicKey].
  467. ///
  468. static RSAPublicKey rsaPublicKeyFromDERBytes(Uint8List bytes) {
  469. var asn1Parser = ASN1Parser(bytes);
  470. var topLevelSeq = asn1Parser.nextObject() as ASN1Sequence;
  471. var publicKeySeq;
  472. if (topLevelSeq.elements![1].runtimeType == ASN1BitString) {
  473. var publicKeyBitString = topLevelSeq.elements![1] as ASN1BitString;
  474. var publicKeyAsn =
  475. ASN1Parser(publicKeyBitString.stringValues as Uint8List?);
  476. publicKeySeq = publicKeyAsn.nextObject() as ASN1Sequence;
  477. } else {
  478. publicKeySeq = topLevelSeq;
  479. }
  480. var modulus = publicKeySeq.elements![0] as ASN1Integer;
  481. var exponent = publicKeySeq.elements![1] as ASN1Integer;
  482. var rsaPublicKey = RSAPublicKey(modulus.integer!, exponent.integer!);
  483. return rsaPublicKey;
  484. }
  485. ///
  486. /// Decode a [RSAPublicKey] from the given [pem] string formated in the pkcs1 standard.
  487. ///
  488. static RSAPublicKey rsaPublicKeyFromPemPkcs1(String pem) {
  489. var bytes = CryptoUtils.getBytesFromPEMString(pem);
  490. return rsaPublicKeyFromDERBytesPkcs1(bytes);
  491. }
  492. ///
  493. /// Decode the given [bytes] into an [RSAPublicKey].
  494. ///
  495. /// The [bytes] need to follow the the pkcs1 standard
  496. ///
  497. static RSAPublicKey rsaPublicKeyFromDERBytesPkcs1(Uint8List bytes) {
  498. var publicKeyAsn = ASN1Parser(bytes);
  499. var publicKeySeq = publicKeyAsn.nextObject() as ASN1Sequence;
  500. var modulus = publicKeySeq.elements![0] as ASN1Integer;
  501. var exponent = publicKeySeq.elements![1] as ASN1Integer;
  502. var rsaPublicKey = RSAPublicKey(modulus.integer!, exponent.integer!);
  503. return rsaPublicKey;
  504. }
  505. ///
  506. /// Enode the given elliptic curve [publicKey] to PEM format.
  507. ///
  508. /// This is descripted in <https://tools.ietf.org/html/rfc5915>
  509. ///
  510. /// ```ASN1
  511. /// ECPrivateKey ::= SEQUENCE {
  512. /// version INTEGER { ecPrivkeyVer1(1) } (ecPrivkeyVer1),
  513. /// privateKey OCTET STRING
  514. /// parameters [0] ECParameters {{ NamedCurve }} OPTIONAL
  515. /// publicKey [1] BIT STRING OPTIONAL
  516. /// }
  517. ///
  518. /// ```
  519. ///
  520. /// As descripted in the mentioned RFC, all optional values will always be set.
  521. ///
  522. static String encodeEcPrivateKeyToPem(ECPrivateKey ecPrivateKey) {
  523. var outer = ASN1Sequence();
  524. var version = ASN1Integer(BigInt.from(1));
  525. var privateKeyAsBytes = thing.encodeBigInt(ecPrivateKey.d);
  526. var privateKey = ASN1OctetString(octets: privateKeyAsBytes);
  527. var choice = ASN1Sequence(tag: 0xA0);
  528. choice.add(
  529. ASN1ObjectIdentifier.fromName(ecPrivateKey.parameters!.domainName));
  530. var publicKey = ASN1Sequence(tag: 0xA1);
  531. var subjectPublicKey = ASN1BitString(
  532. stringValues: ecPrivateKey.parameters!.G.getEncoded(false));
  533. publicKey.add(subjectPublicKey);
  534. outer.add(version);
  535. outer.add(privateKey);
  536. outer.add(choice);
  537. outer.add(publicKey);
  538. var dataBase64 = base64.encode(outer.encode());
  539. var chunks = StringUtils.chunk(dataBase64, 64);
  540. return '$BEGIN_EC_PRIVATE_KEY\n${chunks.join('\n')}\n$END_EC_PRIVATE_KEY';
  541. }
  542. ///
  543. /// Enode the given elliptic curve [publicKey] to PEM format.
  544. ///
  545. /// This is descripted in <https://tools.ietf.org/html/rfc5480>
  546. ///
  547. /// ```ASN1
  548. /// SubjectPublicKeyInfo ::= SEQUENCE {
  549. /// algorithm AlgorithmIdentifier,
  550. /// subjectPublicKey BIT STRING
  551. /// }
  552. /// ```
  553. ///
  554. static String encodeEcPublicKeyToPem(ECPublicKey publicKey) {
  555. var outer = ASN1Sequence();
  556. var algorithm = ASN1Sequence();
  557. algorithm.add(ASN1ObjectIdentifier.fromName('ecPublicKey'));
  558. algorithm.add(ASN1ObjectIdentifier.fromName('prime256v1'));
  559. var encodedBytes = publicKey.Q!.getEncoded(false);
  560. var subjectPublicKey = ASN1BitString(stringValues: encodedBytes);
  561. outer.add(algorithm);
  562. outer.add(subjectPublicKey);
  563. var dataBase64 = base64.encode(outer.encode());
  564. var chunks = StringUtils.chunk(dataBase64, 64);
  565. return '$BEGIN_EC_PUBLIC_KEY\n${chunks.join('\n')}\n$END_EC_PUBLIC_KEY';
  566. }
  567. ///
  568. /// Decode a [ECPublicKey] from the given [pem] String.
  569. ///
  570. /// Throws an ArgumentError if the given string [pem] is null or empty.
  571. ///
  572. static ECPublicKey ecPublicKeyFromPem(String pem) {
  573. if (pem.isEmpty) {
  574. throw ArgumentError('Argument must not be null.');
  575. }
  576. var bytes = CryptoUtils.getBytesFromPEMString(pem);
  577. return ecPublicKeyFromDerBytes(bytes);
  578. }
  579. ///
  580. /// Decode a [ECPrivateKey] from the given [pem] String.
  581. ///
  582. /// Throws an ArgumentError if the given string [pem] is null or empty.
  583. ///
  584. static ECPrivateKey ecPrivateKeyFromPem(String pem) {
  585. if (pem.isEmpty) {
  586. throw ArgumentError('Argument must not be null.');
  587. }
  588. var bytes = CryptoUtils.getBytesFromPEMString(pem);
  589. return ecPrivateKeyFromDerBytes(
  590. bytes,
  591. pkcs8: pem.startsWith(BEGIN_PRIVATE_KEY),
  592. );
  593. }
  594. ///
  595. /// Decode the given [bytes] into an [ECPrivateKey].
  596. ///
  597. /// [pkcs8] defines the ASN1 format of the given [bytes]. The default is false, so SEC1 is assumed.
  598. ///
  599. /// Supports SEC1 (<https://tools.ietf.org/html/rfc5915>) and PKCS8 (<https://datatracker.ietf.org/doc/html/rfc5208>)
  600. ///
  601. static ECPrivateKey ecPrivateKeyFromDerBytes(Uint8List bytes,
  602. {bool pkcs8 = false}) {
  603. var asn1Parser = ASN1Parser(bytes);
  604. var topLevelSeq = asn1Parser.nextObject() as ASN1Sequence;
  605. var curveName;
  606. var x;
  607. if (pkcs8) {
  608. // Parse the PKCS8 format
  609. var innerSeq = topLevelSeq.elements!.elementAt(1) as ASN1Sequence;
  610. var b2 = innerSeq.elements!.elementAt(1) as ASN1ObjectIdentifier;
  611. var b2Data = b2.objectIdentifierAsString;
  612. var b2Curvedata = ObjectIdentifiers.getIdentifierByIdentifier(b2Data);
  613. if (b2Curvedata != null) {
  614. curveName = b2Curvedata['readableName'];
  615. }
  616. var octetString = topLevelSeq.elements!.elementAt(2) as ASN1OctetString;
  617. asn1Parser = ASN1Parser(octetString.valueBytes);
  618. var octetStringSeq = asn1Parser.nextObject() as ASN1Sequence;
  619. var octetStringKeyData =
  620. octetStringSeq.elements!.elementAt(1) as ASN1OctetString;
  621. x = octetStringKeyData.valueBytes!;
  622. } else {
  623. // Parse the SEC1 format
  624. var privateKeyAsOctetString =
  625. topLevelSeq.elements!.elementAt(1) as ASN1OctetString;
  626. var choice = topLevelSeq.elements!.elementAt(2);
  627. var s = ASN1Sequence();
  628. var parser = ASN1Parser(choice.valueBytes);
  629. while (parser.hasNext()) {
  630. s.add(parser.nextObject());
  631. }
  632. var curveNameOi = s.elements!.elementAt(0) as ASN1ObjectIdentifier;
  633. var data = ObjectIdentifiers.getIdentifierByIdentifier(
  634. curveNameOi.objectIdentifierAsString);
  635. if (data != null) {
  636. curveName = data['readableName'];
  637. }
  638. x = privateKeyAsOctetString.valueBytes!;
  639. }
  640. return ECPrivateKey(thing.decodeBigInt(x), ECDomainParameters(curveName));
  641. }
  642. ///
  643. /// Decode the given [bytes] into an [ECPublicKey].
  644. ///
  645. static ECPublicKey ecPublicKeyFromDerBytes(Uint8List bytes) {
  646. if (bytes.elementAt(0) == 0) {
  647. bytes = bytes.sublist(1);
  648. }
  649. var asn1Parser = ASN1Parser(bytes);
  650. var topLevelSeq = asn1Parser.nextObject() as ASN1Sequence;
  651. var algorithmIdentifierSequence = topLevelSeq.elements![0] as ASN1Sequence;
  652. var curveNameOi = algorithmIdentifierSequence.elements!.elementAt(1)
  653. as ASN1ObjectIdentifier;
  654. var curveName;
  655. var data = ObjectIdentifiers.getIdentifierByIdentifier(
  656. curveNameOi.objectIdentifierAsString);
  657. if (data != null) {
  658. curveName = data['readableName'];
  659. }
  660. var subjectPublicKey = topLevelSeq.elements![1] as ASN1BitString;
  661. var compressed = false;
  662. var pubBytes = subjectPublicKey.valueBytes!;
  663. if (pubBytes.elementAt(0) == 0) {
  664. pubBytes = pubBytes.sublist(1);
  665. }
  666. // Looks good so far!
  667. var firstByte = pubBytes.elementAt(0);
  668. if (firstByte != 4) {
  669. compressed = true;
  670. }
  671. var x = pubBytes.sublist(1, (pubBytes.length / 2).round());
  672. var y = pubBytes.sublist(1 + x.length, pubBytes.length);
  673. var params = ECDomainParameters(curveName);
  674. var bigX = thing.decodeBigIntWithSign(1, x);
  675. var bigY = thing.decodeBigIntWithSign(1, y);
  676. var pubKey = ECPublicKey(
  677. ecc_fp.ECPoint(
  678. params.curve as ecc_fp.ECCurve,
  679. params.curve.fromBigInteger(bigX) as ecc_fp.ECFieldElement?,
  680. params.curve.fromBigInteger(bigY) as ecc_fp.ECFieldElement?,
  681. compressed),
  682. params);
  683. return pubKey;
  684. }
  685. ///
  686. /// Encrypt the given [message] using the given RSA [publicKey].
  687. ///
  688. static Uint8List rsaEncrypt(Uint8List message, RSAPublicKey publicKey) {
  689. var cipher = OAEPEncoding.withSHA256(RSAEngine())
  690. ..init(true, PublicKeyParameter<RSAPublicKey>(publicKey));
  691. return _processInBlocks(cipher, message);
  692. }
  693. ///
  694. /// Decrypt the given [cipherMessage] using the given RSA [privateKey].
  695. ///
  696. static Uint8List rsaDecrypt(Uint8List cipherMessage, RSAPrivateKey privateKey) {
  697. var cipher = OAEPEncoding.withSHA256(RSAEngine())
  698. ..init(false, PrivateKeyParameter<RSAPrivateKey>(privateKey));
  699. return _processInBlocks(cipher, cipherMessage);
  700. }
  701. static Uint8List _processInBlocks(AsymmetricBlockCipher engine, Uint8List input) {
  702. final numBlocks = input.length ~/ engine.inputBlockSize +
  703. ((input.length % engine.inputBlockSize != 0) ? 1 : 0);
  704. final output = Uint8List(numBlocks * engine.outputBlockSize);
  705. var inputOffset = 0;
  706. var outputOffset = 0;
  707. while (inputOffset < input.length) {
  708. final chunkSize = (inputOffset + engine.inputBlockSize <= input.length)
  709. ? engine.inputBlockSize
  710. : input.length - inputOffset;
  711. outputOffset += engine.processBlock(
  712. input, inputOffset, chunkSize, output, outputOffset);
  713. inputOffset += chunkSize;
  714. }
  715. return (output.length == outputOffset)
  716. ? output
  717. : output.sublist(0, outputOffset);
  718. }
  719. ///
  720. /// Signing the given [dataToSign] with the given [privateKey].
  721. ///
  722. /// The default [algorithm] used is **SHA-256/RSA**. All supported algorihms are :
  723. ///
  724. /// * MD2/RSA
  725. /// * MD4/RSA
  726. /// * MD5/RSA
  727. /// * RIPEMD-128/RSA
  728. /// * RIPEMD-160/RSA
  729. /// * RIPEMD-256/RSA
  730. /// * SHA-1/RSA
  731. /// * SHA-224/RSA
  732. /// * SHA-256/RSA
  733. /// * SHA-384/RSA
  734. /// * SHA-512/RSA
  735. ///
  736. static Uint8List rsaSign(RSAPrivateKey privateKey, Uint8List dataToSign,
  737. {String algorithmName = 'SHA-256/RSA'}) {
  738. var signer = Signer(algorithmName) as RSASigner;
  739. signer.init(true, PrivateKeyParameter<RSAPrivateKey>(privateKey));
  740. var sig = signer.generateSignature(dataToSign);
  741. return sig.bytes;
  742. }
  743. ///
  744. /// Verifying the given [signedData] with the given [publicKey] and the given [signature].
  745. /// Will return **true** if the given [signature] matches the [signedData].
  746. ///
  747. /// The default [algorithm] used is **SHA-256/RSA**. All supported algorihms are :
  748. ///
  749. /// * MD2/RSA
  750. /// * MD4/RSA
  751. /// * MD5/RSA
  752. /// * RIPEMD-128/RSA
  753. /// * RIPEMD-160/RSA
  754. /// * RIPEMD-256/RSA
  755. /// * SHA-1/RSA
  756. /// * SHA-224/RSA
  757. /// * SHA-256/RSA
  758. /// * SHA-384/RSA
  759. /// * SHA-512/RSA
  760. ///
  761. static bool rsaVerify(
  762. RSAPublicKey publicKey, Uint8List signedData, Uint8List signature,
  763. {String algorithm = 'SHA-256/RSA'}) {
  764. final sig = RSASignature(signature);
  765. final verifier = Signer(algorithm);
  766. verifier.init(false, PublicKeyParameter<RSAPublicKey>(publicKey));
  767. try {
  768. return verifier.verifySignature(signedData, sig);
  769. } on ArgumentError {
  770. return false;
  771. }
  772. }
  773. ///
  774. /// Signing the given [dataToSign] with the given [privateKey].
  775. ///
  776. /// The default [algorithm] used is **SHA-1/ECDSA**. All supported algorihms are :
  777. ///
  778. /// * SHA-1/ECDSA
  779. /// * SHA-224/ECDSA
  780. /// * SHA-256/ECDSA
  781. /// * SHA-384/ECDSA
  782. /// * SHA-512/ECDSA
  783. /// * SHA-1/DET-ECDSA
  784. /// * SHA-224/DET-ECDSA
  785. /// * SHA-256/DET-ECDSA
  786. /// * SHA-384/DET-ECDSA
  787. /// * SHA-512/DET-ECDSA
  788. ///
  789. static ECSignature ecSign(ECPrivateKey privateKey, Uint8List dataToSign,
  790. {String algorithmName = 'SHA-1/ECDSA'}) {
  791. var signer = Signer(algorithmName) as ECDSASigner;
  792. var params = ParametersWithRandom(
  793. PrivateKeyParameter<ECPrivateKey>(privateKey), _getSecureRandom());
  794. signer.init(true, params);
  795. var sig = signer.generateSignature(dataToSign) as ECSignature;
  796. return sig;
  797. }
  798. ///
  799. /// Verifying the given [signedData] with the given [publicKey] and the given [signature].
  800. /// Will return **true** if the given [signature] matches the [signedData].
  801. ///
  802. /// The default [algorithm] used is **SHA-1/ECDSA**. All supported algorihms are :
  803. ///
  804. /// * SHA-1/ECDSA
  805. /// * SHA-224/ECDSA
  806. /// * SHA-256/ECDSA
  807. /// * SHA-384/ECDSA
  808. /// * SHA-512/ECDSA
  809. /// * SHA-1/DET-ECDSA
  810. /// * SHA-224/DET-ECDSA
  811. /// * SHA-256/DET-ECDSA
  812. /// * SHA-384/DET-ECDSA
  813. /// * SHA-512/DET-ECDSA
  814. ///
  815. static bool ecVerify(
  816. ECPublicKey publicKey, Uint8List signedData, ECSignature signature,
  817. {String algorithm = 'SHA-1/ECDSA'}) {
  818. final verifier = Signer(algorithm) as ECDSASigner;
  819. verifier.init(false, PublicKeyParameter<ECPublicKey>(publicKey));
  820. try {
  821. return verifier.verifySignature(signedData, signature);
  822. } on ArgumentError {
  823. return false;
  824. }
  825. }
  826. ///
  827. /// Returns the modulus of the given [pem] that represents an RSA private key.
  828. ///
  829. /// This equals the following openssl command:
  830. /// ```
  831. /// openssl rsa -noout -modulus -in FILE.key
  832. /// ```
  833. ///
  834. static BigInt getModulusFromRSAPrivateKeyPem(String pem) {
  835. RSAPrivateKey privateKey;
  836. switch (_getPrivateKeyType(pem)) {
  837. case 'RSA':
  838. privateKey = rsaPrivateKeyFromPem(pem);
  839. return privateKey.modulus!;
  840. case 'RSA_PKCS1':
  841. privateKey = rsaPrivateKeyFromPemPkcs1(pem);
  842. return privateKey.modulus!;
  843. case 'ECC':
  844. throw ArgumentError('ECC private key not supported.');
  845. default:
  846. privateKey = rsaPrivateKeyFromPem(pem);
  847. return privateKey.modulus!;
  848. }
  849. }
  850. ///
  851. /// Returns the private key type of the given [pem]
  852. ///
  853. static String _getPrivateKeyType(String pem) {
  854. if (pem.startsWith(BEGIN_RSA_PRIVATE_KEY)) {
  855. return 'RSA_PKCS1';
  856. } else if (pem.startsWith(BEGIN_PRIVATE_KEY)) {
  857. return 'RSA';
  858. } else if (pem.startsWith(BEGIN_EC_PRIVATE_KEY)) {
  859. return 'ECC';
  860. }
  861. return 'RSA';
  862. }
  863. }