mirror of git://gcc.gnu.org/git/gcc.git
				
				
				
			
		
			
				
	
	
		
			364 lines
		
	
	
		
			11 KiB
		
	
	
	
		
			Java
		
	
	
	
			
		
		
	
	
			364 lines
		
	
	
		
			11 KiB
		
	
	
	
		
			Java
		
	
	
	
| /* PrivateCredentials.java -- private key/certificate pairs.
 | |
|    Copyright (C) 2006, 2007  Free Software Foundation, Inc.
 | |
| 
 | |
| This file is a part of GNU Classpath.
 | |
| 
 | |
| GNU Classpath is free software; you can redistribute it and/or modify
 | |
| it under the terms of the GNU General Public License as published by
 | |
| the Free Software Foundation; either version 2 of the License, or (at
 | |
| your option) any later version.
 | |
| 
 | |
| GNU Classpath is distributed in the hope that it will be useful, but
 | |
| WITHOUT ANY WARRANTY; without even the implied warranty of
 | |
| MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
 | |
| General Public License for more details.
 | |
| 
 | |
| You should have received a copy of the GNU General Public License
 | |
| along with GNU Classpath; if not, write to the Free Software
 | |
| Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301
 | |
| USA
 | |
| 
 | |
| Linking this library statically or dynamically with other modules is
 | |
| making a combined work based on this library.  Thus, the terms and
 | |
| conditions of the GNU General Public License cover the whole
 | |
| combination.
 | |
| 
 | |
| As a special exception, the copyright holders of this library give you
 | |
| permission to link this library with independent modules to produce an
 | |
| executable, regardless of the license terms of these independent
 | |
| modules, and to copy and distribute the resulting executable under
 | |
| terms of your choice, provided that you also meet, for each linked
 | |
| independent module, the terms and conditions of the license of that
 | |
| module.  An independent module is a module which is not derived from
 | |
| or based on this library.  If you modify this library, you may extend
 | |
| this exception to your version of the library, but you are not
 | |
| obligated to do so.  If you do not wish to do so, delete this
 | |
| exception statement from your version.  */
 | |
| 
 | |
| 
 | |
| package gnu.javax.net.ssl;
 | |
| 
 | |
| import gnu.java.lang.CPStringBuilder;
 | |
| 
 | |
| import java.io.EOFException;
 | |
| import java.io.InputStream;
 | |
| import java.io.IOException;
 | |
| 
 | |
| import java.math.BigInteger;
 | |
| 
 | |
| import java.security.InvalidKeyException;
 | |
| import java.security.KeyFactory;
 | |
| import java.security.NoSuchAlgorithmException;
 | |
| import java.security.PrivateKey;
 | |
| import java.security.Security;
 | |
| import java.security.cert.Certificate;
 | |
| import java.security.cert.CertificateException;
 | |
| import java.security.cert.CertificateFactory;
 | |
| import java.security.cert.X509Certificate;
 | |
| import java.security.spec.DSAPrivateKeySpec;
 | |
| import java.security.spec.InvalidKeySpecException;
 | |
| import java.security.spec.KeySpec;
 | |
| import java.security.spec.RSAPrivateCrtKeySpec;
 | |
| 
 | |
| import java.util.Collection;
 | |
| import java.util.HashMap;
 | |
| import java.util.LinkedList;
 | |
| import java.util.List;
 | |
| 
 | |
| import javax.net.ssl.ManagerFactoryParameters;
 | |
| import javax.security.auth.callback.Callback;
 | |
| import javax.security.auth.callback.CallbackHandler;
 | |
| import javax.security.auth.callback.PasswordCallback;
 | |
| import javax.security.auth.callback.UnsupportedCallbackException;
 | |
| 
 | |
| import gnu.javax.security.auth.callback.ConsoleCallbackHandler;
 | |
| import gnu.java.security.hash.HashFactory;
 | |
| import gnu.java.security.hash.IMessageDigest;
 | |
| import gnu.javax.crypto.mode.IMode;
 | |
| import gnu.javax.crypto.mode.ModeFactory;
 | |
| import gnu.javax.crypto.pad.WrongPaddingException;
 | |
