Return-Path: X-Original-To: apmail-directory-commits-archive@www.apache.org Delivered-To: apmail-directory-commits-archive@www.apache.org Received: from mail.apache.org (hermes.apache.org [140.211.11.3]) by minotaur.apache.org (Postfix) with SMTP id 1349A10FCF for ; Sun, 13 Dec 2015 02:01:54 +0000 (UTC) Received: (qmail 45301 invoked by uid 500); 13 Dec 2015 02:01:53 -0000 Delivered-To: apmail-directory-commits-archive@directory.apache.org Received: (qmail 45187 invoked by uid 500); 13 Dec 2015 02:01:53 -0000 Mailing-List: contact commits-help@directory.apache.org; run by ezmlm Precedence: bulk List-Help: List-Unsubscribe: List-Post: List-Id: Reply-To: dev@directory.apache.org Delivered-To: mailing list commits@directory.apache.org Received: (qmail 44265 invoked by uid 99); 13 Dec 2015 02:01:53 -0000 Received: from git1-us-west.apache.org (HELO git1-us-west.apache.org) (140.211.11.23) by apache.org (qpsmtpd/0.29) with ESMTP; Sun, 13 Dec 2015 02:01:53 +0000 Received: by git1-us-west.apache.org (ASF Mail Server at git1-us-west.apache.org, from userid 33) id 17F56E08E7; Sun, 13 Dec 2015 02:01:53 +0000 (UTC) Content-Type: text/plain; charset="us-ascii" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit From: drankye@apache.org To: commits@directory.apache.org Date: Sun, 13 Dec 2015 02:02:07 -0000 Message-Id: <693dee3f867047f68efc2fafd92b1a62@git.apache.org> In-Reply-To: <694679759b904a98ad0f860fb74e82ae@git.apache.org> References: <694679759b904a98ad0f860fb74e82ae@git.apache.org> X-Mailer: ASF-Git Admin Mailer Subject: [16/36] directory-kerby git commit: Moved the source codes of not-so-commons-ssl into kerby-pkix module and cleaned up accordingly http://git-wip-us.apache.org/repos/asf/directory-kerby/blob/a2c604ee/kerby-pkix/src/main/java/org/apache/commons/ssl/OpenSSL.java ---------------------------------------------------------------------- diff --git a/kerby-pkix/src/main/java/org/apache/commons/ssl/OpenSSL.java b/kerby-pkix/src/main/java/org/apache/commons/ssl/OpenSSL.java new file mode 100644 index 0000000..617722f --- /dev/null +++ b/kerby-pkix/src/main/java/org/apache/commons/ssl/OpenSSL.java @@ -0,0 +1,723 @@ +/* + * $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 + * . + * + */ + +package org.apache.commons.ssl; + +import org.apache.kerby.util.Base64; +import org.apache.kerby.util.Base64InputStream; +import org.apache.kerby.util.Hex; + +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: + *
  • des, des3, des-ede3-cbc + *
  • aes128, aes192, aes256, aes-256-cbc + *
  • rc2, rc4, bf
+ *
+ * // Encrypt!
+ * byte[] encryptedData = OpenSSL.encrypt( "des3", password, data );
+ * 
+ *

+ * 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. + *

+ * This class is able to decrypt files encrypted with "openssl" unix utility. + *

+ * The "openssl" unix utility is able to decrypt files encrypted by this class. + *

+ * This class is also able to encrypt and decrypt its own files. + * + * @author juliusdavies@gmail.com + * @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: + *

  • des, des3, des-ede3-cbc + *
  • aes128, aes192, aes256, aes-256-cbc + *
  • rc2, rc4, bf
+ * @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: + *
  • des, des3, des-ede3-cbc + *
  • aes128, aes192, aes256, aes-256-cbc + *
  • rc2, rc4, bf
+ * @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: + *
  • des, des3, des-ede3-cbc + *
  • aes128, aes192, aes256, aes-256-cbc + *
  • rc2, rc4, bf
+ * @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: + *
  • des, des3, des-ede3-cbc + *
  • aes128, aes192, aes256, aes-256-cbc + *
  • rc2, rc4, bf
