Ephenodrom / Dart-Basic-Utils

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

Invalid pem generated for ECDSA pair #67

Closed phpcoinn closed 2 years ago

phpcoinn commented 2 years ago

Hi, I am using your lib to implement crypto functions in my project, ECDSA key pair for secp256k1 curve.

AsymmetricKeyPair asymmetricKeyPair = CryptoUtils.generateEcKeyPair(curve: 'secp256k1');
ECPrivateKey ecPrivateKey = asymmetricKeyPair.privateKey as ECPrivateKey;
String ecPrivateKeyPem = CryptoUtils.encodeEcPrivateKeyToPem(ecPrivateKey);

But generated pem string is not correct, when I try to import it with php or nodejs code.

I checked code and found this part confusing:

https://github.com/Ephenodrom/Dart-Basic-Utils/blob/dcb0dcd12235d6e4fdeef7dfcfbd1fc34379fb23/lib/src/CryptoUtils.dart#L591

I think this is wrong because here is encoding of curve parameters, I think there need to be publicKey points x, y.

  ECPoint G = ecPrivateKey.parameters!.G;
  ECPoint? Q = G * ecPrivateKey.d;

But that is not all. I am beginner in Dart, so I can not easy understand code. I think there are also some errors in further encoding of pem, and also added padding ASN1BitString.

Because there is no any other alternative for this, I will try my best to find out and help in solving this issue.

Ephenodrom commented 2 years ago

The ASN1 structure generated by "CryptoUtils.encodeEcPrivateKeyToPem()" looks good so far, compared to a private key generated with OpenSSL. I also tried to generate a CSR using the the generated EC private and public key. It works fine and the CSR was also accepted by the CA DigiCert. Due to the fact that they currently only support "prime256v1", I used this curve.

I would suggest :

Regards

kabaluyot commented 2 years ago

Unfortunately I replicated these errors too. The package won't satisfy this online checker: https://8gwifi.org/ecsignverify.jsp

With secp256k1 curve and SHA-256 hash algorithm.

Ephenodrom commented 2 years ago

@kabaluyot Kann you please try it again with the current master branch? The public key algorithm was hardcoded set to prime256v1.

kabaluyot commented 2 years ago

Yes I already tried this but the main issue as @phpcoinn mentioned was the wrong PEM generated especially for private key for secp256k1. Using this online tool, it doesnt match https://8gwifi.org/ecsignverify.jsp or you can use node crypto for comparison. @Ephenodrom

Ephenodrom commented 2 years ago

@kabaluyot Please try again with https://8gwifi.org/ecsignverify.jsp and the current master branch. I changed the PEM Header for the public key and now the tool at https://8gwifi.org/ecsignverify.jsp is working for me.

The EC private/public key has the same structure as the one generated with openssl. The only difference were :

kabaluyot commented 2 years ago

Hi @Ephenodrom, thanks for the response. Did you happen to check if generated PEM in this package for signatures works well with whats in https://8gwifi.org/ecsignverify.jsp ?

Actually I applied the changes that you added to the PR that I created, but upon checking, looks like the PEM for private key is incorrectly encoded. Hence when trying and verifying signatures generated from Dart to the https://8gwifi.org/ecsignverify.jsp, it wont match.

Also if we can support signature in base64. I got this implementation from https://github.com/Ephenodrom/Dart-Basic-Utils/issues/57#issuecomment-991369507 .. What do you think?

/// Convert ECSignature [signature] to base64 string
  /// This is mainly used for passing signature as string via request/response use cases
  static String ecSignatureToBase64(ECSignature signature) {
    var rUint8ListV2 = MathUtils.encodeBigInt(signature.r);
    var sUint8ListV2 = MathUtils.encodeBigInt(signature.s);

    var bbV2 = BytesBuilder();
    bbV2.add(rUint8ListV2);
    bbV2.add(sUint8ListV2);

    return base64.encode(bbV2.toBytes());
  }
Ephenodrom commented 2 years ago

@kabaluyot As I sad, with the latest commit, I was able to generate a EC keypair that can be used within the tool https://8gwifi.org/ecsignverify.jsp.

This included :

According the ecSignatureToBase64() method. This looks good, so please implement it and do not forget to add some unit tests. You can use the results from https://8gwifi.org/ecsignverify.jsp for the unit test to verify it.

kabaluyot commented 2 years ago

