OpenPGP signing and encrypting in one pass is the most secure way by which we can send encrypted data. We sign the file with our private key and encrypt it with the public key of the person to whom we are sending the message. This way the encrypted file can be read only by the recipient and she can tell with absolute assurance that it was you who sent the message.
The examples below demonstrate in practice how to achieve this with OpenPGP Library for Java.
Sign and encrypt a file in one pass
1. with keys located in files
2. with keys located in a KeyStore
Sign and encrypt a Stream
3. with keys located in files
4. with keys located in a KeyStore
Sign and encrypt a String message
5. with keys in files
6. with keys in a KeyStore
Sign and encrypt for multiple recipients
7. with keys located in files
8. with keys located in a KeyStore
Sign and encrypt multiple files
9. with keys located in files
10. with keys located in a KeyStore
11. Sign and encrypt a file for older PGP version (including PGP 6.x, 7.x, 8.x)
Appendix
1. Sign and encrypt file with keys located in files
This example intentionally sets the parameter withIntegrityCheck to false, as integrity check information is not supported by PGP (r) 6.5 and prior versions, and if you do not know for sure what OpenPGP software your partners are using, it is better to avoid this feature.
import com.didisoft.pgp.PGPLib; public class SignAndEncryptFile { public static void main(String[] args) throws Exception{ // create an instance of the library PGPLib pgp = new PGPLib(); // should output be ASCII or binary boolean asciiArmor = false; // should integrity check information be added, set to true for compatibility with GnuPG 2.2.8+ boolean withIntegrityCheck = true; // sign and encrypt pgp.signAndEncryptFile("INPUT.txt", "our_private_key.asc", "private_key_pass_phrase", "recipient_public_key.asc", "encrypted.pgp", asciiArmor, withIntegrityCheck); } } |
2. Sign and encrypt file with keys located in a KeyStore
This example is equivalent to the above one except that the encryption (public) key of the receiver has been imported to a KeyStore and our secret key has either been generated or imported in the same KeyStore object.
import com.didisoft.pgp.KeyStore; import com.didisoft.pgp.PGPLib; public class KeystoreSignAndEncryptFile { public static void main(String[] args) throws Exception{ // create an instance of the KeyStore KeyStore keyStore = new KeyStore("pgp.keystore", "changeit"); // create an instance of the library PGPLib pgp = new PGPLib(); // is output ASCII or binary boolean asciiArmor = false; // should integrity check information be appended, set to true for compatibility with GnuPG 2.2.8+ boolean withIntegrityCheck = true; // The signing key is usually our private key String signUserId = "demo@didisoft.com"; String signKeyPassword = "changeit"; // the User Id of the recipient, this // example assumes her public key is // already imported in the KeyStore file String encUserId = "recipient@company.com"; pgp.signAndEncryptFile("INPUT.txt", keyStore, signUserId, signKeyPassword, encUserId, "encrypted.pgp", asciiArmor, withIntegrityCheck); } } |
3. Sign and encrypt an input stream with keys located in files
We can sign and encrypt an input stream source too.
The second parameter of this method is often misunderstood. Although the encrypted data is stored in an output stream, the OpenPGP data format stores in addition to the encrypted bytes a file name string associated with them, so we are forced to set an additional artificial file name label.
import java.io.FileInputStream; import java.io.FileOutputStream; import java.io.InputStream; import java.io.OutputStream; import com.didisoft.pgp.PGPLib; public class SignAndEncryptStream { public static void main(String[] args) throws Exception{ // initialize the library PGPLib pgp = new PGPLib(); // is output ASCII or binary boolean asciiOutput = true; // should integrity check information be appended, set to true for compatibility with GnuPG 2.2.8+ boolean withIntegrityCheck = true; // recipient public key as stream InputStream recipientPublicKeyStream = new FileInputStream("recipient_public_key.asc"); // private signing key as stream InputStream privateKeyStream = new FileInputStream("our_private_key.asc"); String privateKeyPassword = "changeit"; // input stream to be encrypted InputStream inputStream = new FileInputStream("INPUT.txt"); // encrypted output destination OutputStream encryptedOutStream = new FileOutputStream("encrypted.pgp"); // Here "INPUT.txt" is just a string to be written in // the message OpenPGP packet which stored // file name label, timestamp, and the actual data bytes pgp.signAndEncryptStream(inputStream, "INPUT.txt", privateKeyStream, privateKeyPassword, recipientPublicKeyStream, encryptedOutStream, asciiOutput, withIntegrityCheck); } } |
4. Sign and encrypt an input stream with keys located in a KeyStore
This example is equivalent to the above one, except the keys are located in a KeyStore.
import java.io.FileInputStream; import java.io.FileOutputStream; import java.io.InputStream; import java.io.OutputStream; import com.didisoft.pgp.KeyStore; import com.didisoft.pgp.PGPLib; public class KeyStoreSignAndEncryptStream { public static void main(String[] args) throws Exception{ // create an instance of the KeyStore KeyStore keyStore = new KeyStore("pgp.keystore", "changeit"); // create an instance of the library PGPLib pgp = new PGPLib(); // is output ASCII or binary boolean asciiArmor = true; // should integrity check information be added, set to true for compatibility with GnuPG 2.2.8+ boolean withIntegrityCheck = true; // The signing key is usually our private key String signUserId = "demo@didisoft.com"; String signKeyPassword = "changeit"; // the User Id of the recipient, this // example assumes her public key is // already imported in the KeyStore file String encUserId = "recipient@company.com"; InputStream dataStream = new FileInputStream("INPUT.txt"); // this parameter is just a label that is associated with // the encrypted content in the OpenPGP archive String encryptedContentLabel = "INPUT.txt"; // create the output stream OutputStream encryptedStream = new FileOutputStream("encrypted.pgp"); pgp.signAndEncryptStream(dataStream, encryptedContentLabel, keyStore, signUserId, signKeyPassword, encUserId, encryptedStream, asciiArmor, withIntegrityCheck); } } |
Sign and encrypt a String message with keys located in files
When signing and encrypting a string message, the output is in ASCII armored format. The default method provided by the library assumes that the String message is UTF-8 encoded.
import com.didisoft.pgp.PGPLib; public class SignAndEncryptString { public static void main(String[] args) throws Exception { String stringToEncrypt = "the quick brown fox jumps"; // create an instance of the library PGPLib pgp = new PGPLib(); // The signing key is usually our private key String privateKeyFile "c:\\my_private_key.asc"; String privateKeyPassword = "my password"; String publicEncryptionKeyFile = "c:\\recipient_public_key.asc"; // sign and encrypt String signedAndEncryptedString = pgp.signAndEncryptString(stringToEncrypt, privateKeyFile, privateKeyPassword, publicEncryptionKeyFile); } } |
Sign and encrypt a String message with keys in a KeyStore
When signing and encrypting a string message, the output is in ASCII armored format. The default method provided by the library assumes that the String message is UTF-8 encoded but an overloaded version exists where a different encoding can be specified.
import com.didisoft.pgp.PGPLib; public class SignAndEncryptString { public static void main(String[] args) throws Exception { String stringToEncrypt = "the quick brown fox jumps"; // create an instance of the library PGPLib pgp = new PGPLib(); // 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"; String publicEncryptionKeyId = "recipient@partner.org"; // sign and encrypt String signedAndEncryptedString = pgp.signAndEncryptString(stringToEncrypt, keyStore, privateKeyId, privateKeyPassword, publicEncryptionKeyId); } } |
Sign and encrypt for multiple recipients with keys in files
OpenPGP signed end encrypted messages can also be produced for multiple recipients just like ordinary .pgp encrypted data. In order to produce signed and encrypted content for multiple recipients, we must provide the public keys of our recipients. The signature key is only one – our private key, as we are the signer who guarantees that the data comes from us.
Lets see how to achieve this when the public keys of the recipients are located in files:
import com.didisoft.pgp.PGPLib; public class SignAndEncryptFile { public static void main(String[] args) throws Exception{ // create an instance of the library PGPLib pgp = new PGPLib(); // should output be ASCII or binary boolean asciiArmor = false; // should integrity check information be added, set to true for compatibility with GnuPG 2.2.8+ boolean withIntegrityCheck = true; String[] recipientKeys = new String[] {"c:\\pgpkeys\\recipient_key1.asc", "c:\\pgpkeys\\recipient_key2.asc", "c:\\pgpkeys\\recipient_key3.asc"}; // sign and encrypt pgp.signAndEncryptFile("c:\\data\\INPUT.txt", "c:\\mykeys\\my_private_key.asc", "key password", recipientKeys, "encrypted_output.pgp", asciiArmor, withIntegrityCheck); } } |
Sign and encrypt for multiple recipients with keys in a KeyStore
When we store our keys in a KeyStore, an overloaded version of the method is available and the keys are referenced either by raw Key Id (of type long), hexadecimal Key Id string, or key User Id (part of or the whole).
The example Java code below encrypts a file by specifying the recipient key Id’s mixing User Id’s and hexadecimal key Ids:
import com.didisoft.pgp.PGPLib; public class SignAndEncryptFile { public static void main(String[] args) throws Exception{ // create an instance of the library PGPLib pgp = new PGPLib(); // should output be ASCII or binary boolean asciiArmor = false; // should integrity check information be added, set to true for compatibility with GnuPG 2.2.8+ boolean withIntegrityCheck = true; // create an instance of the KeyStore KeyStore keyStore = new KeyStore("pgp.keystore", "changeit"); // The signing key is usually our private key String signKeyUserId = "demo@didisoft.com"; String signKeyPassword = "changeit"; String encryptionKeyUserIds = new String[] {"Recipient Company ACM", "832276A1", // --> hexadecimal key id "Recipient Company 3"} ; // sign and encrypt pgp.signAndEncryptFile("c:\\data\\INPUT.txt", keyStore, signKeyUserId, signKeyPassword, encryptionKeyUserIds, "c:\\output\\encrypted_output.pgp", asciiArmor, withIntegrityCheck); } } |
1. Sign and encrypt multiple files
The operation of signing and encrypting multiple data files into a single .pgp archive is compatible with PGP® Desktop. It could be used to automate exchange with other OpenPGP applications that support this multi file format as well.
import com.didisoft.pgp.PGPLib; public class SignAndEncryptFiles { public static void main(String[] args) throws Exception{ // create an instance of the library PGPLib pgp = new PGPLib(); // should output be ASCII or binary boolean asciiArmor = false; // sign and encrypt pgp.signAndEncryptFiles(new String {"INPUT1.txt", "INPUT2.txt"}, "our_private_key.asc", "private_key_pass_phrase", "recipient_public_key.asc", "encrypted.pgp", asciiArmor); } } |
2. Sign and encrypt multiple files with key from a KeyStore
This example is equivalent to the above one except that the encryption (public) key of the receiver has been imported to a KeyStore and our secret key has either been generated or imported in the same KeyStore object.
import com.didisoft.pgp.KeyStore; import com.didisoft.pgp.PGPLib; public class KeystoreSignAndEncryptFiles { public static void main(String[] args) throws Exception{ // create an instance of the KeyStore KeyStore keyStore = new KeyStore("pgp.keystore", "changeit"); // create an instance of the library PGPLib pgp = new PGPLib(); // is output ASCII or binary boolean asciiArmor = false; // The signing key is usually our private key String signUserId = "demo@didisoft.com"; String signKeyPassword = "changeit"; // the User Id of the recipient, this // example assumes her public key is // already imported in the KeyStore file String encUserId = "recipient@company.com"; pgp.signAndEncryptFiles(new String[] {"INPUT1.txt", "INPUT2.txt"}, keyStore, signUserId, signKeyPassword, encUserId, "encrypted.pgp", asciiArmor); } } |
Sign and encrypt a file for older PGP version
Some financial institutions still operate with older version of the PGP software and if you need to exchange signed and encrypted data with them you must use the PGPLib class methods that end with Version3 like PGPLib.signAndEncryptFileVersion3 (version 3 is the packet version number used in PGP 6.x), otherwise your files may be rejected by the other party.
The example below signs and encrypts a file but the embedded signature is in the old format recognized by PGP 6.x compatible systems:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 | import com.didisoft.pgp.PGPLib; public class SignAndEncryptFileV3 { public static void main(String[] args) throws Exception { // create an instance of the library PGPLib pgp = new PGPLib(); // should output be ASCII or binary boolean asciiArmor = false; pgp.signAndEncryptFileVersion3("examples/DataFiles/INPUT.txt", "examples/DataFiles/private.key", "changeit", "examples/DataFiles/public.key", "examples/DataFiles/encrypted.pgp", asciiArmor); } } |
Exception Handling
The quick exception handling of the signAndEncrypt set of methods is to catch only java.io.IOException and com.didisoft.pgp.PGPException.
A more thorough exception handling technique is to catch a few sub classes of PGPException before actually catching PGPException. The example below illustrates which exceptions we can expect:
Java code sample
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.signAndEncrypt... } catch (IOException e) { // error reading input or writing output } catch (NoPrivateKeyFoundException e) { // if the passed private key does not contain a private key or is corrupted } catch (WrongPasswordException e) { // the password for the provided private key is wrong } catch (NoPublicKeyFoundException e) { // if the supplied public key source does not contain a public key // or it is corrupted } catch (KeyIsExpiredException e) { // the supplied public key is expired } catch (KeyIsRevokedException e) { // the supplied public key is revoked } catch (PGPException e) { // general decryption error not among the above ones } } } |
Summary
All of the above mentioned examples produce signatures in OpenPGP version 4 format, which is the current format. Some OpenPGP compatible software systems can expect digital signatures in the older version 3 format, although this is a very rare case. To produce version 3 signatures you can either use the examples above and replace the signAndEncrypt methods with the ones that end with Version3 or see the examples from the Examples folder in the library distribution package.