OpenPGP Integration (Java and JavaScript): OpenPGP.js

Woman contemplates the encrypted web

Thinking of it, the OpenPGP.js library is quite an amazing feat.

You probably know that ProtonMail is now the primary maintainer of this JavaScript implementation of the OpenPGP standard. Tankred Hase, co-founder of the unfortunately now-defunct Whiteout Mail, developed the library before that. Other projects such as Mailvelope also use OpenPGP.js at their core.

Although intimidating at first (and slightly under-documented, IMHO) OpenPGP.js provides one of the easiest and most reliable ways to perform PGP encryption on the front side. Let’s see how!

Quick reminder: this tutorial’s previous posts are available here:

Generate key pairs

OpenPGP can very easily generate key pairs, including ECC  keys since version 3. We shall stick to RSA keys for this tutorial, though.
So, having imported the openpgp library we can now obtain our RSA key pairs as follows:

<script>
  const javascriptUserId = {name: 'javascript', email: 'javascript@codesandnotes.be'};
  const javascriptPassphrase = 'javascript passphrase';
  const javascriptNumBits = 2048;

  // Initialize openpgp
  let openpgp = window.openpgp;
  openpgp.initWorker({path: 'node_modules/openpgp/dist/openpgp.worker.min.js'});

  // Generate some keys!
  openpgp.generateKey({
    userIds: javascriptUserId,
    numBits: javascriptNumBits,
    passphrase: javascriptPassphrase
  }).then(keyPair => {
    console.info(keyPair.publicKeyArmored);
    console.info(keyPair.privateKeyArmored);
  });
</script>

Make sure you choose a good passphrase, and also use the same number of bits than the Java RSA keys.

Great, we now have our JavaScript-generated key pairs. But we also have our Java, server-side generated keys from our previous tutorial. How about using both to perform some front end PGP encryption and decryption tasks?

Front end PGP encryption and decryption

Say Ms JavaScript wants to send an encrypted message to Mr Java using a front end client.
We know we need Mr Java’s public key to encrypt the message.
However we also must use Ms JavaScript’s private key to sign that message! Since the armored private key is protected by passphrase, the code starts by decrypting it. This must be performed once, as long as we have a hold on the unencrypted private key.

One we have all the required elements, we proceed on encrypting the message addressed to Mr Java:

let javascriptPrivateKeys = openpgp.key.readArmored(javascriptPrivateKeysArmored);
let javascriptUnencryptedPrivateKeysPromises = javascriptPrivateKeys.keys.map(javascriptPrivateKey => {
  return javascriptPrivateKey.decrypt(javascriptPassphrase);
});

Promise.all(javascriptUnencryptedPrivateKeysPromises).then(() => {

  return openpgp.encrypt({
    armor: true,
    compression: openpgp.enums.compression.zlib,
    data: 'A message encrypted by javascript',
    privateKeys: javascriptPrivateKeys.keys,
    publicKeys: openpgp.key.readArmored(javaPublicKeysArmored).keys
  });

}).then(encryptedMessage => {
  console.info(encryptedMessage.data);
});

Now let’s assume Mr Java receives that encrypted message and wants to decrypt it. We start by decrypting Mr Java’s armored private key if not done already. Then we use that key to decrypt the message sent by Ms JavaScript. Since we want confirmation that the message really comes from Ms JavaScript we also need her public key:

let javaPrivateKeys = openpgp.key.readArmored(javaPrivateKeysArmored);
let javaUnencryptedPrivateKeysPromises = javaPrivateKeys.keys.map(javaPrivateKey => {
  return javaPrivateKey.decrypt(javaPassphrase);
});

Promise.all(javaUnencryptedPrivateKeysPromises).then(() => {
  
  return openpgp.decrypt({
    message: openpgp.message.readArmored(encryptedMessage.data),
    publicKeys: openpgp.key.readArmored(javascriptPublicKeysArmored).keys,
    privateKeys: javaPrivateKeys.keys
  });
  
}).then(unencryptedMessage => {
  console.info(unencryptedMessage.data);
});

And that’s basically all there is to it.

Running the tutorial code

You can also perform this the other way around, obviously: having Mr Java encrypt a message to Ms JavaScript. In fact, you can find the whole tutorial code on this GitHub repository.
For simplicity, I have set up a Spring Boot web application. Just run the Application class and point your browser to http://localhost:8080 . A simple index HTML page will give you links to run the test code we discussed here.

So far, we’ve been able to generate PGP key pairs on both sides. We also performed encryption and decryption tasks in Java and in JavaScript, using key pairs generated by one side or the other. That’s encouraging!

We’re one step from claiming OpenPGP integration between the two environments, and that’s having JavaScript decrypt a PGP message sent by the Java server.
Don’t worry though: the hardest part is behind us now.

Until next time,

Cheers!

Leave A Comment

Please be polite. We appreciate that. Your email address will not be published and required fields are marked

This site uses Akismet to reduce spam. Learn how your comment data is processed.

%d bloggers like this: