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/01/10 14:31:08 UTC

[24/42] directory-kerberos git commit: Initially import Haox codebase (https://github.com/drankye/haox)

http://git-wip-us.apache.org/repos/asf/directory-kerberos/blob/23c1fd12/3rdparty/not-yet-commons-ssl/src/main/java/org/apache/commons/ssl/LogWrapper.java
----------------------------------------------------------------------
diff --git a/3rdparty/not-yet-commons-ssl/src/main/java/org/apache/commons/ssl/LogWrapper.java b/3rdparty/not-yet-commons-ssl/src/main/java/org/apache/commons/ssl/LogWrapper.java
new file mode 100644
index 0000000..b2baeb9
--- /dev/null
+++ b/3rdparty/not-yet-commons-ssl/src/main/java/org/apache/commons/ssl/LogWrapper.java
@@ -0,0 +1,295 @@
+/*
+ * $HeadURL: http://juliusdavies.ca/svn/not-yet-commons-ssl/tags/commons-ssl-0.3.16/src/java/org/apache/commons/ssl/LogWrapper.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 java.io.BufferedOutputStream;
+import java.io.FileOutputStream;
+import java.io.IOException;
+import java.io.OutputStream;
+import java.io.PrintStream;
+import java.text.DateFormat;
+import java.text.SimpleDateFormat;
+import java.util.Date;
+
+/**
+ * <p/>
+ * LogWrapper can be used for situations where log4j might not be available on
+ * the classpath.  It presents the most basic and critical components of the
+ * log4j API, and passes all log calls through to log4j if possible.  If log4j
+ * is not available, logging is sent to standard-out by default.
+ * <p/>
+ * This default logging to standard-out (which only occurs if log4j is NOT
+ * available) can be disabled or changed via the static setBackupStream() and
+ * setBackupLogFile() methods.
+ *
+ * @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 3-Aug-2006
+ */
+public class LogWrapper {
+
+    // final static String[] LEVELS = {"DEBUG", "INFO", "WARN", "ERROR", "FATAL"};
+    final static String[] LEVELS = {"+", " ", "!", "*", "#"};
+    final static String TIMESTAMP_PATTERN = "zzz:yyyy-MM-dd/HH:mm:ss.SSS";
+    final static int TIMESTAMP_LENGTH = TIMESTAMP_PATTERN.length();
+    final static String LINE_SEPARATOR = System.getProperty("line.separator");
+    final static DateFormat DF = new SimpleDateFormat(TIMESTAMP_PATTERN);
+
+    private final static LogWrapper NOOP = new LogWrapper();
+
+    /** Should we print DEBUG statements if log4j is not available? */
+    private final static boolean DEBUG = true;
+
+    /** true if log4j is available */
+    public final static boolean log4j;
+
+    /**
+     * OutputStream to log to if log4j is not available.  Set it to null to
+     * disable.
+     */
+    private static volatile OutputStream backup = System.out;
+
+    /** The wrappingPrintStream is lazy-initted if we have to log a stacktrace. */
+    private static volatile PrintStream wrappingPrintStream = null;
+
+    private final LogHelper h;
+
+    static {
+        boolean avail = false;
+        try {
+            // LogHelper's constructor will blow up if log4j.jar isn't on the
+            // classpath.
+            LogHelper lh = new LogHelper(LogWrapper.class);
+            lh.hashCode();
+            avail = true;
+        }
+        catch (Throwable t) {
+            avail = false;
+        }
+        finally {
+            log4j = avail;
+        }
+    }
+
+    public static boolean isLog4jAvailable() { return log4j; }
+
+    public static LogWrapper getLogger(Class c) {
+        return log4j ? new LogWrapper(c) : NOOP;
+    }
+
+    public static LogWrapper getLogger(String s) {
+        return log4j ? new LogWrapper(s) : NOOP;
+    }
+
+    private LogWrapper() { this.h = null; }
+
+    private LogWrapper(Class c) { this.h = new LogHelper(c); }
+
+    private LogWrapper(String s) { this.h = new LogHelper(s); }
+
+    public void debug(Object o) {
+        if (t(0, o, null)) {
+            h.debug(o);
+        }
+    }
+
+    public void debug(Object o, Throwable t) {
+        if (t(0, o, t)) {
+            h.debug(o, t);
+        }
+    }
+
+    public void info(Object o) {
+        if (t(1, o, null)) {
+            h.info(o);
+        }
+    }
+
+    public void info(Object o, Throwable t) {
+        if (t(1, o, t)) {
+            h.info(o, t);
+        }
+    }
+
+    public void warn(Object o) {
+        if (t(2, o, null)) {
+            h.warn(o);
+        }
+    }
+
+    public void warn(Object o, Throwable t) {
+        if (t(2, o, t)) {
+            h.warn(o, t);
+        }
+    }
+
+    public void error(Object o) {
+        if (t(3, o, null)) {
+            h.error(o);
+        }
+    }
+
+    public void error(Object o, Throwable t) {
+        if (t(3, o, t)) {
+            h.error(o, t);
+        }
+    }
+
+    public void fatal(Object o) {
+        if (t(4, o, null)) {
+            h.fatal(o);
+        }
+    }
+
+    public void fatal(Object o, Throwable t) {
+        if (t(4, o, t)) {
+            h.fatal(o, t);
+        }
+    }
+
+    public boolean isDebugEnabled() { return log4j ? h.isDebugEnabled() : DEBUG;}
+
+    public boolean isInfoEnabled() { return !log4j || h.isInfoEnabled(); }
+
+    public Object getLog4jLogger() { return log4j ? h.getLog4jLogger() : null; }
+
+
+    /**
+     * Tests if log4j is available.  If not, logs to backup OutputStream (if
+     * backup != null).
+     *
+     * @param level log4j logging level for this statement
+     * @param o     object to log
+     * @param t     throwable to log
+     * @return true if log4j is available, false if log4j is not.  If it returns
+     *         false, as a side-effect, it will also log the statement.
+     */
+    private boolean t(int level, Object o, Throwable t) {
+        if (log4j) {
+            return true;
+        } else {
+            // LogWrapper doesn't log debug statements if Log4j is not available
+            // and DEBUG is false.
+            if (backup != null && (DEBUG || level > 0)) {
+                String s = "";  // log4j allows null
+                if (o != null) {
+                    try {
+                        s = (String) o;
+                    }
+                    catch (ClassCastException cce) {
+                        s = o.toString();
+                    }
+                }
+                int len = s.length() + TIMESTAMP_LENGTH + 9;
+                String timestamp = DF.format(new Date());
+                StringBuffer buf = new StringBuffer(len);
+                buf.append(timestamp);
+                if (LEVELS[level].length() == 1) {
+                    buf.append(LEVELS[level]);
+                } else {
+                    buf.append(' ');
+                    buf.append(LEVELS[level]);
+                    buf.append(' ');
+                }
+                buf.append(s);
+                buf.append(LINE_SEPARATOR);
+                s = buf.toString();
+                byte[] logBytes = s.getBytes();
+                try {
+                    if (t == null) {
+                        backup.write(logBytes);
+                    } else {
+                        synchronized (backup) {
+                            backup.write(logBytes);
+                            if (t != null) {
+                                if (wrappingPrintStream == null) {
+                                    wrappingPrintStream = new PrintStream(backup, false);
+                                }
+                                t.printStackTrace(wrappingPrintStream);
+                                wrappingPrintStream.flush();
+                            }
+                        }
+                    }
+                    backup.flush();   // J2RE 1.5.0 IBM J9 2.3 Linux x86-32 needs this.
+                }
+                catch (IOException ioe) {
+                    throw new RuntimeException(ioe.toString());
+                }
+            }
+            return false;
+        }
+    }
+
+    /**
+     * Set file to log to if log4j is not available.
+     *
+     * @param f path to use for backup log file (if log4j not available)
+     * @throws java.io.IOException if we can't write to the given path
+     */
+    public static void setBackupLogFile(String f)
+        throws IOException {
+        if (!log4j) {
+            OutputStream out = new FileOutputStream(f, true);
+            out = new BufferedOutputStream(out);
+            setBackupStream(out);
+        }
+    }
+
+    /**
+     * Set PrintStream to log to if log4j is not available.  Set to null to
+     * disable.  Default value is System.out.
+     *
+     * @param os outputstream to use for backup logging (if log4j not available)
+     */
+    public static void setBackupStream(OutputStream os) {
+        // synchronize on the old backup - don't want to pull the rug out from
+        // under him if he's working on a big stacktrace or something like that.
+        if (backup != null) {
+            synchronized (backup) {
+                wrappingPrintStream = null;
+                backup = os;
+            }
+        } else {
+            wrappingPrintStream = null;
+            backup = os;
+        }
+    }
+
+    /**
+     * Get the PrintStream we're logging to if log4j is not available.
+     *
+     * @return OutputStream we're using as our log4j replacement.
+     */
+    public static OutputStream getBackupStream() { return backup; }
+
+}

http://git-wip-us.apache.org/repos/asf/directory-kerberos/blob/23c1fd12/3rdparty/not-yet-commons-ssl/src/main/java/org/apache/commons/ssl/OpenSSL.java
----------------------------------------------------------------------
diff --git a/3rdparty/not-yet-commons-ssl/src/main/java/org/apache/commons/ssl/OpenSSL.java b/3rdparty/not-yet-commons-ssl/src/main/java/org/apache/commons/ssl/OpenSSL.java
new file mode 100644
index 0000000..c4d3798
--- /dev/null
+++ b/3rdparty/not-yet-commons-ssl/src/main/java/org/apache/commons/ssl/OpenSSL.java
@@ -0,0 +1,718 @@
+/*
+ * $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.commons.ssl.util.Hex;
+
+import javax.crypto.Cipher;
+import javax.crypto.CipherInputStream;
+import java.io.*;
+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;
+        if (key.length == keySize / 4) // Looks like key is in hex
+        {
+            key = Hex.decode(key);
+        }
+        if (iv.length == ivSize / 4) // Looks like IV is in hex
+        {
+            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-kerberos/blob/23c1fd12/3rdparty/not-yet-commons-ssl/src/main/java/org/apache/commons/ssl/PBETestCreate.java
----------------------------------------------------------------------
diff --git a/3rdparty/not-yet-commons-ssl/src/main/java/org/apache/commons/ssl/PBETestCreate.java b/3rdparty/not-yet-commons-ssl/src/main/java/org/apache/commons/ssl/PBETestCreate.java
new file mode 100644
index 0000000..f962e10
--- /dev/null
+++ b/3rdparty/not-yet-commons-ssl/src/main/java/org/apache/commons/ssl/PBETestCreate.java
@@ -0,0 +1,79 @@
+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-kerberos/blob/23c1fd12/3rdparty/not-yet-commons-ssl/src/main/java/org/apache/commons/ssl/PEMItem.java
----------------------------------------------------------------------
diff --git a/3rdparty/not-yet-commons-ssl/src/main/java/org/apache/commons/ssl/PEMItem.java b/3rdparty/not-yet-commons-ssl/src/main/java/org/apache/commons/ssl/PEMItem.java
new file mode 100644
index 0000000..e0a9684
--- /dev/null
+++ b/3rdparty/not-yet-commons-ssl/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.commons.ssl.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 final static 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-kerberos/blob/23c1fd12/3rdparty/not-yet-commons-ssl/src/main/java/org/apache/commons/ssl/PEMUtil.java
----------------------------------------------------------------------
diff --git a/3rdparty/not-yet-commons-ssl/src/main/java/org/apache/commons/ssl/PEMUtil.java b/3rdparty/not-yet-commons-ssl/src/main/java/org/apache/commons/ssl/PEMUtil.java
new file mode 100644
index 0000000..c2a7099
--- /dev/null
+++ b/3rdparty/not-yet-commons-ssl/src/main/java/org/apache/commons/ssl/PEMUtil.java
@@ -0,0 +1,250 @@
+/*
+ * $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 java.io.ByteArrayInputStream;
+import java.io.ByteArrayOutputStream;
+import java.io.IOException;
+import java.math.BigInteger;
+import java.security.interfaces.RSAPrivateCrtKey;
+import java.security.interfaces.RSAPublicKey;
+import java.security.interfaces.DSAPublicKey;
+import java.security.PublicKey;
+import java.util.*;
+
+/**
+ * @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 {
+    final static String LINE_SEPARATOR = System.getProperty("line.separator");
+
+    public static byte[] encode(Collection items) throws IOException {
+        final byte[] LINE_SEPARATOR_BYTES = 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(LINE_SEPARATOR_BYTES);
+
+            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(LINE_SEPARATOR_BYTES);
+                line = Util.streamToBytes(bin, 48);
+            }
+            if (line.length > 0) {
+                byte[] base64Line = Base64.encodeBase64(line);
+                out.write(base64Line);
+                out.write(LINE_SEPARATOR_BYTES);
+            }
+            out.write("-----END ".getBytes("UTF-8"));
+            out.write(item.pemType.getBytes("UTF-8"));
+            out.write("-----".getBytes("UTF-8"));
+            out.write(LINE_SEPARATOR_BYTES);
+        }
+        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");
+    }
+
+}