| /* |
| * $HeadURL: http://juliusdavies.ca/svn/not-yet-commons-ssl/tags/commons-ssl-0.3.16/src/java/org/apache/commons/ssl/OpenSSL.java $ |
| * $Revision: 144 $ |
| * $Date: 2009-05-25 11:14:29 -0700 (Mon, 25 May 2009) $ |
| * |
| * ==================================================================== |
| * Licensed to the Apache Software Foundation (ASF) under one |
| * or more contributor license agreements. See the NOTICE file |
| * distributed with this work for additional information |
| * regarding copyright ownership. The ASF licenses this file |
| * to you under the Apache License, Version 2.0 (the |
| * "License"); you may not use this file except in compliance |
| * with the License. You may obtain a copy of the License at |
| * |
| * http://www.apache.org/licenses/LICENSE-2.0 |
| * |
| * Unless required by applicable law or agreed to in writing, |
| * software distributed under the License is distributed on an |
| * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY |
| * KIND, either express or implied. See the License for the |
| * specific language governing permissions and limitations |
| * under the License. |
| * ==================================================================== |
| * |
| * This software consists of voluntary contributions made by many |
| * individuals on behalf of the Apache Software Foundation. For more |
| * information on the Apache Software Foundation, please see |
| * <http://www.apache.org/>. |
| * |
| */ |
| |
| package org.apache.commons.ssl; |
| |
| import org.apache.kerby.util.Base64; |
| import org.apache.kerby.util.Base64InputStream; |
| import org.apache.kerby.util.Hex; |
| import org.apache.kerby.util.Util; |
| |
| import javax.crypto.Cipher; |
| import javax.crypto.CipherInputStream; |
| import java.io.BufferedInputStream; |
| import java.io.BufferedOutputStream; |
| import java.io.ByteArrayInputStream; |
| import java.io.FileInputStream; |
| import java.io.IOException; |
| import java.io.InputStream; |
| import java.security.GeneralSecurityException; |
| import java.security.MessageDigest; |
| import java.security.NoSuchAlgorithmException; |
| import java.security.SecureRandom; |
| import java.util.StringTokenizer; |
| |
| /** |
| * Class for encrypting or decrypting data with a password (PBE - password |
| * based encryption). Compatible with "openssl enc" unix utility. An OpenSSL |
| * compatible cipher name must be specified along with the password (try "man enc" on a |
| * unix box to see what's possible). Some examples: |
| * <ul><li>des, des3, des-ede3-cbc |
| * <li>aes128, aes192, aes256, aes-256-cbc |
| * <li>rc2, rc4, bf</ul> |
| * <pre> |
| * <em style="color: green;">// Encrypt!</em> |
| * byte[] encryptedData = OpenSSL.encrypt( "des3", password, data ); |
| * </pre> |
| * <p/> |
| * If you want to specify a raw key and iv directly (without using PBE), use |
| * the methods that take byte[] key, byte[] iv. Those byte[] arrays can be |
| * the raw binary, or they can be ascii (hex representation: '0' - 'F'). If |
| * you want to use PBE to derive the key and iv, then use the methods that |
| * take char[] password. |
| * <p/> |
| * This class is able to decrypt files encrypted with "openssl" unix utility. |
| * <p/> |
| * The "openssl" unix utility is able to decrypt files encrypted by this class. |
| * <p/> |
| * This class is also able to encrypt and decrypt its own files. |
| * |
| * @author <a href="mailto:juliusdavies@cucbc.com">juliusdavies@gmail.com</a> |
| * @since 18-Oct-2007 |
| */ |
| public class OpenSSL { |
| |
| |
| /** |
| * Decrypts data using a password and an OpenSSL compatible cipher |
| * name. |
| * |
| * @param cipher The OpenSSL compatible cipher to use (try "man enc" on a |
| * unix box to see what's possible). Some examples: |
| * <ul><li>des, des3, des-ede3-cbc |
| * <li>aes128, aes192, aes256, aes-256-cbc |
| * <li>rc2, rc4, bf</ul> |
| * @param pwd password to use for this PBE decryption |
| * @param encrypted byte array to decrypt. Can be raw, or base64. |
| * @return decrypted bytes |
| * @throws java.io.IOException problems with encrypted bytes (unlikely!) |
| * @throws java.security.GeneralSecurityException problems decrypting |
| */ |
| public static byte[] decrypt(String cipher, char[] pwd, byte[] encrypted) |
| throws IOException, GeneralSecurityException { |
| ByteArrayInputStream in = new ByteArrayInputStream(encrypted); |
| InputStream decrypted = decrypt(cipher, pwd, in); |
| return Util.streamToBytes(decrypted); |
| } |
| |
| /** |
| * Decrypts data using a password and an OpenSSL compatible cipher |
| * name. |
| * |
| * @param cipher The OpenSSL compatible cipher to use (try "man enc" on a |
| * unix box to see what's possible). Some examples: |
| * <ul><li>des, des3, des-ede3-cbc |
| * <li>aes128, aes192, aes256, aes-256-cbc |
| * <li>rc2, rc4, bf</ul> |
| * @param pwd password to use for this PBE decryption |
| * @param encrypted InputStream to decrypt. Can be raw, or base64. |
| * @return decrypted bytes as an InputStream |
| * @throws java.io.IOException problems with InputStream |
| * @throws java.security.GeneralSecurityException problems decrypting |
| */ |
| public static InputStream decrypt(String cipher, char[] pwd, |
| InputStream encrypted) |
| throws IOException, GeneralSecurityException { |
| CipherInfo cipherInfo = lookup(cipher); |
| boolean salted = false; |
| |
| // First 16 bytes of raw binary will hopefully be OpenSSL's |
| // "Salted__[8 bytes of hex]" thing. Might be in Base64, though. |
| byte[] saltLine = Util.streamToBytes(encrypted, 16); |
| if (saltLine.length <= 0) { |
| throw new IOException("encrypted InputStream is empty"); |
| } |
| String firstEightBytes = ""; |
| if (saltLine.length >= 8) { |
| firstEightBytes = new String(saltLine, 0, 8); |
| } |
| if ("SALTED__".equalsIgnoreCase(firstEightBytes)) { |
| salted = true; |
| } else { |
| // Maybe the reason we didn't find the salt is because we're in |
| // base64. |
| if (Base64.isArrayByteBase64(saltLine)) { |
| InputStream head = new ByteArrayInputStream(saltLine); |
| // Need to put that 16 byte "saltLine" back into the Stream. |
| encrypted = new ComboInputStream(head, encrypted); |
| encrypted = new Base64InputStream(encrypted); |
| saltLine = Util.streamToBytes(encrypted, 16); |
| |
| if (saltLine.length >= 8) { |
| firstEightBytes = new String(saltLine, 0, 8); |
| } |
| if ("SALTED__".equalsIgnoreCase(firstEightBytes)) { |
| salted = true; |
| } |
| } |
| } |
| |
| byte[] salt = null; |
| if (salted) { |
| salt = new byte[8]; |
| System.arraycopy(saltLine, 8, salt, 0, 8); |
| } else { |
| // Encrypted data wasn't salted. Need to put the "saltLine" we |
| // extracted back into the stream. |
| InputStream head = new ByteArrayInputStream(saltLine); |
| encrypted = new ComboInputStream(head, encrypted); |
| } |
| |
| int keySize = cipherInfo.keySize; |
| int ivSize = cipherInfo.ivSize; |
| boolean des2 = cipherInfo.des2; |
| DerivedKey dk = deriveKey(pwd, salt, keySize, ivSize, des2); |
| Cipher c = PKCS8Key.generateCipher( |
| cipherInfo.javaCipher, cipherInfo.blockMode, dk, des2, null, true |
| ); |
| |
| return new CipherInputStream(encrypted, c); |
| } |
| |
| /** |
| * Encrypts data using a password and an OpenSSL compatible cipher |
| * name. |
| * |
| * @param cipher The OpenSSL compatible cipher to use (try "man enc" on a |
| * unix box to see what's possible). Some examples: |
| * <ul><li>des, des3, des-ede3-cbc |
| * <li>aes128, aes192, aes256, aes-256-cbc |
| * <li>rc2, rc4, bf</ul> |
| * @param pwd password to use for this PBE encryption |
| * @param data byte array to encrypt |
| * @return encrypted bytes as an array in base64. First 16 bytes include the |
| * special OpenSSL "Salted__" info encoded into base64. |
| * @throws java.io.IOException problems with the data byte array |
| * @throws java.security.GeneralSecurityException problems encrypting |
| */ |
| public static byte[] encrypt(String cipher, char[] pwd, byte[] data) |
| throws IOException, GeneralSecurityException { |
| // base64 is the default output format. |
| return encrypt(cipher, pwd, data, true); |
| } |
| |
| /** |
| * Encrypts data using a password and an OpenSSL compatible cipher |
| * name. |
| * |
| * @param cipher The OpenSSL compatible cipher to use (try "man enc" on a |
| * unix box to see what's possible). Some examples: |
| * <ul><li>des, des3, des-ede3-cbc |
| * <li>aes128, aes192, aes256, aes-256-cbc |
| * <li>rc2, rc4, bf</ul> |
| * @param pwd password to use for this PBE encryption |
| * @param data InputStream to encrypt |
| * @return encrypted bytes as an InputStream. First 16 bytes include the |
| * special OpenSSL "Salted__" info encoded into base64. |
| * @throws java.io.IOException problems with the data InputStream |
| * @throws java.security.GeneralSecurityException problems encrypting |
| */ |
| public static InputStream encrypt(String cipher, char[] pwd, |
| InputStream data) |
| throws IOException, GeneralSecurityException { |
| // base64 is the default output format. |
| return encrypt(cipher, pwd, data, true); |
| } |
| |
| /** |
| * Encrypts data using a password and an OpenSSL compatible cipher |
| * name. |
| * |
| * @param cipher The OpenSSL compatible cipher to use (try "man enc" on a |
| * unix box to see what's possible). Some examples: |
| * <ul><li>des, des3, des-ede3-cbc |
| * <li>aes128, aes192, aes256, aes-256-cbc |
| * <li>rc2, rc4, bf</ul> |
| * @param pwd password to use for this PBE encryption |
| * @param data byte array to encrypt |
| * @param toBase64 true if resulting InputStream should contain base64, |
| * <br>false if InputStream should contain raw binary. |
| * @return encrypted bytes as an array. First 16 bytes include the |
| * special OpenSSL "Salted__" info. |
| * @throws java.io.IOException problems with the data byte array |
| * @throws java.security.GeneralSecurityException problems encrypting |
| */ |
| public static byte[] encrypt(String cipher, char[] pwd, byte[] data, |
| boolean toBase64) |
| throws IOException, GeneralSecurityException { |
| // we use a salt by default. |
| return encrypt(cipher, pwd, data, toBase64, true); |
| } |
| |
| /** |
| * Encrypts data using a password and an OpenSSL compatible cipher |
| * name. |
| * |
| * @param cipher The OpenSSL compatible cipher to use (try "man enc" on a |
| * unix box to see what's possible). Some examples: |
| * <ul><li>des, des3, des-ede3-cbc |
| * <li>aes128, aes192, aes256, aes-256-cbc |
| * <li>rc2, rc4, bf</ul> |
| * @param pwd password to use for this PBE encryption |
| * @param data InputStream to encrypt |
| * @param toBase64 true if resulting InputStream should contain base64, |
| * <br>false if InputStream should contain raw binary. |
| * @return encrypted bytes as an InputStream. First 16 bytes include the |
| * special OpenSSL "Salted__" info. |
| * @throws java.io.IOException problems with the data InputStream |
| * @throws java.security.GeneralSecurityException problems encrypting |
| */ |
| public static InputStream encrypt(String cipher, char[] pwd, |
| InputStream data, boolean toBase64) |
| throws IOException, GeneralSecurityException { |
| // we use a salt by default. |
| return encrypt(cipher, pwd, data, toBase64, true); |
| } |
| |
| /** |
| * Encrypts data using a password and an OpenSSL compatible cipher |
| * name. |
| * |
| * @param cipher The OpenSSL compatible cipher to use (try "man enc" on a |
| * unix box to see what's possible). Some examples: |
| * <ul><li>des, des3, des-ede3-cbc |
| * <li>aes128, aes192, aes256, aes-256-cbc |
| * <li>rc2, rc4, bf</ul> |
| * @param pwd password to use for this PBE encryption |
| * @param data byte array to encrypt |
| * @param toBase64 true if resulting InputStream should contain base64, |
| * <br>false if InputStream should contain raw binary. |
| * @param useSalt true if a salt should be used to derive the key. |
| * <br>false otherwise. (Best security practises |
| * always recommend using a salt!). |
| * @return encrypted bytes as an array. First 16 bytes include the |
| * special OpenSSL "Salted__" info if <code>useSalt</code> is true. |
| * @throws java.io.IOException problems with the data InputStream |
| * @throws java.security.GeneralSecurityException problems encrypting |
| */ |
| public static byte[] encrypt(String cipher, char[] pwd, byte[] data, |
| boolean toBase64, boolean useSalt) |
| throws IOException, GeneralSecurityException { |
| ByteArrayInputStream in = new ByteArrayInputStream(data); |
| InputStream encrypted = encrypt(cipher, pwd, in, toBase64, useSalt); |
| return Util.streamToBytes(encrypted); |
| } |
| |
| /** |
| * Encrypts data using a password and an OpenSSL compatible cipher |
| * name. |
| * |
| * @param cipher The OpenSSL compatible cipher to use (try "man enc" on a |
| * unix box to see what's possible). Some examples: |
| * <ul><li>des, des3, des-ede3-cbc |
| * <li>aes128, aes192, aes256, aes-256-cbc |
| * <li>rc2, rc4, bf</ul> |
| * @param pwd password to use for this PBE encryption |
| * @param data InputStream to encrypt |
| * @param toBase64 true if resulting InputStream should contain base64, |
| * <br>false if InputStream should contain raw binary. |
| * @param useSalt true if a salt should be used to derive the key. |
| * <br>false otherwise. (Best security practises |
| * always recommend using a salt!). |
| * @return encrypted bytes as an InputStream. First 16 bytes include the |
| * special OpenSSL "Salted__" info if <code>useSalt</code> is true. |
| * @throws java.io.IOException problems with the data InputStream |
| * @throws java.security.GeneralSecurityException problems encrypting |
| */ |
| public static InputStream encrypt(String cipher, char[] pwd, |
| InputStream data, boolean toBase64, |
| boolean useSalt) |
| throws IOException, GeneralSecurityException { |
| CipherInfo cipherInfo = lookup(cipher); |
| byte[] salt = null; |
| if (useSalt) { |
| SecureRandom rand = SecureRandom.getInstance("SHA1PRNG"); |
| salt = new byte[8]; |
| rand.nextBytes(salt); |
| } |
| |
| int keySize = cipherInfo.keySize; |
| int ivSize = cipherInfo.ivSize; |
| boolean des2 = cipherInfo.des2; |
| DerivedKey dk = deriveKey(pwd, salt, keySize, ivSize, des2); |
| Cipher c = PKCS8Key.generateCipher( |
| cipherInfo.javaCipher, cipherInfo.blockMode, dk, des2, null, false |
| ); |
| |
| InputStream cipherStream = new CipherInputStream(data, c); |
| |
| if (useSalt) { |
| byte[] saltLine = new byte[16]; |
| byte[] salted = "Salted__".getBytes(); |
| System.arraycopy(salted, 0, saltLine, 0, salted.length); |
| System.arraycopy(salt, 0, saltLine, salted.length, salt.length); |
| InputStream head = new ByteArrayInputStream(saltLine); |
| cipherStream = new ComboInputStream(head, cipherStream); |
| } |
| if (toBase64) { |
| cipherStream = new Base64InputStream(cipherStream, true); |
| } |
| return cipherStream; |
| } |
| |
| |
| public static byte[] decrypt(String cipher, byte[] key, byte[] iv, |
| byte[] encrypted) |
| throws IOException, GeneralSecurityException { |
| ByteArrayInputStream in = new ByteArrayInputStream(encrypted); |
| InputStream decrypted = decrypt(cipher, key, iv, in); |
| return Util.streamToBytes(decrypted); |
| } |
| |
| public static InputStream decrypt(String cipher, byte[] key, byte[] iv, |
| InputStream encrypted) |
| throws IOException, GeneralSecurityException { |
| CipherInfo cipherInfo = lookup(cipher); |
| byte[] firstLine = Util.streamToBytes(encrypted, 16); |
| if (Base64.isArrayByteBase64(firstLine)) { |
| InputStream head = new ByteArrayInputStream(firstLine); |
| // Need to put that 16 byte "firstLine" back into the Stream. |
| encrypted = new ComboInputStream(head, encrypted); |
| encrypted = new Base64InputStream(encrypted); |
| } else { |
| // Encrypted data wasn't base64. Need to put the "firstLine" we |
| // extracted back into the stream. |
| InputStream head = new ByteArrayInputStream(firstLine); |
| encrypted = new ComboInputStream(head, encrypted); |
| } |
| |
| int keySize = cipherInfo.keySize; |
| int ivSize = cipherInfo.ivSize; |
| // Looks like key is in hex |
| if (key.length == keySize / 4) { |
| key = Hex.decode(key); |
| } |
| // Looks like IV is in hex |
| if (iv.length == ivSize / 4) { |
| iv = Hex.decode(iv); |
| } |
| DerivedKey dk = new DerivedKey(key, iv); |
| Cipher c = PKCS8Key.generateCipher(cipherInfo.javaCipher, |
| cipherInfo.blockMode, |
| dk, cipherInfo.des2, null, true); |
| return new CipherInputStream(encrypted, c); |
| } |
| |
| public static byte[] encrypt(String cipher, byte[] key, byte[] iv, |
| byte[] data) |
| throws IOException, GeneralSecurityException { |
| return encrypt(cipher, key, iv, data, true); |
| } |
| |
| public static byte[] encrypt(String cipher, byte[] key, byte[] iv, |
| byte[] data, boolean toBase64) |
| throws IOException, GeneralSecurityException { |
| ByteArrayInputStream in = new ByteArrayInputStream(data); |
| InputStream encrypted = encrypt(cipher, key, iv, in, toBase64); |
| return Util.streamToBytes(encrypted); |
| } |
| |
| |
| public static InputStream encrypt(String cipher, byte[] key, byte[] iv, |
| InputStream data) |
| throws IOException, GeneralSecurityException { |
| return encrypt(cipher, key, iv, data, true); |
| } |
| |
| public static InputStream encrypt(String cipher, byte[] key, byte[] iv, |
| InputStream data, boolean toBase64) |
| throws IOException, GeneralSecurityException { |
| CipherInfo cipherInfo = lookup(cipher); |
| int keySize = cipherInfo.keySize; |
| int ivSize = cipherInfo.ivSize; |
| if (key.length == keySize / 4) { |
| key = Hex.decode(key); |
| } |
| if (iv.length == ivSize / 4) { |
| iv = Hex.decode(iv); |
| } |
| DerivedKey dk = new DerivedKey(key, iv); |
| Cipher c = PKCS8Key.generateCipher(cipherInfo.javaCipher, |
| cipherInfo.blockMode, |
| dk, cipherInfo.des2, null, false); |
| |
| InputStream cipherStream = new CipherInputStream(data, c); |
| if (toBase64) { |
| cipherStream = new Base64InputStream(cipherStream, true); |
| } |
| return cipherStream; |
| } |
| |
| |
| public static DerivedKey deriveKey(char[] password, byte[] salt, |
| int keySize, boolean des2) |
| throws NoSuchAlgorithmException { |
| return deriveKey(password, salt, keySize, 0, des2); |
| } |
| |
| @SuppressWarnings("PMD.UselessParentheses") |
| public static DerivedKey deriveKey(char[] password, byte[] salt, |
| int keySize, int ivSize, boolean des2) |
| throws NoSuchAlgorithmException { |
| if (des2) { |
| keySize = 128; |
| } |
| MessageDigest md = MessageDigest.getInstance("MD5"); |
| byte[] pwdAsBytes = new byte[password.length]; |
| for (int i = 0; i < password.length; i++) { |
| pwdAsBytes[i] = (byte) password[i]; |
| } |
| |
| md.reset(); |
| byte[] keyAndIv = new byte[(keySize / 8) + (ivSize / 8)]; |
| if (salt == null || salt.length == 0) { |
| // Unsalted! Bad idea! |
| salt = null; |
| } |
| byte[] result; |
| int currentPos = 0; |
| while (currentPos < keyAndIv.length) { |
| md.update(pwdAsBytes); |
| if (salt != null) { |
| // First 8 bytes of salt ONLY! That wasn't obvious to me |
| // when using AES encrypted private keys in "Traditional |
| // SSLeay Format". |
| // |
| // Example: |
| // DEK-Info: AES-128-CBC,8DA91D5A71988E3D4431D9C2C009F249 |
| // |
| // Only the first 8 bytes are salt, but the whole thing is |
| // re-used again later as the IV. MUCH gnashing of teeth! |
| md.update(salt, 0, 8); |
| } |
| result = md.digest(); |
| int stillNeed = keyAndIv.length - currentPos; |
| // Digest gave us more than we need. Let's truncate it. |
| if (result.length > stillNeed) { |
| byte[] b = new byte[stillNeed]; |
| System.arraycopy(result, 0, b, 0, b.length); |
| result = b; |
| } |
| System.arraycopy(result, 0, keyAndIv, currentPos, result.length); |
| currentPos += result.length; |
| if (currentPos < keyAndIv.length) { |
| // Next round starts with a hash of the hash. |
| md.reset(); |
| md.update(result); |
| } |
| } |
| if (des2) { |
| keySize = 192; |
| byte[] buf = new byte[keyAndIv.length + 8]; |
| // Make space where 3rd key needs to go (16th - 24th bytes): |
| System.arraycopy(keyAndIv, 0, buf, 0, 16); |
| if (ivSize > 0) { |
| System.arraycopy(keyAndIv, 16, buf, 24, keyAndIv.length - 16); |
| } |
| keyAndIv = buf; |
| // copy first 8 bytes into last 8 bytes to create 2DES key. |
| System.arraycopy(keyAndIv, 0, keyAndIv, 16, 8); |
| } |
| if (ivSize == 0) { |
| // if ivSize == 0, then "keyAndIv" array is actually all key. |
| |
| // Must be "Traditional SSLeay Format" encrypted private key in |
| // PEM. The "salt" in its entirety (not just first 8 bytes) will |
| // probably be re-used later as the IV (initialization vector). |
| return new DerivedKey(keyAndIv, salt); |
| } else { |
| byte[] key = new byte[keySize / 8]; |
| byte[] iv = new byte[ivSize / 8]; |
| System.arraycopy(keyAndIv, 0, key, 0, key.length); |
| System.arraycopy(keyAndIv, key.length, iv, 0, iv.length); |
| return new DerivedKey(key, iv); |
| } |
| } |
| |
| |
| public static class CipherInfo { |
| public final String javaCipher; |
| public final String blockMode; |
| public final int keySize; |
| public final int ivSize; |
| public final boolean des2; |
| |
| public CipherInfo(String javaCipher, String blockMode, int keySize, |
| int ivSize, boolean des2) { |
| this.javaCipher = javaCipher; |
| this.blockMode = blockMode; |
| this.keySize = keySize; |
| this.ivSize = ivSize; |
| this.des2 = des2; |
| } |
| |
| public String toString() { |
| return javaCipher + "/" + blockMode + " " + keySize + "bit des2=" + des2; |
| } |
| } |
| |
| /** |
| * Converts the way OpenSSL names its ciphers into a Java-friendly naming. |
| * |
| * @param openSSLCipher OpenSSL cipher name, e.g. "des3" or "des-ede3-cbc". |
| * Try "man enc" on a unix box to see what's possible. |
| * @return CipherInfo object with the Java-friendly cipher information. |
| */ |
| public static CipherInfo lookup(String openSSLCipher) { |
| openSSLCipher = openSSLCipher.trim(); |
| if (openSSLCipher.charAt(0) == '-') { |
| openSSLCipher = openSSLCipher.substring(1); |
| } |
| String javaCipher = openSSLCipher.toUpperCase(); |
| String blockMode = "CBC"; |
| int keySize = -1; |
| int ivSize = 64; |
| boolean des2 = false; |
| |
| |
| StringTokenizer st = new StringTokenizer(openSSLCipher, "-"); |
| if (st.hasMoreTokens()) { |
| javaCipher = st.nextToken().toUpperCase(); |
| if (st.hasMoreTokens()) { |
| // Is this the middle token? Or the last token? |
| String tok = st.nextToken(); |
| if (st.hasMoreTokens()) { |
| try { |
| keySize = Integer.parseInt(tok); |
| } catch (NumberFormatException nfe) { |
| // I guess 2nd token isn't an integer |
| String upper = tok.toUpperCase(); |
| if (upper.startsWith("EDE3")) { |
| javaCipher = "DESede"; |
| } else if (upper.startsWith("EDE")) { |
| javaCipher = "DESede"; |
| des2 = true; |
| } |
| } |
| blockMode = st.nextToken().toUpperCase(); |
| } else { |
| try { |
| keySize = Integer.parseInt(tok); |
| } catch (NumberFormatException nfe) { |
| // It's the last token, so must be mode (usually "CBC"). |
| blockMode = tok.toUpperCase(); |
| if (blockMode.startsWith("EDE3")) { |
| javaCipher = "DESede"; |
| blockMode = "ECB"; |
| } else if (blockMode.startsWith("EDE")) { |
| javaCipher = "DESede"; |
| blockMode = "ECB"; |
| des2 = true; |
| } |
| } |
| } |
| } |
| } |
| if (javaCipher.startsWith("BF")) { |
| javaCipher = "Blowfish"; |
| } else if (javaCipher.startsWith("TWOFISH")) { |
| javaCipher = "Twofish"; |
| ivSize = 128; |
| } else if (javaCipher.startsWith("IDEA")) { |
| javaCipher = "IDEA"; |
| } else if (javaCipher.startsWith("CAST6")) { |
| javaCipher = "CAST6"; |
| ivSize = 128; |
| } else if (javaCipher.startsWith("CAST")) { |
| javaCipher = "CAST5"; |
| } else if (javaCipher.startsWith("GOST")) { |
| keySize = 256; |
| } else if (javaCipher.startsWith("DESX")) { |
| javaCipher = "DESX"; |
| } else if ("DES3".equals(javaCipher)) { |
| javaCipher = "DESede"; |
| } else if ("DES2".equals(javaCipher)) { |
| javaCipher = "DESede"; |
| des2 = true; |
| } else if (javaCipher.startsWith("RIJNDAEL")) { |
| javaCipher = "Rijndael"; |
| ivSize = 128; |
| } else if (javaCipher.startsWith("SEED")) { |
| javaCipher = "SEED"; |
| ivSize = 128; |
| } else if (javaCipher.startsWith("SERPENT")) { |
| javaCipher = "Serpent"; |
| ivSize = 128; |
| } else if (javaCipher.startsWith("Skipjack")) { |
| javaCipher = "Skipjack"; |
| ivSize = 128; |
| } else if (javaCipher.startsWith("RC6")) { |
| javaCipher = "RC6"; |
| ivSize = 128; |
| } else if (javaCipher.startsWith("TEA")) { |
| javaCipher = "TEA"; |
| } else if (javaCipher.startsWith("XTEA")) { |
| javaCipher = "XTEA"; |
| } else if (javaCipher.startsWith("AES")) { |
| if (javaCipher.startsWith("AES128")) { |
| keySize = 128; |
| } else if (javaCipher.startsWith("AES192")) { |
| keySize = 192; |
| } else if (javaCipher.startsWith("AES256")) { |
| keySize = 256; |
| } |
| javaCipher = "AES"; |
| ivSize = 128; |
| } else if (javaCipher.startsWith("CAMELLIA")) { |
| if (javaCipher.startsWith("CAMELLIA128")) { |
| keySize = 128; |
| } else if (javaCipher.startsWith("CAMELLIA192")) { |
| keySize = 192; |
| } else if (javaCipher.startsWith("CAMELLIA256")) { |
| keySize = 256; |
| } |
| javaCipher = "CAMELLIA"; |
| ivSize = 128; |
| } |
| if (keySize == -1) { |
| if (javaCipher.startsWith("DESede")) { |
| keySize = 192; |
| } else if (javaCipher.startsWith("DES")) { |
| keySize = 64; |
| } else { |
| // RC2, RC4, RC5 and Blowfish ? |
| keySize = 128; |
| } |
| } |
| return new CipherInfo(javaCipher, blockMode, keySize, ivSize, des2); |
| } |
| |
| |
| /** |
| * @param args command line arguments: [password] [cipher] [file-to-decrypt] |
| * <br>[cipher] == OpenSSL cipher name, e.g. "des3" or "des-ede3-cbc". |
| * Try "man enc" on a unix box to see what's possible. |
| * @throws java.io.IOException problems with the [file-to-decrypt] |
| * @throws java.security.GeneralSecurityException decryption problems |
| */ |
| public static void main(String[] args) |
| throws IOException, GeneralSecurityException { |
| if (args.length < 3) { |
| System.out.println(Version.versionString()); |
| System.out.println("Pure-java utility to decrypt files previously encrypted by \'openssl enc\'"); |
| System.out.println(); |
| System.out.println("Usage: java -cp commons-ssl.jar org.apache.commons.ssl.OpenSSL [args]"); |
| System.out.println(" [args] == [password] [cipher] [file-to-decrypt]"); |
| System.out.println(" [cipher] == des, des3, des-ede3-cbc, aes256, rc2, rc4, bf, bf-cbc, etc..."); |
| System.out.println(" Try 'man enc' on a unix box to see what's possible."); |
| System.out.println(); |
| System.out.println("This utility can handle base64 or raw, salted or unsalted."); |
| System.out.println(); |
| System.exit(1); |
| } |
| char[] password = args[0].toCharArray(); |
| |
| InputStream in = new FileInputStream(args[2]); |
| in = decrypt(args[1], password, in); |
| |
| // in = encrypt( args[ 1 ], pwdAsBytes, in, true ); |
| |
| in = new BufferedInputStream(in); |
| BufferedOutputStream bufOut = new BufferedOutputStream(System.out); |
| Util.pipeStream(in, bufOut, false); |
| bufOut.flush(); |
| System.out.flush(); |
| } |
| |
| } |