panva / paseto

PASETO (Platform-Agnostic SEcurity TOkens) for Node.js with no dependencies
MIT License
428 stars 26 forks source link

question: how to you generate and export private keys? #1

Closed emahuni closed 5 years ago

emahuni commented 5 years ago

How do you generate the private keys to use with createPrivateKey

emahuni commented 5 years ago

ok never mind i figured it out

paseto.V2.generateKey('public')
emahuni commented 5 years ago

here the issue again, I need to export this to a storable format such as a string so that I can use it later to verify a signed token:

  static async getSharedKey () {
    const sharedKey = await paseto.V2.generateKey( 'public');
    console.debug(`sharedKey: `, sharedKey.export(????????????));
    return sharedKey;
  }

how do I do that?

panva commented 5 years ago

It’s the node crypto keyobject, so see https://nodejs.org/api/crypto.html#crypto_class_keyobject for all things related to exporting, instantiating etc.

emahuni commented 5 years ago

I did already, but I can't export the key I am not sure how (newbie to Paseto workflow), that's why I asked you; I am sure you understand what I am missing. Dude your lib is the only sensible and working one of the only 3 I found that try to use PASETO in Nodejs. And from the looks of things this is replacing JWT. But I couldn't reach the final part of actually storing the key safely for later usage as a string:

For public keys, the following encoding options can be used: type: Must be one of 'pkcs1' (RSA only) or 'spki'. format: Must be 'pem' or 'der'. For private keys, the following encoding options can be used: type: Must be one of 'pkcs1' (RSA only), 'pkcs8' or 'sec1' (EC only). format: Must be 'pem' or 'der'. cipher: If specified, the private key will be encrypted with the given cipher and passphrase using PKCS#5 v2.0 password based encryption. passphrase: | The passphrase to use for encryption, see cipher.

Can you please give me an example, this class looks fairly new despite my ignorance, internet isn't helping much. It'd be great if you stick that example into your documentation as well.

I tried a lot of ways still getting stuck, eg:

sharedKey.export({type: 'spki', format: 'pem'})

it tells me something about mismatch of the type of keys. Then I tried the other option and still I get other errors. It'd great if you document or create an exportKey method to do the roundtrip of exporting the key, using a passphrase for ciphering then an importKey method for importing that key easily. This would go a long way.

panva commented 5 years ago

You have a local symmetric key right? So ignore the asymmetric key part.

using a passphrase for ciphering

Symmetric keys do not have a PEM format with encryption.

For symmetric keys, this function allocates a Buffer containing the key material and ignores any options.

key.export().toString(‘hex’)

Then when using the key again

Buffer.from(hexValue, ‘hex’)

It'd great if you document or create an exportKey method to do the roundtrip of exporting the key, using a passphrase for ciphering then an importKey method for importing that key easily. This would go a long way.

I don’t feel like I need to supplement stdlib documentation. The keys are KeyObject instances, when passing key as an argument you pass the same as to a create*Key or an instance to save some performance. Working with KeyObject instances is part of node crypto, not paseto, the generate functions just shortcut generating the right key types and lengths.

panva commented 5 years ago

And from the looks of things this is replacing JWT.

Paseto isn’t replacing anything. JWT is more mature and more flexible. Paseto only exists because people use or implement JWT wrong. Paseto isn’t even a standards body draft

emahuni commented 5 years ago

oh ok, thanks

stevenpetryk commented 4 years ago

For those looking for a fully integrated example, as I was:

import { createSecretKey } from 'crypto'
import { V2 } from 'paseto'
const { encrypt, decrypt, generateKey } = V2

// This is a crypto.KeyObject
const key = await generateKey('local')

// This contains a nice, portable hex string (keep it safe!)
const exportedToHex = key.export().toString('hex')

// This is a separate but identical crypto.KeyObject
const importedFromHex = createSecretKey(Buffer.from(exportedToHex, 'hex'))

// Proof that it works
const encrypted = await encrypt({ test: 'foo' }, key)
console.log(await decrypt(encrypted, importedFromHex))
// => { test: 'foo' }
woodrow-ry commented 4 years ago

The fully integrated example is very helpful, thanks. But were you ever able to get it working with 'public' purpose / asymmetric encryption? I've been able to use the PHP paseto lib to generate a V1 Public RSA keypair, and then use the public key from that keypair within the nodejs lib client side to verify the paseto public token inside a lambda. However, I am trying to get this working using V2, but have been unable to figure out how to store the public key as a string/hex or whatnot from the PHP side and then use that under the nodejs lib to also verify the token, just like I did with V1. Thanks for any feedback you may be able to provide!

panva commented 4 years ago
crypto.generateKeyPairSync('ed25519').publicKey.export({ format: 'pem', type: 'spki' })
crypto.generateKeyPairSync('ed25519').publicKey.export({ format: 'der', type: 'spki' })

https://nodejs.org/api/crypto.html#crypto_crypto_createpublickey_key

https://lapo.it/asn1js/#MCowBQYDK2VwAyEAUDk1T8aAwryGrCrP3ajREJauZEFWwrkPnRzFI1apCMw

const publicPemEd25519 = `-----BEGIN PUBLIC KEY-----
MCowBQYDK2VwAyEAUDk1T8aAwryGrCrP3ajREJauZEFWwrkPnRzFI1apCMw=
-----END PUBLIC KEY-----`;
crypto.createPublicKey(publicPemEd25519)
woodrow-ry commented 4 years ago

@panva - Thank you for that nodejs example! I get the PUBLIC/PRIVATE key like that in PHP using Paseto V1, but do you know how to do so within the paragonie PHP paseto lib for V2?

Currently, I am trying this in PHP:

$privateKey  = AsymmetricSecretKey::generate(new Version2()); 
$publicKey   = $key->getPublicKey();

$token = (string) (new Builder())
            ->setKey($privateKey)
            ->setVersion(new Version2())
            ->setPurpose(Purpose::public())
            ->setExpiration((new DateTime())->add(new DateInterval('P01D')))       
            ->setClaims([
                'claim' => json_decode($this->claimJSON(), true),
            ])->toString();

return $response->withJson([
  'public_key' => bin2hex($publicKey->raw()),
  'token' => $token
]);

And then on the nodejs side with your lib, I am trying to take the public_key and token outputted from the PHP side (above) and verify the claim/payload:

const pubKey = await createPublicKey(Buffer.from(public_key_from_php_response_above, 'hex'));
const token = token_output_from_php_response_above;
const response = await verify(token, pubKey);
console.log(response);

Thank you again for your help and feedback!

panva commented 4 years ago

I cannot help with the PHP side of things. Node keyobject for asymmetric keys works with DER/PEM encoding, not the raw key value you get from PHP.