7

I'd like to generate a public/private keypair in javascript, and use the public key to encrypt message and the private key to decrypt the message.

I prefer native browser support over external libraries. How can I do this in JavaScript?

Modern browsers implement window.crypto.subtle.generateKey. I can use it to generate ECDSA private/public keys to sign/verify messages, this works. But I cannot find a way how to use it to generate pub/private keys to encrypt/decrypt. If I try the generateKey for the recommended AES-GCM Algorithm, it generates just one cryptoKey, which can be probably used to both encrypt and decrypt. But I prefer to get a keypair (publib/private keys), not just a single key. Any suggestions?

This table lists currently supported methods, but it seems none of the green algorithms is what I need: https://diafygi.github.io/webcrypto-examples/

Tomas M
  • 6,919
  • 6
  • 27
  • 33
  • 3
    Since JavaScript runs in the user's browser, how could this possibly be secure? Any debugger would be able to isolate your private key pretty easily, negating the entire purpose of encryption. My recommendation would be to host the private key side in a php or asp or other sever-side process , so the code can't be browsed by the user. – Jim Sep 18 '17 at 19:37
  • Sure, but my problem is that none of the green-marked algos generate a private key at all. – Tomas M Sep 18 '17 at 19:44
  • @Jim sometimes the goal is to isolate the key from the server (ie provide end-to-end encryption to the user). The practicalities of that is a can of worms. – TheGreatContini Sep 18 '17 at 19:48
  • Isn't AES a [symmetric-key algorithm](https://stackoverflow.com/a/273709/691711)? I would only expect it to only produce one, private, key. You would need to use a different algorithm in order to produce a public/private pair. – zero298 Sep 18 '17 at 20:06

3 Answers3

2

Due to ProtonMail's efforts, there is now an open source Symmetric Key Encryption implementation in the browser at: https://openpgpjs.org/

This has had multiple security audits and is the basis of protonmail.com, so it has a fairly good records and maintainer in place. They also have a good summary of important security browser models.

Halcyon
  • 1,376
  • 1
  • 15
  • 22
0

Fortunately the page you pointed at shows that ECDH - including ECDH key pair generation - is supported. This can be used to implement the ECIES encryption scheme. You can then use the raw bits as raw AES key and use that for AES-GCM mode.

The security would of course depend on the system, and Java Script crypto is notoriously hard to get right. This kind of scheme should only be used in addition to TLS, and even then only with extreme care.

Maarten Bodewes
  • 90,524
  • 13
  • 150
  • 263
  • I am not sure if I understand you right. I need a public key which will be sent to other person to encrypt a message, and I need a private key which will stay in the browser so it can decrypt the message later. With your proposal, I will have ECDH keypair (public and private keys) but those can be used only to deriveKeys. So, if I derive an AES key from this, can it be used as a public key to encrypt, and I will be able to use my private (ECDH) key to decrypt? – Tomas M Sep 19 '17 at 06:12
  • No, what ever you do, you need a *trusted public key* of the other person to send him an encrypted message. If you don't have that then anybody could create their own key pair and ask for you to encrypt. Or, if you send them a private key, then anybody could decrypt the message using that. Once you have received the public key then you can create a temporary key pair to derive a secret AES key. You then send the encrypted message & (untrusted) public key to the other person. That other person also derives the AES key and decrypts the message. Everything from "Once" is just a description of IES – Maarten Bodewes Sep 19 '17 at 07:39
-1

mdn example

const encode = (e => e.encode.bind(e))(new TextEncoder)

let { publicKey: pub, privateKey: key } = await crypto.subtle.generateKey({ name: 'ECDH', namedCurve: 'P-521' }, true, ['deriveKey']) // generate key pairs

// get server ecdh public key
let jwk = await fetch('/others public key').then(res=>res.json())
let spub = await crypto.subtle.importKey('jwk', jwk, { name: 'ECDH', namedCurve: 'P-521' }, false, [])

// use spub and key derive a ase key
let gcm = crypto.subtle.deriveKey({ name: 'ECDH', namedCurve: 'P-521', public: spub }, key, { name: 'AES-GCM', length: 256 }, true, ["encrypt", "decrypt"])

// now use gcm to encrypt or decrypt
let text = crypto.subtle.encrypt({ name: 'AES', length: 256 }, gcm, encode('hello world'))

// same on the server