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.

260 lines
9.3 KiB

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