Finally, we get to the last step of our mission: implementing a working PGP exchange between Java and JavaScript. To be honest, we have already completed all the necessary steps in this tutorial’s previous posts. We now simply need to put it all together.
Here’s a quick recap of the previous episodes:
- Java keys generation
- Java PGP encryption
- Front end PGP encryption with OpenPGP.js
- PGP messages exchange between Java and JavaScript
Here we go!
PGP exchange
We start by adding some values to our Identities class, purely for convenience. We specify the user ID details, keys and passphrase of our “JavaScript” user. Remember, we generated those details using OpenPGP.js during this tutorial’s previous post.
We will need those details for two reasons:
- We want to test the OpenPGP.js-generated keys and see if Java is able to use them for encryption / decryption work. Please note that in a normal “exchange” situation we do NOT need the sender’s private keys or passphrase, but only its public keys to verify the received message’s signature!
- We want to test whether a message encrypted by “JavaScript” can be decrypted by “Java”.
So how shall we proceed?
Java – JavaScript artifacts compatibility
Let’s start by checking if we can encrypt and decrypt messages using “JavaScript”‘s keys.
In the OpenPGPTest.java file, the test named encryptMessageAsJavascriptAndDecryptItAsJava()
does exactly that. It uses “JavaScript”‘s artifacts to encrypt a message to Java:
String encryptedMessage = openPgp.encryptAndSign( unencryptedMessage, JAVASCRIPT_USER_ID_EMAIL, JAVASCRIPT_PASSPHRASE, OpenPGP.ArmoredKeyPair.of(JAVASCRIPT_PRIVATE_KEYS, JAVASCRIPT_PUBLIC_KEYS), JAVA_USER_ID_EMAIL, JAVA_PUBLIC_KEYS);
We then make “Java” decrypt the message, using “JavaScript”‘s public keys to verify the message’s signature.
String messageDecryptedByJava = openPgp.decryptAndVerify( encryptedMessage, JAVA_PASSPHRASE, OpenPGP.ArmoredKeyPair.of(JAVA_PRIVATE_KEYS, JAVA_PUBLIC_KEYS), JAVASCRIPT_USER_ID_EMAIL, JAVASCRIPT_PUBLIC_KEYS);
As you probably noticed, we leverage that OpenPGP class we wrote for this previous post. Easy-lazy!
We can of course try this the other way around, encrypting the message as “Java” and decrypting it as “JavaScript”. You can find the test for that in the encryptMessageAsJavaAndDecryptItAsJavascript()
method.
Exchanging those messages
And so we swiftly move to our final milestone: make Java decrypt messages sent using OpenPGP.js and vice-versa.
Remember that, in this post, “JavaScript” encrypted a message addressed to “Java”, using “Java”‘s public keys? Well let’s start by copying the generated message into our OpenPGPTest class:
private static final String JAVASCRIPT_ENCRYPTED_MESSAGE_TO_JAVA = "-----BEGIN PGP MESSAGE-----\n" + "Version: OpenPGP.js v3.0.11\n" + ...
We can now attempt to decrypt that message with Java, as described here:
@Test public void decryptJavascriptMessageToJava() throws IOException, PGPException, NoSuchProviderException { String decryptedMessage = openPgp.decryptAndVerify( JAVASCRIPT_ENCRYPTED_MESSAGE_TO_JAVA, JAVA_PASSPHRASE, OpenPGP.ArmoredKeyPair.of(JAVA_PRIVATE_KEYS, JAVA_PUBLIC_KEYS), JAVASCRIPT_USER_ID_EMAIL, JAVASCRIPT_PUBLIC_KEYS ); assertThat(decryptedMessage).isEqualTo(JAVASCRIPT_UNENCRYPTED_MESSAGE_TO_JAVA); }
Go ahead and run that test: if all went well, it should be green. That means we have successfully managed to decrypt in Java a message that was encrypted in JavaScript!
But does it work the other way around? To verify that, we copy the message we generated in encryptMessageAsJavaAndDecryptItAsJavascript()
into a decrypt-java-to-javascript.html file and attempt to decrypt it using OpenPGP.js and “JavaScript”‘s keys:
<!DOCTYPE html> <html lang="en"> <head> <meta charset="UTF-8"> <title>Encrypt</title> <script src="node_modules/openpgp/dist/openpgp.min.js"></script> </head> <body> <h3>Unencrypted message:</h3> <div id="unencryptedMessage"></div> <script> const javaUserId = {name: 'java', email: 'java@codesandnotes.be'}; const javaPassphrase = 'java passphrase'; const javaNumBits = 2048; javaPublicKeysArmored = `...`; const javascriptUserId = {name: 'javascript', email: 'javascript@codesandnotes.be'}; const javascriptPassphrase = 'javascript passphrase'; const javascriptNumBits = 2048; const javascriptPublicKeysArmored = `...`; const javascriptPrivateKeysArmored = `...`; const javaMessageToJavascript = `-----BEGIN PGP MESSAGE----- lots of characters here... -----END PGP MESSAGE----- `; // Initialize openpgp let openpgp = window.openpgp; openpgp.initWorker({path: 'node_modules/openpgp/dist/openpgp.worker.min.js'}); // Decrypt let javascriptPrivateKeys = openpgp.key.readArmored(javascriptPrivateKeysArmored); let javascriptUnencryptedPrivateKeysPromises = javascriptPrivateKeys.keys.map(javascriptPrivateKey => { return javascriptPrivateKey.decrypt(javascriptPassphrase); }); Promise.all(javascriptUnencryptedPrivateKeysPromises).then(() => { return openpgp.decrypt({ message: openpgp.message.readArmored(javaMessageToJavascript), publicKeys: openpgp.key.readArmored(javaPublicKeysArmored).keys, privateKeys: javascriptPrivateKeys.keys }); }).then((unencryptedMessage) => { document.getElementById('unencryptedMessage').innerHTML = unencryptedMessage.data; console.info(unencryptedMessage.data); }); </script> </body> </html>
Now access that page with your browser and watch our message unfold.
Hurrah !!!
Yes, we did it: we managed to have Java and JavaScript exchange PGP-encrypted messages!
The whole code that sums up this tutorial is available on GitHub, so feel free to check it out and adapt it as you deem fit. Be patient though! Encryption is a sensitive beast that requires patience and… well, lots of unit tests !
Until next time,
Cheers!