AbdelbakiBoukerche / rsa_encrypt

rsa encrypt package
MIT License
17 stars 16 forks source link

encodePublicKeyToPemPKCS1 produces invalid PEM format #1

Closed meyerjoe closed 4 years ago

meyerjoe commented 4 years ago

If I try to import the PEM file anywhere else, I get error messages.

The reason is, that the wrong tag is used.

Instead of

-----BEGIN PRIVATE KEY----- BASE64 ENCODED DATA -----END PRIVATE KEY-----

it should be

-----BEGIN RSA PRIVATE KEY----- BASE64 ENCODED DATA -----END RSA PRIVATE KEY-----

see here for further info: https://stackoverflow.com/questions/20065304/differences-between-begin-rsa-private-key-and-begin-private-key

https://stackoverflow.com/questions/18039401/how-can-i-transform-between-the-two-styles-of-public-key-format-one-begin-rsa

Apart from that, I haven't managed yet to use the key on node.js (Firebase Function) and encrypt something, that could be decrypted properly in Flutter.

Here is the code on node.js (version 10)

` import as functions from 'firebase-functions'; import as admin from 'firebase-admin'; import * as crypto from 'crypto';

export const encrypt = async function(data: any, context: functions.https.CallableContext) {

// ... fetch public key from Firestore

const publicKeyPem = "-----BEGIN RSA PUBLIC KEY-----\n" + "MIICCgKCAgEA1w3+f0DcF+XjBYy3usSRKRTpqREGhpv0Wqh5GK0UAdKXaPpkc9WQ6yJJH2iAE0Rm4nCqs+S8qkCzHUA+c3KSyLGqokLKy7fv49juExkGL4gZNXU8ql8lF1C6QPZBE+FF9JZd4ExoKWKgttWlTU6eYU8+eZD0BrHfLiKwE8YBLc2zKHKWVSscll027jkUynp4AJFjW9q2yRcCkGwDnt2QfAXMZqBfZlXxpyTTSIkfKJ/U7BKrJqObh7ZnrJMd/9qmAXa8XbAQCzxe+pU40q3Q+faJzcfEgkysM1F1myBZjHqQH83XsyNeyvJv8+SVO9Yqs/9TeuHr+tXDQCTiOhMW9/wvMVtkHvaWYhGPN/QW4i1EYzOdsw74o/4ffPhwQIGXTaCwW7D+AWuOEXAqo2OyFaaY5inXAEJglFLzpqlSyL25HGp8qDd637YPyA/ccOmVQp3asbRRiTpvclriLbQ6oHpfuWUTAHLRZhL1qKVaPzd9phK5Rs3fuanNgHhXeyFvyIPA4CwOCwFBon/eP2fWVh2XyOCnJORLFmMc2mXMVess1+9qmXWJnXXdHHgVFU5jxZQ6WDo9IA2eJVmEfy6mH0uEXzA9oEwDSEXH0LpZTSuIaWa+CFz0iw/TCJGsCQw0sOBkVPbz9ZdbgLFaW72tXTWyXFvredqyGXPZl/frq+kCAwEAAQ==" + "\n-----END RSA PUBLIC KEY-----"

String encryptionResult = crypto.publicEncrypt({ key: publicKeyPem }, Buffer.from("my sensitive data")).toString('base64');

// ... store encryptionResult in Firestore

}

`

In Flutter, I do the following:

` // retrieve encryptionResult from Firestore

var stringInUtf = utf8.decode(base64Decode(encryptionResult));

var decryptedStr = decrypt(stringInUtf, privateKey);

`

I'd be very grateful for a hint how to solve this.

AbdelbakiBoukerche commented 4 years ago

Hi, Thank you for showing interest in rsa_encrypt. I created rsa_encrypt to implement end-to-end encryption in my app and it does its job quite good. Thus, I don't think there's much that I can do because originally rsa_encrypt was made to be used internally: "You can only encrypt data and decrypt it in the app itself. The private & public key will only WORK INSIDE THE APP. I don't know your situation but you can encrypt data and send it directly to firestore. and decrypt it only when the data arrives". NOTE: rsa_encrypt uses MIT License so you can modify the source code to match your needs. Happy Coding :)

On Mon, Jun 15, 2020 at 8:48 PM meyerjoe notifications@github.com wrote:

If I try to import the PEM file anywhere else, I get error messages.

The reason is, that the wrong tag is used.

Instead of

-----BEGIN PRIVATE KEY----- BASE64 ENCODED DATA -----END PRIVATE KEY-----

it should be

-----BEGIN RSA PRIVATE KEY----- BASE64 ENCODED DATA -----END RSA PRIVATE KEY-----

see here for further info:

https://stackoverflow.com/questions/20065304/differences-between-begin-rsa-private-key-and-begin-private-key

https://stackoverflow.com/questions/18039401/how-can-i-transform-between-the-two-styles-of-public-key-format-one-begin-rsa

Apart from that, I haven't managed yet to use the key on node.js (Firebase Function) and encrypt something, that could be decrypted properly in Flutter.

Here is the code on node.js (version 10)

` import as functions from 'firebase-functions'; import as admin from 'firebase-admin'; import * as crypto from 'crypto';

export const encrypt = async function(data: any, context: functions.https.CallableContext) {

// ... fetch public key from Firestore

const publicKeyPem = "-----BEGIN RSA PUBLIC KEY-----\n" + "MIICCgKCAgEA1w3+f0DcF+XjBYy3usSRKRTpqREGhpv0Wqh5GK0UAdKXaPpkc9WQ6yJJH2iAE0Rm4nCqs+S8qkCzHUA+c3KSyLGqokLKy7fv49juExkGL4gZNXU8ql8lF1C6QPZBE+FF9JZd4ExoKWKgttWlTU6eYU8+eZD0BrHfLiKwE8YBLc2zKHKWVSscll027jkUynp4AJFjW9q2yRcCkGwDnt2QfAXMZqBfZlXxpyTTSIkfKJ/U7BKrJqObh7ZnrJMd/9qmAXa8XbAQCzxe+pU40q3Q+faJzcfEgkysM1F1myBZjHqQH83XsyNeyvJv8+SVO9Yqs/9TeuHr+tXDQCTiOhMW9/wvMVtkHvaWYhGPN/QW4i1EYzOdsw74o/4ffPhwQIGXTaCwW7D+AWuOEXAqo2OyFaaY5inXAEJglFLzpqlSyL25HGp8qDd637YPyA/ccOmVQp3asbRRiTpvclriLbQ6oHpfuWUTAHLRZhL1qKVaPzd9phK5Rs3fuanNgHhXeyFvyIPA4CwOCwFBon/eP2fWVh2XyOCnJORLFmMc2mXMVess1+9qmXWJnXXdHHgVFU5jxZQ6WDo9IA2eJVmEfy6mH0uEXzA9oEwDSEXH0LpZTSuIaWa+CFz0iw/TCJGsCQw0sOBkVPbz9ZdbgLFaW72tXTWyXFvredqyGXPZl/frq+kCAwEAAQ==" + "\n-----END RSA PUBLIC KEY-----"

String encryptionResult = crypto.publicEncrypt({ key: publicKeyPem }, Buffer.from("my sensitive data")).toString('base64');

// ... store encryptionResult in Firestore

}

`

In Flutter, I do the following:

` // retrieve encryptionResult from Firestore

var stringInUtf = utf8.decode(base64Decode(encryptionResult));

var decryptedStr = decrypt(stringInUtf, privateKey);

`

I'd be very grateful for a hint how to solve this.

— You are receiving this because you are subscribed to this thread. Reply to this email directly, view it on GitHub https://github.com/AbdelbakiBoukerche/rsa_encrypt/issues/1, or unsubscribe https://github.com/notifications/unsubscribe-auth/ALB4GWMVZWLU6VSYLBFIXDTRWZ3KBANCNFSM4N6RHXQA .

meyerjoe commented 4 years ago

thx for the quick reply!

DennisMuchiri commented 2 years ago

@meyerjoe did you get a solution for this?

meyerjoe commented 2 years ago

yes, I got it solved. If I remember correctly, the generated string of this library did not adhere to the standard PEM encoding. It works if you just use this library for encrypting and decrypting, but not, if you use it together with a different lib (e.g. on the server), which expects the standard.

that's why I ended up with this implementation:

import 'package:pointycastle/api.dart';
import 'package:pointycastle/asymmetric/api.dart';
import "package:pointycastle/export.dart";
import "package:asn1lib/asn1lib.dart";

  encodePublicKeyToPem(RSAPublicKey publicKey) {
    var algorithmSeq = new ASN1Sequence();
    var algorithmAsn1Obj = new ASN1Object.fromBytes(
        Uint8List.fromList([0x6, 0x9, 0x2a, 0x86, 0x48, 0x86, 0xf7, 0xd, 0x1, 0x1, 0x1]));
    var paramsAsn1Obj = new ASN1Object.fromBytes(Uint8List.fromList([0x5, 0x0]));
    algorithmSeq.add(algorithmAsn1Obj);
    algorithmSeq.add(paramsAsn1Obj);

    var publicKeySeq = new ASN1Sequence();
    publicKeySeq.add(ASN1Integer(publicKey.modulus));
    publicKeySeq.add(ASN1Integer(publicKey.exponent));
    var publicKeySeqBitString = new ASN1BitString(Uint8List.fromList(publicKeySeq.encodedBytes));

    var topLevelSeq = new 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-----""";
  }

  encodePrivateKeyToPem(RSAPrivateKey privateKey) {
    var version = ASN1Integer(BigInt.from(0));

    var algorithmSeq = new ASN1Sequence();
    var algorithmAsn1Obj = new ASN1Object.fromBytes(
        Uint8List.fromList([0x6, 0x9, 0x2a, 0x86, 0x48, 0x86, 0xf7, 0xd, 0x1, 0x1, 0x1]));
    var paramsAsn1Obj = new ASN1Object.fromBytes(Uint8List.fromList([0x5, 0x0]));
    algorithmSeq.add(algorithmAsn1Obj);
    algorithmSeq.add(paramsAsn1Obj);

    var privateKeySeq = new ASN1Sequence();
    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 publicKeySeqOctetString =
        new ASN1OctetString(Uint8List.fromList(privateKeySeq.encodedBytes));

    var topLevelSeq = new 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-----""";
  }
DennisMuchiri commented 2 years ago

@meyerjoe Thank you very much. @AbdelbakiBoukerche can we have this improvement added to the package?