import 'dart:convert';
|
|
import 'dart:math';
|
|
import 'dart:typed_data';
|
|
import "package:pointycastle/export.dart";
|
|
import "package:asn1lib/asn1lib.dart";
|
|
|
|
/*
|
|
var rsaKeyHelper = RsaKeyHelper();
|
|
var keyPair = rsaKeyHelper.generateRSAkeyPair();
|
|
var rsaPub = keyPair.publicKey;
|
|
var rsaPriv = keyPair.privateKey;
|
|
var cipherText = rsaKeyHelper.rsaEncrypt(rsaPub, Uint8List.fromList('Test Data'.codeUnits));
|
|
print(cipherText);
|
|
var plainText = rsaKeyHelper.rsaDecrypt(rsaPriv, cipherText);
|
|
print(String.fromCharCodes(plainText));
|
|
*/
|
|
|
|
|
|
List<int> decodePEM(String pem) {
|
|
var startsWith = [
|
|
'-----BEGIN PUBLIC KEY-----',
|
|
'-----BEGIN PUBLIC KEY-----',
|
|
'\n-----BEGIN PUBLIC KEY-----',
|
|
'-----BEGIN PRIVATE KEY-----',
|
|
'-----BEGIN PGP PUBLIC KEY BLOCK-----\r\nVersion: React-Native-OpenPGP.js 0.1\r\nComment: http://openpgpjs.org\r\n\r\n',
|
|
'-----BEGIN PGP PRIVATE KEY BLOCK-----\r\nVersion: React-Native-OpenPGP.js 0.1\r\nComment: http://openpgpjs.org\r\n\r\n',
|
|
];
|
|
var endsWith = [
|
|
'-----END PUBLIC KEY-----',
|
|
'-----END PRIVATE KEY-----',
|
|
'-----END PGP PUBLIC KEY BLOCK-----',
|
|
'-----END PGP PRIVATE KEY BLOCK-----',
|
|
];
|
|
bool isOpenPgp = pem.contains('BEGIN PGP');
|
|
|
|
for (var s in startsWith) {
|
|
if (pem.startsWith(s)) {
|
|
pem = pem.substring(s.length);
|
|
}
|
|
}
|
|
|
|
for (var s in endsWith) {
|
|
if (pem.endsWith(s)) {
|
|
pem = pem.substring(0, pem.length - s.length);
|
|
}
|
|
}
|
|
|
|
if (isOpenPgp) {
|
|
var index = pem.indexOf('\r\n');
|
|
pem = pem.substring(0, index);
|
|
}
|
|
|
|
pem = pem.replaceAll('\n', '');
|
|
pem = pem.replaceAll('\r', '');
|
|
|
|
return base64.decode(pem);
|
|
}
|
|
|
|
|
|
class RsaKeyHelper {
|
|
|
|
// Generate secure random sequence for seed
|
|
SecureRandom secureRandom() {
|
|
var secureRandom = FortunaRandom();
|
|
var random = Random.secure();
|
|
List<int> seeds = [];
|
|
for (int i = 0; i < 32; i++) {
|
|
seeds.add(random.nextInt(255));
|
|
}
|
|
secureRandom.seed(KeyParameter(Uint8List.fromList(seeds)));
|
|
|
|
return secureRandom;
|
|
}
|
|
|
|
// Generate RSA key pair
|
|
AsymmetricKeyPair<RSAPublicKey, RSAPrivateKey> generateRSAkeyPair({int bitLength = 2048}) {
|
|
var secureRandom = this.secureRandom();
|
|
|
|
// final keyGen = KeyGenerator('RSA'); // Get using registry
|
|
final keyGen = RSAKeyGenerator();
|
|
|
|
keyGen.init(ParametersWithRandom(
|
|
RSAKeyGeneratorParameters(BigInt.parse('65537'), bitLength, 64),
|
|
secureRandom));
|
|
|
|
// Use the generator
|
|
|
|
final pair = keyGen.generateKeyPair();
|
|
|
|
// Cast the generated key pair into the RSA key types
|
|
|
|
final myPublic = pair.publicKey as RSAPublicKey;
|
|
final myPrivate = pair.privateKey as RSAPrivateKey;
|
|
|
|
return AsymmetricKeyPair<RSAPublicKey, RSAPrivateKey>(myPublic, myPrivate);
|
|
}
|
|
|
|
|
|
// Encrypt data using RSA key
|
|
Uint8List rsaEncrypt(RSAPublicKey myPublic, Uint8List dataToEncrypt) {
|
|
final encryptor = OAEPEncoding(RSAEngine())
|
|
..init(true, PublicKeyParameter<RSAPublicKey>(myPublic)); // true=encrypt
|
|
|
|
return _processInBlocks(encryptor, dataToEncrypt);
|
|
}
|
|
|
|
|
|
// Decrypt data using RSA key
|
|
Uint8List rsaDecrypt(RSAPrivateKey myPrivate, Uint8List cipherText) {
|
|
final decryptor = OAEPEncoding(RSAEngine())
|
|
..init(false, PrivateKeyParameter<RSAPrivateKey>(myPrivate)); // false=decrypt
|
|
|
|
return _processInBlocks(decryptor, cipherText);
|
|
}
|
|
|
|
|
|
// Process blocks after encryption/descryption
|
|
Uint8List _processInBlocks(AsymmetricBlockCipher engine, Uint8List input) {
|
|
final numBlocks = input.length ~/ engine.inputBlockSize +
|
|
((input.length % engine.inputBlockSize != 0) ? 1 : 0);
|
|
|
|
final output = Uint8List(numBlocks * engine.outputBlockSize);
|
|
|
|
var inputOffset = 0;
|
|
var outputOffset = 0;
|
|
while (inputOffset < input.length) {
|
|
final chunkSize = (inputOffset + engine.inputBlockSize <= input.length)
|
|
? engine.inputBlockSize
|
|
: input.length - inputOffset;
|
|
|
|
outputOffset += engine.processBlock(
|
|
input, inputOffset, chunkSize, output, outputOffset);
|
|
|
|
inputOffset += chunkSize;
|
|
}
|
|
|
|
return (output.length == outputOffset)
|
|
? output
|
|
: output.sublist(0, outputOffset);
|
|
}
|
|
|
|
|
|
// Encode RSA public key to pem format
|
|
static encodePublicKeyToPem(RSAPublicKey publicKey) {
|
|
var algorithmSeq = ASN1Sequence();
|
|
var algorithmAsn1Obj = ASN1Object.fromBytes(Uint8List.fromList([0x6, 0x9, 0x2a, 0x86, 0x48, 0x86, 0xf7, 0xd, 0x1, 0x1, 0x1]));
|
|
var paramsAsn1Obj = ASN1Object.fromBytes(Uint8List.fromList([0x5, 0x0]));
|
|
algorithmSeq.add(algorithmAsn1Obj);
|
|
algorithmSeq.add(paramsAsn1Obj);
|
|
|
|
var publicKeySeq = ASN1Sequence();
|
|
publicKeySeq.add(ASN1Integer(publicKey.modulus!));
|
|
publicKeySeq.add(ASN1Integer(publicKey.exponent!));
|
|
var publicKeySeqBitString = ASN1BitString(Uint8List.fromList(publicKeySeq.encodedBytes));
|
|
|
|
var topLevelSeq = ASN1Sequence();
|
|
topLevelSeq.add(algorithmSeq);
|
|
topLevelSeq.add(publicKeySeqBitString);
|
|
var dataBase64 = base64.encode(topLevelSeq.encodedBytes);
|
|
|
|
return """-----BEGIN PUBLIC KEY-----\r\n$dataBase64\r\n-----END PUBLIC KEY-----""";
|
|
}
|
|
|
|
// Parse public key PEM file
|
|
static parsePublicKeyFromPem(pemString) {
|
|
List<int> publicKeyDER = decodePEM(pemString);
|
|
var asn1Parser = ASN1Parser(Uint8List.fromList(publicKeyDER));
|
|
var topLevelSeq = asn1Parser.nextObject() as ASN1Sequence;
|
|
var publicKeyBitString = topLevelSeq.elements[1];
|
|
|
|
var publicKeyAsn = ASN1Parser(publicKeyBitString.contentBytes()!, relaxedParsing: true);
|
|
var publicKeySeq = publicKeyAsn.nextObject() as ASN1Sequence;
|
|
var modulus = publicKeySeq.elements[0] as ASN1Integer;
|
|
var exponent = publicKeySeq.elements[1] as ASN1Integer;
|
|
|
|
RSAPublicKey rsaPublicKey = RSAPublicKey(
|
|
modulus.valueAsBigInteger!,
|
|
exponent.valueAsBigInteger!
|
|
);
|
|
|
|
return rsaPublicKey;
|
|
}
|
|
|
|
|
|
// Encode RSA private key to pem format
|
|
static encodePrivateKeyToPem(RSAPrivateKey privateKey) {
|
|
var version = ASN1Integer(BigInt.zero);
|
|
|
|
var algorithmSeq = ASN1Sequence();
|
|
var algorithmAsn1Obj = ASN1Object.fromBytes(Uint8List.fromList([
|
|
0x6, 0x9, 0x2a, 0x86, 0x48, 0x86, 0xf7, 0xd, 0x1, 0x1, 0x1
|
|
]));
|
|
var paramsAsn1Obj = ASN1Object.fromBytes(Uint8List.fromList([0x5, 0x0]));
|
|
algorithmSeq.add(algorithmAsn1Obj);
|
|
algorithmSeq.add(paramsAsn1Obj);
|
|
|
|
var privateKeySeq = ASN1Sequence();
|
|
var modulus = ASN1Integer(privateKey.n!);
|
|
var publicExponent = ASN1Integer(BigInt.parse('65537'));
|
|
var privateExponent = ASN1Integer(privateKey.privateExponent!);
|
|
var p = ASN1Integer(privateKey.p!);
|
|
var q = ASN1Integer(privateKey.q!);
|
|
var dP = privateKey.privateExponent! % (privateKey.p! - BigInt.one);
|
|
var exp1 = ASN1Integer(dP);
|
|
var dQ = privateKey.privateExponent! % (privateKey.q! - BigInt.one);
|
|
var exp2 = ASN1Integer(dQ);
|
|
var iQ = privateKey.q?.modInverse(privateKey.p!);
|
|
var co = ASN1Integer(iQ!);
|
|
|
|
privateKeySeq.add(version);
|
|
privateKeySeq.add(modulus);
|
|
privateKeySeq.add(publicExponent);
|
|
privateKeySeq.add(privateExponent);
|
|
privateKeySeq.add(p);
|
|
privateKeySeq.add(q);
|
|
privateKeySeq.add(exp1);
|
|
privateKeySeq.add(exp2);
|
|
privateKeySeq.add(co);
|
|
var publicKeySeqOctetString = ASN1OctetString(Uint8List.fromList(privateKeySeq.encodedBytes));
|
|
|
|
var topLevelSeq = ASN1Sequence();
|
|
topLevelSeq.add(version);
|
|
topLevelSeq.add(algorithmSeq);
|
|
topLevelSeq.add(publicKeySeqOctetString);
|
|
var dataBase64 = base64.encode(topLevelSeq.encodedBytes);
|
|
|
|
return """-----BEGIN PRIVATE KEY-----\r\n$dataBase64\r\n-----END PRIVATE KEY-----""";
|
|
}
|
|
|
|
static parsePrivateKeyFromPem(pemString) {
|
|
List<int> privateKeyDER = decodePEM(pemString);
|
|
var asn1Parser = ASN1Parser(Uint8List.fromList(privateKeyDER));
|
|
var topLevelSeq = asn1Parser.nextObject() as ASN1Sequence;
|
|
var version = topLevelSeq.elements[0];
|
|
var algorithm = topLevelSeq.elements[1];
|
|
var privateKey = topLevelSeq.elements[2];
|
|
|
|
asn1Parser = ASN1Parser(privateKey.contentBytes()!);
|
|
var pkSeq = asn1Parser.nextObject() as ASN1Sequence;
|
|
|
|
version = pkSeq.elements[0];
|
|
var modulus = pkSeq.elements[1] as ASN1Integer;
|
|
//var publicExponent = pkSeq.elements[2] as ASN1Integer;
|
|
var privateExponent = pkSeq.elements[3] as ASN1Integer;
|
|
var p = pkSeq.elements[4] as ASN1Integer;
|
|
var q = pkSeq.elements[5] as ASN1Integer;
|
|
var exp1 = pkSeq.elements[6] as ASN1Integer;
|
|
var exp2 = pkSeq.elements[7] as ASN1Integer;
|
|
var co = pkSeq.elements[8] as ASN1Integer;
|
|
|
|
RSAPrivateKey rsaPrivateKey = RSAPrivateKey(
|
|
modulus.valueAsBigInteger!,
|
|
privateExponent.valueAsBigInteger!,
|
|
p.valueAsBigInteger,
|
|
q.valueAsBigInteger
|
|
);
|
|
|
|
return rsaPrivateKey;
|
|
}
|
|
}
|