Java Cryptography | Part 2
Encryption and Digital Signatures
By: James H. Wong
Feb. 11, 2013 02:00 PM
In today's environment, information security is crucial for everyone. Security needs vary widely from protecting social security numbers to guarding corporate strategy. Information espionage can occur at all levels. A human resources employee or manager takes employee personnel files home to work on them and unfortunately loses them or they get stolen. An employee's notes to a supervisor regarding a case are intercepted and read via monitoring software by an outside hacker. The resulting damages can be costly and could be avoided by protecting assets with encryption technology.
This article demonstrates the implementation of the Cryptography header cited in the previous article and illustrates how to encrypt and digitally sign files using a hybrid combination of asymmetric public/private key encryption and symmetric encryption. A symmetric key is used to encrypt the file and the asymmetric public key encrypts the symmetric key. The asymmetric private key decrypts the symmetric key which in turn is used to decrypt the encrypted file.
Figure 1. Asymmetric Key Encryption Functions
The same pair of encryption keys can be used with digital signatures. The private key is used to sign a file and generate a digital signature. The public key is used to verify the authenticity of the signature. The encrypted symmetric key and digital signature along with additional information are stored in the Cryptography header which is affixed to the front of the encrypted file.
Figure 2. Asymmetric Key Signature Functions
The encryption technique requires the Java libraries developed by the Legion of the Bouncy Castle (www.bouncycastle.org). The Bouncy Castle jars, bcprov-jdk15on-147.jar and bcpkix-jdk15on-147.jar, contain all the methods required to encrypt, decrypt, sign and verify a digital signature. The following Java code snippet loads the BouncyCastle provider, which implements the Java Cryptography Security services such as algorithms and key generation.
Generating Public/Private Encryption Keys
Figure 3. Pair of Asymmetric Keys
Step 1: Create an instance of the KeyPairGenerator class specifying the RSA asymmetric algorithm and Bouncy Castle provider. Generate a 1024-bit asymmetric public and private key pair to be stored in a password protected key store file.
//-Generate the pair of Asymmetric Encryption Keys (public/private)
Step 2: Extract four hex digits from the public key to create a unique alias for the filename of the certificate and key store.
KeyFactory tKeyFactory = KeyFactory.getInstance("RSA");
Step 3: Create a certificate to hold the asymmetric public key that can be used to encrypt your confidential information or distributed to others for exchanging encrypted files.
JcaContentSignerBuilder tSignBldr =
Step 4: Save the certificate to disk so that it can be used for encrypting your own personal information or distributing to others.
byte tBA = tCert.getEncoded();
Step 5: Insert the certificate into an array of X509 certificates called a chain. Create a password protected key store file to hold the private key and certificate chain and save it to disk. The key store saves the private key and certificate chain as an entry at a unique key called the alias and is password protected as well. The same password will be used to protect the entry and key store.
KeyStore tKStore = KeyStore.getInstance("JKS", "SUN");
Encryption with Digital Signature
Figure 4: AES Symmetric Key
Step 1: Let's assume you want to encrypt and digitally sign the file, C:\sampleFile.txt. Dynamically generate a symmetric "secret" key using the Java class, KeyGenerator. The symmetric key will be used to encrypt the file. The Java class KeyGenerator is instantiated using the symmetric algorithm, "AES", and provider, BouncyCastle("BC"). The instance of KeyGenerator is initialized with a secure random seed and the maximum key size in bits allowed by your country. The following code illustrates how to generate a symmetric key.
KeyGenerator tKeyGen = KeyGenerator.getInstance("AES", "BC");
Step 2: Generate a Cryptography header that stores cryptographic information used to later decrypt the file and verify the digital signature. Save the symmetric algorithm, mode and padding in the header. The following code illustrates the header instantiation and initialization.
CryptoHeader tHead = new CryptoHeader();
Step 3: Load the owner's certificate and extract the public key. You can also load another person's certificate if you are encrypting the file for someone other than yourself. The public key will be used to encrypt the symmetric key.
InputStream tCertIS = new FileInputStream("C:\\" +tUniqueAlias+ ".cer");
Step 4: Generate a Java Cipher object and initialize it using the owner's or another person's asymmetric public key extracted from the certificate and set its mode to "Cipher.WRAP_MODE". Use the Java Cipher and public key to encrypt and wrap the symmetric key. Store the wrapped encrypted key in the header and its length.
Cipher tCipherRSA = Cipher.getInstance("RSA", "BC");
Figure 5. Wrap Symmetric Key
Step 5: Generate an initialization vector if required by the symmetric mode chosen to encrypt the file. AES is a block cipher symmetric algorithm and the Counter (CTR) mode requires an initialization vector. The AES block size is 16 bytes.
int tSize = Cipher.getInstance("AES", "BC").getBlockSize();
Figure 6. Initialization Vector
Step 6: Use the previously instantiated Cipher and set its mode to "Cipher.ENCRYPT_MODE". Use the public key to encrypt the initialization vector. Store the encrypted vector in the header along with its length.
Figure 7. Wrap Initialization Vector
Step 7:(Optional) If you are using an enterprise CA hierarchy and encrypting for yourself, use the CA asymmetric public key stored in the key store to wrap the symmetric key and encrypt the initialization vector and store both in the header. If encrypting for another person, use the owner's asymmetric key to wrap the symmetric key and encrypt the initialization vector and store both in the header. You can store the values in the header variables, wrappedSymKeyOther and initVectorOther as well as their lengths. This provides the ability for the CA or owner to decrypt the encrypted file.
Step 8: The private key is stored in a Java key store and is password protected. Load the key store using your password. Retrieve the asymmetric private key from the key store using the same password. The asymmetric private key will be used to generate a digital signature and stored in the header.
FileInputStream tStoreFIS=new FileInputStream("C:\\"+tUniqueAlias+".jks");
Figure 8. Private Key
Step 9: Generate a Java Signature object specifying the signature algorithm and provider. Initialize the signature engine with the owner's asymmetric private key. The signature engine is bound to the private key so that only the public key can validate it. Store the signature algorithm in the header so that it can be verified later.
Signature tSigEngine =
Step 10: Generate a Java Cipher object based on the symmetric algorithm, mode, padding and provider which will be used to encrypt the target file. Initialize the Cipher object using the symmetric key and initialization vector and set its mode to "Cipher.ENCRYPT_MODE".
Cipher tCipherEncrypt = Cipher.getInstance("AES/CTR/PKCS7Padding", "BC");
Step 11: Load the file to be encrypted as a Java "FileInputStream". Encrypt the file to a temporary Java "FileOutputStream" using the Java Cipher, symmetric key and initialization vector and in parallel, sign the encrypted data with the signature engine. The stream is processed a buffer at a time till the end of the file is reached. The end result is an encrypted and digitally signed temporary file.
FileOutputStream tFileOS = new FileOutputStream("C:\\$$$$$$$$.tmp");
Figure 9. Encrypt and Sign the File
The code can be made more efficient by allocating larger buffers and writing out the encrypted data after a threshold has been reached.
Step 12: Generate the digital signature from the signature engine after signing the file and store it in the header along with its length. Save the signature algorithm, signature certificate name and its length in the header.
byte tSignature = tSigEngine.sign();
Step 13: Calculate the total size of the header and save in the header along with its version. Write the header into a ByteArrayOutputStream, which can be converted to a byte array. The Cryptography header class contains a method to write out the header to a ByteArrayOutputStream. Write out the byte array to a file using a Java "FileOutputStream."
ByteArrayOutputStream tHeadBAOS = new ByteArrayOutputStream();
Step 14: Append the temporary "encrypted" file to the output stream. The end result is an encrypted file with a digital signature. Note that the file extension is "ASG" instead of "AES" to imply that it is encrypted and digitally signed. The temporary file though encrypted should be securely deleted afterwards by overwriting it.
tInStream = new FileInputStream("C:\\$$$$$$$$.tmp");
This article demonstrates how to encrypt and digitally sign any file using Java Cryptography methods and the Cryptography libraries from Bouncy Castle organization. The Cryptography header provides information required to decipher the file and validate who encrypted its contents. The header also provides the flexibility to expand the usage of Cryptography such as allowing multiple recipients to decrypt a file by using each of their public keys to encrypt the same symmetric key. As society adopts file encryption as a standard way of protection, more creative uses will be invented by future Cyber warriors.
The source code (LaCryptoJarSample.java) is available on the Logical Answers Inc. website under the education web page as an individual file and also within the zip file, laCrypto-4.2.0.zipx.
References and Other Technical Notes
Reader Feedback: Page 1 of 1
SOA World Latest Stories
Subscribe to the World's Most Powerful Newsletters
Subscribe to Our Rss Feeds & Get Your SYS-CON News Live!
SYS-CON Featured Whitepapers
Most Read This Week