E N D
Java Cryptography This is a presentation on how to implement a hybrid combination of RSA asymmetric encryption and AES symmetric encryption using the Java language and the Bouncy Castle Cryptographic libraries. It is assumed the audience is familiar with Cryptography and aware of the difference between asymmetric and symmetric encryption. Also, the audience should be experienced in coding with Java. Java Cryptography
Required Software The following software should be downloaded to implement the RSA/AES cryptographic functions. • Bouncy Castle Jars ver 1.49 – www.BouncyCastle.org. - bcprov-jdk15on-149.jar - bcpkix-jdk15on-149.jar • Java Cryptography Extension (JCE) Unlimited Strength Jurisdiction Policy Files 7 - http://www.oracle.com/technetwork/java/javase/downloads/jce-7-download-432124.html Copy the contents of the zip file to the d:\jre7\lib\security directory. (Make a backup before overwriting) - UnlimitedJCEPolicyJDK7.zip • Java Development Kit version 7. Java Cryptography
Implementation Plan To implement RSA/AES cryptographic functions using Java, we will use the following strategy. • Generate a pair of RSA public/private (encryption/decryption) keys. • Save the encryption key in an X509Certificate and the decryption key and certificate in a password protected KeyStore. • Generate an AES symmetric key. Load the encryption key from the certificate to encrypt the symmetric key and store in a header. • Encrypt and digitally sign data to temporary encrypted file and store signature in header. Write out header and attach temporary encrypted file. • Load the certificate and verify signature with encryption key. • Load the decryption key from the KeyStore and decipher the symmetric key in the header. Use the symmetric key to decrypt the encrypted file. Java Cryptography
Generate Asymmetric Key Pair Java Cryptography
Generate Asymmetric Key Pair • Instantiate a KeyPairGenerator for generating a pair of RSA asymmetric keys. • Generate a secure random number. • Initialize the KeyPairGenerator with desired key size and the secure random number. • Generate a pair of public/private keys. • Code Snippet KeyPairGenerator tKPGen = KeyPairGenerator.getInstance("RSA", "BC"); SecureRandom tRandom = new SecureRandom(); tKPGen.initialize(2048, tRandom); //-Initialize the Asymmetric KEY Size KeyPair tPair = tKPGen.generateKeyPair(); PublicKey tUserPubKey = tPair.getPublic(); //-Encryption key PrivateKey tUserPrivKey = tPair.getPrivate(); //-Decryption key Java Cryptography
Create X509Certificate • Use the X509v3CertificateBuilder class to generate a version 3 certificate and digitally sign it with the SHA512WithRSAEncryption algorithm. • Code Snippet JcaContentSignerBuilder tSignBldr = new JcaContentSignerBuilder("SHA512WithRSAEncryption"); tSignBldr.setProvider("BC"); //-Set provider as BouncyCastle ContentSigner tSigGen = tSignBldr.build(tUserPrivKey); X500NameBuilder tBuilder = new X500NameBuilder(BCStyle.INSTANCE); tBuilder.addRDN(BCStyle.CN, "John X. Doe"); //-Common Name tBuilder.addRDN(BCStyle.E, "jxdoe@acme.com"); //-E-mail tBuilder.addRDN(BCStyle.O, "ACME Inc"); //-Organization tBuilder.addRDN(BCStyle.OU, "Executive Group"); //-Organization Unit tBuilder.addRDN(BCStyle.L, "Detroit").addRDN(BCStyle.ST, "MI").addRDN(BCStyle.C, "USA"); //-City,State,Country org.bouncycastle.asn1.x500.X500Name tX500Name = tBuilder.build(); Calendar tCal = Calendar.getInstance(); tCal.set(2014, 11, 31); //-Dec. 31, 2014, Month is zero indexed X509v3CertificateBuilder tV3CertGen = new JcaX509v3CertificateBuilder( tX500Name, //-Issuer is same as Subject BigInteger.valueOf( System.currentTimeMillis()), //-Serial Number new java.util.Date(), //-Date start = today tCal.getTime(), //-Date end tX500Name, //-Subject tUserPubKey); //-Public RSA Key X509CertificateHolder tCertHolder = tV3CertGen.build(tSigGen); JcaX509CertificateConverter tConverter = new JcaX509CertificateConverter().setProvider("BC"); X509Certificate tCert = tConverter.getCertificate(tCertHolder); Java Cryptography
Save X509Certificate to File • The certificate can be validated by date and its digital signature verified with the public encryption key. • Convert certificate to byte array and write it out to file. • Code Snippet tCert.checkValidity(new Date()); //-Throws exception if certificate expired tCert.verify(tUserPubKey); //-Throws exception if certificate is invalid byte[] tBA = tCert.getEncoded(); //-Convert certificate to byte array File tFile = new File("F:\\ jxdoe_nnnn.cer"); FileOutputStream tFOS = new FileOutputStream(tFile); tFOS.write(tBA); tFOS.close(); Java Cryptography
Generate and Save KeyStore • Instantiate an array of certificates called a chain and store the built one holding the public encryption key into it. • Instantiate a KeyStore to hold RSA private decryption key and certificate chain. It is password protected. • Code Snippet KeyStore tKStore = KeyStore.getInstance("JKS", "SUN"); tKStore.load(null, null); //-Initialize the KeyStore X509Certificate[] tChain = new X509Certificate[1]; //-Put certificate into a chain tChain[0] = tCert; tKStore.setKeyEntry("jxdoe_nnnn", tUserPrivKey, "password".toCharArray(), tChain); tFOS = new FileOutputStream("F:\\jxdoe_nnnn.jks"); tKStore.store(tFOS, "password".toCharArray()); //-Set KeyStore password tFOS.close(); Java Cryptography
Encrypting/Signing a File Java Cryptography
Encrypting/Signing a File A file can be encrypted using the following steps. • Select a file to be encrypted, e.g. C:\SampleFile.txt • Generate a symmetric AES 256-bit key. • Generate an initialization vector. • Load the certificate and retrieve the public encryption key to encrypt the symmetric key and vector and store in header. • Load the keystore and retrieve the private decryption key. • Instantiate a cipher with the symmetric key and initialization vector to encrypt the file. • Use the cipher to encrypt the file and write out the results to temporary file. Use the decryption key to generate a signature and save in the header. • Write out the header and append the temporary encrypted file. Java Cryptography
Generating Symmetric Key and Initialization Vector • The KeyGenerator will create a 256-bit AES symmetric key. • The initialization vector is a byte[] that is populated by the SecureRandom class. • Once the key and vector are created, they can be encrypted with the public RSA key and stored in the encrypted file. • Code Snippet KeyGenerator tKeyGen = KeyGenerator.getInstance("AES", "BC"); SecureRandom tRandom2 = new SecureRandom(); tKeyGen.init(256, tRandom2); //-256-bit SecretKey tSymmetricKey = tKeyGen.generateKey(); //-Generate the initialization vector since the mode chosen requires one int tSize = Cipher.getInstance("AES", "BC") .getBlockSize(); byte[] tInitVector = new byte[tSize]; SecureRandom tRandom3 = new SecureRandom(); tRandom3.nextBytes(tInitVector); //-Fill the initialization vector IvParameterSpec tIVSpec = new IvParameterSpec(tInitVector); Java Cryptography
Load Certificate and Retrieve Public Encryption Key • The CertificateFactory will load a certificate allowing access to the public RSA encryption key. • After the certificate is loaded, extract the public key. • The certificate can be distributed to others so that they can encrypt files and e-mail them to you as attachments since only the owner of the private key can decrypt them. • Code Snippet InputStream tIStream = new FileInputStream("F:\\jxdoe_nnnn.cer"); CertificateFactory tFactory = CertificateFactory.getInstance("X.509", "BC"); X509Certificate tLoadedCert = (X509Certificate)tFactory.generateCertificate(tIStream); tIStream.close(); tUserPubKey = tLoadedCert.getPublicKey(); //-Encryption key Java Cryptography
Generate Cryptography Header • A Cryptography header is needed to hold the information used to encrypt and digitally sign a file or Java object. • Code Snippet //-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. CryptoHeader tHead = new CryptoHeader(); tHead.setEncryptFlag(true); tHead.setSignedFlag(true); tHead.symKeyAlg(1); //-AES tHead.symKeyMode(5); //-CTR Segmented Integer Counter mode (requires an init vector) tHead.symKeyPadding(2); //-PKCS7 Padding tHead.decryptIDLength(tUniqueAlias.length()); tHead.decryptID(tUniqueAlias); //-Owner’s unique alias tHead.asymKeyAlg(1); //-Asymmetric algorithm RSA Java Cryptography
Encrypt the Symmetric Key and Initialization Vector • The Java and Bouncy Castle libraries provide features to protect the symmetric key and initialization vector. • Instantiate a Cipher object with the Public Encryption Key. Encrypt the key and vector and store in the header. • Code Snippet //-Wrap(Encrypt) the AES symmetric key using the asymmetric public key and //-store it in the header. Cipher tCipherRSA = Cipher.getInstance("RSA", "BC"); tCipherRSA.init(Cipher.WRAP_MODE, tUserPubKey); byte[] tWrappedKey = tCipherRSA.wrap(tSymmetricKey); tHead.wrappedSymKeyLength(tWrappedKey.length); tHead.wrappedSymKey(tWrappedKey); //-Encrypt the initialization vector using the same cipher and store it //-in the header. tCipherRSA.init(Cipher.ENCRYPT_MODE, tUserPubKey); byte[] tInitVectorEncrypted = tCipherRSA.doFinal(tIVSpec.getIV()); tHead.initVectorLength(tInitVectorEncrypted.length); tHead.initVector(tInitVectorEncrypted); Java Cryptography
Load KeyStore and Retrieve Private Decryption Key • Load the password protected keystore and retrieve the RSA private decryption key. • Instantiate a Signature Engine with the decryption key to sign the encrypted data. Initialize it to generate a signature. • Code Snippet //-Retrieve the private decryption key stored in a Java key store associated to a unique alias FileInputStream tStoreFIS = new FileInputStream(tWorkingDir + "\\jxdoe_nnnn.jks"); KeyStore tMyKStore = KeyStore.getInstance("JKS", "SUN"); char[] tPW = "password".toCharArray(); tMyKStore.load(tStoreFIS, tPW); PrivateKey tPrivKey = (PrivateKey)tMyKStore.getKey(tUniqueAlias, tPW); tStoreFIS.close(); //-Generate a Java Signature object to sign the encrypted file and store the //-information in the header. Signature tSigEngine = Signature.getInstance("SHA512WithRSAEncryption", "BC"); tSigEngine.initSign(tPrivKey); //-Initialize engine to GENERATE a signature tHead.signatureAlg(12); //-SHA512WithRSAEncryption Java Cryptography
Create a Symmetric Key Cipher • The Cipher class provides the encrypt/decrypt functions for both asymmetric and symmetric keys. • Instantiate an AES symmetric key cipher using Segmented Integer Counter mode (CTR) and PKCS7 padding which will be used to encrypt the file. • Modes are used so that each block of data is encrypted differently. • Code Snippet //-Generate a Java Cipher object based on the symmetric algorithm, mode, padding //-and provider which will be used to encrypt the target file. Cipher tCipherEncrypt = Cipher.getInstance("AES/CTR/PKCS7Padding", "BC"); tCipherEncrypt.init(Cipher.ENCRYPT_MODE, tSymmetricKey, tIVSpec); Java Cryptography
Use the Cipher to Encrypt • Loop through the source file and encrypt the data a buffer at a time. Process each encrypted buffer for a digital signature. Write out the results to a temporary file which will be deleted afterwards. • Code Snippet InputStream tFileIS = new FileInputStream(tWorkingDir + "\\sampleFile.txt"); FileOutputStream tFileOS = new FileOutputStream(tWorkingDir + "\\$$$$$$$$.tmp"); byte[] tInBuffer = new byte[4096]; byte[] tOutBuffer = new byte[4096]; int tNumOfBytesRead = tFileIS.read(tInBuffer); while (tNumOfBytesRead == tInBuffer.length) { //-Encrypt the input data and store in output buffer int tNumOfBytesUpdated = tCipherEncrypt.update(tInBuffer, 0, tInBuffer.length, tOutBuffer); tSigEngine.update(tOutBuffer, 0, tNumOfBytesUpdated); //-Sign the encrypted data tFileOS.write(tOutBuffer, 0, tNumOfBytesUpdated); //-Write encrypted data to temp file tNumOfBytesRead = tFileIS.read(tInBuffer); //-Read in the next buffer of source data } if (tNumOfBytesRead > 0) { //-Process the remaining bytes in the input file. tOutBuffer = tCipherEncrypt.doFinal(tInBuffer, 0, tNumOfBytesRead); } else { tOutBuffer = tCipherEncrypt.doFinal(); } tSigEngine.update(tOutBuffer); //-Sign the remaining bytes tFileOS.write(tOutBuffer, 0, tOutBuffer.length); … //-Close the input and temporary files Java Cryptography
Generate Digital Signature • Once the signature engine has finished processing the encrypted data, it can generate a digital signature. • The signature is saved in the header as a byte[]. • Code Snippet //-Generate the digital signature from the signature engine after //-signing the file and store the information in the header. byte[] tSignature = tSigEngine.sign(); tHead.signature(tSignature); tHead.signatureLength(tSignature.length); tHead.verifySigCertName( “jxdoe_nnnn.cer"); tHead.verifySigCertNameLength(tHead.verifySigCertName().length()); Java Cryptography
Save the Encrypted/Signed File • Translate the header into a byte[] and save to disk. • Append the temporary encrypted file. • Code Snippet //-Calculate the total size of the header and save. Write out header using Java FileOutputStream. FileOutputStream tFileOStream = new FileOutputStream(“d:\SampleFile.txt.jxdoe_nnnn.asg”); byte[] tArray = (byte[])tHead.writeOutHeaderAsByteArray(); tFileOStream.write(tArray, 0, tArray.length); //-Append the temporary “encrypted” file to the output stream. tFileIS = new FileInputStream(tTempFileName); byte[] tBuffer = new byte[4096]; int tLength = tFileIS.read(tBuffer); while (tLength > 0) { tFileOStream.write(tBuffer, 0, tLength); tLength = tFileIS.read(tBuffer); } tFileOStream.close(); tFileIS.close(); … //-Securely delete the temporary file by overwriting it. Java Cryptography
Verifying Digital Signature Java Cryptography
Verifying Digital Signature A signed encrypted file can verify the signature using the following steps. • Select a file to be validated, e.g. C:\SampleFile.txt.jxdoe_nnnn.asg. • Read in the Crypto Header • Load the certificate so you have access to the public encryption key. • Instantiate a Signature engine object in VERIFY mode using the encryption key. • Use the Signature engine to process the encrypted file. • Use the Signature engine to verify the validity of the signature generated with the decryption key stored in the header. Java Cryptography
Read the Cryptography Header • After selecting the encrypted file to decipher, extract the filename of the decrypted file. • Create a DataInputStream to the encrypted file and read in the Cryptography header. • Code Snippet //-Create input stream to the encrypted file and read in the header. String tDecryptFile = tEncryptedSignedFile.getName(); //-Parse off the alias and .asg extension to derive the decrypted output file name tDecryptFile = tDecryptFile.substring(0, tDecryptFile.lastIndexOf('.')); tDecryptFile = tDecryptFile.substring(0, tDecryptFile.lastIndexOf('.')); FileOutputStream tFileOStream = new FileOutputStream(tWorkingDir + "\\" + tDecryptFile); DataInputStream tDInStream = new DataInputStream(new FileInputStream(tEncryptedSignedFile)); Object tRC = CryptoHeader.readHeader(tDInStream); CryptoHeader tHead = (CryptoHeader)tRC; Java Cryptography
Load Certificate and Retrieve Public Encryption Key • The CertificateFactory will load a certificate allowing access to the public RSA encryption key. • After the certificate is loaded, extract the encryption key and instantiate a signature engine. • Code Snippet InputStream tIStream = new FileInputStream(“d:\\jxdoe_nnnn.cer"); CertificateFactory tFactory = CertificateFactory.getInstance("X.509", "BC"); X509Certificate tLoadedCert = (X509Certificate)tFactory.generateCertificate(tIStream); tIStream.close(); tUserPubKey = tLoadedCert.getPublicKey(); //-Encryption key //-Instantiate a Java signature engine associated with the signature algorithm //-stored in the header. Initialize it with the asymmetric public key. String tSigAlg = tHead.signatureAlgDesc(); Signature tSgnVerifyEngine = Signature.getInstance(tSigAlg, "BC"); tSgnVerifyEngine.initVerify(tUserPubKey); //-Initialize to VERIFY a signature Java Cryptography
Use Engine to Process Encrypted File • After the signature engine is instantiated with the public encryption key, read in the rest of the encrypted input file and use the engine to process it. • Code Snippet //-Calculate hash number(signature) that will be compared with signature stored in the header. int tBlockSize = 4096; byte[] tBuffer = new byte[tBlockSize]; int tLength = tDInStream.read(tBuffer); while (tLength == tBlockSize) { tSgnVerifyEngine.update(tBuffer, 0, tBlockSize); tLength = tDInStream.read(tBuffer); } //-Are there any bytes left over to process?? if (tLength > 0) { tSgnVerifyEngine.update(tBuffer, 0, tLength); } tDInStream.close(); Java Cryptography
Verify Signature • After processing the encrypted file, the signature engine will verify the signature generated with the private decryption key stored in the header. • A match means the files has NOT been altered. • Code Snippet //-After the file has been processed, use the Java signature engine to //-verify its result with the digital signature. //-A Boolean result is returned on whether the signature was valid. Boolean tResult = tSgnVerifyEngine.verify(tCurrSignature); if (tResult) { System.out.println("-Signature has been VERIFIED. Result is " + tResult); } else { System.out.println("-Signature has NOT been VERIFIED! Result is " + tResult); System.exit(-1); //-Leave if signature is NOT valid } Java Cryptography
Decrypting an Encrypted File Java Cryptography
Decrypting an Encrypted File An encrypted file can be decrypted using the following steps. • Select a file to be decrypted, e.g. C:\SampleFile.txt.jxdoe_nnnn.asg. • Read in the Crypto Header • Load the KeyStore so you have access to the private decryption key. • Use the private decryption key to decrypt the AES 256-bit symmetric key and initialization vector. • Instantiate a cipher with the symmetric key and initialization vector to decrypt the file. • Use the cipher to decrypt the file and write out the results. Java Cryptography
Read the Cryptography Header • After selected the encrypted file to decipher, extract the filename for the decrypted file. • Create a DataInputStream to the encrypted file and read in the Cryptography header. • Code Snippet //-Create input stream to the encrypted file and read in the header. String tDecryptFile = tEncryptedSignedFile.getName(); //-Parse off the alias and .asg extension to derive the decrypted output file name tDecryptFile = tDecryptFile.substring(0, tDecryptFile.lastIndexOf('.')); tDecryptFile = tDecryptFile.substring(0, tDecryptFile.lastIndexOf('.')); FileOutputStream tFileOStream = new FileOutputStream(tWorkingDir + "\\" + tDecryptFile); DataInputStream tDInStream = new DataInputStream(new FileInputStream(tEncryptedSignedFile)); Object tRC = CryptoHeader.readHeader(tDInStream); CryptoHeader tHead = (CryptoHeader)tRC; Java Cryptography
Loading a KeyStore • The KeyStore class will load a FileInputStream referencing the externally saved KeyStore file. • After the KeyStore is loaded, extract the private key via its password. • Code Snippet //-Load the key store using your password. Retrieve the asymmetric private decryption key //-from the key store using the same password. The decryption key will //-be used to decipher the symmetric key. FileInputStream tFIStream = new FileInputStream(tWorkingDir + "\\" + tUniqueAlias + ".jks"); KeyStore tMyKStore = KeyStore.getInstance("JKS", "SUN"); //tPW = "password".toCharArray(); tMyKStore.load(tFIStream, tPW); PrivateKey tPrivKey = (PrivateKey)tMyKStore.getKey(tUniqueAlias, tPW); tFIStream.close(); //-Always close streams when done with them. Java Cryptography
Retrieve Symmetric Key and Initialization Vector • The symmetric key and initialization vector are stored in the header of the encrypted file. • The key and vector are decrypted with the RSA private decryption key. • A decryption Cipher is generated with the key and vector. • Code Snippet //-Generate a Java Cipher object using the asymmetric private key //-and set its mode to "Cipher.UNWRAP_MODE”. Cipher tCipherRSA = Cipher.getInstance("RSA", "BC"); tCipherRSA.init(Cipher.UNWRAP_MODE, (PrivateKey)tPrivKey); //-Use the Java Cipher to unwrap(decrypt) the symmetric key. //-The symmetric key will be used to decrypt the file. String tAlg = tHead.symKeyAlgDesc(); byte[] tWrappedSymKey = tHead.wrappedSymKey(); SecretKey tSymKey = (SecretKey)tCipherRSA.unwrap(tWrappedSymKey,tAlg, Cipher.SECRET_KEY); //-Re-initialize the same Cipher to Cipher.DECRYPT_MODE. //-Use the Cipher to decrypt the initialization vector. tCipherRSA.init(Cipher.DECRYPT_MODE, (PrivateKey)tPrivKey); byte[] tInitVector = tCipherRSA.doFinal(tHead.initVector()); IvParameterSpec tIvParmSpec = new IvParameterSpec(tInitVector); Java Cryptography
Use the Cipher to Decrypt • After the cipher is instantiated, read in the rest of the input file and use the cipher to decrypt it and save it to disk. • Code Snippet //-Instantiate a symmetric Cipher for DECRYPTION Cipher tCipherDecrypt = Cipher.getInstance("AES/CTR/PKCS7Padding", "BC"); tCipherDecrypt.init(Cipher.DECRYPT_MODE, tSymKey, tIvParmSpec); //-Use the Java Cipher to decrypt the rest of the file. The end result is a decrypted file. byte[] tInBuffer = new byte[4096]; byte[] tOutBuffer = new byte[4096]; int tNumOfBytesRead = tDInStream.read(tInBuffer); while (tNumOfBytesRead == tInBuffer.length) { //-Decrypt the input data and store in output buffer int tNumOfBytesUpdated = tCipherDecrypt.update(tInBuffer, 0, tInBuffer.length, tOutBuffer); tFileOStream.write(tOutBuffer, 0, tNumOfBytesUpdated); tNumOfBytesRead = tDInStream.read(tInBuffer); } if (tNumOfBytesRead > 0) { //-Process the remaining bytes in the input file. tOutBuffer = tCipherDecrypt.doFinal(tInBuffer, 0, tNumOfBytesRead); } else { tOutBuffer = tCipherDecrypt.doFinal(); } tFileOStream.write(tOutBuffer, 0, tOutBuffer.length); tFileOStream.close(); tDInStream.close(); Java Cryptography
References • Source code for the presentation is in the file, JavaCryptoSample.java on the Logical Answers Inc. website on the education page. • Hook, David. Beginning Cryptography with Java. Wrox Press. ISBN: 0-7645-9633-0. August 2005. 448 pages. • Horstman, Cay and Cornell, Gary. Core Java 2 Volume II-Advanced Features. Sun Microsystems Press. ISBN: 0-13-092738-4. 2002. 1024 pages. • Singh, Simon. The Code Book. Doubleday ISBN: 0-38-549531-5. September 14, 1999. 416 pages. Java Cryptography
Contact Information Logical Answers Inc. 491 Leetonia Ave Troy, Michigan 48085-5518 (248) 528-4498 www.LogicalAnswers.com jhwong@logicalanswers.com We offer custom programming and technology consulting services. Our DocuArmor suite of cryptographic products are for sale offering encryption and secure socket communications. Java Cryptography