This article demonstrates how to decrypt data that has been OpenPGP signed and encrypted in one pass. We can still decrypt it with the standard decryption methods, but with the provided one pass decryption and signature verification we can also verify that the sender of the message is the one that we expect.
In order to execute the methods illustrated below we need our private key (in order to decrypt the message) and the public key of the sender (to verify the digital signature embedded in the message).
Note: The methods illustrated below are available as of version 3.1 of the library. If you are looking for the old tutorial, please check here.
Decrypt and verify a file in one pass
1. with keys located in files on the disk
2. with keys located in a KeyStore
Decrypt and verify a Stream in one pass
3. with keys supplied also as Streams
4. with keys located in a KeyStore
Decrypt and verify a String data
5. with keys in files
6. with keys in a KeyStore
Decrypt and verify archive that contains multiple files
7. with keys located in files on the disk
8. with keys located in a KeyStore
Appendix A
1. Decrypt and verify file with keys located in files on the disk
Note that even if the signature is invalid the contained file will be extracted.
import com.didisoft.pgp.*; public class DecryptAndVerifyFileDemo { public static void main(String[] args) throws Exception{ // create an instance of the library PGPLib pgp = new PGPLib(); // verify and extract the signed content SignatureCheckResult signatureCheck = pgp.decryptAndVerify("encrypted.pgp", "decryption_private_key.asc", "decryption key password", "sender_public_key.asc", "OUTPUT.txt"); if (signatureCheck == SignatureCheckResult.SignatureVerified) { System.out.println("The signature is valid."); } else if (signatureCheck == SignatureCheckResult.SignatureBroken) { System.out.println("Message corrupted or signature forged"); } else if (signatureCheck == SignatureCheckResult.PublicKeyNotMatching) { System.out.println("Signature not matching provided public key (the message is from another sender)"); } else { System.out.println("No signature found in message"); } } } |
2. Decrypt file with keys located in a KeyStore
We must specify our password for the private key that will be used to decrypt the file (the library searches for a key with Key ID equal to the one used to encrypt the file). For the verification a public key with Key ID equal to the one in the digital signature is searched among the public keys stored in the KeyStore.
import com.didisoft.pgp.*; public class DecryptAndVerifyFileDemo { public static void main(String[] args) throws Exception{ // create an instance of the library PGPLib pgp = new PGPLib(); // initialize the KeyStore KeyStore keyStore = new KeyStore("mykeys.keystore", "password"); // verify and extract the signed content SignatureCheckResult signatureCheck = pgp.decryptAndVerify("encrypted.pgp", keyStore, "decryption key password", "OUTPUT.txt"); if (signatureCheck == SignatureCheckResult.SignatureVerified) { System.out.println("The signature is valid."); } else if (signatureCheck == SignatureCheckResult.SignatureBroken) { System.out.println("Message corrupted or signature forged"); } else if (signatureCheck == SignatureCheckResult.PublicKeyNotMatching) { System.out.println("No matching public key was found in the KeyStore"); } else { System.out.println("No signature found in message"); } } } |
3. Decrypt stream with keys supplied as streams
This method gives more freedom on how the encrypted data and OpenPGP keys are stored.
import java.io.*; import com.didisoft.pgp.*; public class DecryptAndVerifyStreamDemo { public static void main(String[] args) throws Exception{ // create an instance of the library PGPLib pgp = new PGPLib(); InputStream dataStream = new FileInputStream("signed.pgp"); InputStream decryptionKeyStream = new FileInputStream("decryption_private_key.asc"); InputStream verificationKeyStream = new FileInputStream("sender_public_key.asc"); OutputStream outputStream = new FileOutputStream("OUTPUT.txt"); try { // verify and extract the signed content SignatureCheckResult signatureCheck = pgp.decryptAndVerify(dataStream, decryptionKeyStream, "decryption key password", verificationKeyStream, outputStream); if (signatureCheck == SignatureCheckResult.SignatureVerified) { System.out.println("The signature is valid."); } else if (signatureCheck == SignatureCheckResult.SignatureBroken) { System.out.println("Message corrupted or signature forged"); } else if (signatureCheck == SignatureCheckResult.PublicKeyNotMatching) { System.out.println("Signature not matching provided public key (the message is from another sender)"); } else { System.out.println("No signature found in message"); } } finally { dataStream.close(); decryptionKeyStream.close(); verificationKeyStream.close(); outputStream.close(); } } } |
4. Decrypt stream with keys located in a KeyStore
This example is equivalent to the decrypt above, only the encrypted data and the decrypted output are streams.
import java.io.*; import com.didisoft.pgp.*; public class DecryptAndVerifyStreamDemo { public static void main(String[] args) throws Exception{ // create an instance of the library PGPLib pgp = new PGPLib(); InputStream dataStream = new FileInputStream("signed.pgp"); KeyStore keyStore = new KeyStore("mylocal.keystore", "my password"); OutputStream outputStream = new FileOutputStream("OUTPUT.txt"); try { // verify and extract the signed content SignatureCheckResult signatureCheck = pgp.decryptAndVerify(dataStream, keyStore, "decryption key password", outputStream); if (signatureCheck == SignatureCheckResult.SignatureVerified) { System.out.println("The signature is valid."); } else if (signatureCheck == SignatureCheckResult.SignatureBroken) { System.out.println("Message corrupted or signature forged"); } else if (signatureCheck == SignatureCheckResult.PublicKeyNotMatching) { System.out.println("No matching public key was found in the KeyStore"); } else { System.out.println("No signature found in message"); } } finally { dataStream.close(); outputStream.close(); } } } |
Decrypt and verify a String data
ASCII armored signed and encrypted OpenPGP data can be handled as a String input and decrypted and verified with the method the decryptAndVerify:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 | import java.io.*; import com.didisoft.pgp.*; public class DecryptAndVerifyStringDemo { public static void main(String[] args) throws Exception { // create an instance of the library PGPLib pgp = new PGPLib(); String openpgpMessage = ""; // Fill here the OpenPGP encrypted and signed message StringBuffer decryptedMessage = new StringBuffer(); // The public key of the sender used to verify the message String publicKeyFile = "c:\\public_key.asc"; String privateKeyFile = "c:\\my_private_key.asc"; String privateKeyPassword = "my passsword"; // verify and extract the signed content SignatureCheckResult signatureCheck = pgp.decryptAndVerify(openpgpMessage, privateKeyFile, privateKeyPassword, publicKeyFile, decryptedMessage); if (signatureCheck == SignatureCheckResult.SignatureVerified) { System.out.println("The signature is valid."); } else if (signatureCheck == SignatureCheckResult.SignatureBroken) { System.out.println("Message corrupted or signature forged"); } else if (signatureCheck == SignatureCheckResult.PublicKeyNotMatching) { System.out.println("Signature not matching provided public key (the message is from another sender)"); } else { System.out.println("No signature found in message"); } System.out.println("plain message: " + decryptedMessage.toString()); } } |
Decrypt and verify a String data
When our keys are in a KeyStore we can also perform the same decrypt and verify operation over ASCII armored signed and encrypted OpenPGP data available as a String input with the method the decryptAndVerifyString:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 | import java.io.*; import com.didisoft.pgp.*; public class DecryptAndVerifyStringDemo { public static void main(String[] args) throws Exception { // create an instance of the library PGPLib pgp = new PGPLib(); String openpgpMessage = "..."; // Fill here the OpenPGP encrypted and signed message StringBuffer decryptedMessage = new StringBuffer(); // The signing key is usually our private key KeyStore keystore = KeyStore.openFile("mylocal.keystore", "my store password"); String privateKeyId "me@company.com"; String privateKeyPassword = "my key password"; // verify and extract the signed content SignatureCheckResult signatureCheck = pgp.decryptAndVerifyString(openpgpMessage, keystore, privateKeyPassword, decryptedMessage); if (signatureCheck == SignatureCheckResult.SignatureVerified) { System.out.println("The signature is valid."); } else if (signatureCheck == SignatureCheckResult.SignatureBroken) { System.out.println("Message corrupted or signature forged"); } else if (signatureCheck == SignatureCheckResult.PublicKeyNotMatching) { System.out.println("Signature not matching provided public key (the message is from another sender)"); } else { System.out.println("No signature found in message"); } System.out.println("plain message: " + decryptedMessage.toString()); } } |
Decrypt and verify .pgp archive that may contain multiple files
In the case when a .pgp file contains multiple encrypted files inside, we may wish to verify the digital signature of the .pgp archieve and still obtain a list of the embedded file names, when they are extracted. In order to do so, we must perform this in two steps, because the above mentioned PGPLib.decryptAndVerify methods return only the signature checking result, but no information what was decrypted.
The example below illustrates this two step process where we use the methods PGPLib.verifyWithoutExtracting and PGPLib.decryptTo:
import com.didisoft.pgp.*; public class DecryptAndVerifyMultiple { public static void main(String[] args) throws Exception{ // create an instance of the library PGPLib pgp = new PGPLib(); // verify and extract the signed content SignatureCheckResult signatureCheck = pgp.verifyWithoutExtracting("encrypted.pgp", "decryption_private_key.asc", "decryption key password", "sender_public_key.asc"); if (signatureCheck == SignatureCheckResult.SignatureVerified) { System.out.println("The signature is valid."); } else if (signatureCheck == SignatureCheckResult.SignatureBroken) { System.out.println("Message corrupted or signature forged"); } else if (signatureCheck == SignatureCheckResult.PublicKeyNotMatching) { System.out.println("Signature not matching provided public key (the message is from another sender)"); } else { System.out.println("No signature found in message"); } String[] decryptedFiles = pgp.decryptFileTo("encrypted.pgp", "decryption_private_key.asc", "decryption key password", "c:\\Temp"); } } |
Decrypt and verify .pgp archive that may contain multiple files with a KeyStore
This example is equivalent to the above one, but it’s using a KeyStore:
import com.didisoft.pgp.*; public class DecryptAndVerifyMultiple { public static void main(String[] args) throws Exception{ // create an instance of the library PGPLib pgp = new PGPLib(); // initialize the KeyStore KeyStore keyStore = new KeyStore("mykeys.keystore", "password"); // verify and extract the signed content SignatureCheckResult signatureCheck = pgp.verifyWithoutExtracting("encrypted.pgp", keyStore, "decryption key password"); if (signatureCheck == SignatureCheckResult.SignatureVerified) { System.out.println("The signature is valid."); } else if (signatureCheck == SignatureCheckResult.SignatureBroken) { System.out.println("Message corrupted or signature forged"); } else if (signatureCheck == SignatureCheckResult.PublicKeyNotMatching) { System.out.println("Signature not matching provided public key (the message is from another sender)"); } else { System.out.println("No signature found in message"); } String[] decryptedFiles = pgp.decryptFileTo("encrypted.pgp", keyStore, "decryption key password"); } } |
Exception Handling
We can simply catch java.io.IOException and com.didisoft.PGPException. If we are interested to identify more deeply what has gone wrong, we can catch a number of PGPException sub classes beforehand.
Check the example below for a list of the expected exception sub classes.
import java.io.IOException; import com.didisoft.pgp.*; import com.didisoft.pgp.exceptions.*; public class ExceptionHandlingDemo { public static void main(String[] a) { PGPLib pgp = new PGPLib(); try { pgp.decryptAndVerify... } catch (IOException e) { // error reading input or writing output } catch (NonPGPDataException e) { // the passed encrypted input is not a valid OpenPGP archive } catch (IntegrityCheckException e) { // the passed encrypted input is corrupted } catch (NoPublicKeyFoundException e) { // if the passed public key file does not contain a public key or is corrupted } catch (FileIsPBEEncryptedException e) { // the passed encrypted input is encrypted with a password, // but we try to decrypt it with a private key } catch (NoPrivateKeyFoundException e) { // if the supplied private key source does not contain a private key // or is corrupted } catch (WrongPrivateKeyException e) { // the encrypted input was encrypted with a different private key // than the provided one } catch (WrongPasswordException e) { // the password for the provided private key is wrong } catch (DetachedSignatureException e) { // the input is not an encrypted message, but a detached OpenPGP signature } catch (PGPException e) { // general decryption error not among the above ones } } } |
Summary
This chapter illustrated how to decrypt and verify a one pass signed and encrypted OpenPGP data.