You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@directory.apache.org by dr...@apache.org on 2015/12/13 03:02:07 UTC
[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
+ * <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 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);
+ }
+
+ 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();
+ }
+
+}
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
+ * <http://www.apache.org/>.
+ *
+ */
+
+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 <a href="http://www.cucbc.com/">www.cucbc.com</a>
+ * @author <a href="mailto:juliusdavies@cucbc.com">juliusdavies@cucbc.com</a>
+ * @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
+ * <http://www.apache.org/>.
+ *
+ */
+
+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 <a href="http://www.cucbc.com/">www.cucbc.com</a>
+ * @author <a href="mailto:juliusdavies@cucbc.com">juliusdavies@cucbc.com</a>
+ * @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");
+ }
+
+}