+ * @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: + *
  • des, des3, des-ede3-cbc + *
  • aes128, aes192, aes256, aes-256-cbc + *
  • rc2, rc4, bf
+ * @param pwd password to use for this PBE encryption + * @param data byte array to encrypt + * @param toBase64 true if resulting InputStream should contain base64, + *
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: + *
  • des, des3, des-ede3-cbc + *
  • aes128, aes192, aes256, aes-256-cbc + *
  • rc2, rc4, bf
+ * @param pwd password to use for this PBE encryption + * @param data InputStream to encrypt + * @param toBase64 true if resulting InputStream should contain base64, + *
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: + *
  • des, des3, des-ede3-cbc + *
  • aes128, aes192, aes256, aes-256-cbc + *
  • rc2, rc4, bf
+ * @param pwd password to use for this PBE encryption + * @param data byte array to encrypt + * @param toBase64 true if resulting InputStream should contain base64, + *
false if InputStream should contain raw binary. + * @param useSalt true if a salt should be used to derive the key. + *
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 useSalt 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: + *
  • des, des3, des-ede3-cbc + *
  • aes128, aes192, aes256, aes-256-cbc + *
  • rc2, rc4, bf
+ * @param pwd password to use for this PBE encryption + * @param data InputStream to encrypt + * @param toBase64 true if resulting InputStream should contain base64, + *
false if InputStream should contain raw binary. + * @param useSalt true if a salt should be used to derive the key. + *
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 useSalt 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); + } + + 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] + *
[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(); + } + +} http://git-wip-us.apache.org/repos/asf/directory-kerby/blob/a2c604ee/kerby-pkix/src/main/java/org/apache/commons/ssl/PBETestCreate.java ---------------------------------------------------------------------- diff --git a/kerby-pkix/src/main/java/org/apache/commons/ssl/PBETestCreate.java b/kerby-pkix/src/main/java/org/apache/commons/ssl/PBETestCreate.java new file mode 100644 index 0000000..a3dcf7b --- /dev/null +++ b/kerby-pkix/src/main/java/org/apache/commons/ssl/PBETestCreate.java @@ -0,0 +1,77 @@ +package org.apache.commons.ssl; + +import java.io.File; +import java.io.FileInputStream; +import java.io.FileOutputStream; +import java.io.IOException; +import java.util.Iterator; +import java.util.Map; +import java.util.Properties; +import java.util.TreeSet; + +/** + * @author Julius Davies + * @since 4-Jul-2007 + */ +public class PBETestCreate { + + public static void main(String[] args) throws Exception { + FileInputStream in = new FileInputStream(args[0]); + Properties p = new Properties(); + p.load(in); + in.close(); + + String targetDir = p.getProperty("target"); + File dir = new File(targetDir); + dir.mkdirs(); + if (!dir.exists()) { + throw new IOException(dir.getCanonicalPath() + " doesn't exist!"); + } + + TreeSet ciphers = new TreeSet(); + Iterator it = p.entrySet().iterator(); + while (it.hasNext()) { + Map.Entry entry = (Map.Entry) it.next(); + String key = (String) entry.getKey(); + if (!"target".equalsIgnoreCase(key)) { + ciphers.add(key); + ciphers.add(key + "-cbc"); + ciphers.add(key + "-cfb"); + ciphers.add(key + "-cfb1"); + ciphers.add(key + "-cfb8"); + ciphers.add(key + "-ecb"); + ciphers.add(key + "-ofb"); + } + } + + byte[] toEncrypt = "Hello World!".getBytes("UTF-8"); + char[] pwd = "changeit".toCharArray(); + it = ciphers.iterator(); + while (it.hasNext()) { + String cipher = (String) it.next(); + String cipherPadded = Util.pad(cipher, 15, false); + String fileNameBase64 = cipher + ".base64"; + String fileNameRaw = cipher + ".raw"; + String d = dir.getCanonicalPath() + "/"; + try { + byte[] base64 = OpenSSL.encrypt(cipher, pwd, toEncrypt, true); + FileOutputStream out = new FileOutputStream(d + fileNameBase64); + out.write(base64); + out.close(); + } catch (Exception e) { + System.err.println("FAILURE \t" + cipherPadded + "\t" + fileNameBase64 + "\t" + e); + } + + try { + byte[] raw = OpenSSL.encrypt(cipher, pwd, toEncrypt, false); + FileOutputStream out = new FileOutputStream(d + fileNameRaw); + out.write(raw); + out.close(); + } catch (Exception e) { + System.err.println("FAILURE \t" + cipherPadded + "\t" + fileNameRaw + "\t" + e); + } + + } + } + +} http://git-wip-us.apache.org/repos/asf/directory-kerby/blob/a2c604ee/kerby-pkix/src/main/java/org/apache/commons/ssl/PEMItem.java ---------------------------------------------------------------------- diff --git a/kerby-pkix/src/main/java/org/apache/commons/ssl/PEMItem.java b/kerby-pkix/src/main/java/org/apache/commons/ssl/PEMItem.java new file mode 100644 index 0000000..846340a --- /dev/null +++ b/kerby-pkix/src/main/java/org/apache/commons/ssl/PEMItem.java @@ -0,0 +1,106 @@ +/* + * $HeadURL: http://juliusdavies.ca/svn/not-yet-commons-ssl/tags/commons-ssl-0.3.16/src/java/org/apache/commons/ssl/PEMItem.java $ + * $Revision: 121 $ + * $Date: 2007-11-13 21:26:57 -0800 (Tue, 13 Nov 2007) $ + * + * ==================================================================== + * 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 + * . + * + */ + +package org.apache.commons.ssl; + +import org.apache.kerby.util.Hex; + +import java.util.Collections; +import java.util.Map; +import java.util.StringTokenizer; +import java.util.TreeMap; + +/** + * @author Credit Union Central of British Columbia + * @author www.cucbc.com + * @author juliusdavies@cucbc.com + * @since 13-Aug-2006 + */ +public class PEMItem { + public static final String DEK_INFO = "dek-info"; + + private final byte[] derBytes; + public final String pemType; + public final Map properties; + + public final String dekInfo; + public final byte[] iv; + public final String cipher; + public final boolean des2; + public final String mode; + public final int keySizeInBits; + + public PEMItem(byte[] derBytes, String type) { + this(derBytes, type, null); + } + + public PEMItem(byte[] derBytes, String type, Map properties) { + this.derBytes = derBytes; + this.pemType = type; + if (properties == null) { + properties = new TreeMap(); // empty map + } + this.properties = Collections.unmodifiableMap(properties); + String di = (String) properties.get(DEK_INFO); + String diCipher = ""; + String diIV = ""; + if (di != null) { + StringTokenizer st = new StringTokenizer(di, ","); + if (st.hasMoreTokens()) { + diCipher = st.nextToken().trim().toLowerCase(); + } + if (st.hasMoreTokens()) { + diIV = st.nextToken().trim().toLowerCase(); + } + } + this.dekInfo = diCipher; + this.iv = Hex.decode(diIV); + if (!"".equals(diCipher)) { + OpenSSL.CipherInfo cipherInfo = OpenSSL.lookup(diCipher); + this.cipher = cipherInfo.javaCipher; + this.mode = cipherInfo.blockMode; + this.keySizeInBits = cipherInfo.keySize; + this.des2 = cipherInfo.des2; + } else { + this.mode = ""; + cipher = "UNKNOWN"; + keySizeInBits = -1; + des2 = false; + } + } + + public byte[] getDerBytes() { + byte[] b = new byte[derBytes.length]; + System.arraycopy(derBytes, 0, b, 0, derBytes.length); + return b; + } + +} http://git-wip-us.apache.org/repos/asf/directory-kerby/blob/a2c604ee/kerby-pkix/src/main/java/org/apache/commons/ssl/PEMUtil.java ---------------------------------------------------------------------- diff --git a/kerby-pkix/src/main/java/org/apache/commons/ssl/PEMUtil.java b/kerby-pkix/src/main/java/org/apache/commons/ssl/PEMUtil.java new file mode 100644 index 0000000..26dfa67 --- /dev/null +++ b/kerby-pkix/src/main/java/org/apache/commons/ssl/PEMUtil.java @@ -0,0 +1,258 @@ +/* + * $HeadURL: http://juliusdavies.ca/svn/not-yet-commons-ssl/tags/commons-ssl-0.3.16/src/java/org/apache/commons/ssl/PEMUtil.java $ + * $Revision: 153 $ + * $Date: 2009-09-15 22:40:53 -0700 (Tue, 15 Sep 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 + * . + * + */ + +package org.apache.commons.ssl; + +import org.apache.commons.ssl.util.ByteArrayReadLine; +import org.apache.kerby.util.Base64; + +import java.io.ByteArrayInputStream; +import java.io.ByteArrayOutputStream; +import java.io.IOException; +import java.math.BigInteger; +import java.security.PublicKey; +import java.security.interfaces.DSAPublicKey; +import java.security.interfaces.RSAPrivateCrtKey; +import java.security.interfaces.RSAPublicKey; +import java.util.ArrayList; +import java.util.Collection; +import java.util.Collections; +import java.util.HashMap; +import java.util.Iterator; +import java.util.LinkedList; +import java.util.List; +import java.util.Map; + +/** + * @author Credit Union Central of British Columbia + * @author www.cucbc.com + * @author juliusdavies@cucbc.com + * @since 13-Aug-2006 + */ +public class PEMUtil { + static final String LINE_SEPARATOR = System.getProperty("line.separator"); + + public static byte[] encode(Collection items) throws IOException { + final byte[] lineSeparatorBytes = LINE_SEPARATOR.getBytes("UTF-8"); + ByteArrayOutputStream out = new ByteArrayOutputStream(8192); + Iterator it = items.iterator(); + while (it.hasNext()) { + PEMItem item = (PEMItem) it.next(); + out.write("-----BEGIN ".getBytes("UTF-8")); + out.write(item.pemType.getBytes("UTF-8")); + out.write("-----".getBytes("UTF-8")); + out.write(lineSeparatorBytes); + + byte[] derBytes = item.getDerBytes(); + ByteArrayInputStream bin = new ByteArrayInputStream(derBytes); + byte[] line = Util.streamToBytes(bin, 48); + while (line.length == 48) { + byte[] base64Line = Base64.encodeBase64(line); + out.write(base64Line); + out.write(lineSeparatorBytes); + line = Util.streamToBytes(bin, 48); + } + if (line.length > 0) { + byte[] base64Line = Base64.encodeBase64(line); + out.write(base64Line); + out.write(lineSeparatorBytes); + } + out.write("-----END ".getBytes("UTF-8")); + out.write(item.pemType.getBytes("UTF-8")); + out.write("-----".getBytes("UTF-8")); + out.write(lineSeparatorBytes); + } + return out.toByteArray(); + } + + public static List decode(byte[] pemBytes) { + LinkedList pemItems = new LinkedList(); + ByteArrayInputStream in = new ByteArrayInputStream(pemBytes); + ByteArrayReadLine readLine = new ByteArrayReadLine(in); + String line = readLine.next(); + while (line != null) { + int len = 0; + byte[] decoded; + ArrayList listOfByteArrays = new ArrayList(64); + Map properties = new HashMap(); + String type = "[unknown]"; + while (line != null && !beginBase64(line)) { + line = readLine.next(); + } + if (line != null) { + String upperLine = line.toUpperCase(); + int x = upperLine.indexOf("-BEGIN") + "-BEGIN".length(); + int y = upperLine.indexOf("-", x); + type = upperLine.substring(x, y).trim(); + line = readLine.next(); + } + while (line != null && !endBase64(line)) { + line = Util.trim(line); + if (!"".equals(line)) { + int x = line.indexOf(':'); + if (x > 0) { + String k = line.substring(0, x).trim(); + String v = ""; + if (line.length() > x + 1) { + v = line.substring(x + 1).trim(); + } + properties.put(k.toLowerCase(), v.toLowerCase()); + } else { + byte[] base64 = line.getBytes(); + byte[] rawBinary = Base64.decodeBase64(base64); + listOfByteArrays.add(rawBinary); + len += rawBinary.length; + } + } + line = readLine.next(); + } + if (line != null) { + line = readLine.next(); + } + + if (!listOfByteArrays.isEmpty()) { + decoded = new byte[len]; + int pos = 0; + Iterator it = listOfByteArrays.iterator(); + while (it.hasNext()) { + byte[] oneLine = (byte[]) it.next(); + System.arraycopy(oneLine, 0, decoded, pos, oneLine.length); + pos += oneLine.length; + } + PEMItem item = new PEMItem(decoded, type, properties); + pemItems.add(item); + } + } + + // closing ByteArrayInputStream is a NO-OP + // in.close(); + + return pemItems; + } + + private static boolean beginBase64(String line) { + line = line != null ? line.trim().toUpperCase() : ""; + int x = line.indexOf("-BEGIN"); + return x > 0 && startsAndEndsWithDashes(line); + } + + private static boolean endBase64(String line) { + line = line != null ? line.trim().toUpperCase() : ""; + int x = line.indexOf("-END"); + return x > 0 && startsAndEndsWithDashes(line); + } + + private static boolean startsAndEndsWithDashes(String line) { + line = Util.trim(line); + char c = line.charAt(0); + char d = line.charAt(line.length() - 1); + return c == '-' && d == '-'; + } + + public static String formatRSAPrivateKey(RSAPrivateCrtKey key) { + StringBuffer buf = new StringBuffer(2048); + buf.append("Private-Key:"); + buf.append(LINE_SEPARATOR); + buf.append("modulus:"); + buf.append(LINE_SEPARATOR); + buf.append(formatBigInteger(key.getModulus(), 129 * 2)); + buf.append(LINE_SEPARATOR); + buf.append("publicExponent: "); + buf.append(key.getPublicExponent()); + buf.append(LINE_SEPARATOR); + buf.append("privateExponent:"); + buf.append(LINE_SEPARATOR); + buf.append(formatBigInteger(key.getPrivateExponent(), 128 * 2)); + buf.append(LINE_SEPARATOR); + buf.append("prime1:"); + buf.append(LINE_SEPARATOR); + buf.append(formatBigInteger(key.getPrimeP(), 65 * 2)); + buf.append(LINE_SEPARATOR); + buf.append("prime2:"); + buf.append(LINE_SEPARATOR); + buf.append(formatBigInteger(key.getPrimeQ(), 65 * 2)); + buf.append(LINE_SEPARATOR); + buf.append("exponent1:"); + buf.append(LINE_SEPARATOR); + buf.append(formatBigInteger(key.getPrimeExponentP(), 65 * 2)); + buf.append(LINE_SEPARATOR); + buf.append("exponent2:"); + buf.append(LINE_SEPARATOR); + buf.append(formatBigInteger(key.getPrimeExponentQ(), 65 * 2)); + buf.append(LINE_SEPARATOR); + buf.append("coefficient:"); + buf.append(LINE_SEPARATOR); + buf.append(formatBigInteger(key.getCrtCoefficient(), 65 * 2)); + return buf.toString(); + } + + public static String formatBigInteger(BigInteger bi, int length) { + String s = bi.toString(16); + StringBuffer buf = new StringBuffer(s.length()); + int zeroesToAppend = length - s.length(); + int count = 0; + buf.append(" "); + for (int i = 0; i < zeroesToAppend; i++) { + count++; + buf.append('0'); + if (i % 2 == 1) { + buf.append(':'); + } + } + for (int i = 0; i < s.length() - 2; i++) { + count++; + buf.append(s.charAt(i)); + if (i % 2 == 1) { + buf.append(':'); + } + if (count % 30 == 0) { + buf.append(LINE_SEPARATOR); + buf.append(" "); + } + } + buf.append(s.substring(s.length() - 2)); + return buf.toString(); + } + + public static String toPem(PublicKey key) throws IOException { + PEMItem item = null; + if (key instanceof RSAPublicKey) { + item = new PEMItem(key.getEncoded(), "PUBLIC KEY"); + } else if (key instanceof DSAPublicKey) { + item = new PEMItem(key.getEncoded(), "PUBLIC KEY"); + } else { + throw new IOException("Not an RSA or DSA key"); + } + byte[] pem = encode(Collections.singleton(item)); + return new String(pem, "UTF-8"); + } + +}