Closed blelump closed 4 years ago
I think you’re missing the fact that node does not work with arbitrary raw keys but DER/PEM encoded ones and only needs the 32 byte private one
e.g. PEM
-----BEGIN PRIVATE KEY-----
MC4CAQAwBQYDK2VwBCIEILRPjkQvM1vRgoBFHSGUzSgoUVaultWVGJ7jekxq8hS+
-----END PRIVATE KEY-----
https://lapo.it/asn1js/#MC4CAQAwBQYDK2VwBCIEILRPjkQvM1vRgoBFHSGUzSgoUVaultWVGJ7jekxq8hS-
What you're passing to createPrivateKey({ key: sk, format: 'der', type: 'pkcs8' })
as sk
is not a pkcs8 formatted der private key.
@panva do you have any suggestion to overcome this, I mean to get it / encode it in DER format?
1) get the 32 byte private key raw value from sodium 2) encode it as per pkcs8 (see the link for parsed DER structure above)
I don't have the code excerpt at hand, but you can trace back this code https://github.com/panva/jose/blob/v1.25.0/lib/help/key_utils.js#L278-L291
@panva , thanks for help!
This one "passes":
'use strict';
const nacl = require("tweetnacl");
const util = require("tweetnacl-util");
const paseto = require('paseto');
const { V2 } = paseto
const { V2: { sign, verify } } = paseto
const crypto = require('crypto')
const { promisify } = require('util')
const generateKeyPair = promisify(crypto.generateKeyPair)
const {
createPrivateKey,
createPublicKey
} = require('crypto');
const asn = require('asn1.js')
var OneAsymmetricKey = asn.define('OneAsymmetricKey', function() {
this.seq().obj(
this.key('version').int(),
this.key('algorithm').use(AlgorithmIdentifier),
this.key('privateKey').use(PrivateKey)
);
});
var PrivateKey = asn.define('PrivateKey', function() {
this.octstr().contains().obj(
this.key('privateKey').octstr()
);
});
var AlgorithmIdentifier = asn.define('AlgorithmIdentifier', function() {
this.seq().obj(
this.key('algorithm').objid(),
);
});
const a = nacl.sign.keyPair();
let sk = a.secretKey;
let sk_cp = Buffer.from(sk.slice(0, 32));
var output = OneAsymmetricKey.encode({
version: 0,
privateKey: { privateKey: sk_cp },
algorithm: { algorithm: '1.3.101.112'.split('.') }
}, 'der');
output.write('04', 12, 1, 'hex');
console.log(output.toString('base64'))
const priv = createPrivateKey({ key: output, format: 'der', type: 'pkcs8' });
const pub = createPublicKey({ key: priv, format: 'der', type: 'pkcs1' });
(async () => {
const token = await sign({ sub: 'johndoe' }, priv)
console.log(token)
const payload = await verify(token, pub)
console.log(payload)
})()
That's why i said walk back the code, to reimplement it using e.g. asn1.js
yourself, your own code. What you're requiring are internals and I do not recommend anyone doing so.
Thanks @panva ! Wouldn't solve this without your help, appreciate that!
First tried to find something out of the box and got into https://github.com/digitalbazaar/forge with asn1 encode/decode, but then following your comment from the above link https://github.com/panva/jose/blob/v1.25.0/lib/help/key_utils.js#L287 ended up with updated example above only with dependency to asn1.js
.
I sign the token with another library (libsodium) in another language (scala) and verify using this library. I have spent the entire day trying to convert libsodium ed25519 public key to something nodejs crypto module can understand, eventually, I end up with the following:
const {createPublicKey} = require("crypto");
const der = Buffer.concat([Buffer.from("302a300506032b6570032100", "hex"), libsodiumPublicKey]);
const publicKey = createPublicKey({
key: der,
format: "der",
type: "spki"
});
I thought it might help someone else in the future.
If libsodium supported actual ASN.1 encoded keys rather than arbitrary binary data as keys you'd be set. Anyway libsodium <> openssl (ASN.1) conversion is not of concern to this lib, ASN.1 is a used standard for a good reason. The code from @somdoron works for ed25519 public keys tho. It won't work for private ones.
I agree, libsodium supporting ASN.1 would be nice and would save me a day. Yes, I know it only works for public keys (I'm currently not concerned with private keys at the moment).
Hi!
Problem: lack of compatibility(?) between generated private keys via Nodejs Crypto module and various libsodium implementations.
It seems that Nodejs Crypto expects 48 bytes length keys whereas libsodium implementations provide 64 bytes length private keys. Of course due to that fact
const token = await sign({ sub: 'johndoe' }, privateKey)
fails.Already stated a question on Nodejs https://github.com/nodejs/help/issues/2543 help repo, but perhaps you could put more light on this problem, maybe I'm just missing something?
A low level example of the problem: