Ephenodrom / Dart-Basic-Utils

A dart package for many helper methods fitting common situations
MIT License
364 stars 77 forks source link

Support for PKCS#1 format keys #18

Closed hongfeiyang closed 4 years ago

hongfeiyang commented 4 years ago

Is there any support for PKCS#1 format keys in the future? I see the current implementation only supports PKCS#8 format keys, it would be a great addition to the existing functionalities in the package.

Ephenodrom commented 4 years ago

Hello and thanks for the feature request.

Can you describe what do you want to achieve ? As I understand it, you want to encode / decode RSA private and public keys to and from PKCS#1 format ?

Is this correct ?

hongfeiyang commented 4 years ago

Yes that's correct. I have read through how the package form RSA keys from pkcs8 Pem string and convert RSA keys to pkcs8 pem string, and I used some of your source code to achieve PKCS1 formatting, however it would be good if this function is included in the package

Ephenodrom commented 4 years ago

Yes this sounds like a good idea. I will try to implement this maybe next week. If you already have some working code,feel free to create a pull request :).

Ephenodrom commented 4 years ago

@hongfeiyang One step is done, please update to 2.6.1 to use the following methods from the CryptoUtils class:

String encodeRSAPrivateKeyToPemPkcs1(RSAPrivateKey rsaPrivateKey);
String encodeRSAPublicKeyToPemPkcs1(RSAPublicKey rsaPublicKey);

The next step is to create a RSAPrivateKey and RSAPublicKey from a given PEM string.

hongfeiyang commented 4 years ago

@Ephenodrom Thanks :)

hongfeiyang commented 4 years ago

@Ephenodrom I would like to share with you the code I used to convert RSA keys to PKCS1 strings back and forth, I don't know how are going you going to change the underlying structure so I did not create a pull request, but I hope this could be helpful

+String encodeRSAPrivateKeyToPKCS1PemString(RSAPrivateKey privateKey) {
+  var privateKeySeq = ASN1Sequence();
+  var version = ASN1Integer(BigInt.from(0));
+  var modulus = ASN1Integer(privateKey.n);
+  var publicExponent = ASN1Integer(BigInt.parse('65537'));
+  var privateExponent = ASN1Integer(privateKey.d);
+  var p = ASN1Integer(privateKey.p);
+  var q = ASN1Integer(privateKey.q);
+  var dP = privateKey.d % (privateKey.p - BigInt.from(1));
+  var exp1 = ASN1Integer(dP);
+  var dQ = privateKey.d % (privateKey.q - BigInt.from(1));
+  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 dataBase64 = base64.encode(privateKeySeq.encodedBytes);
Add a comment to this line
+  var chunks = StringUtils.chunk(dataBase64, 64);
+  return """-----BEGIN RSA PRIVATE KEY-----\n${chunks.join('\n')}\n-----END RSA PRIVATE KEY-----""";
+}
+
+String encodeRSAPublicKeyToPKCS1PemString(RSAPublicKey publicKey) {
+  var publicKeySeq = ASN1Sequence();
+  publicKeySeq.add(ASN1Integer(publicKey.modulus));
+  publicKeySeq.add(ASN1Integer(publicKey.exponent));
+
+  var dataBase64 = base64.encode(publicKeySeq.encodedBytes);
+  var chunks = StringUtils.chunk(dataBase64, 64);
+
+  return """-----BEGIN RSA PUBLIC KEY-----\n${chunks.join('\n')}\n-----END RSA PUBLIC KEY-----""";
+}
+
+RSAPublicKey loadRSAPublicKeyFromPKCS8PemString(String pem) {
   return X509Utils.publicKeyFromPem(pem);
 }
+
+RSAPrivateKey loadRSAPrivateKeyFromPKCS1PemString(String pem) {
+  var bytes = X509Utils.getBytesFromPEMString(pem);
+  var asn1Parser = ASN1Parser(bytes);
+  var pkSeq = asn1Parser.nextObject() as ASN1Sequence;
+
+  var modulus = pkSeq.elements[1] as ASN1Integer;
+  //ASN1Integer 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;
+  //ASN1Integer exp1 = pkSeq.elements[6] as ASN1Integer;
+  //ASN1Integer exp2 = pkSeq.elements[7] as ASN1Integer;
+  //ASN1Integer co = pkSeq.elements[8] as ASN1Integer;
+
+  var rsaPrivateKey = RSAPrivateKey(
+      modulus.valueAsBigInteger,
+      privateExponent.valueAsBigInteger,
+      p.valueAsBigInteger,
+      q.valueAsBigInteger);
+
+  return rsaPrivateKey;
+}
+
+RSAPublicKey loadRSAPublicKeyFromPKCS1PemString(String pem) {
+  var bytes = X509Utils.getBytesFromPEMString(pem);
+  var publicKeyAsn = ASN1Parser(bytes);
+  ASN1Sequence publicKeySeq = publicKeyAsn.nextObject();
+  var modulus = publicKeySeq.elements[0] as ASN1Integer;
+  var exponent = publicKeySeq.elements[1] as ASN1Integer;
+
+  var rsaPublicKey =
+      RSAPublicKey(modulus.valueAsBigInteger, exponent.valueAsBigInteger);
+  return rsaPublicKey;
+}
Ephenodrom commented 4 years ago

@hongfeiyang Please update to version 2.6.2.

These are all methods from the CryptoUtils class that should cover your feature request. Thanks for the code, i used some of that and added unit tests.

String encodeRSAPrivateKeyToPemPkcs1(RSAPrivateKey rsaPrivateKey);
String encodeRSAPublicKeyToPemPkcs1(RSAPublicKey rsaPublicKey);
RSAPublicKey rsaPublicKeyFromPemPkcs1(String pem);
RSAPrivateKey rsaPrivateKeyFromPemPkcs1(String pem);
RSAPublicKey rsaPublicKeyFromDERBytesPkcs1(Uint8List bytes);
RSAPrivateKey rsaPrivateKeyFromDERBytesPkcs1(Uint8List bytes);