So I tried implementing ecSignatureToBase64() with the latest master. Here are the steps I took:

  1. Generate key pairs and encode to PEM (from Dart)
 // generate key pairs
  var ec = CryptoUtils.generateEcKeyPair(curve: "secp256k1");
  var privKey = ec.privateKey as ECPrivateKey;
  var pubKey = ec.publicKey as ECPublicKey;

  // convert to pem
  var privKeyPem = CryptoUtils.encodeEcPrivateKeyToPem(privKey);
  var pubKeyPem = CryptoUtils.encodeEcPublicKeyToPem(pubKey);

  print('PrivKey PEM:\n$privKeyPem');
  print('PubKey PEM:\n$pubKeyPem');

  // convert pem to base64
  var pubKeyBase64 = base64Encode(pubKeyPem.codeUnits);
  print('PubKey base64: $pubKeyBase64');

  // decode keys from pem
  var decodedPrivKey = CryptoUtils.ecPrivateKeyFromPem(privKeyPem);
  var decodedPubKey = CryptoUtils.ecPublicKeyFromPem(pubKeyPem);

  // sign message
  var message = Uint8List.fromList("Hello world!".codeUnits);
  var signature = CryptoUtils.ecSign(decodedPrivKey, message,
      algorithmName: 'SHA-256/ECDSA'); // can be SHA-256/ECDSA or other
  var encodedSignature = CryptoUtils.ecSignatureToBase64(signature);
  print('Signature in base64: $encodedSignature');

  // verify message using ECSignature
  var isVerifiedByECSignature = CryptoUtils.ecVerify(
      decodedPubKey, message, signature,
      algorithm: 'SHA-256/ECDSA');
  print('ECSignature verification result: $isVerifiedByECSignature');

With the ff PEM pairs and result:

PrivKey PEM:
-----BEGIN EC PRIVATE KEY-----
MHUCAQEEIQCNeTXcKDOh232Nh4/wXDYbAN4a/7zZt4kcIjTd+Fy5aKAHBgUrgQQA
CqFEA0IABHm+Zn753LusVaBilc6HCwcCm/zbLc4o2VnygVsW+BeYSDradyajxGVd
pPv8DhEIqP0XtEimhVQZnEfQj/sQ1Lg=
-----END EC PRIVATE KEY-----
PubKey PEM:
-----BEGIN PUBLIC KEY-----
MFYwEAYHKoZIzj0CAQYFK4EEAAoDQgAEL1xolif+76OIrvhEf8yL5m93ulxbha4M
aovLQr38tZ5yEOkM9acw+NOf9mkrfspYDFoRs5vjON4Cbjsn3DlIfg==
-----END PUBLIC KEY-----
PubKey base64: LS0tLS1CRUdJTiBQVUJMSUMgS0VZLS0tLS0KTUZZd0VBWUhLb1pJemowQ0FRWUZLNEVFQUFvRFFnQUVMMXhvbGlmKzc2T0lydmhFZjh5TDVtOTN1bHhiaGE0TQphb3ZMUXIzOHRaNXlFT2tNOWFjdytOT2Y5bWtyZnNwWURGb1JzNXZqT040Q2Jqc24zRGxJZmc9PQotLS0tLUVORCBQVUJMSUMgS0VZLS0tLS0=
Signature in base64: LgDzj/gbIreZhLWSkRUgrXuL3YiaEd+vAKFjdPtYqT3dKokDxLGL04z7xfog5qLpWOTxH34/0jC2eUN6caXpKQ==
ECSignature verification result: true
  1. Copy PEM pairs to https://8gwifi.org/ecsignverify.jsp and try to verify the message and signature result. Notice that in step 1, the ECSignature verification indeed returns true

I always get this error

Screen Shot 2022-06-23 at 4 34 15 AM
kabaluyot commented 2 years ago

@Ephenodrom

Ephenodrom commented 2 years ago

@kabaluyot Please take a look at my commits. I found out that the signature is indeed an ASN1 structure, so I made some changees to the ecSignatureToBase64() method. I also added a ecSignatureFromBase64() method. It should work now as expected.

Ephenodrom commented 2 years ago

@kabaluyot I think with the current commits we can close this issue and #57. Can you confirm this ? After that you can complete your PR and then I will merge and upload a new version on pub.dev.

kabaluyot commented 2 years ago

Ok will verify the commits you pushed in master

kabaluyot commented 2 years ago

@Ephenodrom I can confirm now that the signature is verified correctly with external tool:

Screen Shot 2022-06-27 at 8 38 23 AM
kabaluyot commented 2 years ago

For verify and example, here is the PR: https://github.com/Ephenodrom/Dart-Basic-Utils/pull/72

Ephenodrom commented 2 years ago

This can be considered as closed and fixed with the upcoming release. The package can now generate a DER encoded valid signature.