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.

257 lines
9.3 KiB

  1. import 'dart:convert';
  2. import 'dart:math';
  3. import 'dart:typed_data';
  4. import 'package:pointycastle/src/platform_check/platform_check.dart';
  5. import "package:pointycastle/export.dart";
  6. import "package:asn1lib/asn1lib.dart";
  7. /*
  8. var rsaKeyHelper = RsaKeyHelper();
  9. var keyPair = rsaKeyHelper.generateRSAkeyPair();
  10. var rsaPub = keyPair.publicKey;
  11. var rsaPriv = keyPair.privateKey;
  12. var cipherText = rsaKeyHelper.rsaEncrypt(rsaPub, Uint8List.fromList('Test Data'.codeUnits));
  13. print(cipherText);
  14. var plainText = rsaKeyHelper.rsaDecrypt(rsaPriv, cipherText);
  15. print(String.fromCharCodes(plainText));
  16. */
  17. List<int> decodePEM(String pem) {
  18. var startsWith = [
  19. "-----BEGIN PUBLIC KEY-----",
  20. "-----BEGIN PRIVATE KEY-----",
  21. "-----BEGIN PGP PUBLIC KEY BLOCK-----\r\nVersion: React-Native-OpenPGP.js 0.1\r\nComment: http://openpgpjs.org\r\n\r\n",
  22. "-----BEGIN PGP PRIVATE KEY BLOCK-----\r\nVersion: React-Native-OpenPGP.js 0.1\r\nComment: http://openpgpjs.org\r\n\r\n",
  23. ];
  24. var endsWith = [
  25. "-----END PUBLIC KEY-----",
  26. "-----END PRIVATE KEY-----",
  27. "-----END PGP PUBLIC KEY BLOCK-----",
  28. "-----END PGP PRIVATE KEY BLOCK-----",
  29. ];
  30. bool isOpenPgp = pem.contains('BEGIN PGP');
  31. for (var s in startsWith) {
  32. if (pem.startsWith(s)) {
  33. pem = pem.substring(s.length);
  34. }
  35. }
  36. for (var s in endsWith) {
  37. if (pem.endsWith(s)) {
  38. pem = pem.substring(0, pem.length - s.length);
  39. }
  40. }
  41. if (isOpenPgp) {
  42. var index = pem.indexOf('\r\n');
  43. pem = pem.substring(0, index);
  44. }
  45. pem = pem.replaceAll('\n', '');
  46. pem = pem.replaceAll('\r', '');
  47. return base64.decode(pem);
  48. }
  49. class RsaKeyHelper {
  50. // Generate secure random sequence for seed
  51. SecureRandom secureRandom() {
  52. var secureRandom = FortunaRandom();
  53. var random = Random.secure();
  54. List<int> seeds = [];
  55. for (int i = 0; i < 32; i++) {
  56. seeds.add(random.nextInt(255));
  57. }
  58. secureRandom.seed(KeyParameter(Uint8List.fromList(seeds)));
  59. return secureRandom;
  60. }
  61. // Generate RSA key pair
  62. AsymmetricKeyPair<RSAPublicKey, RSAPrivateKey> generateRSAkeyPair({int bitLength = 2048}) {
  63. var secureRandom = this.secureRandom();
  64. // final keyGen = KeyGenerator('RSA'); // Get using registry
  65. final keyGen = RSAKeyGenerator();
  66. keyGen.init(ParametersWithRandom(
  67. RSAKeyGeneratorParameters(BigInt.parse('65537'), bitLength, 64),
  68. secureRandom));
  69. // Use the generator
  70. final pair = keyGen.generateKeyPair();
  71. // Cast the generated key pair into the RSA key types
  72. final myPublic = pair.publicKey as RSAPublicKey;
  73. final myPrivate = pair.privateKey as RSAPrivateKey;
  74. return AsymmetricKeyPair<RSAPublicKey, RSAPrivateKey>(myPublic, myPrivate);
  75. }
  76. // Encrypt data using RSA key
  77. Uint8List rsaEncrypt(RSAPublicKey myPublic, Uint8List dataToEncrypt) {
  78. final encryptor = OAEPEncoding(RSAEngine())
  79. ..init(true, PublicKeyParameter<RSAPublicKey>(myPublic)); // true=encrypt
  80. return _processInBlocks(encryptor, dataToEncrypt);
  81. }
  82. // Decrypt data using RSA key
  83. Uint8List rsaDecrypt(RSAPrivateKey myPrivate, Uint8List cipherText) {
  84. final decryptor = OAEPEncoding(RSAEngine())
  85. ..init(false, PrivateKeyParameter<RSAPrivateKey>(myPrivate)); // false=decrypt
  86. return _processInBlocks(decryptor, cipherText);
  87. }
  88. // Process blocks after encryption/descryption
  89. Uint8List _processInBlocks(AsymmetricBlockCipher engine, Uint8List input) {
  90. final numBlocks = input.length ~/ engine.inputBlockSize +
  91. ((input.length % engine.inputBlockSize != 0) ? 1 : 0);
  92. final output = Uint8List(numBlocks * engine.outputBlockSize);
  93. var inputOffset = 0;
  94. var outputOffset = 0;
  95. while (inputOffset < input.length) {
  96. final chunkSize = (inputOffset + engine.inputBlockSize <= input.length)
  97. ? engine.inputBlockSize
  98. : input.length - inputOffset;
  99. outputOffset += engine.processBlock(
  100. input, inputOffset, chunkSize, output, outputOffset);
  101. inputOffset += chunkSize;
  102. }
  103. return (output.length == outputOffset)
  104. ? output
  105. : output.sublist(0, outputOffset);
  106. }
  107. // Encode RSA public key to pem format
  108. static encodePublicKeyToPem(RSAPublicKey publicKey) {
  109. var algorithmSeq = ASN1Sequence();
  110. var algorithmAsn1Obj = ASN1Object.fromBytes(Uint8List.fromList([0x6, 0x9, 0x2a, 0x86, 0x48, 0x86, 0xf7, 0xd, 0x1, 0x1, 0x1]));
  111. var paramsAsn1Obj = ASN1Object.fromBytes(Uint8List.fromList([0x5, 0x0]));
  112. algorithmSeq.add(algorithmAsn1Obj);
  113. algorithmSeq.add(paramsAsn1Obj);
  114. var publicKeySeq = ASN1Sequence();
  115. publicKeySeq.add(ASN1Integer(publicKey.modulus!));
  116. publicKeySeq.add(ASN1Integer(publicKey.exponent!));
  117. var publicKeySeqBitString = ASN1BitString(Uint8List.fromList(publicKeySeq.encodedBytes));
  118. var topLevelSeq = ASN1Sequence();
  119. topLevelSeq.add(algorithmSeq);
  120. topLevelSeq.add(publicKeySeqBitString);
  121. var dataBase64 = base64.encode(topLevelSeq.encodedBytes);
  122. return """-----BEGIN PUBLIC KEY-----\r\n$dataBase64\r\n-----END PUBLIC KEY-----""";
  123. }
  124. // Parse public key PEM file
  125. static parsePublicKeyFromPem(pemString) {
  126. List<int> publicKeyDER = decodePEM(pemString);
  127. var asn1Parser = ASN1Parser(Uint8List.fromList(publicKeyDER));
  128. var topLevelSeq = asn1Parser.nextObject() as ASN1Sequence;
  129. var publicKeyBitString = topLevelSeq.elements[1];
  130. var publicKeyAsn = ASN1Parser(publicKeyBitString.contentBytes()!, relaxedParsing: true);
  131. var publicKeySeq = publicKeyAsn.nextObject() as ASN1Sequence;
  132. var modulus = publicKeySeq.elements[0] as ASN1Integer;
  133. var exponent = publicKeySeq.elements[1] as ASN1Integer;
  134. RSAPublicKey rsaPublicKey = RSAPublicKey(
  135. modulus.valueAsBigInteger!,
  136. exponent.valueAsBigInteger!
  137. );
  138. return rsaPublicKey;
  139. }
  140. // Encode RSA private key to pem format
  141. static encodePrivateKeyToPem(RSAPrivateKey privateKey) {
  142. var version = ASN1Integer(BigInt.from(0));
  143. var algorithmSeq = ASN1Sequence();
  144. var algorithmAsn1Obj = ASN1Object.fromBytes(Uint8List.fromList([0x6, 0x9, 0x2a, 0x86, 0x48, 0x86, 0xf7, 0xd, 0x1, 0x1, 0x1]));
  145. var paramsAsn1Obj = ASN1Object.fromBytes(Uint8List.fromList([0x5, 0x0]));
  146. algorithmSeq.add(algorithmAsn1Obj);
  147. algorithmSeq.add(paramsAsn1Obj);
  148. var privateKeySeq = ASN1Sequence();
  149. var modulus = ASN1Integer(privateKey.n!);
  150. var publicExponent = ASN1Integer(BigInt.parse('65537'));
  151. var privateExponent = ASN1Integer(privateKey.privateExponent!);
  152. var p = ASN1Integer(privateKey.p!);
  153. var q = ASN1Integer(privateKey.q!);
  154. var dP = privateKey.privateExponent! % (privateKey.p! - BigInt.from(1));
  155. var exp1 = ASN1Integer(dP);
  156. var dQ = privateKey.privateExponent! % (privateKey.q! - BigInt.from(1));
  157. var exp2 = ASN1Integer(dQ);
  158. var iQ = privateKey.q?.modInverse(privateKey.p!);
  159. var co = ASN1Integer(iQ!);
  160. privateKeySeq.add(version);
  161. privateKeySeq.add(modulus);
  162. privateKeySeq.add(publicExponent);
  163. privateKeySeq.add(privateExponent);
  164. privateKeySeq.add(p);
  165. privateKeySeq.add(q);
  166. privateKeySeq.add(exp1);
  167. privateKeySeq.add(exp2);
  168. privateKeySeq.add(co);
  169. var publicKeySeqOctetString = ASN1OctetString(Uint8List.fromList(privateKeySeq.encodedBytes));
  170. var topLevelSeq = ASN1Sequence();
  171. topLevelSeq.add(version);
  172. topLevelSeq.add(algorithmSeq);
  173. topLevelSeq.add(publicKeySeqOctetString);
  174. var dataBase64 = base64.encode(topLevelSeq.encodedBytes);
  175. return """-----BEGIN PRIVATE KEY-----\r\n$dataBase64\r\n-----END PRIVATE KEY-----""";
  176. }
  177. static parsePrivateKeyFromPem(pemString) {
  178. List<int> privateKeyDER = decodePEM(pemString);
  179. var asn1Parser = ASN1Parser(Uint8List.fromList(privateKeyDER));
  180. var topLevelSeq = asn1Parser.nextObject() as ASN1Sequence;
  181. var version = topLevelSeq.elements[0];
  182. var algorithm = topLevelSeq.elements[1];
  183. var privateKey = topLevelSeq.elements[2];
  184. asn1Parser = ASN1Parser(privateKey.contentBytes()!);
  185. var pkSeq = asn1Parser.nextObject() as ASN1Sequence;
  186. version = pkSeq.elements[0];
  187. var modulus = pkSeq.elements[1] as ASN1Integer;
  188. //var publicExponent = pkSeq.elements[2] as ASN1Integer;
  189. var privateExponent = pkSeq.elements[3] as ASN1Integer;
  190. var p = pkSeq.elements[4] as ASN1Integer;
  191. var q = pkSeq.elements[5] as ASN1Integer;
  192. var exp1 = pkSeq.elements[6] as ASN1Integer;
  193. var exp2 = pkSeq.elements[7] as ASN1Integer;
  194. var co = pkSeq.elements[8] as ASN1Integer;
  195. RSAPrivateKey rsaPrivateKey = RSAPrivateKey(
  196. modulus.valueAsBigInteger!,
  197. privateExponent.valueAsBigInteger!,
  198. p.valueAsBigInteger,
  199. q.valueAsBigInteger
  200. );
  201. return rsaPrivateKey;
  202. }
  203. }