summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorCasey Marshall <csm@gnu.org>2006-07-12 19:11:02 +0000
committerCasey Marshall <csm@gnu.org>2006-07-12 19:11:02 +0000
commit72b81548e9dbe0b788102063f36c51155fa542f3 (patch)
tree875df06881514974087a0f3accf30f14ac270087
parent7b7589d526048282a55c5765b6fac08920c290b9 (diff)
downloadclasspath-72b81548e9dbe0b788102063f36c51155fa542f3.tar.gz
2006-07-12 Casey Marshall <csm@gnu.org>
* gnu/javax/net/ssl/provider/AbstractHandshake.java (engine, inParams, outParams, tasks, serverRandom, clientRandom, compression): new fields. (<init>): take an SSLEngineImpl parameter; init `tasks.' (handleInput): return NEED_TASK if we have tasks. (getInputParams, getOutputParams): implement here; mark final. (getTask): new method. (checkKeyExchange): new method. (reallocateBuffer): use `compact.' (diffieHellmanPhase1, diffieHellmanPhase2): removed. (DHPhase, CertVerifier): new classes. (generateMasterSecret): add asserts. (setupSecurityParameters): new method. * gnu/javax/net/ssl/provider/Certificate.java (certificates): fix reading multiple certificates. * gnu/javax/net/ssl/provider/ClientCertificateTypeList.java: implement Iterable<ClientCertificateType>. (iterator): new method. * gnu/javax/net/ssl/provider/ClientDiffieHellmanPublic.java: make public; implement Builder. (<init>): make public. (<init>): new constructor. (wrap): new method. (buffer): new method. (publicValue): make public; use `rewind.' (setPublicValue): use `Util.trim;' use `rewind.' (length): return proper length. * gnu/javax/net/ssl/provider/ClientHandshake.java: new file. * gnu/javax/net/ssl/provider/ClientKeyExchange.java: remove unused imports; make public, non-final. (buffer): make protected, non-final. (suite, version): make protected. (<init>): make public. (length): return 0 for NONE key exchange algorithm. * gnu/javax/net/ssl/provider/ClientKeyExchangeBuilder.java: new file. * gnu/javax/net/ssl/provider/DelegatedTask.java: new file. * gnu/javax/net/ssl/provider/DiffieHellman.java (getParams): use AccessController instead of Util. * gnu/javax/net/ssl/provider/EncryptedPreMasterSecret.java: make public; implement Builder. (<init>): make public. (<init>): new constructor. (buffer): new method. (encryptedSecret): make public; fix SSLv3 handling. (setEncryptedSecret): make public; rewind the buffer after putting the value. (length): fix length computation. * gnu/javax/net/ssl/provider/ExchangeKeys.java: make public. (buffer): make protected, non-final. (<init>): made public; don't check null. * gnu/javax/net/ssl/provider/Jessie.java (<init>): add "SSL" alias. * gnu/javax/net/ssl/provider/ServerHandshake.java: clean up unused imports. (engine, compression, clientRandom, serverRandom, clientSessionID, inParams, outParams, keyAgreement): moved to superclass. (genDH, certVerifier, certLoader, keyExchangeTask): new fields. (<init>): pass engine to superclass constructor. (implHandleInput): throw `AlertException' when it makes sense; run long-running tasks as delegated tasks; return NEED_TASK if we scheduled a delegated task. (implHandleOutput): generate keys for continued sessions; run long-running tasks as delegated tasks; return NEED_TASK if we scheduled a delegated task. (status): also return NEED_TASK as appropriate. (getInputParams, getOutputParams): removed. (checkKeyExchange): new method. (genDiffieHellman): removed. (signParams): throw exceptions. (CertLoader, GenDH, RSAKeyExchange): new classes. * gnu/javax/net/ssl/provider/SSLContextImpl.java (engineGetServerSocketFactory): implement. (engineGetSocketFactory): implement. (defaultRandom): use AccessController instead of Util. * gnu/javax/net/ssl/provider/SSLEngineImpl.java (<init>): use `defaultSuites.' (defaultSuites): new method. (startHandshake): start client handshake in client mode. (getDelegatedTask): implement. (unwrap, wrap): send alert if we catch an AlertException during handshaking. * gnu/javax/net/ssl/provider/SSLServerSocketFactoryImpl.java: new file. * gnu/javax/net/ssl/provider/SSLServerSocketImpl.java: new file. * gnu/javax/net/ssl/provider/SSLSocketFactoryImpl.java: new file. * gnu/javax/net/ssl/provider/SSLSocketImpl.java: new file. * gnu/javax/net/ssl/provider/X509TrustManagerFactory.java (sep, JSSE_CERTS, CA_CERTS, engineInit): use AccessController, not Util. (checkTrusted): don't require revocation checking. * java/util/Collections.java (CheckedMap.entrySet): casting hack. * java/util/concurrent/CopyOnWriteArrayList.java: new file.
-rw-r--r--ChangeLog-ssl-nio95
-rw-r--r--gnu/javax/net/ssl/provider/AbstractHandshake.java277
-rw-r--r--gnu/javax/net/ssl/provider/Certificate.java6
-rw-r--r--gnu/javax/net/ssl/provider/ClientCertificateTypeList.java7
-rw-r--r--gnu/javax/net/ssl/provider/ClientDiffieHellmanPublic.java57
-rw-r--r--gnu/javax/net/ssl/provider/ClientHandshake.java860
-rw-r--r--gnu/javax/net/ssl/provider/ClientKeyExchange.java32
-rw-r--r--gnu/javax/net/ssl/provider/ClientKeyExchangeBuilder.java75
-rw-r--r--gnu/javax/net/ssl/provider/DelegatedTask.java93
-rw-r--r--gnu/javax/net/ssl/provider/DiffieHellman.java6
-rw-r--r--gnu/javax/net/ssl/provider/EncryptedPreMasterSecret.java51
-rw-r--r--gnu/javax/net/ssl/provider/ExchangeKeys.java7
-rw-r--r--gnu/javax/net/ssl/provider/Jessie.java2
-rw-r--r--gnu/javax/net/ssl/provider/SSLContextImpl.java22
-rw-r--r--gnu/javax/net/ssl/provider/SSLEngineImpl.java67
-rw-r--r--gnu/javax/net/ssl/provider/SSLServerSocketFactoryImpl.java108
-rw-r--r--gnu/javax/net/ssl/provider/SSLServerSocketImpl.java199
-rw-r--r--gnu/javax/net/ssl/provider/SSLSocketFactoryImpl.java137
-rw-r--r--gnu/javax/net/ssl/provider/SSLSocketImpl.java814
-rw-r--r--gnu/javax/net/ssl/provider/ServerHandshake.java588
-rw-r--r--gnu/javax/net/ssl/provider/X509TrustManagerFactory.java31
-rw-r--r--java/util/Collections.java2
-rw-r--r--java/util/concurrent/CopyOnWriteArrayList.java438
23 files changed, 3507 insertions, 467 deletions
diff --git a/ChangeLog-ssl-nio b/ChangeLog-ssl-nio
index bd3a86232..8e68d46b2 100644
--- a/ChangeLog-ssl-nio
+++ b/ChangeLog-ssl-nio
@@ -1,3 +1,98 @@
+2006-07-12 Casey Marshall <csm@gnu.org>
+
+ * gnu/javax/net/ssl/provider/AbstractHandshake.java
+ (engine, inParams, outParams, tasks, serverRandom, clientRandom,
+ compression): new fields.
+ (<init>): take an SSLEngineImpl parameter; init `tasks.'
+ (handleInput): return NEED_TASK if we have tasks.
+ (getInputParams, getOutputParams): implement here; mark final.
+ (getTask): new method.
+ (checkKeyExchange): new method.
+ (reallocateBuffer): use `compact.'
+ (diffieHellmanPhase1, diffieHellmanPhase2): removed.
+ (DHPhase, CertVerifier): new classes.
+ (generateMasterSecret): add asserts.
+ (setupSecurityParameters): new method.
+ * gnu/javax/net/ssl/provider/Certificate.java (certificates): fix
+ reading multiple certificates.
+ * gnu/javax/net/ssl/provider/ClientCertificateTypeList.java:
+ implement Iterable<ClientCertificateType>.
+ (iterator): new method.
+ * gnu/javax/net/ssl/provider/ClientDiffieHellmanPublic.java: make
+ public; implement Builder.
+ (<init>): make public.
+ (<init>): new constructor.
+ (wrap): new method.
+ (buffer): new method.
+ (publicValue): make public; use `rewind.'
+ (setPublicValue): use `Util.trim;' use `rewind.'
+ (length): return proper length.
+ * gnu/javax/net/ssl/provider/ClientHandshake.java: new file.
+ * gnu/javax/net/ssl/provider/ClientKeyExchange.java: remove unused
+ imports; make public, non-final.
+ (buffer): make protected, non-final.
+ (suite, version): make protected.
+ (<init>): make public.
+ (length): return 0 for NONE key exchange algorithm.
+ * gnu/javax/net/ssl/provider/ClientKeyExchangeBuilder.java: new
+ file.
+ * gnu/javax/net/ssl/provider/DelegatedTask.java: new file.
+ * gnu/javax/net/ssl/provider/DiffieHellman.java (getParams): use
+ AccessController instead of Util.
+ * gnu/javax/net/ssl/provider/EncryptedPreMasterSecret.java: make
+ public; implement Builder.
+ (<init>): make public.
+ (<init>): new constructor.
+ (buffer): new method.
+ (encryptedSecret): make public; fix SSLv3 handling.
+ (setEncryptedSecret): make public; rewind the buffer after putting
+ the value.
+ (length): fix length computation.
+ * gnu/javax/net/ssl/provider/ExchangeKeys.java: make public.
+ (buffer): make protected, non-final.
+ (<init>): made public; don't check null.
+ * gnu/javax/net/ssl/provider/Jessie.java (<init>): add "SSL" alias.
+ * gnu/javax/net/ssl/provider/ServerHandshake.java: clean up unused
+ imports.
+ (engine, compression, clientRandom, serverRandom, clientSessionID,
+ inParams, outParams, keyAgreement): moved to superclass.
+ (genDH, certVerifier, certLoader, keyExchangeTask): new fields.
+ (<init>): pass engine to superclass constructor.
+ (implHandleInput): throw `AlertException' when it makes sense; run
+ long-running tasks as delegated tasks; return NEED_TASK if we
+ scheduled a delegated task.
+ (implHandleOutput): generate keys for continued sessions; run
+ long-running tasks as delegated tasks; return NEED_TASK if we
+ scheduled a delegated task.
+ (status): also return NEED_TASK as appropriate.
+ (getInputParams, getOutputParams): removed.
+ (checkKeyExchange): new method.
+ (genDiffieHellman): removed.
+ (signParams): throw exceptions.
+ (CertLoader, GenDH, RSAKeyExchange): new classes.
+ * gnu/javax/net/ssl/provider/SSLContextImpl.java
+ (engineGetServerSocketFactory): implement.
+ (engineGetSocketFactory): implement.
+ (defaultRandom): use AccessController instead of Util.
+ * gnu/javax/net/ssl/provider/SSLEngineImpl.java (<init>): use
+ `defaultSuites.'
+ (defaultSuites): new method.
+ (startHandshake): start client handshake in client mode.
+ (getDelegatedTask): implement.
+ (unwrap, wrap): send alert if we catch an AlertException during
+ handshaking.
+ * gnu/javax/net/ssl/provider/SSLServerSocketFactoryImpl.java: new
+ file.
+ * gnu/javax/net/ssl/provider/SSLServerSocketImpl.java: new file.
+ * gnu/javax/net/ssl/provider/SSLSocketFactoryImpl.java: new file.
+ * gnu/javax/net/ssl/provider/SSLSocketImpl.java: new file.
+ * gnu/javax/net/ssl/provider/X509TrustManagerFactory.java
+ (sep, JSSE_CERTS, CA_CERTS, engineInit): use AccessController, not
+ Util.
+ (checkTrusted): don't require revocation checking.
+ * java/util/Collections.java (CheckedMap.entrySet): casting hack.
+ * java/util/concurrent/CopyOnWriteArrayList.java: new file.
+
2006-07-09 Casey Marshall <csm@gnu.org>
* gnu/java/io/ByteBufferOutputStream.java (write): new method.
diff --git a/gnu/javax/net/ssl/provider/AbstractHandshake.java b/gnu/javax/net/ssl/provider/AbstractHandshake.java
index 6166bbde2..f930f2301 100644
--- a/gnu/javax/net/ssl/provider/AbstractHandshake.java
+++ b/gnu/javax/net/ssl/provider/AbstractHandshake.java
@@ -39,28 +39,46 @@ exception statement from your version. */
package gnu.javax.net.ssl.provider;
import gnu.classpath.ByteArray;
-import gnu.classpath.Configuration;
import gnu.classpath.debug.Component;
import gnu.classpath.debug.SystemLogger;
+import gnu.java.security.action.GetSecurityPropertyAction;
import gnu.java.security.prng.IRandom;
import gnu.java.security.prng.LimitReachedException;
+import gnu.javax.security.auth.callback.CertificateCallback;
+import gnu.javax.security.auth.callback.DefaultCallbackHandler;
import java.nio.ByteBuffer;
+import java.security.AccessController;
import java.security.DigestException;
+import java.security.InvalidAlgorithmParameterException;
import java.security.InvalidKeyException;
import java.security.MessageDigest;
import java.security.NoSuchAlgorithmException;
+import java.security.PrivilegedExceptionAction;
import java.security.SecureRandom;
+import java.security.cert.CertificateException;
+import java.security.cert.X509Certificate;
import java.util.Arrays;
import java.util.HashMap;
-import java.util.logging.Logger;
+import java.util.LinkedList;
+import java.util.zip.Deflater;
+import java.util.zip.Inflater;
+import javax.crypto.Cipher;
import javax.crypto.KeyAgreement;
+import javax.crypto.Mac;
+import javax.crypto.NoSuchPaddingException;
import javax.crypto.interfaces.DHPrivateKey;
import javax.crypto.interfaces.DHPublicKey;
+import javax.crypto.spec.IvParameterSpec;
+import javax.crypto.spec.SecretKeySpec;
import javax.net.ssl.SSLEngineResult;
import javax.net.ssl.SSLException;
+import javax.net.ssl.X509TrustManager;
import javax.net.ssl.SSLEngineResult.HandshakeStatus;
+import javax.security.auth.callback.Callback;
+import javax.security.auth.callback.CallbackHandler;
+import javax.security.auth.callback.ConfirmationCallback;
/**
* The base interface for handshake implementations. Concrete
@@ -149,13 +167,23 @@ public abstract class AbstractHandshake
protected MessageDigest sha;
protected MessageDigest md5;
+ protected final SSLEngineImpl engine;
protected KeyAgreement keyAgreement;
protected byte[] preMasterSecret;
-
- protected AbstractHandshake() throws NoSuchAlgorithmException
+ protected InputSecurityParameters inParams;
+ protected OutputSecurityParameters outParams;
+ protected LinkedList<DelegatedTask> tasks;
+ protected Random serverRandom;
+ protected Random clientRandom;
+ protected CompressionMethod compression;
+
+ protected AbstractHandshake(SSLEngineImpl engine)
+ throws NoSuchAlgorithmException
{
+ this.engine = engine;
sha = MessageDigest.getInstance("SHA-1");
md5 = MessageDigest.getInstance("MD5");
+ tasks = new LinkedList<DelegatedTask>();
}
/**
@@ -171,6 +199,9 @@ public abstract class AbstractHandshake
public final HandshakeStatus handleInput (ByteBuffer fragment)
throws SSLException
{
+ if (!tasks.isEmpty())
+ return HandshakeStatus.NEED_TASK;
+
HandshakeStatus status = status();
if (status != HandshakeStatus.NEED_UNWRAP)
return status;
@@ -227,9 +258,12 @@ public abstract class AbstractHandshake
* appropriately.
* @return An {@link SSLEngineResult} describing the result.
*/
- public final SSLEngineResult.HandshakeStatus handleOutput (ByteBuffer fragment)
+ public final HandshakeStatus handleOutput (ByteBuffer fragment)
throws SSLException
{
+ if (!tasks.isEmpty())
+ return HandshakeStatus.NEED_TASK;
+
int orig = fragment.position();
SSLEngineResult.HandshakeStatus status = implHandleOutput(fragment);
if (doHash())
@@ -266,7 +300,11 @@ public abstract class AbstractHandshake
* @return The input parameters for the newly established session.
* @throws SSLException If the handshake is not complete.
*/
- abstract InputSecurityParameters getInputParams() throws SSLException;
+ final InputSecurityParameters getInputParams() throws SSLException
+ {
+ checkKeyExchange();
+ return inParams;
+ }
/**
* Return a new instance of output security parameters, initialized with
@@ -276,7 +314,23 @@ public abstract class AbstractHandshake
* @return The output parameters for the newly established session.
* @throws SSLException If the handshake is not complete.
*/
- abstract OutputSecurityParameters getOutputParams() throws SSLException;
+ final OutputSecurityParameters getOutputParams() throws SSLException
+ {
+ checkKeyExchange();
+ return outParams;
+ }
+
+ /**
+ * Fetch a delegated task waiting to run, if any.
+ *
+ * @return The task.
+ */
+ final Runnable getTask()
+ {
+ if (tasks.isEmpty())
+ return null;
+ return tasks.removeFirst();
+ }
/**
* Used by the skeletal code to query the current status of the handshake.
@@ -286,7 +340,20 @@ public abstract class AbstractHandshake
*
* @return The current handshake status.
*/
- abstract SSLEngineResult.HandshakeStatus status();
+ abstract HandshakeStatus status();
+
+ /**
+ * Check if the key exchange completed successfully, throwing an exception
+ * if not.
+ *
+ * <p>Note that we assume that the caller of our SSLEngine is correct, and
+ * that they did run the delegated tasks that encapsulate the key exchange.
+ * What we are primarily checking, therefore, is that no error occurred in the
+ * key exchange operation itself.
+ *
+ * @throws SSLException If the key exchange did not complete successfully.
+ */
+ abstract void checkKeyExchange() throws SSLException;
/**
* Handle an SSLv2 client hello. This is only used by SSL servers.
@@ -376,11 +443,8 @@ public abstract class AbstractHandshake
// down.
if (handshakeOffset > 0)
{
- ByteBuffer tmp = handshakeBuffer.duplicate();
- tmp.flip();
- tmp.position(handshakeOffset);
- handshakeBuffer.position(0);
- handshakeBuffer.put(tmp);
+ handshakeBuffer.flip().position(handshakeOffset);
+ handshakeBuffer.compact();
handshakeOffset = 0;
}
return;
@@ -664,29 +728,116 @@ Certificate.signature.sha_hash
}
}
- protected void diffieHellmanPhase1(DHPublicKey dhKey) throws SSLException
+ protected class DHPhase extends DelegatedTask
{
- try
- {
- keyAgreement.doPhase(dhKey, false);
- }
- catch (InvalidKeyException ike)
- {
- throw new SSLException(ike);
- }
+ private final DHPublicKey key;
+
+ protected DHPhase(DHPublicKey key)
+ {
+ this.key = key;
+ }
+
+ protected void implRun() throws InvalidKeyException, SSLException
+ {
+ keyAgreement.doPhase(key, true);
+ preMasterSecret = keyAgreement.generateSecret();
+ generateMasterSecret(clientRandom, serverRandom, engine.session());
+ byte[][] keys = generateKeys(clientRandom, serverRandom, engine.session());
+ setupSecurityParameters(keys, engine.getUseClientMode(), engine, compression);
+ }
}
- protected void diffieHellmanPhase2(DHPublicKey dhKey) throws SSLException
+ protected class CertVerifier extends DelegatedTask
{
- try
- {
- keyAgreement.doPhase(dhKey, true);
- preMasterSecret = keyAgreement.generateSecret();
- }
- catch (InvalidKeyException ike)
- {
- throw new SSLException(ike);
- }
+ private final boolean clientSide;
+ private final X509Certificate[] chain;
+ private boolean verified;
+
+ protected CertVerifier(boolean clientSide, X509Certificate[] chain)
+ {
+ this.clientSide = clientSide;
+ this.chain = chain;
+ }
+
+ boolean verified()
+ {
+ return verified;
+ }
+
+ protected void implRun()
+ {
+ X509TrustManager tm = engine.contextImpl.trustManager;
+ if (clientSide)
+ {
+ try
+ {
+ tm.checkServerTrusted(chain, null);
+ verified = true;
+ }
+ catch (CertificateException ce)
+ {
+ if (Debug.DEBUG)
+ logger.log(Component.SSL_DELEGATED_TASK, "cert verify", ce);
+ // For client connections, ask the user if the certificate is OK.
+ CallbackHandler verify = new DefaultCallbackHandler();
+ GetSecurityPropertyAction gspa
+ = new GetSecurityPropertyAction("jessie.certificate.handler");
+ String clazz = AccessController.doPrivileged(gspa);
+ try
+ {
+ ClassLoader cl =
+ AccessController.doPrivileged(new PrivilegedExceptionAction<ClassLoader>()
+ {
+ public ClassLoader run() throws Exception
+ {
+ return ClassLoader.getSystemClassLoader();
+ }
+ });
+ verify = (CallbackHandler) cl.loadClass(clazz).newInstance();
+ }
+ catch (Exception x)
+ {
+ // Ignore.
+ if (Debug.DEBUG)
+ logger.log(Component.SSL_DELEGATED_TASK,
+ "callback handler loading", x);
+ }
+ // XXX Internationalize
+ CertificateCallback confirm =
+ new CertificateCallback(chain[0],
+ "The server's certificate could not be verified. There is no proof " +
+ "that this server is who it claims to be, or that their certificate " +
+ "is valid. Do you wish to continue connecting? ");
+
+ try
+ {
+ verify.handle(new Callback[] { confirm });
+ verified = confirm.getSelectedIndex() == ConfirmationCallback.YES;
+ }
+ catch (Exception x)
+ {
+ if (Debug.DEBUG)
+ logger.log(Component.SSL_DELEGATED_TASK,
+ "callback handler exception", x);
+ verified = false;
+ }
+ }
+ }
+ else
+ {
+ try
+ {
+ tm.checkClientTrusted(chain, null);
+ }
+ catch (CertificateException ce)
+ {
+ verified = false;
+ }
+ }
+
+ if (verified)
+ engine.session().setPeerVerified(true);
+ }
}
protected void generateMasterSecret(Random clientRandom,
@@ -694,6 +845,10 @@ Certificate.signature.sha_hash
SessionImpl session)
throws SSLException
{
+ assert(clientRandom != null);
+ assert(serverRandom != null);
+ assert(session != null);
+
if (Debug.DEBUG_KEY_EXCHANGE)
logger.logv(Component.SSL_KEY_EXCHANGE, "preMasterSecret:\n{0}",
new ByteArray(preMasterSecret));
@@ -771,4 +926,62 @@ Certificate.signature.sha_hash
for (int i = 0; i < preMasterSecret.length; i++)
preMasterSecret[i] = 0;
}
+
+ protected void setupSecurityParameters(byte[][] keys, boolean isClient,
+ SSLEngineImpl engine,
+ CompressionMethod compression)
+ throws SSLException
+ {
+ assert(keys.length == 6);
+ assert(engine != null);
+ assert(compression != null);
+
+ try
+ {
+ CipherSuite s = engine.session().suite;
+ Cipher inCipher = s.cipher();
+ Mac inMac = s.mac(engine.session().version);
+ Inflater inflater = (compression == CompressionMethod.ZLIB
+ ? new Inflater() : null);
+ inCipher.init(Cipher.DECRYPT_MODE,
+ new SecretKeySpec(keys[isClient ? 3 : 2],
+ s.cipherAlgorithm().toString()),
+ new IvParameterSpec(keys[isClient ? 5 : 4]));
+ inMac.init(new SecretKeySpec(keys[isClient ? 1 : 0],
+ inMac.getAlgorithm()));
+ inParams = new InputSecurityParameters(inCipher, inMac,
+ inflater,
+ engine.session(), s);
+
+ Cipher outCipher = s.cipher();
+ Mac outMac = s.mac(engine.session().version);
+ Deflater deflater = (compression == CompressionMethod.ZLIB
+ ? new Deflater() : null);
+ outCipher.init(Cipher.ENCRYPT_MODE,
+ new SecretKeySpec(keys[isClient ? 2 : 3],
+ s.cipherAlgorithm().toString()),
+ new IvParameterSpec(keys[isClient ? 4 : 5]));
+ outMac.init(new SecretKeySpec(keys[isClient ? 0 : 1],
+ outMac.getAlgorithm()));
+ outParams = new OutputSecurityParameters(outCipher, outMac,
+ deflater,
+ engine.session(), s);
+ }
+ catch (InvalidAlgorithmParameterException iape)
+ {
+ throw new SSLException(iape);
+ }
+ catch (InvalidKeyException ike)
+ {
+ throw new SSLException(ike);
+ }
+ catch (NoSuchAlgorithmException nsae)
+ {
+ throw new SSLException(nsae);
+ }
+ catch (NoSuchPaddingException nspe)
+ {
+ throw new SSLException(nspe);
+ }
+ }
} \ No newline at end of file
diff --git a/gnu/javax/net/ssl/provider/Certificate.java b/gnu/javax/net/ssl/provider/Certificate.java
index 81f5b60ca..8ff91e557 100644
--- a/gnu/javax/net/ssl/provider/Certificate.java
+++ b/gnu/javax/net/ssl/provider/Certificate.java
@@ -109,11 +109,11 @@ public class Certificate implements Handshake.Body
int length2 = (((b.get () & 0xFF) << 16)
| (b.getShort () & 0xFFFF));
byte[] buf = new byte[length2];
- buffer.position(i+3);
- buffer.get (buf);
+ b.position(i+3);
+ b.get (buf);
list.add(factory.generateCertificate (new ByteArrayInputStream (buf)));
i += length2 + 3;
- buffer.position(i);
+ b.position(i);
}
return list;
}
diff --git a/gnu/javax/net/ssl/provider/ClientCertificateTypeList.java b/gnu/javax/net/ssl/provider/ClientCertificateTypeList.java
index eedf34342..1a1886b88 100644
--- a/gnu/javax/net/ssl/provider/ClientCertificateTypeList.java
+++ b/gnu/javax/net/ssl/provider/ClientCertificateTypeList.java
@@ -49,7 +49,7 @@ import java.util.ConcurrentModificationException;
import java.util.ListIterator;
import java.util.NoSuchElementException;
-public class ClientCertificateTypeList
+public class ClientCertificateTypeList implements Iterable<ClientCertificateType>
{
private final ByteBuffer buffer;
private int modCount;
@@ -74,6 +74,11 @@ public class ClientCertificateTypeList
return CertificateRequest.ClientCertificateType.forValue
(buffer.get (index + 1) & 0xFF);
}
+
+ public java.util.Iterator<ClientCertificateType> iterator()
+ {
+ return new Iterator();
+ }
public void put (final int index, final CertificateRequest.ClientCertificateType type)
{
diff --git a/gnu/javax/net/ssl/provider/ClientDiffieHellmanPublic.java b/gnu/javax/net/ssl/provider/ClientDiffieHellmanPublic.java
index cccf7f1d6..8af8b850b 100644
--- a/gnu/javax/net/ssl/provider/ClientDiffieHellmanPublic.java
+++ b/gnu/javax/net/ssl/provider/ClientDiffieHellmanPublic.java
@@ -56,40 +56,55 @@ struct {
} dh_public;
} ClientDiffieHellmanPublic;</pre>
*/
-class ClientDiffieHellmanPublic extends ExchangeKeys
+public class ClientDiffieHellmanPublic extends ExchangeKeys implements Builder
{
- ClientDiffieHellmanPublic (final ByteBuffer buffer)
+ public ClientDiffieHellmanPublic(final ByteBuffer buffer)
{
- super (buffer);
+ super(buffer);
+ }
+
+ public ClientDiffieHellmanPublic(final BigInteger Yc)
+ {
+ super(wrap(Yc));
+ }
+
+ private static ByteBuffer wrap(BigInteger Yc)
+ {
+ byte[] b = Util.trim(Yc);
+ ByteBuffer ret = ByteBuffer.allocate(b.length + 2);
+ ret.putShort((short) b.length);
+ ret.put(b);
+ return (ByteBuffer) ret.rewind();
}
- BigInteger publicValue ()
+ public ByteBuffer buffer()
+ {
+ return (ByteBuffer) buffer.duplicate().rewind().limit(length());
+ }
+
+ public BigInteger publicValue()
{
- int len = length ();
+ int len = length() - 2;
byte[] b = new byte[len];
- buffer.position (2);
- buffer.get (b);
- return new BigInteger (1, b);
+ buffer.position(2);
+ buffer.get(b);
+ buffer.rewind();
+ return new BigInteger(1, b);
}
- void setPublicValue (final BigInteger y)
+ public void setPublicValue(final BigInteger Yc)
{
- byte[] buf = y.toByteArray ();
- int length = buf.length;
- int offset = 0;
- if (buf[0] == 0)
- {
- length--;
- offset++;
- }
- buffer.putShort (0, (short) length);
- buffer.position (2);
- buffer.put (buf, offset, length);
+ byte[] buf = Util.trim(Yc);
+ if (buffer.capacity() < buf.length + 2)
+ buffer = ByteBuffer.allocate(buf.length + 2);
+ buffer.putShort((short) buf.length);
+ buffer.put(buf);
+ buffer.rewind();
}
public int length ()
{
- return buffer.getShort (0) & 0xFFFF;
+ return (buffer.getShort(0) & 0xFFFF) + 2;
}
public String toString ()
diff --git a/gnu/javax/net/ssl/provider/ClientHandshake.java b/gnu/javax/net/ssl/provider/ClientHandshake.java
new file mode 100644
index 000000000..2b0ebf407
--- /dev/null
+++ b/gnu/javax/net/ssl/provider/ClientHandshake.java
@@ -0,0 +1,860 @@
+/* ClientHandshake.java --
+ Copyright (C) 2006 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.provider;
+
+import static gnu.javax.net.ssl.provider.ClientHandshake.State.*;
+
+import gnu.classpath.debug.Component;
+import gnu.java.security.action.GetSecurityPropertyAction;
+import gnu.javax.crypto.key.dh.GnuDHPublicKey;
+import gnu.javax.net.ssl.AbstractSessionContext;
+import gnu.javax.net.ssl.Session;
+import gnu.javax.net.ssl.provider.Alert.Description;
+import gnu.javax.net.ssl.provider.Alert.Level;
+import gnu.javax.net.ssl.provider.CertificateRequest.ClientCertificateType;
+
+import java.nio.ByteBuffer;
+import java.security.AccessController;
+import java.security.InvalidAlgorithmParameterException;
+import java.security.InvalidKeyException;
+import java.security.KeyPair;
+import java.security.KeyPairGenerator;
+import java.security.MessageDigest;
+import java.security.NoSuchAlgorithmException;
+import java.security.PrivateKey;
+import java.security.SignatureException;
+import java.security.cert.CertificateException;
+import java.security.cert.X509Certificate;
+import java.util.Arrays;
+import java.util.LinkedList;
+import java.util.List;
+
+import javax.crypto.BadPaddingException;
+import javax.crypto.Cipher;
+import javax.crypto.IllegalBlockSizeException;
+import javax.crypto.NoSuchPaddingException;
+import javax.crypto.interfaces.DHPrivateKey;
+import javax.crypto.interfaces.DHPublicKey;
+import javax.crypto.spec.DHParameterSpec;
+import javax.net.ssl.SSLException;
+import javax.net.ssl.SSLPeerUnverifiedException;
+import javax.net.ssl.X509ExtendedKeyManager;
+import javax.net.ssl.SSLEngineResult.HandshakeStatus;
+import javax.security.auth.x500.X500Principal;
+
+/**
+ * @author Casey Marshall (csm@gnu.org)
+ */
+public class ClientHandshake extends AbstractHandshake
+{
+ static enum State
+ {
+ WRITE_CLIENT_HELLO (false, true),
+ READ_SERVER_HELLO (true, false),
+ READ_CERTIFICATE (true, false),
+ READ_SERVER_KEY_EXCHANGE (true, false),
+ READ_CERTIFICATE_REQUEST (true, false),
+ READ_SERVER_HELLO_DONE (true, false),
+ WRITE_CERTIFICATE (false, true),
+ WRITE_CLIENT_KEY_EXCHANGE (false, true),
+ WRITE_CERTIFICATE_VERIFY (false, true),
+ WRITE_FINISHED (false, true),
+ READ_FINISHED (true, false),
+ DONE (false, false);
+
+ private final boolean isWriteState;
+ private final boolean isReadState;
+
+ private State(boolean isReadState, boolean isWriteState)
+ {
+ this.isReadState = isReadState;
+ this.isWriteState = isWriteState;
+ }
+
+ boolean isReadState()
+ {
+ return isReadState;
+ }
+
+ boolean isWriteState()
+ {
+ return isWriteState;
+ }
+ }
+
+ private State state;
+ private ByteBuffer outBuffer;
+ private boolean continuedSession;
+ private SessionImpl continued;
+ private KeyPair dhPair;
+ private String keyAlias;
+ private PrivateKey privateKey;
+
+ // Delegated tasks.
+ private CertVerifier certVerifier;
+ private ParamsVerifier paramsVerifier;
+ private DelegatedTask keyExchange;
+ private CertLoader certLoader;
+ private GenCertVerify genCertVerify;
+
+ public ClientHandshake(SSLEngineImpl engine) throws NoSuchAlgorithmException
+ {
+ super(engine);
+ state = WRITE_CLIENT_HELLO;
+ continuedSession = false;
+ }
+
+ /* (non-Javadoc)
+ * @see gnu.javax.net.ssl.provider.AbstractHandshake#implHandleInput()
+ */
+ @Override protected HandshakeStatus implHandleInput() throws SSLException
+ {
+ if (state == DONE)
+ return HandshakeStatus.FINISHED;
+
+ if (state.isWriteState()
+ || (outBuffer != null && outBuffer.hasRemaining()))
+ return HandshakeStatus.NEED_WRAP;
+
+ // Copy the current buffer, and prepare it for reading.
+ ByteBuffer buffer = handshakeBuffer.duplicate ();
+ buffer.flip();
+ buffer.position(handshakeOffset);
+
+ Handshake handshake = new Handshake(buffer.slice(),
+ engine.session().suite,
+ engine.session().version);
+
+ if (Debug.DEBUG)
+ logger.logv(Component.SSL_HANDSHAKE, "processing in state {0}:\n{1}",
+ state, handshake);
+
+ switch (state)
+ {
+ // Server Hello.
+ case READ_SERVER_HELLO:
+ {
+ if (handshake.type() != Handshake.Type.SERVER_HELLO)
+ throw new AlertException(new Alert(Alert.Level.FATAL,
+ Alert.Description.UNEXPECTED_MESSAGE));
+ ServerHello hello = (ServerHello) handshake.body();
+ serverRandom = hello.random().copy();
+ engine.session().suite = hello.cipherSuite();
+ engine.session().version = hello.version();
+ compression = hello.compressionMethod();
+ Session.ID serverId = new Session.ID(hello.sessionId());
+ if (continued != null
+ && continued.id().equals(serverId))
+ {
+ continuedSession = true;
+ engine.setSession(continued);
+ }
+ else if (engine.getEnableSessionCreation())
+ {
+ ((AbstractSessionContext) engine.contextImpl
+ .engineGetClientSessionContext()).put(engine.session());
+ }
+
+ if (continuedSession)
+ {
+ byte[][] keys = generateKeys(clientRandom, serverRandom,
+ engine.session());
+ setupSecurityParameters(keys, true, engine, compression);
+ state = READ_FINISHED;
+ }
+ else
+ state = READ_CERTIFICATE;
+ }
+ break;
+
+ // Server Certificate.
+ case READ_CERTIFICATE:
+ {
+ if (handshake.type() != Handshake.Type.CERTIFICATE)
+ {
+ // We need a certificate for non-anonymous suites.
+ if (engine.session().suite.signatureAlgorithm() != SignatureAlgorithm.ANONYMOUS)
+ throw new AlertException(new Alert(Level.FATAL,
+ Description.UNEXPECTED_MESSAGE));
+ state = READ_SERVER_KEY_EXCHANGE;
+ }
+ Certificate cert = (Certificate) handshake.body();
+ X509Certificate[] chain = null;
+ try
+ {
+ chain = cert.certificates().toArray(new X509Certificate[0]);
+ }
+ catch (CertificateException ce)
+ {
+ throw new AlertException(new Alert(Level.FATAL,
+ Description.BAD_CERTIFICATE),
+ ce);
+ }
+ catch (NoSuchAlgorithmException nsae)
+ {
+ throw new AlertException(new Alert(Level.FATAL,
+ Description.UNSUPPORTED_CERTIFICATE),
+ nsae);
+ }
+ engine.session().setPeerCertificates(chain);
+ certVerifier = new CertVerifier(true, chain);
+ tasks.add(certVerifier);
+
+ // If we are doing an RSA key exchange, generate our parameters.
+ if (engine.session().suite.keyExchangeAlgorithm()
+ == KeyExchangeAlgorithm.RSA)
+ {
+ keyExchange = new RSAGen();
+ tasks.add(keyExchange);
+ state = READ_CERTIFICATE_REQUEST;
+ }
+ else
+ state = READ_SERVER_KEY_EXCHANGE;
+ }
+ break;
+
+ // Server Key Exchange.
+ case READ_SERVER_KEY_EXCHANGE:
+ {
+ CipherSuite s = engine.session().suite;
+ // XXX also SRP.
+ if (!s.isEphemeralDH() &&
+ !(s.keyExchangeAlgorithm() == KeyExchangeAlgorithm.DIFFIE_HELLMAN
+ && s.signatureAlgorithm() == SignatureAlgorithm.ANONYMOUS))
+ throw new AlertException(new Alert(Level.FATAL,
+ Description.UNEXPECTED_MESSAGE));
+
+ ServerKeyExchange skex = (ServerKeyExchange) handshake.body();
+ ByteBuffer paramsBuffer = null;
+ if (s.keyExchangeAlgorithm() == KeyExchangeAlgorithm.DIFFIE_HELLMAN)
+ {
+ ServerDHParams dhParams = (ServerDHParams) skex.params();
+ ByteBuffer b = dhParams.buffer();
+ paramsBuffer = ByteBuffer.allocate(b.remaining());
+ paramsBuffer.put(b);
+ }
+
+ if (s.signatureAlgorithm() != SignatureAlgorithm.ANONYMOUS)
+ {
+ byte[] signature = skex.signature().signature();
+ paramsVerifier = new ParamsVerifier(paramsBuffer, signature);
+ tasks.add(paramsVerifier);
+ }
+
+ if (s.keyExchangeAlgorithm() == KeyExchangeAlgorithm.DIFFIE_HELLMAN)
+ {
+ ServerDHParams dhParams = (ServerDHParams) skex.params();
+ DHPublicKey serverKey = new GnuDHPublicKey(null,
+ dhParams.p(),
+ dhParams.g(),
+ dhParams.y());
+ DHParameterSpec params = new DHParameterSpec(dhParams.p(),
+ dhParams.g());
+ keyExchange = new ClientDHGen(serverKey, params);
+ tasks.add(keyExchange);
+ }
+ state = READ_CERTIFICATE_REQUEST;
+ }
+ break;
+
+ // Certificate Request.
+ case READ_CERTIFICATE_REQUEST:
+ {
+ if (handshake.type() != Handshake.Type.CERTIFICATE_REQUEST)
+ {
+ state = READ_SERVER_HELLO_DONE;
+ return HandshakeStatus.NEED_UNWRAP;
+ }
+
+ CertificateRequest req = (CertificateRequest) handshake.body();
+ ClientCertificateTypeList types = req.types();
+ LinkedList<String> typeList = new LinkedList<String>();
+ for (ClientCertificateType t : types)
+ typeList.add(t.name());
+
+ X500PrincipalList issuers = req.authorities();
+ LinkedList<X500Principal> issuerList = new LinkedList<X500Principal>();
+ for (X500Principal p : issuers)
+ issuerList.add(p);
+
+ certLoader = new CertLoader(typeList, issuerList);
+ tasks.add(certLoader);
+ }
+ break;
+
+ // Server Hello Done.
+ case READ_SERVER_HELLO_DONE:
+ {
+ if (handshake.type() != Handshake.Type.SERVER_HELLO_DONE)
+ throw new AlertException(new Alert(Level.FATAL,
+ Description.UNEXPECTED_MESSAGE));
+ state = WRITE_CERTIFICATE;
+ }
+ break;
+
+ // Finished.
+ case READ_FINISHED:
+ {
+ if (handshake.type() != Handshake.Type.FINISHED)
+ throw new AlertException(new Alert(Level.FATAL,
+ Description.UNEXPECTED_MESSAGE));
+
+ Finished serverFinished = (Finished) handshake.body();
+ MessageDigest md5copy = null;
+ MessageDigest shacopy = null;
+ try
+ {
+ md5copy = (MessageDigest) md5.clone();
+ shacopy = (MessageDigest) sha.clone();
+ }
+ catch (CloneNotSupportedException cnse)
+ {
+ // We're improperly configured to use a non-cloneable
+ // md5/sha-1, OR there's a runtime bug.
+ throw new SSLException(cnse);
+ }
+ Finished clientFinished =
+ new Finished(generateFinished(md5copy, shacopy,
+ false, engine.session()),
+ engine.session().version);
+
+ if (Debug.DEBUG)
+ logger.logv(Component.SSL_HANDSHAKE, "clientFinished: {0}",
+ clientFinished);
+
+ if (engine.session().version == ProtocolVersion.SSL_3)
+ {
+ if (!Arrays.equals(clientFinished.md5Hash(),
+ serverFinished.md5Hash())
+ || !Arrays.equals(clientFinished.shaHash(),
+ serverFinished.shaHash()))
+ {
+ engine.session().invalidate();
+ throw new SSLException("session verify failed");
+ }
+ }
+ else
+ {
+ if (!Arrays.equals(clientFinished.verifyData(),
+ serverFinished.verifyData()))
+ {
+ engine.session().invalidate();
+ throw new SSLException("session verify failed");
+ }
+ }
+
+ if (continuedSession)
+ {
+ engine.changeCipherSpec();
+ state = WRITE_FINISHED;
+ }
+ else
+ state = DONE;
+ }
+ break;
+
+ default:
+ throw new IllegalStateException("invalid state: " + state);
+ }
+
+ handshakeOffset += handshake.length() + 4;
+
+ if (!tasks.isEmpty())
+ return HandshakeStatus.NEED_TASK;
+ if (state.isWriteState()
+ || (outBuffer != null && outBuffer.hasRemaining()))
+ return HandshakeStatus.NEED_WRAP;
+ if (state.isReadState())
+ return HandshakeStatus.NEED_UNWRAP;
+
+ return HandshakeStatus.FINISHED;
+ }
+
+ /* (non-Javadoc)
+ * @see gnu.javax.net.ssl.provider.AbstractHandshake#implHandleOutput(java.nio.ByteBuffer)
+ */
+ @Override protected HandshakeStatus implHandleOutput(ByteBuffer fragment)
+ throws SSLException
+ {
+ if (Debug.DEBUG)
+ logger.logv(Component.SSL_HANDSHAKE, "output to {0}; state:{1}; outBuffer:{2}",
+ fragment, state, outBuffer);
+
+ // Drain the output buffer, if it needs it.
+ if (outBuffer != null && outBuffer.hasRemaining())
+ {
+ int l = Math.min(fragment.remaining(), outBuffer.remaining());
+ fragment.put((ByteBuffer) outBuffer.duplicate().limit(outBuffer.position() + l));
+ outBuffer.position(outBuffer.position() + l);
+ }
+
+ if (!fragment.hasRemaining())
+ {
+ if (state.isWriteState() || outBuffer.hasRemaining())
+ return HandshakeStatus.NEED_WRAP;
+ else
+ return HandshakeStatus.NEED_UNWRAP;
+ }
+
+outer_loop:
+ while (fragment.remaining() >= 4 && state.isWriteState())
+ {
+ if (Debug.DEBUG)
+ logger.logv(Component.SSL_HANDSHAKE, "loop state={0}", state);
+
+ switch (state)
+ {
+ case WRITE_CLIENT_HELLO:
+ {
+ ClientHelloBuilder hello = new ClientHelloBuilder();
+ AbstractSessionContext ctx = (AbstractSessionContext)
+ engine.contextImpl.engineGetClientSessionContext();
+ continued = (SessionImpl) ctx.getSession(engine.getPeerHost(),
+ engine.getPeerPort());
+ engine.session().setId(new Session.ID(new byte[0]));
+ Session.ID sid = engine.session().id();
+ // If we have a session that we may want to continue, send
+ // that ID.
+ if (continued != null)
+ sid = continued.id();
+
+ hello.setSessionId(sid.id());
+ hello.setVersion(chooseVersion());
+ hello.setCipherSuites(getSuites());
+ hello.setCompressionMethods(getCompressionMethods());
+ Random r = hello.random();
+ r.setGmtUnixTime(Util.unixTime());
+ byte[] nonce = new byte[28];
+ engine.session().random().nextBytes(nonce);
+ r.setRandomBytes(nonce);
+ clientRandom = r.copy();
+ // XXX extensions?
+
+ fragment.putInt((Handshake.Type.CLIENT_HELLO.getValue() << 24)
+ | (hello.length() & 0xFFFFFF));
+ outBuffer = hello.buffer();
+ int l = Math.min(fragment.remaining(), outBuffer.remaining());
+ fragment.put((ByteBuffer) outBuffer.duplicate()
+ .limit(outBuffer.position() + l));
+ outBuffer.position(outBuffer.position() + l);
+
+ state = READ_SERVER_HELLO;
+ }
+ break;
+
+ case WRITE_CERTIFICATE:
+ {
+ java.security.cert.Certificate[] chain
+ = engine.session().getLocalCertificates();
+ if (chain != null)
+ {
+ CertificateBuilder cert
+ = new CertificateBuilder(CertificateType.X509);
+ try
+ {
+ cert.setCertificates(Arrays.asList(chain));
+ }
+ catch (CertificateException ce)
+ {
+ throw new AlertException(new Alert(Level.FATAL,
+ Description.INTERNAL_ERROR),
+ ce);
+ }
+
+ outBuffer = cert.buffer();
+
+ fragment.putInt((Handshake.Type.CERTIFICATE.getValue() << 24)
+ | (cert.length() & 0xFFFFFF));
+
+ int l = Math.min(fragment.remaining(), outBuffer.remaining());
+ fragment.put((ByteBuffer) outBuffer.duplicate()
+ .limit(outBuffer.position() + l));
+ outBuffer.position(outBuffer.position() + l);
+ }
+ state = WRITE_CLIENT_KEY_EXCHANGE;
+ }
+ break;
+
+ case WRITE_CLIENT_KEY_EXCHANGE:
+ {
+ KeyExchangeAlgorithm kea = engine.session().suite.keyExchangeAlgorithm();
+ ClientKeyExchangeBuilder ckex
+ = new ClientKeyExchangeBuilder(engine.session().suite,
+ engine.session().version);
+ if (kea == KeyExchangeAlgorithm.DIFFIE_HELLMAN)
+ {
+ assert(dhPair != null);
+ DHPublicKey pubkey = (DHPublicKey) dhPair.getPublic();
+ ClientDiffieHellmanPublic pub
+ = new ClientDiffieHellmanPublic(pubkey.getY());
+ ckex.setExchangeKeys(pub.buffer());
+ }
+ if (kea == KeyExchangeAlgorithm.RSA)
+ {
+ assert(keyExchange instanceof RSAGen);
+ assert(keyExchange.hasRun());
+ if (keyExchange.thrown() != null)
+ throw new AlertException(new Alert(Level.FATAL,
+ Description.HANDSHAKE_FAILURE),
+ keyExchange.thrown());
+ EncryptedPreMasterSecret epms
+ = new EncryptedPreMasterSecret(((RSAGen) keyExchange).encryptedSecret(),
+ engine.session().version);
+ ckex.setExchangeKeys(epms.buffer());
+ }
+
+ if (Debug.DEBUG)
+ logger.logv(Component.SSL_HANDSHAKE, "{0}", ckex);
+
+ outBuffer = ckex.buffer();
+ if (Debug.DEBUG)
+ logger.logv(Component.SSL_HANDSHAKE, "client kex buffer {0}", outBuffer);
+ fragment.putInt((Handshake.Type.CLIENT_KEY_EXCHANGE.getValue() << 24)
+ | (ckex.length() & 0xFFFF));
+ int l = Math.min(fragment.remaining(), outBuffer.remaining());
+ fragment.put((ByteBuffer) outBuffer.duplicate().limit(outBuffer.position() + l));
+ outBuffer.position(outBuffer.position() + l);
+
+ if (privateKey != null)
+ {
+ genCertVerify = new GenCertVerify();
+ tasks.add(genCertVerify);
+ state = WRITE_CERTIFICATE_VERIFY;
+ }
+ else
+ {
+ engine.changeCipherSpec();
+ state = WRITE_FINISHED;
+ }
+ }
+ // Both states terminate in a NEED_TASK, or a need to change cipher
+ // specs; so we can't write any more messages here.
+ break outer_loop;
+
+ case WRITE_CERTIFICATE_VERIFY:
+ {
+ // XXX
+ engine.changeCipherSpec();
+ state = WRITE_FINISHED;
+ }
+ break outer_loop;
+
+ case WRITE_FINISHED:
+ {
+ MessageDigest md5copy = null;
+ MessageDigest shacopy = null;
+ try
+ {
+ md5copy = (MessageDigest) md5.clone();
+ shacopy = (MessageDigest) sha.clone();
+ }
+ catch (CloneNotSupportedException cnse)
+ {
+ // We're improperly configured to use a non-cloneable
+ // md5/sha-1, OR there's a runtime bug.
+ throw new SSLException(cnse);
+ }
+ outBuffer
+ = generateFinished(md5copy, shacopy, true,
+ engine.session());
+
+ fragment.putInt((Handshake.Type.FINISHED.getValue() << 24)
+ | outBuffer.remaining() & 0xFFFFFF);
+
+ int l = Math.min(outBuffer.remaining(), fragment.remaining());
+ fragment.put((ByteBuffer) outBuffer.duplicate().limit(outBuffer.position() + l));
+ outBuffer.position(outBuffer.position() + l);
+
+ if (continuedSession)
+ state = DONE;
+ else
+ state = READ_FINISHED;
+ }
+ break;
+
+ default:
+ throw new IllegalStateException("invalid state: " + state);
+ }
+ }
+
+ if (!tasks.isEmpty())
+ return HandshakeStatus.NEED_TASK;
+ if (state.isWriteState() ||
+ (outBuffer != null && outBuffer.hasRemaining()))
+ return HandshakeStatus.NEED_WRAP;
+ if (state.isReadState())
+ return HandshakeStatus.NEED_UNWRAP;
+
+ return HandshakeStatus.FINISHED;
+ }
+
+ /* (non-Javadoc)
+ * @see gnu.javax.net.ssl.provider.AbstractHandshake#status()
+ */
+ @Override HandshakeStatus status()
+ {
+ if (state.isReadState())
+ return HandshakeStatus.NEED_UNWRAP;
+ if (state.isWriteState())
+ return HandshakeStatus.NEED_WRAP;
+ return HandshakeStatus.FINISHED;
+ }
+
+ @Override void checkKeyExchange() throws SSLException
+ {
+ // XXX implement.
+ }
+
+ /* (non-Javadoc)
+ * @see gnu.javax.net.ssl.provider.AbstractHandshake#handleV2Hello(java.nio.ByteBuffer)
+ */
+ @Override void handleV2Hello(ByteBuffer hello) throws SSLException
+ {
+ throw new SSLException("this should be impossible");
+ }
+
+ private ProtocolVersion chooseVersion() throws SSLException
+ {
+ // Select the highest enabled version, for our initial key exchange.
+ ProtocolVersion version = null;
+ for (String ver : engine.getEnabledProtocols())
+ {
+ try
+ {
+ ProtocolVersion v = ProtocolVersion.forName(ver);
+ if (version == null || version.compareTo(v) < 0)
+ version = v;
+ }
+ catch (Exception x)
+ {
+ continue;
+ }
+ }
+
+ if (version == null)
+ throw new SSLException("no suitable enabled versions");
+
+ return version;
+ }
+
+ private List<CipherSuite> getSuites() throws SSLException
+ {
+ List<CipherSuite> suites = new LinkedList<CipherSuite>();
+ for (String s : engine.getEnabledCipherSuites())
+ {
+ CipherSuite suite = CipherSuite.forName(s);
+ if (suite != null)
+ suites.add(suite);
+ }
+ if (suites.isEmpty())
+ throw new SSLException("no cipher suites enabled");
+ return suites;
+ }
+
+ private List<CompressionMethod> getCompressionMethods()
+ {
+ List<CompressionMethod> methods = new LinkedList<CompressionMethod>();
+ GetSecurityPropertyAction gspa = new GetSecurityPropertyAction("jessie.enable.compression");
+ if (Boolean.valueOf(AccessController.doPrivileged(gspa)))
+ methods.add(CompressionMethod.ZLIB);
+ methods.add(CompressionMethod.NULL);
+ return methods;
+ }
+
+ // Delegated tasks.
+
+ class ParamsVerifier extends DelegatedTask
+ {
+ private final ByteBuffer paramsBuffer;
+ private final byte[] signature;
+ private boolean verified;
+
+ ParamsVerifier(ByteBuffer paramsBuffer, byte[] signature)
+ {
+ this.paramsBuffer = paramsBuffer;
+ this.signature = signature;
+ }
+
+ public void implRun()
+ throws InvalidKeyException, NoSuchAlgorithmException,
+ SSLPeerUnverifiedException, SignatureException
+ {
+ java.security.Signature s
+ = java.security.Signature.getInstance(engine.session().suite
+ .signatureAlgorithm().algorithm());
+ s.initVerify(engine.session().getPeerCertificates()[0]);
+ s.update(paramsBuffer);
+ verified = s.verify(signature);
+ synchronized (this)
+ {
+ notifyAll();
+ }
+ }
+
+ boolean verified()
+ {
+ return verified;
+ }
+ }
+
+ class ClientDHGen extends DelegatedTask
+ {
+ private final DHPublicKey serverKey;
+ private final DHParameterSpec params;
+
+ ClientDHGen(DHPublicKey serverKey, DHParameterSpec params)
+ {
+ this.serverKey = serverKey;
+ this.params = params;
+ }
+
+ public void implRun()
+ throws InvalidAlgorithmParameterException, NoSuchAlgorithmException,
+ SSLException
+ {
+ if (Debug.DEBUG)
+ logger.log(Component.SSL_DELEGATED_TASK, "running client DH phase");
+ if (paramsVerifier != null)
+ {
+ synchronized (paramsVerifier)
+ {
+ try
+ {
+ while (!paramsVerifier.hasRun())
+ paramsVerifier.wait(500);
+ }
+ catch (InterruptedException ie)
+ {
+ // Ignore.
+ }
+ }
+ }
+ KeyPairGenerator gen = KeyPairGenerator.getInstance("DH");
+ gen.initialize(params, engine.session().random());
+ dhPair = gen.generateKeyPair();
+ if (Debug.DEBUG_KEY_EXCHANGE)
+ logger.logv(Component.SSL_KEY_EXCHANGE,
+ "client keys public:{0} private:{1}", dhPair.getPublic(),
+ dhPair.getPrivate());
+
+ // We have enough info to do the full key exchange; so let's do it.
+ initDiffieHellman((DHPrivateKey) dhPair.getPrivate(), engine.session().random());
+ DHPhase phase = new DHPhase(serverKey);
+ phase.run();
+ if (phase.thrown() != null)
+ throw new SSLException(phase.thrown());
+ }
+ }
+
+ class CertLoader extends DelegatedTask
+ {
+ private final List<String> keyTypes;
+ private final List<X500Principal> issuers;
+
+ CertLoader(List<String> keyTypes, List<X500Principal> issuers)
+ {
+ this.keyTypes = keyTypes;
+ this.issuers = issuers;
+ }
+
+ public void implRun()
+ {
+ X509ExtendedKeyManager km = engine.contextImpl.keyManager;
+ if (km == null)
+ return;
+ keyAlias = km.chooseEngineClientAlias(keyTypes.toArray(new String[keyTypes.size()]),
+ issuers.toArray(new X500Principal[issuers.size()]),
+ engine);
+ engine.session().setLocalCertificates(km.getCertificateChain(keyAlias));
+ privateKey = km.getPrivateKey(keyAlias);
+ }
+ }
+
+ class RSAGen extends DelegatedTask
+ {
+ private byte[] encryptedPreMasterSecret;
+
+ public void implRun()
+ throws BadPaddingException, IllegalBlockSizeException, InvalidKeyException,
+ NoSuchAlgorithmException, NoSuchPaddingException,
+ SSLException
+ {
+ if (certVerifier != null)
+ {
+ synchronized (certVerifier)
+ {
+ try
+ {
+ while (!certVerifier.hasRun())
+ certVerifier.wait(500);
+ }
+ catch (InterruptedException ie)
+ {
+ // Ignore.
+ }
+ }
+ }
+ preMasterSecret = new byte[48];
+ engine.session().random().nextBytes(preMasterSecret);
+ preMasterSecret[0] = (byte) engine.session().version.major();
+ preMasterSecret[1] = (byte) engine.session().version.minor();
+ Cipher rsa = Cipher.getInstance("RSA");
+ rsa.init(Cipher.ENCRYPT_MODE, engine.session().getPeerCertificates()[0]);
+ encryptedPreMasterSecret = rsa.doFinal(preMasterSecret);
+
+ // Generate our session keys, because we can.
+ generateMasterSecret(clientRandom, serverRandom, engine.session());
+ byte[][] keys = generateKeys(clientRandom, serverRandom, engine.session());
+ setupSecurityParameters(keys, true, engine, compression);
+ }
+
+ byte[] encryptedSecret()
+ {
+ return encryptedPreMasterSecret;
+ }
+ }
+
+ class GenCertVerify extends DelegatedTask
+ {
+ public void implRun()
+ {
+ // XXX implement me.
+ }
+ }
+}
diff --git a/gnu/javax/net/ssl/provider/ClientKeyExchange.java b/gnu/javax/net/ssl/provider/ClientKeyExchange.java
index 199905a99..439725856 100644
--- a/gnu/javax/net/ssl/provider/ClientKeyExchange.java
+++ b/gnu/javax/net/ssl/provider/ClientKeyExchange.java
@@ -38,24 +38,12 @@ exception statement from your version. */
package gnu.javax.net.ssl.provider;
-import java.io.BufferedReader;
-import java.io.DataInputStream;
-import java.io.InputStream;
-import java.io.IOException;
-import java.io.OutputStream;
import java.io.PrintWriter;
-import java.io.StringReader;
import java.io.StringWriter;
-import java.math.BigInteger;
-
import java.nio.ByteBuffer;
import java.nio.ByteOrder;
-import java.security.PublicKey;
-import java.security.interfaces.RSAKey;
-import javax.crypto.interfaces.DHPublicKey;
-
/**
* The client key exchange message.
*
@@ -67,21 +55,21 @@ struct {
} exchange_keys;
} ClientKeyExchange;</pre>
*/
-final class ClientKeyExchange implements Handshake.Body
+public class ClientKeyExchange implements Handshake.Body
{
// Fields.
// -------------------------------------------------------------------------
- private final ByteBuffer buffer;
- private final CipherSuite suite;
- private final ProtocolVersion version;
+ protected ByteBuffer buffer;
+ protected final CipherSuite suite;
+ protected final ProtocolVersion version;
// Constructors.
// -------------------------------------------------------------------------
- ClientKeyExchange (final ByteBuffer buffer, final CipherSuite suite,
- final ProtocolVersion version)
+ public ClientKeyExchange (final ByteBuffer buffer, final CipherSuite suite,
+ final ProtocolVersion version)
{
suite.getClass();
version.getClass ();
@@ -93,7 +81,7 @@ final class ClientKeyExchange implements Handshake.Body
// Instance methods.
// -------------------------------------------------------------------------
- ExchangeKeys exchangeKeys ()
+ public ExchangeKeys exchangeKeys ()
{
KeyExchangeAlgorithm alg = suite.keyExchangeAlgorithm();
if (alg == KeyExchangeAlgorithm.RSA)
@@ -103,9 +91,11 @@ final class ClientKeyExchange implements Handshake.Body
throw new IllegalArgumentException("unsupported key exchange");
}
- public int length ()
+ public int length()
{
- return exchangeKeys ().length ();
+ if (suite.keyExchangeAlgorithm() == KeyExchangeAlgorithm.NONE)
+ return 0;
+ return exchangeKeys().length();
}
public String toString ()
diff --git a/gnu/javax/net/ssl/provider/ClientKeyExchangeBuilder.java b/gnu/javax/net/ssl/provider/ClientKeyExchangeBuilder.java
new file mode 100644
index 000000000..ebebdcc0e
--- /dev/null
+++ b/gnu/javax/net/ssl/provider/ClientKeyExchangeBuilder.java
@@ -0,0 +1,75 @@
+/* ClientKeyExchangeBuilder.java --
+ Copyright (C) 2006 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.provider;
+
+import java.nio.ByteBuffer;
+
+/**
+ * Builder for {@link ClientKeyExchange} objects.
+ *
+ * @author Casey Marshall (csm@gnu.org)
+ */
+public class ClientKeyExchangeBuilder extends ClientKeyExchange
+ implements Builder
+{
+ public ClientKeyExchangeBuilder(CipherSuite suite, ProtocolVersion version)
+ {
+ super(ByteBuffer.allocate(512), suite, version);
+ }
+
+ /* (non-Javadoc)
+ * @see gnu.javax.net.ssl.provider.Builder#buffer()
+ */
+ public ByteBuffer buffer()
+ {
+ return ((ByteBuffer) buffer.duplicate().position(0).limit(length())).slice();
+ }
+
+ public void setExchangeKeys(ByteBuffer exchangeKeys)
+ {
+ // For SSLv3 and RSA key exchange, the message is sent without length.
+ // So we use the precise capacity of the buffer to signal the size of
+ // the message.
+ if (buffer.capacity() < exchangeKeys.remaining()
+ || (suite.keyExchangeAlgorithm() == KeyExchangeAlgorithm.RSA
+ && version == ProtocolVersion.SSL_3))
+ buffer = ByteBuffer.allocate(exchangeKeys.remaining());
+ ((ByteBuffer) buffer.duplicate().position(0)).put(exchangeKeys);
+ }
+}
diff --git a/gnu/javax/net/ssl/provider/DelegatedTask.java b/gnu/javax/net/ssl/provider/DelegatedTask.java
new file mode 100644
index 000000000..200d4d457
--- /dev/null
+++ b/gnu/javax/net/ssl/provider/DelegatedTask.java
@@ -0,0 +1,93 @@
+/* DelegatedTask.java --
+ Copyright (C) 2006 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.provider;
+
+import gnu.classpath.debug.Component;
+import gnu.classpath.debug.SystemLogger;
+
+/**
+ * @author Casey Marshall (csm@gnu.org)
+ */
+public abstract class DelegatedTask implements Runnable
+{
+ private static final SystemLogger logger = SystemLogger.SYSTEM;
+ private boolean hasRun;
+ protected Throwable thrown;
+
+ protected DelegatedTask()
+ {
+ hasRun = false;
+ }
+
+ public final void run()
+ {
+ if (hasRun)
+ throw new IllegalStateException("task already ran");
+ try
+ {
+ if (Debug.DEBUG)
+ logger.logv(Component.SSL_DELEGATED_TASK,
+ "running delegated task {0} in {1}", this,
+ Thread.currentThread());
+ implRun();
+ }
+ catch (Throwable t)
+ {
+ if (Debug.DEBUG)
+ logger.log(Component.SSL_DELEGATED_TASK, "task threw exception", t);
+ thrown = t;
+ }
+ finally
+ {
+ hasRun = true;
+ }
+ }
+
+ public final boolean hasRun()
+ {
+ return hasRun;
+ }
+
+ public final Throwable thrown()
+ {
+ return thrown;
+ }
+
+ protected abstract void implRun() throws Throwable;
+}
diff --git a/gnu/javax/net/ssl/provider/DiffieHellman.java b/gnu/javax/net/ssl/provider/DiffieHellman.java
index ad48c7959..5a5275712 100644
--- a/gnu/javax/net/ssl/provider/DiffieHellman.java
+++ b/gnu/javax/net/ssl/provider/DiffieHellman.java
@@ -39,6 +39,9 @@ exception statement from your version. */
package gnu.javax.net.ssl.provider;
import java.math.BigInteger;
+import java.security.AccessController;
+
+import gnu.java.security.action.GetSecurityPropertyAction;
import gnu.javax.crypto.key.dh.GnuDHPrivateKey;
/**
@@ -72,7 +75,8 @@ final class DiffieHellman
static GnuDHPrivateKey getParams()
{
BigInteger p = DiffieHellman.GROUP_5;
- String group = Util.getSecurityProperty("jessie.key.dh.group");
+ String group = AccessController.doPrivileged
+ (new GetSecurityPropertyAction("jessie.key.dh.group"));
if (group != null)
{
group = group.trim();
diff --git a/gnu/javax/net/ssl/provider/EncryptedPreMasterSecret.java b/gnu/javax/net/ssl/provider/EncryptedPreMasterSecret.java
index adb3e1ea1..edfc6f566 100644
--- a/gnu/javax/net/ssl/provider/EncryptedPreMasterSecret.java
+++ b/gnu/javax/net/ssl/provider/EncryptedPreMasterSecret.java
@@ -51,47 +51,66 @@ struct {
public-key-encrypted PreMasterSecret pre_master_secret;
} EncryptedPreMasterSecret;</pre>
*/
-final class EncryptedPreMasterSecret extends ExchangeKeys
+public final class EncryptedPreMasterSecret extends ExchangeKeys implements Builder
{
private final ProtocolVersion version;
- EncryptedPreMasterSecret (final ByteBuffer buffer, final ProtocolVersion version)
+ public EncryptedPreMasterSecret(ByteBuffer buffer, ProtocolVersion version)
{
- super (buffer);
- version.getClass ();
+ super(buffer);
+ version.getClass();
this.version = version;
}
+
+ public EncryptedPreMasterSecret(byte[] encryptedSecret, ProtocolVersion version)
+ {
+ this(ByteBuffer.allocate(version == ProtocolVersion.SSL_3
+ ? encryptedSecret.length
+ : encryptedSecret.length + 2), version);
+ ByteBuffer b = buffer.duplicate();
+ if (version != ProtocolVersion.SSL_3)
+ b.putShort((short) encryptedSecret.length);
+ b.put(encryptedSecret);
+ }
+
+ public ByteBuffer buffer()
+ {
+ return (ByteBuffer) buffer.duplicate().rewind();
+ }
- byte[] encryptedSecret ()
+ public byte[] encryptedSecret()
{
byte[] secret;
if (version == ProtocolVersion.SSL_3)
{
buffer.position (0);
secret = new byte[buffer.limit ()];
+ buffer.get(secret);
}
else
{
- int len = buffer.getShort (0) & 0xFFFF;
+ int len = buffer.getShort(0) & 0xFFFF;
secret = new byte[len];
- buffer.position (2);
- buffer.get (secret);
+ buffer.position(2);
+ buffer.get(secret);
}
return secret;
}
- void setEncryptedSecret (final byte[] secret, final int offset, final int length)
+ public void setEncryptedSecret(final byte[] secret, final int offset, final int length)
{
if (version == ProtocolVersion.SSL_3)
{
- buffer.position (0);
- buffer.put (secret, offset, length);
+ buffer.position(0);
+ buffer.put(secret, offset, length);
+ buffer.rewind();
}
else
{
- buffer.putShort (0, (short) length);
- buffer.position (2);
- buffer.put (secret, offset, length);
+ buffer.putShort(0, (short) length);
+ buffer.position(2);
+ buffer.put(secret, offset, length);
+ buffer.rewind();
}
}
@@ -99,11 +118,11 @@ final class EncryptedPreMasterSecret extends ExchangeKeys
{
if (version == ProtocolVersion.SSL_3)
{
- return buffer.position (0).limit ();
+ return buffer.capacity();
}
else
{
- return buffer.getShort (0) & 0xFFFF;
+ return (buffer.getShort(0) & 0xFFFF) + 2;
}
}
diff --git a/gnu/javax/net/ssl/provider/ExchangeKeys.java b/gnu/javax/net/ssl/provider/ExchangeKeys.java
index 3a48963e0..3ee79d7d0 100644
--- a/gnu/javax/net/ssl/provider/ExchangeKeys.java
+++ b/gnu/javax/net/ssl/provider/ExchangeKeys.java
@@ -40,14 +40,13 @@ package gnu.javax.net.ssl.provider;
import java.nio.ByteBuffer;
-abstract class ExchangeKeys implements Constructed
+public abstract class ExchangeKeys implements Constructed
{
- final ByteBuffer buffer;
+ protected ByteBuffer buffer;
- ExchangeKeys (final ByteBuffer buffer)
+ public ExchangeKeys (final ByteBuffer buffer)
{
- buffer.getClass ();
this.buffer = buffer;
}
} \ No newline at end of file
diff --git a/gnu/javax/net/ssl/provider/Jessie.java b/gnu/javax/net/ssl/provider/Jessie.java
index 5886b6c36..66f2576b3 100644
--- a/gnu/javax/net/ssl/provider/Jessie.java
+++ b/gnu/javax/net/ssl/provider/Jessie.java
@@ -59,6 +59,7 @@ import java.security.Provider;
*/
public class Jessie extends Provider
{
+ private static final long serialVersionUID = -1;
public static final String VERSION = "2.0.0";
public static final double VERSION_DOUBLE = 2.0;
@@ -80,6 +81,7 @@ public class Jessie extends Provider
put("Alg.Alias.SSLContext.TLSv1", "TLSv1.1");
put("Alg.Alias.SSLContext.TLSv1.0", "TLSv1.1");
put("Alg.Alias.SSLContext.TLS", "TLSv1.1");
+ put("Alg.Alias.SSLContext.SSL", "TLSv1.1");
put("KeyManagerFactory.JessieX509", X509KeyManagerFactory.class.getName());
put("TrustManagerFactory.JessieX509", X509TrustManagerFactory.class.getName());
diff --git a/gnu/javax/net/ssl/provider/SSLContextImpl.java b/gnu/javax/net/ssl/provider/SSLContextImpl.java
index 22fcbd7d8..e1a8ea252 100644
--- a/gnu/javax/net/ssl/provider/SSLContextImpl.java
+++ b/gnu/javax/net/ssl/provider/SSLContextImpl.java
@@ -38,12 +38,12 @@ exception statement from your version. */
package gnu.javax.net.ssl.provider;
+import gnu.java.security.action.GetSecurityPropertyAction;
import gnu.javax.net.ssl.AbstractSessionContext;
import gnu.javax.net.ssl.NullManagerParameters;
import gnu.javax.net.ssl.SRPTrustManager;
-import gnu.javax.net.ssl.StaticTrustAnchors;
-import java.security.InvalidAlgorithmParameterException;
+import java.security.AccessController;
import java.security.KeyManagementException;
import java.security.KeyStore;
import java.security.KeyStoreException;
@@ -63,7 +63,6 @@ import javax.net.ssl.SSLSocketFactory;
import javax.net.ssl.TrustManager;
import javax.net.ssl.TrustManagerFactory;
import javax.net.ssl.X509ExtendedKeyManager;
-import javax.net.ssl.X509KeyManager;
import javax.net.ssl.X509TrustManager;
/**
@@ -71,8 +70,7 @@ import javax.net.ssl.X509TrustManager;
*
* @author Casey Marshall (csm@gnu.org)
*/
-public final class SSLContextImpl
- extends SSLContextSpi
+public final class SSLContextImpl extends SSLContextSpi
{
AbstractSessionContext serverContext;
AbstractSessionContext clientContext;
@@ -145,8 +143,7 @@ public final class SSLContextImpl
*/
protected @Override SSLServerSocketFactory engineGetServerSocketFactory()
{
- // TODO Auto-generated method stub
- return null;
+ return new SSLServerSocketFactoryImpl(this);
}
/* (non-Javadoc)
@@ -154,8 +151,7 @@ public final class SSLContextImpl
*/
protected @Override SSLSocketFactory engineGetSocketFactory()
{
- // TODO Auto-generated method stub
- return null;
+ return new SSLSocketFactoryImpl(this);
}
/* (non-Javadoc)
@@ -302,11 +298,11 @@ public final class SSLContextImpl
*/
private SecureRandom defaultRandom()
{
- String alg = Util.getSecurityProperty("gnu.javax.net.ssl.secureRandom");
+ GetSecurityPropertyAction gspa
+ = new GetSecurityPropertyAction("gnu.javax.net.ssl.secureRandom");
+ String alg = AccessController.doPrivileged(gspa);
if (alg == null)
- {
- alg = "Fortuna";
- }
+ alg = "Fortuna";
SecureRandom rand = null;
try
{
diff --git a/gnu/javax/net/ssl/provider/SSLEngineImpl.java b/gnu/javax/net/ssl/provider/SSLEngineImpl.java
index b7f19946a..c8c55979a 100644
--- a/gnu/javax/net/ssl/provider/SSLEngineImpl.java
+++ b/gnu/javax/net/ssl/provider/SSLEngineImpl.java
@@ -44,8 +44,6 @@ import gnu.classpath.debug.SystemLogger;
import gnu.java.io.ByteBufferOutputStream;
import gnu.javax.net.ssl.Session;
import gnu.javax.net.ssl.SSLRecordHandler;
-import gnu.javax.net.ssl.provider.Alert.Description;
-import gnu.javax.net.ssl.provider.Alert.Level;
import java.nio.BufferOverflowException;
import java.nio.ByteBuffer;
@@ -54,18 +52,14 @@ import java.nio.ByteOrder;
import java.security.NoSuchAlgorithmException;
import java.util.ArrayList;
import java.util.List;
-import java.util.logging.Logger;
import java.util.zip.DataFormatException;
-import javax.crypto.BadPaddingException;
import javax.crypto.IllegalBlockSizeException;
import javax.crypto.ShortBufferException;
-import javax.net.ssl.SSLContext;
import javax.net.ssl.SSLEngine;
import javax.net.ssl.SSLEngineResult;
import javax.net.ssl.SSLException;
import javax.net.ssl.SSLSession;
-import javax.net.ssl.SSLSessionContext;
import javax.net.ssl.SSLEngineResult.HandshakeStatus;
import javax.net.ssl.SSLEngineResult.Status;
@@ -149,7 +143,12 @@ public final class SSLEngineImpl extends SSLEngine
ProtocolVersion.TLS_1.toString(),
ProtocolVersion.SSL_3.toString()
};
- enabledSuites = new String[] {
+ enabledSuites = defaultSuites();
+ }
+
+ static String[] defaultSuites()
+ {
+ return new String[] {
CipherSuite.TLS_DHE_DSS_WITH_AES_256_CBC_SHA.toString(),
CipherSuite.TLS_DHE_RSA_WITH_AES_256_CBC_SHA.toString(),
CipherSuite.TLS_DH_DSS_WITH_AES_256_CBC_SHA.toString(),
@@ -224,8 +223,15 @@ public final class SSLEngineImpl extends SSLEngine
break;
case CLIENT:
- throw new UnsupportedOperationException("client handshake not yet implemented");
- //break;
+ try
+ {
+ handshake = new ClientHandshake(this);
+ }
+ catch (NoSuchAlgorithmException nsae)
+ {
+ throw new SSLException(nsae);
+ }
+ break;
}
}
@@ -244,7 +250,9 @@ public final class SSLEngineImpl extends SSLEngine
@Override
public Runnable getDelegatedTask()
{
- return null;
+ if (handshake == null)
+ return null;
+ return handshake.getTask();
}
@Override
@@ -266,9 +274,11 @@ public final class SSLEngineImpl extends SSLEngine
}
@Override
- public SSLEngineResult.HandshakeStatus getHandshakeStatus()
+ public HandshakeStatus getHandshakeStatus()
{
- return handshakeStatus;
+ if (handshake == null)
+ return HandshakeStatus.NOT_HANDSHAKING;
+ return handshake.status();
}
@Override
@@ -603,7 +613,17 @@ public final class SSLEngineImpl extends SSLEngine
{
if (handshake == null)
beginHandshake();
- handshakeStatus = handshake.handleInput(msg);
+ try
+ {
+ handshakeStatus = handshake.handleInput(msg);
+ }
+ catch (AlertException ae)
+ {
+ lastAlert = ae.alert();
+ return new SSLEngineResult(SSLEngineResult.Status.OK,
+ SSLEngineResult.HandshakeStatus.NEED_WRAP,
+ 0, 0);
+ }
if (Debug.DEBUG)
logger.logv(Component.SSL_HANDSHAKE, "handshake status {0}", handshakeStatus);
result = new SSLEngineResult(SSLEngineResult.Status.OK,
@@ -658,7 +678,9 @@ public final class SSLEngineImpl extends SSLEngine
ContentType type = null;
ByteBuffer sysMessage = null;
-
+ if (Debug.DEBUG)
+ logger.logv(Component.SSL_RECORD_LAYER, "wrap {0} {1} {2} {3} / {4}",
+ sources, offset, length, sink, getHandshakeStatus());
if (lastAlert != null)
{
type = ContentType.ALERT;
@@ -675,7 +697,7 @@ public final class SSLEngineImpl extends SSLEngine
sysMessage = ByteBuffer.allocate(1);
sysMessage.put(0, (byte) 1);
}
- else if (handshakeStatus == SSLEngineResult.HandshakeStatus.NEED_WRAP)
+ else if (getHandshakeStatus() == SSLEngineResult.HandshakeStatus.NEED_WRAP)
{
// If we are not encrypting, optimize the handshake to fill
// the buffer directly.
@@ -692,8 +714,8 @@ public final class SSLEngineImpl extends SSLEngine
if (Debug.DEBUG)
logger.logv(Component.SSL_RECORD_LAYER, "emitting record:\n{0}",
new Record((ByteBuffer) sink.duplicate().position(orig)));
- SSLEngineResult result = new SSLEngineResult(SSLEngineResult.Status.OK,
- handshakeStatus, 0, produced);
+ SSLEngineResult result = new SSLEngineResult(SSLEngineResult.Status.OK,
+ handshakeStatus, 0, produced);
// Note, this will only happen if we transition from
// TLS_NULL_WITH_NULL_NULL *to* TLS_NULL_WITH_NULL_NULL, which
@@ -709,7 +731,16 @@ public final class SSLEngineImpl extends SSLEngine
// Rough guideline; XXX.
sysMessage = ByteBuffer.allocate(sink.remaining() - 2048);
type = ContentType.HANDSHAKE;
- handshakeStatus = handshake.handleOutput(sysMessage);
+ try
+ {
+ handshakeStatus = handshake.handleOutput(sysMessage);
+ }
+ catch (AlertException ae)
+ {
+ lastAlert = ae.alert();
+ return new SSLEngineResult(Status.OK,
+ HandshakeStatus.NEED_WRAP, 0, 0);
+ }
sysMessage.flip();
if (Debug.DEBUG)
logger.logv(Component.SSL_HANDSHAKE, "handshake status {0}",
diff --git a/gnu/javax/net/ssl/provider/SSLServerSocketFactoryImpl.java b/gnu/javax/net/ssl/provider/SSLServerSocketFactoryImpl.java
new file mode 100644
index 000000000..dc80dc782
--- /dev/null
+++ b/gnu/javax/net/ssl/provider/SSLServerSocketFactoryImpl.java
@@ -0,0 +1,108 @@
+/* SSLServerSocketFactoryImpl.java --
+ Copyright (C) 2006 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.provider;
+
+import java.io.IOException;
+import java.net.InetAddress;
+import java.net.InetSocketAddress;
+
+import javax.net.ssl.SSLServerSocketFactory;
+
+/**
+ * @author Casey Marshall (csm@gnu.org)
+ */
+public class SSLServerSocketFactoryImpl extends SSLServerSocketFactory
+{
+ private final SSLContextImpl contextImpl;
+
+ public SSLServerSocketFactoryImpl(SSLContextImpl contextImpl)
+ {
+ this.contextImpl = contextImpl;
+ }
+
+ /* (non-Javadoc)
+ * @see javax.net.ssl.SSLServerSocketFactory#getDefaultCipherSuites()
+ */
+ @Override public String[] getDefaultCipherSuites()
+ {
+ return SSLEngineImpl.defaultSuites();
+ }
+
+ /* (non-Javadoc)
+ * @see javax.net.ssl.SSLServerSocketFactory#getSupportedCipherSuites()
+ */
+ @Override public String[] getSupportedCipherSuites()
+ {
+ return CipherSuite.availableSuiteNames().toArray(new String[0]);
+ }
+
+ /* (non-Javadoc)
+ * @see javax.net.ServerSocketFactory#createServerSocket(int)
+ */
+ @Override public SSLServerSocketImpl createServerSocket(int port)
+ throws IOException
+ {
+ SSLServerSocketImpl socket = new SSLServerSocketImpl(contextImpl);
+ socket.bind(new InetSocketAddress(port));
+ return socket;
+ }
+
+ /* (non-Javadoc)
+ * @see javax.net.ServerSocketFactory#createServerSocket(int, int)
+ */
+ @Override public SSLServerSocketImpl createServerSocket(int port, int backlog)
+ throws IOException
+ {
+ SSLServerSocketImpl socket = new SSLServerSocketImpl(contextImpl);
+ socket.bind(new InetSocketAddress(port), backlog);
+ return socket;
+ }
+
+ /* (non-Javadoc)
+ * @see javax.net.ServerSocketFactory#createServerSocket(int, int, java.net.InetAddress)
+ */
+ @Override public SSLServerSocketImpl createServerSocket(int port, int backlog,
+ InetAddress bindAddress)
+ throws IOException
+ {
+ SSLServerSocketImpl socket = new SSLServerSocketImpl(contextImpl);
+ socket.bind(new InetSocketAddress(bindAddress, port), backlog);
+ return socket;
+ }
+}
diff --git a/gnu/javax/net/ssl/provider/SSLServerSocketImpl.java b/gnu/javax/net/ssl/provider/SSLServerSocketImpl.java
new file mode 100644
index 000000000..41ef5f1cf
--- /dev/null
+++ b/gnu/javax/net/ssl/provider/SSLServerSocketImpl.java
@@ -0,0 +1,199 @@
+/* SSLServerSocketImpl.java --
+ Copyright (C) 2006 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.provider;
+
+import java.io.IOException;
+
+import javax.net.ssl.SSLServerSocket;
+
+/**
+ * @author Casey Marshall (csm@gnu.org)
+ */
+public class SSLServerSocketImpl extends SSLServerSocket
+{
+ private final SSLContextImpl contextImpl;
+
+ private boolean enableSessionCreation;
+ private String[] enabledCipherSuites;
+ private String[] enabledProtocols;
+ private boolean needClientAuth;
+ private boolean wantClientAuth;
+ private boolean clientMode;
+
+ public SSLServerSocketImpl(SSLContextImpl contextImpl) throws IOException
+ {
+ super();
+ this.contextImpl = contextImpl;
+ enableSessionCreation = true;
+ enabledCipherSuites = SSLEngineImpl.defaultSuites();
+ enabledProtocols = new String[] { ProtocolVersion.SSL_3.toString(),
+ ProtocolVersion.TLS_1.toString(),
+ ProtocolVersion.TLS_1_1.toString() };
+ needClientAuth = false;
+ wantClientAuth = false;
+ clientMode = false;
+ }
+
+ /* (non-Javadoc)
+ * @see javax.net.ssl.SSLServerSocket#getEnableSessionCreation()
+ */
+ @Override public boolean getEnableSessionCreation()
+ {
+ return enableSessionCreation;
+ }
+
+ /* (non-Javadoc)
+ * @see javax.net.ssl.SSLServerSocket#getEnabledCipherSuites()
+ */
+ @Override public String[] getEnabledCipherSuites()
+ {
+ return (String[]) enabledCipherSuites.clone();
+ }
+
+ /* (non-Javadoc)
+ * @see javax.net.ssl.SSLServerSocket#getEnabledProtocols()
+ */
+ @Override public String[] getEnabledProtocols()
+ {
+ return (String[]) enabledProtocols.clone();
+ }
+
+ /* (non-Javadoc)
+ * @see javax.net.ssl.SSLServerSocket#getNeedClientAuth()
+ */
+ @Override public boolean getNeedClientAuth()
+ {
+ return needClientAuth;
+ }
+
+ /* (non-Javadoc)
+ * @see javax.net.ssl.SSLServerSocket#getSupportedCipherSuites()
+ */
+ @Override public String[] getSupportedCipherSuites()
+ {
+ return CipherSuite.availableSuiteNames().toArray(new String[0]);
+ }
+
+ /* (non-Javadoc)
+ * @see javax.net.ssl.SSLServerSocket#getSupportedProtocols()
+ */
+ @Override public String[] getSupportedProtocols()
+ {
+ return new String[] { ProtocolVersion.SSL_3.toString(),
+ ProtocolVersion.TLS_1.toString(),
+ ProtocolVersion.TLS_1_1.toString() };
+ }
+
+ /* (non-Javadoc)
+ * @see javax.net.ssl.SSLServerSocket#getUseClientMode()
+ */
+ @Override public boolean getUseClientMode()
+ {
+ return clientMode;
+ }
+
+ /* (non-Javadoc)
+ * @see javax.net.ssl.SSLServerSocket#getWantClientAuth()
+ */
+ @Override public boolean getWantClientAuth()
+ {
+ return wantClientAuth;
+ }
+
+ /* (non-Javadoc)
+ * @see javax.net.ssl.SSLServerSocket#setEnableSessionCreation(boolean)
+ */
+ @Override public void setEnableSessionCreation(final boolean enabled)
+ {
+ enableSessionCreation = enabled;
+ }
+
+ /* (non-Javadoc)
+ * @see javax.net.ssl.SSLServerSocket#setEnabledCipherSuites(java.lang.String[])
+ */
+ @Override public void setEnabledCipherSuites(final String[] suites)
+ {
+ enabledCipherSuites = (String[]) suites.clone();
+ }
+
+ /* (non-Javadoc)
+ * @see javax.net.ssl.SSLServerSocket#setEnabledProtocols(java.lang.String[])
+ */
+ @Override public void setEnabledProtocols(final String[] protocols)
+ {
+ enabledProtocols = (String[]) protocols.clone();
+ }
+
+ /* (non-Javadoc)
+ * @see javax.net.ssl.SSLServerSocket#setNeedClientAuth(boolean)
+ */
+ @Override public void setNeedClientAuth(final boolean needAuth)
+ {
+ needClientAuth = needAuth;
+ }
+
+ /* (non-Javadoc)
+ * @see javax.net.ssl.SSLServerSocket#setUseClientMode(boolean)
+ */
+ @Override public void setUseClientMode(final boolean clientMode)
+ {
+ this.clientMode = clientMode;
+ }
+
+ /* (non-Javadoc)
+ * @see javax.net.ssl.SSLServerSocket#setWantClientAuth(boolean)
+ */
+ @Override public void setWantClientAuth(final boolean wantAuth)
+ {
+ wantClientAuth = wantAuth;
+ }
+
+ @Override public SSLSocketImpl accept() throws IOException
+ {
+ SSLSocketImpl socketImpl = new SSLSocketImpl(contextImpl, null, -1);
+ implAccept(socketImpl);
+ socketImpl.setEnableSessionCreation(enableSessionCreation);
+ socketImpl.setEnabledCipherSuites(enabledCipherSuites);
+ socketImpl.setEnabledProtocols(enabledProtocols);
+ socketImpl.setNeedClientAuth(needClientAuth);
+ socketImpl.setUseClientMode(clientMode);
+ socketImpl.setWantClientAuth(wantClientAuth);
+ return socketImpl;
+ }
+}
diff --git a/gnu/javax/net/ssl/provider/SSLSocketFactoryImpl.java b/gnu/javax/net/ssl/provider/SSLSocketFactoryImpl.java
new file mode 100644
index 000000000..6c804f9c6
--- /dev/null
+++ b/gnu/javax/net/ssl/provider/SSLSocketFactoryImpl.java
@@ -0,0 +1,137 @@
+/* SSLSocketFactoryImpl.java --
+ Copyright (C) 2006 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.provider;
+
+import java.io.IOException;
+import java.net.InetAddress;
+import java.net.InetSocketAddress;
+import java.net.Socket;
+import java.net.UnknownHostException;
+
+import javax.net.ssl.SSLSocketFactory;
+
+/**
+ * @author Casey Marshall (csm@gnu.org)
+ */
+public class SSLSocketFactoryImpl extends SSLSocketFactory
+{
+ /**
+ * The SSLContextImpl that created us.
+ */
+ private final SSLContextImpl contextImpl;
+
+ public SSLSocketFactoryImpl(SSLContextImpl contextImpl)
+ {
+ this.contextImpl = contextImpl;
+ }
+
+ /* (non-Javadoc)
+ * @see javax.net.ssl.SSLSocketFactory#createSocket(java.net.Socket, java.lang.String, int, boolean)
+ */
+ @Override public Socket createSocket(Socket socket, String host, int port,
+ boolean autoClose)
+ throws IOException
+ {
+ return new SSLSocketImpl(contextImpl, host, port, socket, autoClose);
+ }
+
+ /* (non-Javadoc)
+ * @see javax.net.ssl.SSLSocketFactory#getDefaultCipherSuites()
+ */
+ @Override public String[] getDefaultCipherSuites()
+ {
+ return SSLEngineImpl.defaultSuites();
+ }
+
+ /* (non-Javadoc)
+ * @see javax.net.ssl.SSLSocketFactory#getSupportedCipherSuites()
+ */
+ @Override public String[] getSupportedCipherSuites()
+ {
+ return CipherSuite.availableSuiteNames().toArray(new String[0]);
+ }
+
+ /* (non-Javadoc)
+ * @see javax.net.SocketFactory#createSocket(java.lang.String, int)
+ */
+ @Override public SSLSocketImpl createSocket(String host, int port)
+ throws IOException, UnknownHostException
+ {
+ SSLSocketImpl socket = new SSLSocketImpl(contextImpl, host, port);
+ InetSocketAddress endpoint = new InetSocketAddress(host, port);
+ socket.connect(endpoint);
+ return socket;
+ }
+
+ /* (non-Javadoc)
+ * @see javax.net.SocketFactory#createSocket(java.lang.String, int, java.net.InetAddress, int)
+ */
+ @Override public SSLSocketImpl createSocket(String host, int port,
+ InetAddress localHost, int localPort)
+ throws IOException, UnknownHostException
+ {
+ SSLSocketImpl socket = createSocket(host, port);
+ socket.bind(new InetSocketAddress(localHost, localPort));
+ return socket;
+ }
+
+ /* (non-Javadoc)
+ * @see javax.net.SocketFactory#createSocket(java.net.InetAddress, int)
+ */
+ @Override public SSLSocketImpl createSocket(InetAddress host, int port)
+ throws IOException
+ {
+ SSLSocketImpl socket = new SSLSocketImpl(contextImpl,
+ host.getCanonicalHostName(), port);
+ socket.connect(new InetSocketAddress(host, port));
+ return socket;
+ }
+
+ /* (non-Javadoc)
+ * @see javax.net.SocketFactory#createSocket(java.net.InetAddress, int, java.net.InetAddress, int)
+ */
+ @Override public SSLSocketImpl createSocket(InetAddress host, int port,
+ InetAddress localHost, int localPort)
+ throws IOException
+ {
+ SSLSocketImpl socket = createSocket(host, port);
+ socket.bind(new InetSocketAddress(localHost, localPort));
+ return socket;
+ }
+}
diff --git a/gnu/javax/net/ssl/provider/SSLSocketImpl.java b/gnu/javax/net/ssl/provider/SSLSocketImpl.java
new file mode 100644
index 000000000..284094314
--- /dev/null
+++ b/gnu/javax/net/ssl/provider/SSLSocketImpl.java
@@ -0,0 +1,814 @@
+/* SSLSocketImpl.java -- implementation of an SSL client socket.
+ Copyright (C) 2006 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.provider;
+
+import gnu.classpath.debug.Component;
+import gnu.classpath.debug.SystemLogger;
+
+import java.io.DataInputStream;
+import java.io.EOFException;
+import java.io.IOException;
+import java.io.InputStream;
+import java.io.OutputStream;
+import java.net.InetAddress;
+import java.net.Socket;
+import java.net.SocketAddress;
+import java.net.SocketException;
+import java.nio.ByteBuffer;
+import java.nio.channels.SocketChannel;
+import java.util.HashSet;
+import java.util.Set;
+
+import javax.net.ssl.HandshakeCompletedEvent;
+import javax.net.ssl.HandshakeCompletedListener;
+import javax.net.ssl.SSLEngineResult;
+import javax.net.ssl.SSLException;
+import javax.net.ssl.SSLSession;
+import javax.net.ssl.SSLSocket;
+import javax.net.ssl.SSLEngineResult.HandshakeStatus;
+import javax.net.ssl.SSLEngineResult.Status;
+
+/**
+ * @author Casey Marshall (csm@gnu.org)
+ */
+public class SSLSocketImpl extends SSLSocket
+{
+ private class SocketOutputStream extends OutputStream
+ {
+ private final ByteBuffer buffer;
+ private final OutputStream out;
+
+ SocketOutputStream() throws IOException
+ {
+ buffer = ByteBuffer.wrap(new byte[getSession().getPacketBufferSize()]);
+ if (underlyingSocket != null)
+ out = underlyingSocket.getOutputStream();
+ else
+ out = SSLSocketImpl.super.getOutputStream();
+ }
+
+ @Override public void write(byte[] buf, int off, int len) throws IOException
+ {
+ if (!initialHandshakeDone
+ || engine.getHandshakeStatus() != HandshakeStatus.NOT_HANDSHAKING)
+ doHandshake();
+
+ int k = 0;
+ while (k < len)
+ {
+ synchronized (engine)
+ {
+ int l = Math.min(len-k, getSession().getApplicationBufferSize());
+ ByteBuffer in = ByteBuffer.wrap(buf, off+k, l);
+ SSLEngineResult result = engine.wrap(in, buffer);
+ if (result.getStatus() == Status.CLOSED)
+ return;
+ if (result.getStatus() != Status.OK)
+ throw new SSLException("unexpected SSL state " + result.getStatus());
+ buffer.flip();
+ out.write(buffer.array(), 0, buffer.limit());
+ k += result.bytesConsumed();
+ }
+ }
+ }
+
+ @Override public void write(int b) throws IOException
+ {
+ write(new byte[] { (byte) b });
+ }
+
+ @Override public void close() throws IOException
+ {
+ SSLSocketImpl.this.close();
+ }
+ }
+
+ private class SocketInputStream extends InputStream
+ {
+ private final ByteBuffer inBuffer;
+ private final ByteBuffer appBuffer;
+ private final DataInputStream in;
+
+ SocketInputStream() throws IOException
+ {
+ inBuffer = ByteBuffer.wrap(new byte[getSession().getPacketBufferSize()]);
+ inBuffer.limit(0);
+ appBuffer = ByteBuffer.allocate(getSession().getApplicationBufferSize());
+ appBuffer.flip();
+ if (underlyingSocket != null)
+ in = new DataInputStream(underlyingSocket.getInputStream());
+ else
+ in = new DataInputStream(SSLSocketImpl.super.getInputStream());
+ }
+
+ @Override public int read(byte[] buf, int off, int len) throws IOException
+ {
+ if (!initialHandshakeDone ||
+ engine.getHandshakeStatus() != HandshakeStatus.NOT_HANDSHAKING)
+ doHandshake();
+
+ if (!appBuffer.hasRemaining())
+ {
+ int x = in.read();
+ if (x == -1)
+ return -1;
+ inBuffer.clear();
+ inBuffer.put((byte) x);
+ inBuffer.putInt(in.readInt());
+ int reclen = inBuffer.getShort(3) & 0xFFFF;
+ in.readFully(inBuffer.array(), 5, reclen);
+ inBuffer.position(0).limit(reclen + 5);
+ synchronized (engine)
+ {
+ appBuffer.clear();
+ SSLEngineResult result = engine.unwrap(inBuffer, appBuffer);
+ Status status = result.getStatus();
+ if (status == Status.CLOSED && result.bytesProduced() == 0)
+ return -1;
+ }
+ inBuffer.compact();
+ appBuffer.flip();
+ }
+ int l = Math.min(len, appBuffer.remaining());
+ appBuffer.get(buf, off, l);
+ return l;
+ }
+
+ @Override public int read() throws IOException
+ {
+ byte[] b = new byte[1];
+ if (read(b) == -1)
+ return -1;
+ return b[0] & 0xFF;
+ }
+ }
+
+ private static final SystemLogger logger = SystemLogger.getSystemLogger();
+
+ private SSLEngineImpl engine;
+ private Set<HandshakeCompletedListener> listeners;
+ private Socket underlyingSocket;
+ private boolean isHandshaking;
+ private IOException handshakeException;
+ private boolean initialHandshakeDone = false;
+ private final boolean autoClose;
+
+ public SSLSocketImpl(SSLContextImpl contextImpl, String host, int port)
+ {
+ this(contextImpl, host, port, null, false);
+ }
+
+ public SSLSocketImpl(SSLContextImpl contextImpl, String host, int port,
+ Socket underlyingSocket, boolean autoClose)
+ {
+ engine = new SSLEngineImpl(contextImpl, host, port);
+ engine.setUseClientMode(true); // default to client mode
+ listeners = new HashSet<HandshakeCompletedListener>();
+ this.underlyingSocket = underlyingSocket;
+ this.autoClose = autoClose;
+ }
+
+ /* (non-Javadoc)
+ * @see javax.net.ssl.SSLSocket#addHandshakeCompletedListener(javax.net.ssl.HandshakeCompletedListener)
+ */
+ @Override
+ public void addHandshakeCompletedListener(HandshakeCompletedListener listener)
+ {
+ listeners.add(listener);
+ }
+
+ /* (non-Javadoc)
+ * @see javax.net.ssl.SSLSocket#getEnableSessionCreation()
+ */
+ @Override public boolean getEnableSessionCreation()
+ {
+ return engine.getEnableSessionCreation();
+ }
+
+ /* (non-Javadoc)
+ * @see javax.net.ssl.SSLSocket#getEnabledCipherSuites()
+ */
+ @Override public String[] getEnabledCipherSuites()
+ {
+ return engine.getEnabledCipherSuites();
+ }
+
+ /* (non-Javadoc)
+ * @see javax.net.ssl.SSLSocket#getEnabledProtocols()
+ */
+ @Override public String[] getEnabledProtocols()
+ {
+ return engine.getEnabledProtocols();
+ }
+
+ /* (non-Javadoc)
+ * @see javax.net.ssl.SSLSocket#getNeedClientAuth()
+ */
+ @Override public boolean getNeedClientAuth()
+ {
+ return engine.getNeedClientAuth();
+ }
+
+ /* (non-Javadoc)
+ * @see javax.net.ssl.SSLSocket#getSession()
+ */
+ @Override public SSLSession getSession()
+ {
+ return engine.getSession();
+ }
+
+ /* (non-Javadoc)
+ * @see javax.net.ssl.SSLSocket#getSupportedCipherSuites()
+ */
+ @Override public String[] getSupportedCipherSuites()
+ {
+ return engine.getSupportedCipherSuites();
+ }
+
+ /* (non-Javadoc)
+ * @see javax.net.ssl.SSLSocket#getSupportedProtocols()
+ */
+ @Override public String[] getSupportedProtocols()
+ {
+ return engine.getSupportedProtocols();
+ }
+
+ /* (non-Javadoc)
+ * @see javax.net.ssl.SSLSocket#getUseClientMode()
+ */
+ @Override public boolean getUseClientMode()
+ {
+ return engine.getUseClientMode();
+ }
+
+ /* (non-Javadoc)
+ * @see javax.net.ssl.SSLSocket#getWantClientAuth()
+ */
+ @Override public boolean getWantClientAuth()
+ {
+ return engine.getWantClientAuth();
+ }
+
+ /* (non-Javadoc)
+ * @see javax.net.ssl.SSLSocket#removeHandshakeCompletedListener(javax.net.ssl.HandshakeCompletedListener)
+ */
+ @Override
+ public void removeHandshakeCompletedListener(HandshakeCompletedListener listener)
+ {
+ listeners.remove(listener);
+ }
+
+ /* (non-Javadoc)
+ * @see javax.net.ssl.SSLSocket#setEnableSessionCreation(boolean)
+ */
+ @Override public void setEnableSessionCreation(boolean enable)
+ {
+ engine.setEnableSessionCreation(enable);
+ }
+
+ /* (non-Javadoc)
+ * @see javax.net.ssl.SSLSocket#setEnabledCipherSuites(java.lang.String[])
+ */
+ @Override public void setEnabledCipherSuites(String[] suites)
+ {
+ engine.setEnabledCipherSuites(suites);
+ }
+
+ /* (non-Javadoc)
+ * @see javax.net.ssl.SSLSocket#setEnabledProtocols(java.lang.String[])
+ */
+ @Override public void setEnabledProtocols(String[] protocols)
+ {
+ engine.setEnabledProtocols(protocols);
+ }
+
+ /* (non-Javadoc)
+ * @see javax.net.ssl.SSLSocket#setNeedClientAuth(boolean)
+ */
+ @Override public void setNeedClientAuth(boolean needAuth)
+ {
+ engine.setNeedClientAuth(needAuth);
+ }
+
+ /* (non-Javadoc)
+ * @see javax.net.ssl.SSLSocket#setUseClientMode(boolean)
+ */
+ @Override public void setUseClientMode(boolean clientMode)
+ {
+ engine.setUseClientMode(clientMode);
+ }
+
+ /* (non-Javadoc)
+ * @see javax.net.ssl.SSLSocket#setWantClientAuth(boolean)
+ */
+ @Override public void setWantClientAuth(boolean wantAuth)
+ {
+ engine.setWantClientAuth(wantAuth);
+ }
+
+ /* (non-Javadoc)
+ * @see javax.net.ssl.SSLSocket#startHandshake()
+ */
+ @Override public void startHandshake() throws IOException
+ {
+ if (isHandshaking)
+ return;
+
+ if (handshakeException != null)
+ throw handshakeException;
+
+ Thread t = new Thread(new Runnable()
+ {
+ public void run()
+ {
+ try
+ {
+ doHandshake();
+ }
+ catch (IOException ioe)
+ {
+ handshakeException = ioe;
+ }
+ }
+ }, "HandshakeThread@" + System.identityHashCode(this));
+ t.start();
+ }
+
+ void doHandshake() throws IOException
+ {
+ synchronized (engine)
+ {
+ if (isHandshaking)
+ {
+ try
+ {
+ engine.wait();
+ }
+ catch (InterruptedException ie)
+ {
+ }
+ return;
+ }
+ isHandshaking = true;
+ }
+
+ if (initialHandshakeDone)
+ throw new SSLException("rehandshaking not yet implemented");
+
+ long now = -System.currentTimeMillis();
+ engine.beginHandshake();
+
+ HandshakeStatus status = engine.getHandshakeStatus();
+ assert(status != HandshakeStatus.NOT_HANDSHAKING);
+
+ ByteBuffer inBuffer = ByteBuffer.wrap(new byte[getSession().getPacketBufferSize()]);
+ inBuffer.position(inBuffer.limit());
+ ByteBuffer outBuffer = ByteBuffer.wrap(new byte[getSession().getPacketBufferSize()]);
+ ByteBuffer emptyBuffer = ByteBuffer.allocate(0);
+ SSLEngineResult result = null;
+
+ DataInputStream sockIn = null;
+ if (underlyingSocket != null)
+ sockIn = new DataInputStream(underlyingSocket.getInputStream());
+ else
+ sockIn = new DataInputStream(super.getInputStream());
+
+ OutputStream sockOut = null;
+ if (underlyingSocket != null)
+ sockOut = underlyingSocket.getOutputStream();
+ else
+ sockOut = super.getOutputStream();
+
+ while (status != HandshakeStatus.NOT_HANDSHAKING
+ && status != HandshakeStatus.FINISHED)
+ {
+ logger.logv(Component.SSL_HANDSHAKE, "socket processing state {0}",
+ status);
+
+ if (inBuffer.capacity() != getSession().getPacketBufferSize())
+ {
+ ByteBuffer b
+ = ByteBuffer.wrap(new byte[getSession().getPacketBufferSize()]);
+ if (inBuffer.hasRemaining())
+ b.put(inBuffer).flip();
+ inBuffer = b;
+ }
+ if (outBuffer.capacity() != getSession().getPacketBufferSize())
+ outBuffer
+ = ByteBuffer.wrap(new byte[getSession().getPacketBufferSize()]);
+
+ switch (status)
+ {
+ case NEED_UNWRAP:
+ // Read in a single SSL record.
+ inBuffer.clear();
+ int i = sockIn.read();
+ if (i == -1)
+ throw new EOFException();
+ if ((i & 0x80) == 0x80) // SSLv2 client hello.
+ {
+ inBuffer.put((byte) i);
+ int v2len = (i & 0x7f) << 8;
+ i = sockIn.read();
+ v2len = v2len | (i & 0xff);
+ inBuffer.put((byte) i);
+ sockIn.readFully(inBuffer.array(), 2, v2len);
+ inBuffer.position(0).limit(v2len + 2);
+ }
+ else
+ {
+ inBuffer.put((byte) i);
+ inBuffer.putInt(sockIn.readInt());
+ int reclen = inBuffer.getShort(3) & 0xFFFF;
+ sockIn.readFully(inBuffer.array(), 5, reclen);
+ inBuffer.position(0).limit(reclen + 5);
+ }
+ result = engine.unwrap(inBuffer, emptyBuffer);
+ status = result.getHandshakeStatus();
+ if (result.getStatus() != Status.OK)
+ throw new SSLException("unexpected SSL status "
+ + result.getStatus());
+ break;
+
+ case NEED_WRAP:
+ {
+ outBuffer.clear();
+ result = engine.wrap(emptyBuffer, outBuffer);
+ status = result.getHandshakeStatus();
+ if (result.getStatus() != Status.OK)
+ throw new SSLException("unexpected SSL status "
+ + result.getStatus());
+ outBuffer.flip();
+ sockOut.write(outBuffer.array(), outBuffer.position(),
+ outBuffer.limit());
+ }
+ break;
+
+ case NEED_TASK:
+ {
+ Runnable task;
+ while ((task = engine.getDelegatedTask()) != null)
+ task.run();
+ status = engine.getHandshakeStatus();
+ }
+ break;
+
+ case FINISHED:
+ break;
+ }
+ }
+
+ initialHandshakeDone = true;
+
+ HandshakeCompletedEvent hce = new HandshakeCompletedEvent(this, getSession());
+ for (HandshakeCompletedListener l : listeners)
+ {
+ try
+ {
+ l.handshakeCompleted(hce);
+ }
+ catch (ThreadDeath td)
+ {
+ throw td;
+ }
+ catch (Throwable x)
+ {
+ logger.log(Component.WARNING,
+ "HandshakeCompletedListener threw exception", x);
+ }
+ }
+
+ now += System.currentTimeMillis();
+ if (Debug.DEBUG)
+ logger.logv(Component.SSL_HANDSHAKE,
+ "handshake completed in {0}ms in thread {1}", now,
+ Thread.currentThread().getName());
+
+ synchronized (engine)
+ {
+ isHandshaking = false;
+ engine.notifyAll();
+ }
+ }
+
+ // Methods overriding Socket.
+
+ @Override public void bind(SocketAddress bindpoint) throws IOException
+ {
+ if (underlyingSocket != null)
+ underlyingSocket.bind(bindpoint);
+ else
+ super.bind(bindpoint);
+ }
+
+ @Override public void connect(SocketAddress endpoint) throws IOException
+ {
+ if (underlyingSocket != null)
+ underlyingSocket.connect(endpoint);
+ else
+ super.connect(endpoint);
+ }
+
+ @Override public void connect(SocketAddress endpoint, int timeout)
+ throws IOException
+ {
+ if (underlyingSocket != null)
+ underlyingSocket.connect(endpoint, timeout);
+ else
+ super.connect(endpoint, timeout);
+ }
+
+ @Override public InetAddress getInetAddress()
+ {
+ if (underlyingSocket != null)
+ return underlyingSocket.getInetAddress();
+ return super.getInetAddress();
+ }
+
+ @Override public InetAddress getLocalAddress()
+ {
+ if (underlyingSocket != null)
+ return underlyingSocket.getLocalAddress();
+ return super.getLocalAddress();
+ }
+
+ @Override public int getPort()
+ {
+ if (underlyingSocket != null)
+ return underlyingSocket.getPort();
+ return super.getPort();
+ }
+
+ @Override public int getLocalPort()
+ {
+ if (underlyingSocket != null)
+ return underlyingSocket.getLocalPort();
+ return super.getLocalPort();
+ }
+
+ @Override public SocketAddress getRemoteSocketAddress()
+ {
+ if (underlyingSocket != null)
+ return underlyingSocket.getRemoteSocketAddress();
+ return super.getRemoteSocketAddress();
+ }
+
+ public SocketAddress getLocalSocketAddress()
+ {
+ if (underlyingSocket != null)
+ return underlyingSocket.getLocalSocketAddress();
+ return super.getLocalSocketAddress();
+ }
+
+ @Override public SocketChannel getChannel()
+ {
+ throw new UnsupportedOperationException("use javax.net.ssl.SSLEngine for NIO");
+ }
+
+ @Override public InputStream getInputStream() throws IOException
+ {
+ return new SocketInputStream();
+ }
+
+ @Override public OutputStream getOutputStream() throws IOException
+ {
+ return new SocketOutputStream();
+ }
+
+ @Override public void setTcpNoDelay(boolean on) throws SocketException
+ {
+ if (underlyingSocket != null)
+ underlyingSocket.setTcpNoDelay(on);
+ else
+ super.setTcpNoDelay(on);
+ }
+
+ @Override public boolean getTcpNoDelay() throws SocketException
+ {
+ if (underlyingSocket != null)
+ return underlyingSocket.getTcpNoDelay();
+ return super.getTcpNoDelay();
+ }
+
+ @Override public void setSoLinger(boolean on, int linger) throws SocketException
+ {
+ if (underlyingSocket != null)
+ underlyingSocket.setSoLinger(on, linger);
+ else
+ super.setSoLinger(on, linger);
+ }
+
+ public int getSoLinger() throws SocketException
+ {
+ if (underlyingSocket != null)
+ return underlyingSocket.getSoLinger();
+ return super.getSoLinger();
+ }
+
+ @Override public void sendUrgentData(int x) throws IOException
+ {
+ throw new UnsupportedOperationException("not supported");
+ }
+
+ @Override public void setOOBInline(boolean on) throws SocketException
+ {
+ if (underlyingSocket != null)
+ underlyingSocket.setOOBInline(on);
+ else
+ super.setOOBInline(on);
+ }
+
+ @Override public boolean getOOBInline() throws SocketException
+ {
+ if (underlyingSocket != null)
+ return underlyingSocket.getOOBInline();
+ return super.getOOBInline();
+ }
+
+ @Override public void setSoTimeout(int timeout) throws SocketException
+ {
+ if (underlyingSocket != null)
+ underlyingSocket.setSoTimeout(timeout);
+ else
+ super.setSoTimeout(timeout);
+ }
+
+ @Override public int getSoTimeout() throws SocketException
+ {
+ if (underlyingSocket != null)
+ return underlyingSocket.getSoTimeout();
+ return super.getSoTimeout();
+ }
+
+ @Override public void setSendBufferSize(int size) throws SocketException
+ {
+ if (underlyingSocket != null)
+ underlyingSocket.setSendBufferSize(size);
+ else
+ super.setSendBufferSize(size);
+ }
+
+ @Override public int getSendBufferSize() throws SocketException
+ {
+ if (underlyingSocket != null)
+ return underlyingSocket.getSendBufferSize();
+ return super.getSendBufferSize();
+ }
+
+ @Override public void setReceiveBufferSize(int size) throws SocketException
+ {
+ if (underlyingSocket != null)
+ underlyingSocket.setReceiveBufferSize(size);
+ else
+ underlyingSocket.setReceiveBufferSize(size);
+ }
+
+ @Override public int getReceiveBufferSize() throws SocketException
+ {
+ if (underlyingSocket != null)
+ return underlyingSocket.getReceiveBufferSize();
+ return super.getReceiveBufferSize();
+ }
+
+ @Override public void setKeepAlive(boolean on) throws SocketException
+ {
+ if (underlyingSocket != null)
+ underlyingSocket.setKeepAlive(on);
+ else
+ super.setKeepAlive(on);
+ }
+
+ @Override public boolean getKeepAlive() throws SocketException
+ {
+ if (underlyingSocket != null)
+ return underlyingSocket.getKeepAlive();
+ return super.getKeepAlive();
+ }
+
+ @Override public void setTrafficClass(int tc) throws SocketException
+ {
+ if (underlyingSocket != null)
+ underlyingSocket.setTrafficClass(tc);
+ else
+ super.setTrafficClass(tc);
+ }
+
+ @Override public int getTrafficClass() throws SocketException
+ {
+ if (underlyingSocket != null)
+ return underlyingSocket.getTrafficClass();
+ return super.getTrafficClass();
+ }
+
+ @Override public void setReuseAddress(boolean reuseAddress)
+ throws SocketException
+ {
+ if (underlyingSocket != null)
+ underlyingSocket.setReuseAddress(reuseAddress);
+ else
+ super.setReuseAddress(reuseAddress);
+ }
+
+ @Override public boolean getReuseAddress() throws SocketException
+ {
+ if (underlyingSocket != null)
+ return underlyingSocket.getReuseAddress();
+ return super.getReuseAddress();
+ }
+
+ @Override public void close() throws IOException
+ {
+ // XXX closure alerts.
+ if (underlyingSocket != null && autoClose)
+ underlyingSocket.close();
+ else
+ super.close();
+ }
+
+ @Override public void shutdownInput() throws IOException
+ {
+ if (underlyingSocket != null)
+ underlyingSocket.shutdownInput();
+ else
+ super.shutdownInput();
+ }
+
+ @Override public void shutdownOutput() throws IOException
+ {
+ if (underlyingSocket != null)
+ underlyingSocket.shutdownOutput();
+ else
+ super.shutdownOutput();
+ }
+
+ @Override public boolean isConnected()
+ {
+ if (underlyingSocket != null)
+ return underlyingSocket.isConnected();
+ return super.isConnected();
+ }
+
+ @Override public boolean isBound()
+ {
+ if (underlyingSocket != null)
+ return underlyingSocket.isBound();
+ return super.isBound();
+ }
+
+ @Override public boolean isClosed()
+ {
+ if (underlyingSocket != null)
+ return underlyingSocket.isClosed();
+ return super.isClosed();
+ }
+
+ @Override public boolean isInputShutdown()
+ {
+ if (underlyingSocket != null)
+ return underlyingSocket.isInputShutdown();
+ return super.isInputShutdown();
+ }
+
+ @Override public boolean isOutputShutdown()
+ {
+ if (underlyingSocket != null)
+ return underlyingSocket.isOutputShutdown();
+ return super.isOutputShutdown();
+ }
+}
diff --git a/gnu/javax/net/ssl/provider/ServerHandshake.java b/gnu/javax/net/ssl/provider/ServerHandshake.java
index 047b6f306..d339c1cc5 100644
--- a/gnu/javax/net/ssl/provider/ServerHandshake.java
+++ b/gnu/javax/net/ssl/provider/ServerHandshake.java
@@ -38,23 +38,17 @@ exception statement from your version. */
package gnu.javax.net.ssl.provider;
-import static gnu.javax.net.ssl.provider.Extension.Type.*;
-import static gnu.javax.net.ssl.provider.KeyExchangeAlgorithm.*;
import static gnu.javax.net.ssl.provider.ServerHandshake.State.*;
-import static gnu.javax.net.ssl.provider.SignatureAlgorithm.*;
import static gnu.javax.net.ssl.provider.Handshake.Type.*;
-import gnu.classpath.Configuration;
import gnu.classpath.debug.Component;
import gnu.java.security.action.GetSecurityPropertyAction;
import gnu.javax.crypto.key.dh.GnuDHPublicKey;
import gnu.javax.net.ssl.AbstractSessionContext;
import gnu.javax.net.ssl.Session;
-import gnu.javax.net.ssl.Session.ID;
+import gnu.javax.net.ssl.provider.Alert.Description;
import gnu.javax.net.ssl.provider.CertificateRequest.ClientCertificateType;
-import gnu.javax.net.ssl.provider.ServerNameList.ServerName;
-import java.nio.BufferOverflowException;
import java.nio.ByteBuffer;
import java.security.AccessController;
@@ -74,26 +68,18 @@ import java.util.Arrays;
import java.util.HashSet;
import java.util.List;
import java.util.logging.Level;
-import java.util.zip.Deflater;
-import java.util.zip.Inflater;
import javax.crypto.BadPaddingException;
import javax.crypto.Cipher;
import javax.crypto.IllegalBlockSizeException;
-import javax.crypto.KeyAgreement;
-import javax.crypto.Mac;
import javax.crypto.NoSuchPaddingException;
import javax.crypto.interfaces.DHPrivateKey;
import javax.crypto.interfaces.DHPublicKey;
import javax.crypto.spec.DHParameterSpec;
-import javax.crypto.spec.IvParameterSpec;
-import javax.crypto.spec.SecretKeySpec;
import javax.net.ssl.SSLException;
import javax.net.ssl.SSLPeerUnverifiedException;
import javax.net.ssl.SSLSession;
-import javax.net.ssl.SSLSessionContext;
import javax.net.ssl.X509ExtendedKeyManager;
-import javax.net.ssl.X509TrustManager;
import javax.net.ssl.SSLEngineResult.HandshakeStatus;
import javax.security.auth.x500.X500Principal;
@@ -140,12 +126,7 @@ class ServerHandshake extends AbstractHandshake
private State state;
- private final SSLEngineImpl engine;
-
/* Handshake result fields. */
- private CompressionMethod compression;
- private Random clientRandom;
- private Random serverRandom;
private ByteBuffer outBuffer;
private boolean clientHadExtensions = false;
private boolean continuedSession = false;
@@ -154,28 +135,22 @@ class ServerHandshake extends AbstractHandshake
private X509Certificate clientCert = null;
private X509Certificate localCert = null;
private boolean helloV2 = false;
-
- /**
- * We remember this to ensure that we use a different one if the client's
- * session ID happens to be the same as our random one (this is possible,
- * if the random we're initialized with is predictable).
- */
- private Session.ID clientSessionID;
-
- private InputSecurityParameters inParams;
- private OutputSecurityParameters outParams;
-
- private KeyAgreement keyAgreement;
private KeyPair dhPair;
+
+ // Delegated tasks we use.
+ private GenDH genDH;
+ private CertVerifier certVerifier;
+ private CertLoader certLoader;
+ private DelegatedTask keyExchangeTask;
ServerHandshake (boolean writeHelloRequest, final SSLEngineImpl engine)
throws NoSuchAlgorithmException
{
+ super(engine);
if (writeHelloRequest)
state = WRITE_HELLO_REQUEST;
else
state = READ_CLIENT_HELLO;
- this.engine = engine;
handshakeOffset = 0;
}
@@ -249,7 +224,6 @@ class ServerHandshake extends AbstractHandshake
* implementation.
*
* XXX Maybe consider implementing lzo (GNUTLS supports that).
- * XXX Maybe add way to disable zlib support, through properties.
*/
private static CompressionMethod chooseCompression (final CompressionMethodList comps)
throws SSLException
@@ -284,7 +258,7 @@ class ServerHandshake extends AbstractHandshake
public @Override HandshakeStatus implHandleInput()
throws SSLException
- {
+ {
if (state == DONE)
return HandshakeStatus.FINISHED;
@@ -317,9 +291,9 @@ class ServerHandshake extends AbstractHandshake
// supports).
case READ_CLIENT_HELLO:
if (handshake.type () != CLIENT_HELLO)
- throw new SSLException ("expecting client hello");
- // XXX throw better exception.
-
+ throw new AlertException(new Alert(Alert.Level.FATAL,
+ Alert.Description.UNEXPECTED_MESSAGE));
+
{
ClientHello hello = (ClientHello) handshake.body ();
engine.session().version
@@ -426,8 +400,8 @@ class ServerHandshake extends AbstractHandshake
= cert.certificates().toArray(new X509Certificate[0]);
if (chain.length == 0)
throw new CertificateException("no certificates in chain");
- X509TrustManager tm = engine.contextImpl.trustManager;
- tm.checkClientTrusted(chain, null);
+ certVerifier = new CertVerifier(false, chain);
+ tasks.add(certVerifier);
engine.session().setPeerCertificates(chain);
clientCert = chain[0];
// Delay setting 'peerVerified' until CertificateVerify.
@@ -474,114 +448,24 @@ class ServerHandshake extends AbstractHandshake
new GnuDHPublicKey(null, myKey.getParams().getP(),
myKey.getParams().getG(),
pub.publicValue());
- diffieHellmanPhase2(clientKey);
+ keyExchangeTask = new DHPhase(clientKey);
+ tasks.add(keyExchangeTask);
}
+
if (engine.session().suite.keyExchangeAlgorithm() ==
KeyExchangeAlgorithm.RSA)
{
EncryptedPreMasterSecret secret = (EncryptedPreMasterSecret)
kex.exchangeKeys();
- Cipher rsa = null;
- try
- {
- rsa = Cipher.getInstance("RSA");
- rsa.init(Cipher.DECRYPT_MODE, localCert);
- }
- catch (InvalidKeyException ike)
- {
- throw new SSLException(ike);
- }
- catch (NoSuchAlgorithmException nsae)
- {
- throw new SSLException(nsae);
- }
- catch (NoSuchPaddingException nspe)
- {
- // Shouldn't happen; RSA only has one padding here.
- throw new SSLException(nspe);
- }
-
- try
- {
- preMasterSecret = rsa.doFinal(secret.encryptedSecret());
- }
- catch (BadPaddingException bpe)
- {
- throw new SSLException(bpe);
- }
- catch (IllegalBlockSizeException ibse)
- {
- throw new SSLException(ibse);
- }
+ keyExchangeTask = new RSAKeyExchange(secret.encryptedSecret());
+ tasks.add(keyExchangeTask);
}
// XXX SRP
- // Generate the master keys.
- generateMasterSecret(clientRandom, serverRandom, engine.session());
-
- // Initialize our security parameters.
- byte[][] keys = generateKeys(clientRandom, serverRandom,
- engine.session());
- try
- {
- CipherSuite s = engine.session().suite;
- Cipher inCipher = s.cipher();
- Mac inMac = s.mac(engine.session().version);
- Inflater inflater = (compression == CompressionMethod.ZLIB
- ? new Inflater() : null);
- inCipher.init(Cipher.DECRYPT_MODE,
- new SecretKeySpec(keys[2],
- s.cipherAlgorithm().toString()),
- new IvParameterSpec(keys[4]));
- inMac.init(new SecretKeySpec(keys[0],
- inMac.getAlgorithm()));
- inParams = new InputSecurityParameters(inCipher, inMac,
- inflater,
- engine.session(), s);
-
- Cipher outCipher = s.cipher();
- Mac outMac = s.mac(engine.session().version);
- Deflater deflater = (compression == CompressionMethod.ZLIB
- ? new Deflater() : null);
- outCipher.init(Cipher.ENCRYPT_MODE,
- new SecretKeySpec(keys[3],
- s.cipherAlgorithm().toString()),
- new IvParameterSpec(keys[5]));
- outMac.init(new SecretKeySpec(keys[1],
- outMac.getAlgorithm()));
- outParams = new OutputSecurityParameters(outCipher, outMac,
- deflater,
- engine.session(), s);
- }
- catch (InvalidAlgorithmParameterException iape)
- {
- throw new SSLException(iape);
- }
- catch (InvalidKeyException ike)
- {
- throw new SSLException(ike);
- }
- catch (NoSuchAlgorithmException nsae)
- {
- throw new SSLException(nsae);
- }
- catch (NoSuchPaddingException nspe)
- {
- throw new SSLException(nspe);
- }
-
if (clientCert != null)
state = READ_CERTIFICATE_VERIFY;
else
- {
- if (continuedSession)
- {
- engine.changeCipherSpec();
- state = WRITE_FINISHED;
- }
- else
- state = READ_FINISHED;
- }
+ state = READ_FINISHED;
}
break;
@@ -602,7 +486,8 @@ class ServerHandshake extends AbstractHandshake
try
{
verifyClient(verify.signature());
- engine.session().setPeerVerified(true);
+ if (certVerifier != null && certVerifier.verified())
+ engine.session().setPeerVerified(true);
}
catch (SignatureException se)
{
@@ -634,10 +519,9 @@ class ServerHandshake extends AbstractHandshake
case READ_FINISHED:
{
if (handshake.type() != FINISHED)
- {
- throw new SSLException("expecting FINISHED message");
- }
-
+ throw new AlertException(new Alert(Alert.Level.FATAL,
+ Description.UNEXPECTED_MESSAGE));
+
Finished clientFinished = (Finished) handshake.body();
MessageDigest md5copy = null;
@@ -696,12 +580,14 @@ class ServerHandshake extends AbstractHandshake
handshakeOffset += handshake.length() + 4;
+ if (!tasks.isEmpty())
+ return HandshakeStatus.NEED_TASK;
if (state.isReadState())
return HandshakeStatus.NEED_UNWRAP;
if (state.isWriteState())
return HandshakeStatus.NEED_WRAP;
- return HandshakeStatus.FINISHED; // XXX ???
+ return HandshakeStatus.FINISHED;
}
public @Override HandshakeStatus implHandleOutput (ByteBuffer fragment)
@@ -818,15 +704,15 @@ output_loop:
case WRITE_SERVER_HELLO:
{
ServerHelloBuilder hello = new ServerHelloBuilder();
- hello.setVersion (engine.session().version);
+ hello.setVersion(engine.session().version);
Random r = hello.random();
- r.setGmtUnixTime ((int) (System.currentTimeMillis() / 1000));
+ r.setGmtUnixTime(Util.unixTime());
byte[] nonce = new byte[28];
engine.session().random().nextBytes(nonce);
r.setRandomBytes(nonce);
- serverRandom = r.copy ();
+ serverRandom = r.copy();
hello.setSessionId(engine.session().getId());
- hello.setCipherSuite (engine.session().suite);
+ hello.setCipherSuite(engine.session().suite);
hello.setCompressionMethod(compression);
if (clientHadExtensions)
{
@@ -847,15 +733,38 @@ output_loop:
fragment.put((ByteBuffer) outBuffer.duplicate().limit(outBuffer.position() + l));
outBuffer.position(outBuffer.position() + l);
+
if (continuedSession)
{
+ byte[][] keys = generateKeys(clientRandom, serverRandom,
+ engine.session());
+ setupSecurityParameters(keys, false, engine, compression);
engine.changeCipherSpec();
state = WRITE_FINISHED;
}
+ else if (engine.session().suite.signatureAlgorithm()
+ != SignatureAlgorithm.ANONYMOUS)
+ {
+ certLoader = new CertLoader();
+ tasks.add(certLoader);
+ state = WRITE_CERTIFICATE;
+ break output_loop;
+ }
+ else if (engine.session().suite.isEphemeralDH())
+ {
+ genDH = new GenDH();
+ tasks.add(genDH);
+ state = WRITE_SERVER_KEY_EXCHANGE;
+ break output_loop;
+ }
+ else if (engine.getWantClientAuth() || engine.getNeedClientAuth())
+ {
+ state = WRITE_CERTIFICATE_REQUEST;
+ }
else
- state = WRITE_CERTIFICATE;
+ state = WRITE_SERVER_HELLO_DONE;
}
- break output_loop; // XXX temporary
+ break;
// Certificate.
//
@@ -864,54 +773,15 @@ output_loop:
// itself (usually, servers must authenticate).
case WRITE_CERTIFICATE:
{
- if (engine.session().suite.keyExchangeAlgorithm() == null)
- {
- state = WRITE_SERVER_KEY_EXCHANGE;
- break;
- }
- String sigAlg = null;
- switch (engine.session().suite.keyExchangeAlgorithm())
- {
- case NONE:
- break;
-
- case RSA:
- sigAlg = "RSA";
- break;
-
- case DIFFIE_HELLMAN:
- if (engine.session().suite.isEphemeralDH())
- sigAlg = "DHE_";
- else
- sigAlg = "DH_";
- switch (engine.session().suite.signatureAlgorithm())
- {
- case RSA:
- sigAlg += "RSA";
- break;
-
- case DSA:
- sigAlg += "DSS";
- break;
- }
-
- case SRP:
- switch (engine.session().suite.signatureAlgorithm())
- {
- case RSA:
- sigAlg = "SRP_RSA";
- break;
-
- case DSA:
- sigAlg = "SRP_DSS";
- break;
- }
- }
- X509ExtendedKeyManager km = engine.contextImpl.keyManager;
- Principal[] issuers = null; // XXX use TrustedAuthorities extension.
- keyAlias = km.chooseEngineServerAlias(sigAlg, issuers, engine);
- X509Certificate[] chain = km.getCertificateChain(keyAlias);
-
+ // We must have scheduled a certificate loader to run.
+ assert(certLoader != null);
+ assert(certLoader.hasRun());
+ if (certLoader.thrown() != null)
+ throw new AlertException(new Alert(Alert.Level.FATAL,
+ Alert.Description.HANDSHAKE_FAILURE),
+ certLoader.thrown());
+ java.security.cert.Certificate[] chain
+ = engine.session().getLocalCertificates();
CertificateBuilder cert = new CertificateBuilder(CertificateType.X509);
try
{
@@ -921,8 +791,6 @@ output_loop:
{
throw new SSLException(ce);
}
- engine.session().setLocalCertificates(chain);
- localCert = chain[0];
if (Debug.DEBUG)
{
@@ -939,7 +807,22 @@ output_loop:
fragment.put((ByteBuffer) outBuffer.duplicate().limit(outBuffer.position() + l));
outBuffer.position(outBuffer.position() + l);
- state = WRITE_SERVER_KEY_EXCHANGE;
+ CipherSuite s = engine.session().suite;
+ if (s.isEphemeralDH()
+ || (s.keyExchangeAlgorithm() == KeyExchangeAlgorithm.DIFFIE_HELLMAN
+ && s.signatureAlgorithm() == SignatureAlgorithm.ANONYMOUS))
+ {
+ genDH = new GenDH();
+ tasks.add(genDH);
+ state = WRITE_SERVER_KEY_EXCHANGE;
+ break output_loop;
+ }
+ else if (engine.getWantClientAuth() || engine.getNeedClientAuth())
+ {
+ state = WRITE_CERTIFICATE_REQUEST;
+ }
+ else
+ state = WRITE_SERVER_HELLO_DONE;
}
break output_loop; // XXX temporary
@@ -954,58 +837,46 @@ output_loop:
{
KeyExchangeAlgorithm kex = engine.session().suite.keyExchangeAlgorithm();
+ ByteBuffer paramBuffer = null;
+ ByteBuffer sigBuffer = null;
if (kex == KeyExchangeAlgorithm.DIFFIE_HELLMAN)
{
- genDiffieHellman();
+ assert(genDH != null);
+ assert(genDH.hasRun());
+ if (genDH.thrown() != null)
+ throw new AlertException(new Alert(Alert.Level.FATAL,
+ Alert.Description.HANDSHAKE_FAILURE),
+ genDH.thrown());
+ assert(dhPair != null);
initDiffieHellman((DHPrivateKey) dhPair.getPrivate(),
engine.session().random());
+ paramBuffer = genDH.paramsBuffer;
+ sigBuffer = genDH.sigBuffer;
}
// XXX handle SRP
- if (kex != KeyExchangeAlgorithm.NONE
- && kex != KeyExchangeAlgorithm.RSA
- && engine.session().suite.isEphemeralDH())
- {
- // This key exchange requires server params; construct them.
- ByteBuffer paramBuffer = null;
+ ServerKeyExchangeBuilder ske
+ = new ServerKeyExchangeBuilder(engine.session().suite);
+ ske.setParams(paramBuffer);
+ if (sigBuffer != null)
+ ske.setSignature(sigBuffer);
- if (kex == KeyExchangeAlgorithm.DIFFIE_HELLMAN)
- {
- DHPublicKey pubKey = (DHPublicKey) dhPair.getPublic();
- ServerDHParams params =
- new ServerDHParams(pubKey.getParams().getP(),
- pubKey.getParams().getG(),
- pubKey.getY());
- paramBuffer = params.buffer();
- if (Debug.DEBUG)
- logger.logv(Component.SSL_HANDSHAKE, "server DH params:\n{0}",
- Util.hexDump(paramBuffer));
- }
- // XXX handle SRP
-
- ByteBuffer sigBuffer = signParams(paramBuffer.duplicate());
- ServerKeyExchangeBuilder ske
- = new ServerKeyExchangeBuilder(engine.session().suite);
- ske.setParams(paramBuffer);
- ske.setSignature(sigBuffer);
-
- if (Debug.DEBUG)
- logger.log(Component.SSL_HANDSHAKE, "{0}", ske);
+ if (Debug.DEBUG)
+ logger.log(Component.SSL_HANDSHAKE, "{0}", ske);
- outBuffer = ske.buffer();
- int l = Math.min(fragment.remaining(), outBuffer.remaining());
- fragment.putInt((SERVER_KEY_EXCHANGE.getValue() << 24)
- | (ske.length() & 0xFFFFFF));
- fragment.put((ByteBuffer) outBuffer.duplicate().limit(outBuffer.position() + l));
- outBuffer.position(outBuffer.position() + l);
- }
+ outBuffer = ske.buffer();
+ int l = Math.min(fragment.remaining(), outBuffer.remaining());
+ fragment.putInt((SERVER_KEY_EXCHANGE.getValue() << 24)
+ | (ske.length() & 0xFFFFFF));
+ fragment.put((ByteBuffer) outBuffer.duplicate().limit(outBuffer.position() + l));
+ outBuffer.position(outBuffer.position() + l);
if (engine.getWantClientAuth() || engine.getNeedClientAuth())
state = WRITE_CERTIFICATE_REQUEST;
else
state = WRITE_SERVER_HELLO_DONE;
}
- break output_loop; // XXX temporary
+ break;
// Certificate Request.
//
@@ -1048,7 +919,7 @@ output_loop:
state = WRITE_SERVER_HELLO_DONE;
}
- break output_loop; // XXX temporary
+ break;
// Server Hello Done.
//
@@ -1115,6 +986,8 @@ output_loop:
break;
}
}
+ if (!tasks.isEmpty())
+ return HandshakeStatus.NEED_TASK;
if (state.isWriteState() || outBuffer.hasRemaining())
return HandshakeStatus.NEED_WRAP;
if (state.isReadState())
@@ -1125,6 +998,8 @@ output_loop:
@Override HandshakeStatus status()
{
+ if (!tasks.isEmpty())
+ return HandshakeStatus.NEED_TASK;
if (state.isReadState())
return HandshakeStatus.NEED_UNWRAP;
if (state.isWriteState())
@@ -1132,19 +1007,21 @@ output_loop:
return HandshakeStatus.FINISHED;
}
-
- @Override InputSecurityParameters getInputParams() throws SSLException
- {
- if (inParams == null)
- throw new SSLException("premature usage of input security parameters");
- return inParams;
- }
-
- @Override OutputSecurityParameters getOutputParams() throws SSLException
+
+ @Override void checkKeyExchange() throws SSLException
{
- if (outParams == null)
- throw new SSLException("premature usage of output security parameters");
- return outParams;
+ if (continuedSession) // No key exchange needed.
+ return;
+ if (keyExchangeTask == null) // An error if we never created one.
+ throw new AlertException(new Alert(Alert.Level.FATAL,
+ Alert.Description.INTERNAL_ERROR));
+ if (!keyExchangeTask.hasRun()) // An error if the caller never ran it.
+ throw new AlertException(new Alert(Alert.Level.FATAL,
+ Alert.Description.INTERNAL_ERROR));
+ if (keyExchangeTask.thrown() != null) // An error was thrown.
+ throw new AlertException(new Alert(Alert.Level.FATAL,
+ Alert.Description.HANDSHAKE_FAILURE),
+ keyExchangeTask.thrown());
}
@Override void handleV2Hello(ByteBuffer hello)
@@ -1154,84 +1031,23 @@ output_loop:
sha.update((ByteBuffer) hello.duplicate().position(2).limit(len+2));
helloV2 = true;
}
-
- /**
- * Generate, or fetch from our certificate, the Diffie-Hellman exchange
- * parameters.
- *
- * @throws SSLException
- */
- private void genDiffieHellman() throws SSLException
- {
- try
- {
- // XXX generating a DH key, especially given a large prime, can
- // take a while. We should perhaps look into making this a delegated
- // task, so we don't block other connections when generating keys.
- //
- // Also, I should investigate whether or not these DH parameters are
- // "secure" or not; I mean, I'm not really worried, because SSH uses
- // similar static parameters, but this *could* be a potential hole.
- if (engine.session().suite.isEphemeralDH())
- {
- KeyPairGenerator dhGen = KeyPairGenerator.getInstance("DH");
- DHParameterSpec dhparams = DiffieHellman.getParams().getParams();
- dhGen.initialize(dhparams, engine.session().random());
- dhPair = dhGen.generateKeyPair();
- }
- else
- {
- X509Certificate cert = (X509Certificate)
- engine.session().getLocalCertificates()[0];
- PrivateKey key = engine.contextImpl.keyManager.getPrivateKey(keyAlias);
- dhPair = new KeyPair(cert.getPublicKey(), key);
- }
- if (Debug.DEBUG_KEY_EXCHANGE)
- logger.logv(Component.SSL_KEY_EXCHANGE,
- "Diffie-Hellman public:{0} private:{1}",
- dhPair.getPublic(), dhPair.getPrivate());
- }
- catch (InvalidAlgorithmParameterException iape)
- {
- throw new SSLException(iape);
- }
- catch (NoSuchAlgorithmException nsae)
- {
- throw new SSLException(nsae);
- }
- }
-
- private ByteBuffer signParams(ByteBuffer serverParams) throws SSLException
+ private ByteBuffer signParams(ByteBuffer serverParams)
+ throws NoSuchAlgorithmException, InvalidKeyException, SignatureException
{
- try
- {
- SignatureAlgorithm alg = engine.session().suite.signatureAlgorithm();
- java.security.Signature sig
- = java.security.Signature.getInstance(alg.algorithm());
- PrivateKey key = engine.contextImpl.keyManager.getPrivateKey(keyAlias);
- if (Debug.DEBUG_KEY_EXCHANGE)
- logger.logv(Component.SSL_HANDSHAKE, "server key: {0}", key);
- sig.initSign(key);
- sig.update(clientRandom.buffer());
- sig.update(serverRandom.buffer());
- sig.update(serverParams);
- byte[] sigVal = sig.sign();
- Signature signature = new Signature(sigVal, engine.session().suite.signatureAlgorithm());
- return signature.buffer();
- }
- catch (NoSuchAlgorithmException nsae)
- {
- throw new SSLException(nsae);
- }
- catch (InvalidKeyException ike)
- {
- throw new SSLException(ike);
- }
- catch (SignatureException se)
- {
- throw new SSLException(se);
- }
+ SignatureAlgorithm alg = engine.session().suite.signatureAlgorithm();
+ java.security.Signature sig
+ = java.security.Signature.getInstance(alg.algorithm());
+ PrivateKey key = engine.contextImpl.keyManager.getPrivateKey(keyAlias);
+ if (Debug.DEBUG_KEY_EXCHANGE)
+ logger.logv(Component.SSL_HANDSHAKE, "server key: {0}", key);
+ sig.initSign(key);
+ sig.update(clientRandom.buffer());
+ sig.update(serverRandom.buffer());
+ sig.update(serverParams);
+ byte[] sigVal = sig.sign();
+ Signature signature = new Signature(sigVal, engine.session().suite.signatureAlgorithm());
+ return signature.buffer();
}
private void verifyClient(byte[] sigValue) throws SSLException, SignatureException
@@ -1275,4 +1091,126 @@ output_loop:
throw new SSLException(nsae);
}
}
+
+ // Delegated tasks.
+
+ class CertLoader extends DelegatedTask
+ {
+ CertLoader()
+ {
+ }
+
+ public void implRun() throws SSLException
+ {
+ String sigAlg = null;
+ switch (engine.session().suite.keyExchangeAlgorithm())
+ {
+ case NONE:
+ break;
+
+ case RSA:
+ sigAlg = "RSA";
+ break;
+
+ case DIFFIE_HELLMAN:
+ if (engine.session().suite.isEphemeralDH())
+ sigAlg = "DHE_";
+ else
+ sigAlg = "DH_";
+ switch (engine.session().suite.signatureAlgorithm())
+ {
+ case RSA:
+ sigAlg += "RSA";
+ break;
+
+ case DSA:
+ sigAlg += "DSS";
+ break;
+ }
+
+ case SRP:
+ switch (engine.session().suite.signatureAlgorithm())
+ {
+ case RSA:
+ sigAlg = "SRP_RSA";
+ break;
+
+ case DSA:
+ sigAlg = "SRP_DSS";
+ break;
+ }
+ }
+ X509ExtendedKeyManager km = engine.contextImpl.keyManager;
+ Principal[] issuers = null; // XXX use TrustedAuthorities extension.
+ keyAlias = km.chooseEngineServerAlias(sigAlg, issuers, engine);
+ if (keyAlias == null)
+ throw new SSLException("no certificates available");
+ X509Certificate[] chain = km.getCertificateChain(keyAlias);
+ engine.session().setLocalCertificates(chain);
+ localCert = chain[0];
+ if (engine.session().suite.keyExchangeAlgorithm() == KeyExchangeAlgorithm.DIFFIE_HELLMAN
+ && !engine.session().suite.isEphemeralDH())
+ dhPair = new KeyPair(localCert.getPublicKey(),
+ km.getPrivateKey(keyAlias));
+ }
+ }
+
+ /**
+ * Delegated task for generating Diffie-Hellman parameters.
+ */
+ private class GenDH extends DelegatedTask
+ {
+ ByteBuffer paramsBuffer;
+ ByteBuffer sigBuffer;
+
+ protected void implRun()
+ throws NoSuchAlgorithmException, InvalidAlgorithmParameterException,
+ InvalidKeyException, SignatureException
+ {
+ KeyPairGenerator dhGen = KeyPairGenerator.getInstance("DH");
+ DHParameterSpec dhparams = DiffieHellman.getParams().getParams();
+ dhGen.initialize(dhparams, engine.session().random());
+ dhPair = dhGen.generateKeyPair();
+ DHPublicKey pub = (DHPublicKey) dhPair.getPublic();
+
+ // Generate the parameters message.
+ ServerDHParams params = new ServerDHParams(pub.getParams().getP(),
+ pub.getParams().getG(),
+ pub.getY());
+ paramsBuffer = params.buffer();
+
+ // Sign the parameters, if needed.
+ if (engine.session().suite.signatureAlgorithm() != SignatureAlgorithm.ANONYMOUS)
+ {
+ sigBuffer = signParams(paramsBuffer);
+ paramsBuffer.rewind();
+ }
+ if (Debug.DEBUG_KEY_EXCHANGE)
+ logger.logv(Component.SSL_KEY_EXCHANGE,
+ "Diffie-Hellman public:{0} private:{1}",
+ dhPair.getPublic(), dhPair.getPrivate());
+ }
+ }
+
+ class RSAKeyExchange extends DelegatedTask
+ {
+ private final byte[] encryptedPreMasterSecret;
+
+ RSAKeyExchange(byte[] encryptedPreMasterSecret)
+ {
+ this.encryptedPreMasterSecret = encryptedPreMasterSecret;
+ }
+
+ public void implRun()
+ throws BadPaddingException, IllegalBlockSizeException, InvalidKeyException,
+ NoSuchAlgorithmException, NoSuchPaddingException, SSLException
+ {
+ Cipher rsa = Cipher.getInstance("RSA");
+ rsa.init(Cipher.DECRYPT_MODE, localCert);
+ preMasterSecret = rsa.doFinal(encryptedPreMasterSecret);
+ generateMasterSecret(clientRandom, serverRandom, engine.session());
+ byte[][] keys = generateKeys(clientRandom, serverRandom, engine.session());
+ setupSecurityParameters(keys, false, engine, compression);
+ }
+ }
} \ No newline at end of file
diff --git a/gnu/javax/net/ssl/provider/X509TrustManagerFactory.java b/gnu/javax/net/ssl/provider/X509TrustManagerFactory.java
index b41c8bc3a..1a0591284 100644
--- a/gnu/javax/net/ssl/provider/X509TrustManagerFactory.java
+++ b/gnu/javax/net/ssl/provider/X509TrustManagerFactory.java
@@ -47,6 +47,7 @@ import java.util.HashSet;
import java.util.LinkedList;
import java.util.Set;
+import java.security.AccessController;
import java.security.InvalidAlgorithmParameterException;
import java.security.KeyStore;
import java.security.KeyStoreException;
@@ -65,6 +66,7 @@ import javax.net.ssl.TrustManager;
import javax.net.ssl.TrustManagerFactorySpi;
import javax.net.ssl.X509TrustManager;
+import gnu.java.security.action.GetPropertyAction;
import gnu.java.security.x509.X509CertPath;
import gnu.javax.net.ssl.NullManagerParameters;
import gnu.javax.net.ssl.StaticTrustAnchors;
@@ -79,19 +81,22 @@ public class X509TrustManagerFactory extends TrustManagerFactorySpi
// Constants and fields.
// -------------------------------------------------------------------------
- private static final String sep = Util.getProperty("file.separator");
+ private static final String sep
+ = AccessController.doPrivileged(new GetPropertyAction("file.separator"));
/**
* The location of the JSSE key store.
*/
- private static final String JSSE_CERTS = Util.getProperty("java.home")
- + sep + "lib" + sep + "security" + sep + "jssecerts";
+ private static final String JSSE_CERTS
+ = AccessController.doPrivileged(new GetPropertyAction("java.home"))
+ + sep + "lib" + sep + "security" + sep + "jssecerts";
/**
* The location of the system key store, containing the CA certs.
*/
- private static final String CA_CERTS = Util.getProperty("java.home")
- + sep + "lib" + sep + "security" + sep + "cacerts";
+ private static final String CA_CERTS
+ = AccessController.doPrivileged(new GetPropertyAction("java.home"))
+ + sep + "lib" + sep + "security" + sep + "cacerts";
private Manager current;
@@ -136,13 +141,14 @@ public class X509TrustManagerFactory extends TrustManagerFactorySpi
{
if (store == null)
{
- String s = Util.getProperty("javax.net.ssl.trustStoreType");
+ GetPropertyAction gpa = new GetPropertyAction("javax.net.ssl.trustStoreType");
+ String s = AccessController.doPrivileged(gpa);
if (s == null)
s = KeyStore.getDefaultType();
store = KeyStore.getInstance(s);
try
{
- s = Util.getProperty("javax.net.ssl.trustStore");
+ s = AccessController.doPrivileged(gpa.setParameters("javax.net.ssl.trustStore"));
FileInputStream in = null;
if (s == null)
{
@@ -159,20 +165,20 @@ public class X509TrustManagerFactory extends TrustManagerFactorySpi
{
in = new FileInputStream(s);
}
- String p = Util.getProperty("javax.net.ssl.trustStorePassword");
+ String p = AccessController.doPrivileged(gpa.setParameters("javax.net.ssl.trustStorePassword"));
store.load(in, p != null ? p.toCharArray() : null);
}
catch (IOException ioe)
{
- throw new KeyStoreException(ioe.toString());
+ throw new KeyStoreException(ioe);
}
catch (CertificateException ce)
{
- throw new KeyStoreException(ce.toString());
+ throw new KeyStoreException(ce);
}
catch (NoSuchAlgorithmException nsae)
{
- throw new KeyStoreException(nsae.toString());
+ throw new KeyStoreException(nsae);
}
}
@@ -263,6 +269,9 @@ public class X509TrustManagerFactory extends TrustManagerFactorySpi
try
{
params = new PKIXParameters(anchors);
+ // XXX we probably do want to enable revocation, but it's a pain
+ // in the ass.
+ params.setRevocationEnabled(false);
}
catch (InvalidAlgorithmParameterException iape)
{
diff --git a/java/util/Collections.java b/java/util/Collections.java
index 40268379a..9c91bf279 100644
--- a/java/util/Collections.java
+++ b/java/util/Collections.java
@@ -6619,7 +6619,7 @@ public class Collections
if (entries == null)
{
Class<Map.Entry<K,V>> klass =
- (Class<Map.Entry<K,V>>) Map.Entry.class;
+ (Class<Map.Entry<K,V>>) (Class) Map.Entry.class;
entries = new CheckedEntrySet<Map.Entry<K,V>,K,V>(m.entrySet(),
klass,
keyType,
diff --git a/java/util/concurrent/CopyOnWriteArrayList.java b/java/util/concurrent/CopyOnWriteArrayList.java
new file mode 100644
index 000000000..ee9f7ac3a
--- /dev/null
+++ b/java/util/concurrent/CopyOnWriteArrayList.java
@@ -0,0 +1,438 @@
+/* CopyOnWriteArrayList.java
+ Copyright (C) 2006 Free Software Foundation
+
+This file is 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, 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; see the file COPYING. If not, write to the
+Free Software Foundation, Inc., 51 Franklin Street, 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 java.util.concurrent;
+
+import java.io.IOException;
+import java.io.ObjectInputStream;
+import java.io.ObjectOutputStream;
+import java.io.Serializable;
+import java.lang.reflect.Array;
+import java.util.AbstractList;
+import java.util.Collection;
+import java.util.Iterator;
+import java.util.List;
+import java.util.RandomAccess;
+
+/** @since 1.5 */
+public class CopyOnWriteArrayList<E> extends AbstractList<E> implements
+ List<E>, RandomAccess, Cloneable, Serializable
+{
+ /**
+ * Where the data is stored.
+ */
+ private transient E[] data;
+
+ /**
+ * Construct a new ArrayList with the default capacity (16).
+ */
+ public CopyOnWriteArrayList()
+ {
+ data = (E[]) new Object[0];
+ }
+
+ /**
+ * Construct a new ArrayList, and initialize it with the elements in the
+ * supplied Collection. The initial capacity is 110% of the Collection's size.
+ *
+ * @param c
+ * the collection whose elements will initialize this list
+ * @throws NullPointerException
+ * if c is null
+ */
+ public CopyOnWriteArrayList(Collection< ? extends E> c)
+ {
+ // FIXME ... correct? use c.toArray()
+ data = (E[]) new Object[c.size()];
+ int index = 0;
+ for (E value : c)
+ data[index++] = value;
+ }
+
+ /**
+ * Returns the number of elements in this list.
+ *
+ * @return the list size
+ */
+ public int size()
+ {
+ return data.length;
+ }
+
+ /**
+ * Checks if the list is empty.
+ *
+ * @return true if there are no elements
+ */
+ public boolean isEmpty()
+ {
+ return data.length == 0;
+ }
+
+ /**
+ * Returns true iff element is in this ArrayList.
+ *
+ * @param e
+ * the element whose inclusion in the List is being tested
+ * @return true if the list contains e
+ */
+ public boolean contains(Object e)
+ {
+ return indexOf(e) != -1;
+ }
+
+ /**
+ * Returns the lowest index at which element appears in this List, or -1 if it
+ * does not appear.
+ *
+ * @param e
+ * the element whose inclusion in the List is being tested
+ * @return the index where e was found
+ */
+ public int indexOf(Object e)
+ {
+ E[] data = this.data;
+ for (int i = 0; i < data.length; i++)
+ if (equals(e, data[i]))
+ return i;
+ return -1;
+ }
+
+ /**
+ * Returns the highest index at which element appears in this List, or -1 if
+ * it does not appear.
+ *
+ * @param e
+ * the element whose inclusion in the List is being tested
+ * @return the index where e was found
+ */
+ public int lastIndexOf(Object e)
+ {
+ E[] data = this.data;
+ for (int i = data.length - 1; i >= 0; i--)
+ if (equals(e, data[i]))
+ return i;
+ return -1;
+ }
+
+ /**
+ * Creates a shallow copy of this ArrayList (elements are not cloned).
+ *
+ * @return the cloned object
+ */
+ public Object clone()
+ {
+ CopyOnWriteArrayList<E> clone = null;
+ try
+ {
+ clone = (CopyOnWriteArrayList<E>) super.clone();
+ clone.data = (E[]) data.clone();
+ }
+ catch (CloneNotSupportedException e)
+ {
+ // Impossible to get here.
+ }
+ return clone;
+ }
+
+ /**
+ * Returns an Object array containing all of the elements in this ArrayList.
+ * The array is independent of this list.
+ *
+ * @return an array representation of this list
+ */
+ public Object[] toArray()
+ {
+ E[] data = this.data;
+ E[] array = (E[]) new Object[data.length];
+ System.arraycopy(data, 0, array, 0, data.length);
+ return array;
+ }
+
+ /**
+ * Returns an Array whose component type is the runtime component type of the
+ * passed-in Array. The returned Array is populated with all of the elements
+ * in this ArrayList. If the passed-in Array is not large enough to store all
+ * of the elements in this List, a new Array will be created and returned; if
+ * the passed-in Array is <i>larger</i> than the size of this List, then
+ * size() index will be set to null.
+ *
+ * @param a
+ * the passed-in Array
+ * @return an array representation of this list
+ * @throws ArrayStoreException
+ * if the runtime type of a does not allow an element in this list
+ * @throws NullPointerException
+ * if a is null
+ */
+ public <T> T[] toArray(T[] a)
+ {
+ E[] data = this.data;
+ if (a.length < data.length)
+ a = (T[]) Array.newInstance(a.getClass().getComponentType(), data.length);
+ else if (a.length > data.length)
+ a[data.length] = null;
+ System.arraycopy(data, 0, a, 0, data.length);
+ return a;
+ }
+
+ /**
+ * Retrieves the element at the user-supplied index.
+ *
+ * @param index
+ * the index of the element we are fetching
+ * @throws IndexOutOfBoundsException
+ * if index &lt; 0 || index &gt;= size()
+ */
+ public E get(int index)
+ {
+ return data[index];
+ }
+
+ /**
+ * Sets the element at the specified index. The new element, e, can be an
+ * object of any type or null.
+ *
+ * @param index
+ * the index at which the element is being set
+ * @param e
+ * the element to be set
+ * @return the element previously at the specified index
+ * @throws IndexOutOfBoundsException
+ * if index &lt; 0 || index &gt;= 0
+ */
+ public synchronized E set(int index, E e)
+ {
+ E result = data[index];
+ E[] newData = data.clone();
+ newData[index] = e;
+ data = newData;
+ return result;
+ }
+
+ /**
+ * Appends the supplied element to the end of this list. The element, e, can
+ * be an object of any type or null.
+ *
+ * @param e
+ * the element to be appended to this list
+ * @return true, the add will always succeed
+ */
+ public synchronized boolean add(E e)
+ {
+ E[] data = this.data;
+ E[] newData = (E[]) new Object[data.length];
+ System.arraycopy(data, 0, newData, 0, data.length);
+ newData[data.length] = e;
+ this.data = newData;
+ return true;
+ }
+
+ /**
+ * Adds the supplied element at the specified index, shifting all elements
+ * currently at that index or higher one to the right. The element, e, can be
+ * an object of any type or null.
+ *
+ * @param index
+ * the index at which the element is being added
+ * @param e
+ * the item being added
+ * @throws IndexOutOfBoundsException
+ * if index &lt; 0 || index &gt; size()
+ */
+ public synchronized void add(int index, E e)
+ {
+ E[] data = this.data;
+ E[] newData = (E[]) new Object[data.length];
+ System.arraycopy(data, 0, newData, 0, index);
+ newData[index] = e;
+ System.arraycopy(data, index, newData, index + 1, data.length - index);
+ this.data = newData;
+ }
+
+ /**
+ * Removes the element at the user-supplied index.
+ *
+ * @param index
+ * the index of the element to be removed
+ * @return the removed Object
+ * @throws IndexOutOfBoundsException
+ * if index &lt; 0 || index &gt;= size()
+ */
+ public synchronized E remove(int index)
+ {
+ E[] data = this.data;
+ E[] newData = (E[]) new Object[data.length - 1];
+ System.arraycopy(data, 0, newData, 0, index - 1);
+ System.arraycopy(data, index + 1, newData, index,
+ data.length - index - 1);
+ E r = data[index];
+ this.data = newData;
+ return r;
+ }
+
+ /**
+ * Removes all elements from this List
+ */
+ public synchronized void clear()
+ {
+ data = (E[]) new Object[0];
+ }
+
+ /**
+ * Add each element in the supplied Collection to this List. It is undefined
+ * what happens if you modify the list while this is taking place; for
+ * example, if the collection contains this list. c can contain objects of any
+ * type, as well as null values.
+ *
+ * @param c
+ * a Collection containing elements to be added to this List
+ * @return true if the list was modified, in other words c is not empty
+ * @throws NullPointerException
+ * if c is null
+ */
+ public synchronized boolean addAll(Collection< ? extends E> c)
+ {
+ return addAll(data.length, c);
+ }
+
+ /**
+ * Add all elements in the supplied collection, inserting them beginning at
+ * the specified index. c can contain objects of any type, as well as null
+ * values.
+ *
+ * @param index
+ * the index at which the elements will be inserted
+ * @param c
+ * the Collection containing the elements to be inserted
+ * @throws IndexOutOfBoundsException
+ * if index &lt; 0 || index &gt; 0
+ * @throws NullPointerException
+ * if c is null
+ */
+ public synchronized boolean addAll(int index, Collection< ? extends E> c)
+ {
+ E[] data = this.data;
+ Iterator<? extends E> itr = c.iterator();
+ int csize = c.size();
+ if (csize == 0)
+ return false;
+
+ E[] newData = (E[]) new Object[data.length + csize];
+ System.arraycopy(data, 0, newData, 0, data.length);
+ int end = data.length;
+ for (E value : c)
+ newData[end++] = value;
+ this.data = newData;
+ return true;
+ }
+
+ public synchronized boolean addIfAbsent(E val)
+ {
+ if (contains(val))
+ return false;
+ add(val);
+ return true;
+ }
+
+ public synchronized int addAllAbsent(Collection<? extends E> c)
+ {
+ int result = 0;
+ for (E val : c)
+ {
+ if (addIfAbsent(val))
+ ++result;
+ }
+ return result;
+ }
+
+ /**
+ * Serializes this object to the given stream.
+ *
+ * @param s
+ * the stream to write to
+ * @throws IOException
+ * if the underlying stream fails
+ * @serialData the size field (int), the length of the backing array (int),
+ * followed by its elements (Objects) in proper order.
+ */
+ private void writeObject(ObjectOutputStream s) throws IOException
+ {
+ // The 'size' field.
+ s.defaultWriteObject();
+ // We serialize unused list entries to preserve capacity.
+ int len = data.length;
+ s.writeInt(len);
+ // it would be more efficient to just write "size" items,
+ // this need readObject read "size" items too.
+ for (int i = 0; i < data.length; i++)
+ s.writeObject(data[i]);
+ }
+
+ /**
+ * Deserializes this object from the given stream.
+ *
+ * @param s
+ * the stream to read from
+ * @throws ClassNotFoundException
+ * if the underlying stream fails
+ * @throws IOException
+ * if the underlying stream fails
+ * @serialData the size field (int), the length of the backing array (int),
+ * followed by its elements (Objects) in proper order.
+ */
+ private void readObject(ObjectInputStream s) throws IOException,
+ ClassNotFoundException
+ {
+ // the `size' field.
+ s.defaultReadObject();
+ int capacity = s.readInt();
+ data = (E[]) new Object[capacity];
+ for (int i = 0; i < capacity; i++)
+ data[i] = (E) s.readObject();
+ }
+
+ static final boolean equals(Object o1, Object o2)
+ {
+ return o1 == null ? o2 == null : o1.equals(o2);
+ }
+
+ Object[] getArray()
+ {
+ return data;
+ }
+}