| 
 | |
| import gnu.java.security.der.DER;
 | |
| import gnu.java.security.der.DERReader;
 | |
| import gnu.java.util.Base64;
 | |
| 
 | |
| /**
 | |
|  * An instance of a manager factory parameters for holding a single
 | |
|  * certificate/private key pair, encoded in PEM format.
 | |
|  */
 | |
| public class PrivateCredentials implements ManagerFactoryParameters
 | |
| {
 | |
| 
 | |
|   // Fields.
 | |
|   // -------------------------------------------------------------------------
 | |
| 
 | |
|   public static final String BEGIN_DSA = "-----BEGIN DSA PRIVATE KEY";
 | |
|   public static final String END_DSA   = "-----END DSA PRIVATE KEY";
 | |
|   public static final String BEGIN_RSA = "-----BEGIN RSA PRIVATE KEY";
 | |
|   public static final String END_RSA   = "-----END RSA PRIVATE KEY";
 | |
| 
 | |
|   private List<PrivateKey> privateKeys;
 | |
|   private List<X509Certificate[]> certChains;
 | |
| 
 | |
|   // Constructor.
 | |
|   // -------------------------------------------------------------------------
 | |
| 
 | |
|   public PrivateCredentials()
 | |
|   {
 | |
|     privateKeys = new LinkedList<PrivateKey>();
 | |
|     certChains = new LinkedList<X509Certificate[]>();
 | |
|   }
 | |
| 
 | |
|   // Instance methods.
 | |
|   // -------------------------------------------------------------------------
 | |
| 
 | |
|   public void add(InputStream certChain, InputStream privateKey)
 | |
|     throws CertificateException, InvalidKeyException, InvalidKeySpecException,
 | |
|            IOException, NoSuchAlgorithmException, WrongPaddingException
 | |
|   {
 | |
|     CertificateFactory cf = CertificateFactory.getInstance("X.509");
 | |
|     Collection<? extends Certificate> certs = cf.generateCertificates(certChain);
 | |
|     X509Certificate[] chain = (X509Certificate[]) certs.toArray(new X509Certificate[0]);
 | |
| 
 | |
|     String alg = null;
 | |
|     String line = readLine(privateKey);
 | |
|     String finalLine = null;
 | |
|     if (line.startsWith(BEGIN_DSA))
 | |
|       {
 | |
|         alg = "DSA";
 | |
|         finalLine = END_DSA;
 | |
|       }
 | |
|     else if (line.startsWith(BEGIN_RSA))
 | |
|       {
 | |
|         alg = "RSA";
 | |
|         finalLine = END_RSA;
 | |
|       }
 | |
|     else
 | |
|       throw new IOException("Unknown private key type.");
 | |
| 
 | |
|     boolean encrypted = false;
 | |
|     String cipher = null;
 | |
|     String salt = null;
 | |
|     CPStringBuilder base64 = new CPStringBuilder();
 | |
|     while (true)
 | |
|       {
 | |
|         line = readLine(privateKey);
 | |
|         if (line == null)
 | |
|           throw new EOFException("premature end-of-file");
 | |
|         else if (line.startsWith("Proc-Type: 4,ENCRYPTED"))
 | |
|           encrypted = true;
 | |
|         else if (line.startsWith("DEK-Info: "))
 | |
|           {
 | |
|             int i = line.indexOf(',');
 | |
|             if (i < 0)
 | |
|               cipher = line.substring(10).trim();
 | |
|             else
 | |
|               {
 | |
|                 cipher = line.substring(10, i).trim();
 | |
|                 salt = line.substring(i + 1).trim();
 | |
|               }
 | |
|           }
 | |
|         else if (line.startsWith(finalLine))
 | |
|           break;
 | |
|         else if (line.length() > 0)
 | |
|           {
 | |
|             base64.append(line);
 | |
|             base64.append(System.getProperty("line.separator"));
 | |
|           }
 | |
|       }
 | |
| 
 | |
|     byte[] enckey = Base64.decode(base64.toString());
 | |
|     if (encrypted)
 | |
|       {
 | |
|         enckey = decryptKey(enckey, cipher, toByteArray(salt));
 | |
|       }
 | |
| 
 | |
|     DERReader der = new DERReader(enckey);
 | |
|     if (der.read().getTag() != DER.SEQUENCE)
 | |
|       throw new IOException("malformed DER sequence");
 | |
|     der.read(); // version
 | |
| 
 | |
|     KeyFactory kf = KeyFactory.getInstance(alg);
 | |
|     KeySpec spec = null;
 | |
|     if (alg.equals("DSA"))
 | |
|       {
 | |
|         BigInteger p = (BigInteger) der.read().getValue();
 | |
|         BigInteger q = (BigInteger) der.read().getValue();
 | |
|         BigInteger g = (BigInteger) der.read().getValue();
 | |
|         der.read(); // y
 | |
|         BigInteger x = (BigInteger) der.read().getValue();
 | |
|         spec = new DSAPrivateKeySpec(x, p, q, g);
 | |
|       }
 | |
|     else
 | |
|       {
 | |
|         spec = new RSAPrivateCrtKeySpec(
 | |
|           (BigInteger) der.read().getValue(),  // modulus
 | |
|           (BigInteger) der.read().getValue(),  // pub exponent
 | |
|           (BigInteger) der.read().getValue(),  // priv expenent
 | |
|           (BigInteger) der.read().getValue(),  // prime p
 | |
|           (BigInteger) der.read().getValue(),  // prime q
 | |
|           (BigInteger) der.read().getValue(),  // d mod (p-1)
 | |
|           (BigInteger) der.read().getValue(),  // d mod (q-1)
 | |
|           (BigInteger) der.read().getValue()); // coefficient
 | |
|       }
 | |
| 
 | |
|     privateKeys.add(kf.generatePrivate(spec));
 | |
|     certChains.add(chain);
 | |
|   }
 | |
| 
 | |
|   public List<PrivateKey> getPrivateKeys()
 | |
|   {
 | |
|     if (isDestroyed())
 | |
|       {
 | |
|         throw new IllegalStateException("this object is destroyed");
 | |
|       }
 | |
|     return privateKeys;
 | |
|   }
 | |
| 
 | |
|   public List<X509Certificate[]> getCertChains()
 | |
|   {
 | |
|     return certChains;
 | |
|   }
 | |
| 
 | |
|   public void destroy()
 | |
|   {
 | |
|     privateKeys.clear();
 | |
|     privateKeys = null;
 | |
|   }
 | |
| 
 | |
|   public boolean isDestroyed()
 | |
|   {
 | |
|     return (privateKeys == null);
 | |
|   }
 | |
| 
 | |
|   // Own methods.
 | |
|   // -------------------------------------------------------------------------
 | |
| 
 | |
|   private String readLine(InputStream in) throws IOException
 | |
|   {
 | |
|     boolean eol_is_cr = System.getProperty("line.separator").equals("\r");
 | |
|     CPStringBuilder str = new CPStringBuilder();
 | |
|     while (true)
 | |
|       {
 | |
|         int i = in.read();
 | |
|         if (i == -1)
 | |
|           {
 | |
|             if (str.length() > 0)
 | |
|               break;
 | |
|             else
 | |
|               return null;
 | |
|           }
 | |
|         else if (i == '\r')
 | |
|           {
 | |
|             if (eol_is_cr)
 | |
|               break;
 | |
|           }
 | |
|         else if (i == '\n')
 | |
|           break;
 | |
|         else
 | |
|           str.append((char) i);
 | |
|       }
 | |
|     return str.toString();
 | |
|   }
 | |
| 
 | |
|   private byte[] decryptKey(byte[] ct, String cipher, byte[] salt)
 | |
|     throws IOException, InvalidKeyException, WrongPaddingException
 | |
|   {
 | |
|     byte[] pt = new byte[ct.length];
 | |
|     IMode mode = null;
 | |
|     if (cipher.equals("DES-EDE3-CBC"))
 | |
|       {
 | |
|         mode = ModeFactory.getInstance("CBC", "TripleDES", 8);
 | |
|         HashMap attr = new HashMap();
 | |
|         attr.put(IMode.KEY_MATERIAL, deriveKey(salt, 24));
 | |
|         attr.put(IMode.IV, salt);
 | |
|         attr.put(IMode.STATE, new Integer(IMode.DECRYPTION));
 | |
|         mode.init(attr);
 | |
|       }
 | |
|     else if (cipher.equals("DES-CBC"))
 | |
|       {
 | |
|         mode = ModeFactory.getInstance("CBC", "DES", 8);
 | |
|         HashMap attr = new HashMap();
 | |
|         attr.put(IMode.KEY_MATERIAL, deriveKey(salt, 8));
 | |
|         attr.put(IMode.IV, salt);
 | |
|         attr.put(IMode.STATE, new Integer(IMode.DECRYPTION));
 | |
|         mode.init(attr);
 | |
|       }
 | |
|     else
 | |
|       throw new IllegalArgumentException("unknown cipher: " + cipher);
 | |
| 
 | |
|     for (int i = 0; i < ct.length; i += 8)
 | |
|       mode.update(ct, i, pt, i);
 | |
| 
 | |
|     int pad = pt[pt.length-1];
 | |
|     if (pad < 1 || pad > 8)
 | |
|       throw new WrongPaddingException();
 | |
|     for (int i = pt.length - pad; i < pt.length; i++)
 | |
|       {
 | |
|         if (pt[i] != pad)
 | |
|           throw new WrongPaddingException();
 | |
|       }
 | |
| 
 | |
|     byte[] result = new byte[pt.length - pad];
 | |
|     System.arraycopy(pt, 0, result, 0, result.length);
 | |
|     return result;
 | |
|   }
 | |
| 
 | |
|   private byte[] deriveKey(byte[] salt, int keylen)
 | |
|     throws IOException
 | |
|   {
 | |
|     CallbackHandler passwordHandler = new ConsoleCallbackHandler();
 | |
|     try
 | |
|       {
 | |
|         Class c = Class.forName(Security.getProperty("jessie.password.handler"));
 | |
|         passwordHandler = (CallbackHandler) c.newInstance();
 | |
|       }
 | |
|     catch (Exception x) { }
 | |
| 
 | |
|     PasswordCallback passwdCallback =
 | |
|       new PasswordCallback("Enter PEM passphrase: ", false);
 | |
|     try
 | |
|       {
 | |
|         passwordHandler.handle(new Callback[] { passwdCallback });
 | |
|       }
 | |
|     catch (UnsupportedCallbackException uce)
 | |
|       {
 | |
|         throw new IOException("specified handler cannot handle passwords");
 | |
|       }
 | |
|     char[] passwd = passwdCallback.getPassword();
 | |
| 
 | |
|     IMessageDigest md5 = HashFactory.getInstance("MD5");
 | |
|     byte[] key = new byte[keylen];
 | |
|     int count = 0;
 | |
|     while (count < keylen)
 | |
|       {
 | |
|         for (int i = 0; i < passwd.length; i++)
 | |
|           md5.update((byte) passwd[i]);
 | |
|         md5.update(salt, 0, salt.length);
 | |
|         byte[] digest = md5.digest();
 | |
|         int len = Math.min(digest.length, keylen - count);
 | |
|         System.arraycopy(digest, 0, key, count, len);
 | |
|         count += len;
 | |
|         if (count >= keylen)
 | |
|           break;
 | |
|         md5.reset();
 | |
|         md5.update(digest, 0, digest.length);
 | |
|       }
 | |
|     passwdCallback.clearPassword();
 | |
|     return key;
 | |
|   }
 | |
| 
 | |
|   private byte[] toByteArray(String hex)
 | |
|   {
 | |
|     hex = hex.toLowerCase();
 | |
|     byte[] buf = new byte[hex.length() / 2];
 | |
|     int j = 0;
 | |
|     for (int i = 0; i < buf.length; i++)
 | |
|       {
 | |
|         buf[i] = (byte) ((Character.digit(hex.charAt(j++), 16) << 4) |
 | |
|                           Character.digit(hex.charAt(j++), 16));
 | |
|       }
 | |
|     return buf;
 | |
|   }
 | |
| }
 |