(back to article)

Decrypt data that has previously been encrypted via OpenSSL using a password

This example uses the BouncyCastle library via the standard Java APIs. The standard Java distribution does not support the slightly non-standard OpenSSL approach to mapping (password,salt) into (key,IV) so using BouncyCastle is necessary.

While the native BouncyCastle APIs are cleaner than the Java ones, you might have a requirement to use the standard APIs for some reason. This is how to do it..


package example;
import java.security.GeneralSecurityException;
import java.security.Security;
import java.util.Arrays;
import javax.crypto.Cipher;
import javax.crypto.SecretKey;
import javax.crypto.SecretKeyFactory;
import javax.crypto.spec.PBEKeySpec;
import org.bouncycastle.jce.provider.BouncyCastleProvider;
/**
 * Implement code to decrypt a file that has been encrypted by openssl using the aes-***-cbc algorithm and a <i>password</i>.
 */
public class OpenSSLAesDecipherer {
 // Encryption algorithm. Note that the "strength" (bitsize) is controlled by the key object that is used.
 //
 // Note also that if a "CBC" cipher is selected, then normally an IvParameterSpec object wrapping the IV
 // data must be passed to the Cipher.init method. However Openssl has an interesting "hack" where the IV
 // is derived from the password using an openssl-specific extension to the normal algorithms; the
 // bouncycastle PBEKey implementation knows how to do this, and the bouncycastle AES Cipher implementation
 // checks whether its key parameter is a bouncycastle key, and if so then downcasts it and retrieves the
 // IV value. Ugly but effective.
 private static final String CIPHER_ID = "AES/CBC/PKCS5Padding";
 private static final String BOUNCY_CASTLE_PROVIDER_ID = "BC";
 private final int keyLenBits;
 static {
 Security.addProvider(new BouncyCastleProvider());
 }
 OpenSSLAesDecipherer(int keyLenBits) {
 this.keyLenBits = keyLenBits;
 }
 public byte[] decipher(byte[] pwd, byte[] src) throws GeneralSecurityException {
 // openssl non-standard extension: salt embedded at start of encrypted file
 byte[] salt = Arrays.copyOfRange(src, 8, 16); // 0..7 is "SALTED__", 8..15 is the salt
 Cipher cipher = Cipher.getInstance(CIPHER_ID, BOUNCY_CASTLE_PROVIDER_ID);
 SecretKey key = getKey(pwd, salt);
 cipher.init(Cipher.DECRYPT_MODE, key);
 return cipher.doFinal(src, 16, src.length-16);
 }
 
 private SecretKey getKey(byte[] pwd, byte[] salt) throws GeneralSecurityException {http://moi.vonos.net/2013/02/eclipse-emf/
 // Convert array of 8-bit-bytes to array of 16-bit-chars, treating input as ASCII..
 char[] pwdAsChars = new char[pwd.length];
 for(int i=0; i<pwd.length; ++i) {
 pwdAsChars[i] = (char) pwd[i];
 }
 // Use bouncycastle implementation of openssl non-standard (pwd,salt)->(key,iv) algorithm.
 // * PBE = Password Based Encryption
 // * CBC = Cipher Block Chaining (ie IV is needed)
 // The bouncycastle Cipher implementation for algorithm "AES/CBC" checks whether its key object
 // is a bouncycastle implementation, and if so then extracts the IV (if any).
 String keyAlgo = String.format("PBEWITHMD5AND%dBITAES-CBC-OPENSSL", keyLenBits);
 SecretKeyFactory factory = SecretKeyFactory.getInstance(keyAlgo, BOUNCY_CASTLE_PROVIDER_ID);
 PBEKeySpec keySpec = new PBEKeySpec(pwdAsChars, salt, 1); // NB: iteration-count param here is currently ignored
 SecretKey keyObj = factory.generateSecret(keySpec);
 return keyObj;
 }
}

AltStyle によって変換されたページ (->オリジナル) /