Skip to content

Instantly share code, notes, and snippets.

@Ajnasz
Created February 27, 2023 16:36
Show Gist options
  • Star 0 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save Ajnasz/8eccb3ed5a5d9b1882c16e706312fe70 to your computer and use it in GitHub Desktop.
Save Ajnasz/8eccb3ed5a5d9b1882c16e706312fe70 to your computer and use it in GitHub Desktop.

Signature, sign message crypto, security

https://community.letsencrypt.org/t/when-choosing-an-elliptic-curve-look-for-a-safe-curve/161837

7.1.3.1.2 ECDSA The CA SHALL indicate an ECDSA key using the id‐ecPublicKey (OID: 1.2.840.10045.2.1) algorithm identifier. The parameters MUST use the namedCurve encoding.

For P‐256 keys, the namedCurve MUST be secp256r1 (OID: 1.2.840.10045.3.1.7).
For P‐384 keys, the namedCurve MUST be secp384r1 (OID: 1.3.132.0.34).
For P‐521 keys, the namedCurve MUST be secp521r1 (OID: 1.3.132.0.35).
When encoded, the AlgorithmIdentifier for ECDSA keys MUST be byte‐for‐byte
identical with the following hex‐encoded bytes:
For P‐256 keys, 301306072a8648ce3d020106082a8648ce3d030107.
For P‐384 keys, 301006072a8648ce3d020106052b81040022.
For P‐521 keys, 301006072a8648ce3d020106052b81040023.

This isn't generally something that the average implementation developer has to consider. Choices are largely already made by standards and crypto libraries (and you really shouldn't write your own crypto library, unless you already know all these things by heart).

In a web context, ECDSA is generally only used with P-256 or P-384. Chrome doesn't even support anything else 116.

Example implementation

Generating keys

'use strict';
const crypto = require('crypto');
const fs = require('fs');

const { privateKey, publicKey } = crypto.generateKeyPairSync('ec', { namedCurve: 'P-256' });
fs.writeFileSync('private.pem', privateKey.export({ type: 'pkcs8', format: 'pem' }));
fs.writeFileSync('public.pem', publicKey.export({ type: 'spki', format: 'pem' }));

Sign and verify

const sign = crypto.createSign('SHA256');
sign.update('some data to sign');
sign.end();

// sign with the key read from remote
const signature = sign.sign(fs.readFileSync('private.pem'), 'base64');

const verify = crypto.createVerify('SHA256');
verify.update('some data to sign');
verify.end();
console.log(verify.verify(publicKey, signature, 'base64'));

Different encoding

const signature = sign.sign({
  key: fs.readFileSync('private.pem'),
  dsaEncoding: 'ieee-p1363',
}, 'base64');

const verify = crypto.createVerify('SHA256');
verify.update('some data to sign');
verify.end();
console.log(verify.verify({
  key: publicKey,
  dsaEncoding: 'ieee-p1363',
}, signature, 'base64'));

Additional notes

https://stackoverflow.com/a/39651457

The answer turns out to be that the Node crypto module generates ASN.1/DER signatures, while other APIs like jsrsasign and SubtleCrypto produce a “concatenated” signature. In both cases, the signature is a concatenation of (r, s). The difference is that ASN.1 does so with the minimum number of bytes, plus some payload length data; while the P1363 format uses two 32-bit hex encoded integers, zero-padding them if necessary.

Generate key with openssl

https://stackoverflow.com/a/51179147

Private key

openssl ecparam -out ec_key.pem -name prime256v1 -genkey

Set password for private key

openssl pkcs8 -topk8 -in ec_key.pem -out ec_key.pw.pem

The ec_key.key can be deleted but the private key will be password protected!

Public key

openssl ec -in ec_key.pw.pem -pubout -out ec_key.pub.pem
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment