From 554fd8c5195424bdbcabf5de30fdc183aba391bd Mon Sep 17 00:00:00 2001 From: upstream source tree Date: Sun, 15 Mar 2015 20:14:05 -0400 Subject: obtained gcc-4.6.4.tar.bz2 from upstream website; verified gcc-4.6.4.tar.bz2.sig; imported gcc-4.6.4 source tree from verified upstream tarball. downloading a git-generated archive based on the 'upstream' tag should provide you with a source tree that is binary identical to the one extracted from the above tarball. if you have obtained the source via the command 'git clone', however, do note that line-endings of files in your working directory might differ from line-endings of the respective files in the upstream repository. --- .../javax/net/ssl/provider/AbstractHandshake.java | 1205 +++++++++++++++++ .../gnu/javax/net/ssl/provider/Alert.java | 288 ++++ .../gnu/javax/net/ssl/provider/AlertException.java | 101 ++ .../gnu/javax/net/ssl/provider/Builder.java | 66 + .../gnu/javax/net/ssl/provider/Certificate.java | 177 +++ .../javax/net/ssl/provider/CertificateBuilder.java | 94 ++ .../javax/net/ssl/provider/CertificateRequest.java | 155 +++ .../ssl/provider/CertificateRequestBuilder.java | 111 ++ .../net/ssl/provider/CertificateStatusRequest.java | 272 ++++ .../net/ssl/provider/CertificateStatusType.java | 13 + .../javax/net/ssl/provider/CertificateType.java | 62 + .../gnu/javax/net/ssl/provider/CertificateURL.java | 388 ++++++ .../javax/net/ssl/provider/CertificateVerify.java | 83 ++ .../javax/net/ssl/provider/CipherAlgorithm.java | 47 + .../gnu/javax/net/ssl/provider/CipherSuite.java | 837 ++++++++++++ .../javax/net/ssl/provider/CipherSuiteList.java | 283 ++++ .../ssl/provider/ClientCertificateTypeList.java | 227 ++++ .../net/ssl/provider/ClientDHE_PSKParameters.java | 122 ++ .../ssl/provider/ClientDiffieHellmanPublic.java | 129 ++ .../javax/net/ssl/provider/ClientHandshake.java | 1153 ++++++++++++++++ .../gnu/javax/net/ssl/provider/ClientHello.java | 240 ++++ .../javax/net/ssl/provider/ClientHelloBuilder.java | 137 ++ .../gnu/javax/net/ssl/provider/ClientHelloV2.java | 158 +++ .../javax/net/ssl/provider/ClientKeyExchange.java | 132 ++ .../net/ssl/provider/ClientKeyExchangeBuilder.java | 75 ++ .../net/ssl/provider/ClientPSKParameters.java | 121 ++ .../net/ssl/provider/ClientRSA_PSKParameters.java | 122 ++ .../javax/net/ssl/provider/CompressionMethod.java | 69 + .../net/ssl/provider/CompressionMethodList.java | 281 ++++ .../gnu/javax/net/ssl/provider/Constructed.java | 86 ++ .../gnu/javax/net/ssl/provider/ContentType.java | 89 ++ .../gnu/javax/net/ssl/provider/Debug.java | 66 + .../gnu/javax/net/ssl/provider/DelegatedTask.java | 93 ++ .../gnu/javax/net/ssl/provider/DiffieHellman.java | 289 ++++ .../javax/net/ssl/provider/EmptyExchangeKeys.java | 77 ++ .../net/ssl/provider/EncryptedPreMasterSecret.java | 148 +++ .../gnu/javax/net/ssl/provider/ExchangeKeys.java | 54 + .../gnu/javax/net/ssl/provider/Extension.java | 246 ++++ .../gnu/javax/net/ssl/provider/ExtensionList.java | 290 +++++ .../gnu/javax/net/ssl/provider/Finished.java | 173 +++ .../gnu/javax/net/ssl/provider/Handshake.java | 299 +++++ .../gnu/javax/net/ssl/provider/HelloRequest.java | 72 + .../net/ssl/provider/InputSecurityParameters.java | 334 +++++ .../gnu/javax/net/ssl/provider/Jessie.java | 102 ++ .../net/ssl/provider/KeyExchangeAlgorithm.java | 57 + .../gnu/javax/net/ssl/provider/MacAlgorithm.java | 47 + .../gnu/javax/net/ssl/provider/MacException.java | 53 + .../javax/net/ssl/provider/MaxFragmentLength.java | 59 + .../net/ssl/provider/OutputSecurityParameters.java | 294 +++++ .../provider/PreSharedKeyManagerFactoryImpl.java | 118 ++ .../javax/net/ssl/provider/ProtocolVersion.java | 200 +++ .../gnu/javax/net/ssl/provider/Random.java | 150 +++ .../gnu/javax/net/ssl/provider/Record.java | 198 +++ .../net/ssl/provider/SRPTrustManagerFactory.java | 223 ++++ .../gnu/javax/net/ssl/provider/SSLContextImpl.java | 315 +++++ .../gnu/javax/net/ssl/provider/SSLEngineImpl.java | 842 ++++++++++++ .../gnu/javax/net/ssl/provider/SSLHMac.java | 158 +++ .../net/ssl/provider/SSLRSASignatureImpl.java | 234 ++++ .../gnu/javax/net/ssl/provider/SSLRandom.java | 165 +++ .../ssl/provider/SSLServerSocketFactoryImpl.java | 108 ++ .../net/ssl/provider/SSLServerSocketImpl.java | 199 +++ .../net/ssl/provider/SSLSocketFactoryImpl.java | 143 ++ .../gnu/javax/net/ssl/provider/SSLSocketImpl.java | 740 +++++++++++ .../javax/net/ssl/provider/SSLv3HMacMD5Impl.java | 116 ++ .../javax/net/ssl/provider/SSLv3HMacSHAImpl.java | 116 ++ .../net/ssl/provider/ServerDHE_PSKParameters.java | 148 +++ .../gnu/javax/net/ssl/provider/ServerDHParams.java | 248 ++++ .../javax/net/ssl/provider/ServerHandshake.java | 1377 ++++++++++++++++++++ .../gnu/javax/net/ssl/provider/ServerHello.java | 231 ++++ .../javax/net/ssl/provider/ServerHelloBuilder.java | 131 ++ .../javax/net/ssl/provider/ServerHelloDone.java | 66 + .../javax/net/ssl/provider/ServerKeyExchange.java | 173 +++ .../net/ssl/provider/ServerKeyExchangeBuilder.java | 89 ++ .../net/ssl/provider/ServerKeyExchangeParams.java | 50 + .../gnu/javax/net/ssl/provider/ServerNameList.java | 311 +++++ .../net/ssl/provider/ServerPSKParameters.java | 127 ++ .../javax/net/ssl/provider/ServerRSAParams.java | 163 +++ .../net/ssl/provider/ServerRSA_PSKParameters.java | 62 + .../gnu/javax/net/ssl/provider/SessionImpl.java | 192 +++ .../gnu/javax/net/ssl/provider/Signature.java | 157 +++ .../javax/net/ssl/provider/SignatureAlgorithm.java | 62 + .../net/ssl/provider/SimpleSessionContext.java | 144 ++ .../gnu/javax/net/ssl/provider/TLSHMac.java | 137 ++ .../gnu/javax/net/ssl/provider/TLSRandom.java | 252 ++++ .../gnu/javax/net/ssl/provider/TruncatedHMAC.java | 76 ++ .../javax/net/ssl/provider/TrustedAuthorities.java | 297 +++++ .../net/ssl/provider/UnresolvedExtensionValue.java | 81 ++ .../classpath/gnu/javax/net/ssl/provider/Util.java | 495 +++++++ .../javax/net/ssl/provider/X500PrincipalList.java | 272 ++++ .../net/ssl/provider/X509KeyManagerFactory.java | 396 ++++++ .../net/ssl/provider/X509TrustManagerFactory.java | 295 +++++ 91 files changed, 20133 insertions(+) create mode 100644 libjava/classpath/gnu/javax/net/ssl/provider/AbstractHandshake.java create mode 100644 libjava/classpath/gnu/javax/net/ssl/provider/Alert.java create mode 100644 libjava/classpath/gnu/javax/net/ssl/provider/AlertException.java create mode 100644 libjava/classpath/gnu/javax/net/ssl/provider/Builder.java create mode 100644 libjava/classpath/gnu/javax/net/ssl/provider/Certificate.java create mode 100644 libjava/classpath/gnu/javax/net/ssl/provider/CertificateBuilder.java create mode 100644 libjava/classpath/gnu/javax/net/ssl/provider/CertificateRequest.java create mode 100644 libjava/classpath/gnu/javax/net/ssl/provider/CertificateRequestBuilder.java create mode 100644 libjava/classpath/gnu/javax/net/ssl/provider/CertificateStatusRequest.java create mode 100644 libjava/classpath/gnu/javax/net/ssl/provider/CertificateStatusType.java create mode 100644 libjava/classpath/gnu/javax/net/ssl/provider/CertificateType.java create mode 100644 libjava/classpath/gnu/javax/net/ssl/provider/CertificateURL.java create mode 100644 libjava/classpath/gnu/javax/net/ssl/provider/CertificateVerify.java create mode 100644 libjava/classpath/gnu/javax/net/ssl/provider/CipherAlgorithm.java create mode 100644 libjava/classpath/gnu/javax/net/ssl/provider/CipherSuite.java create mode 100644 libjava/classpath/gnu/javax/net/ssl/provider/CipherSuiteList.java create mode 100644 libjava/classpath/gnu/javax/net/ssl/provider/ClientCertificateTypeList.java create mode 100644 libjava/classpath/gnu/javax/net/ssl/provider/ClientDHE_PSKParameters.java create mode 100644 libjava/classpath/gnu/javax/net/ssl/provider/ClientDiffieHellmanPublic.java create mode 100644 libjava/classpath/gnu/javax/net/ssl/provider/ClientHandshake.java create mode 100644 libjava/classpath/gnu/javax/net/ssl/provider/ClientHello.java create mode 100644 libjava/classpath/gnu/javax/net/ssl/provider/ClientHelloBuilder.java create mode 100644 libjava/classpath/gnu/javax/net/ssl/provider/ClientHelloV2.java create mode 100644 libjava/classpath/gnu/javax/net/ssl/provider/ClientKeyExchange.java create mode 100644 libjava/classpath/gnu/javax/net/ssl/provider/ClientKeyExchangeBuilder.java create mode 100644 libjava/classpath/gnu/javax/net/ssl/provider/ClientPSKParameters.java create mode 100644 libjava/classpath/gnu/javax/net/ssl/provider/ClientRSA_PSKParameters.java create mode 100644 libjava/classpath/gnu/javax/net/ssl/provider/CompressionMethod.java create mode 100644 libjava/classpath/gnu/javax/net/ssl/provider/CompressionMethodList.java create mode 100644 libjava/classpath/gnu/javax/net/ssl/provider/Constructed.java create mode 100644 libjava/classpath/gnu/javax/net/ssl/provider/ContentType.java create mode 100644 libjava/classpath/gnu/javax/net/ssl/provider/Debug.java create mode 100644 libjava/classpath/gnu/javax/net/ssl/provider/DelegatedTask.java create mode 100644 libjava/classpath/gnu/javax/net/ssl/provider/DiffieHellman.java create mode 100644 libjava/classpath/gnu/javax/net/ssl/provider/EmptyExchangeKeys.java create mode 100644 libjava/classpath/gnu/javax/net/ssl/provider/EncryptedPreMasterSecret.java create mode 100644 libjava/classpath/gnu/javax/net/ssl/provider/ExchangeKeys.java create mode 100644 libjava/classpath/gnu/javax/net/ssl/provider/Extension.java create mode 100644 libjava/classpath/gnu/javax/net/ssl/provider/ExtensionList.java create mode 100644 libjava/classpath/gnu/javax/net/ssl/provider/Finished.java create mode 100644 libjava/classpath/gnu/javax/net/ssl/provider/Handshake.java create mode 100644 libjava/classpath/gnu/javax/net/ssl/provider/HelloRequest.java create mode 100644 libjava/classpath/gnu/javax/net/ssl/provider/InputSecurityParameters.java create mode 100644 libjava/classpath/gnu/javax/net/ssl/provider/Jessie.java create mode 100644 libjava/classpath/gnu/javax/net/ssl/provider/KeyExchangeAlgorithm.java create mode 100644 libjava/classpath/gnu/javax/net/ssl/provider/MacAlgorithm.java create mode 100644 libjava/classpath/gnu/javax/net/ssl/provider/MacException.java create mode 100644 libjava/classpath/gnu/javax/net/ssl/provider/MaxFragmentLength.java create mode 100644 libjava/classpath/gnu/javax/net/ssl/provider/OutputSecurityParameters.java create mode 100644 libjava/classpath/gnu/javax/net/ssl/provider/PreSharedKeyManagerFactoryImpl.java create mode 100644 libjava/classpath/gnu/javax/net/ssl/provider/ProtocolVersion.java create mode 100644 libjava/classpath/gnu/javax/net/ssl/provider/Random.java create mode 100644 libjava/classpath/gnu/javax/net/ssl/provider/Record.java create mode 100644 libjava/classpath/gnu/javax/net/ssl/provider/SRPTrustManagerFactory.java create mode 100644 libjava/classpath/gnu/javax/net/ssl/provider/SSLContextImpl.java create mode 100644 libjava/classpath/gnu/javax/net/ssl/provider/SSLEngineImpl.java create mode 100644 libjava/classpath/gnu/javax/net/ssl/provider/SSLHMac.java create mode 100644 libjava/classpath/gnu/javax/net/ssl/provider/SSLRSASignatureImpl.java create mode 100644 libjava/classpath/gnu/javax/net/ssl/provider/SSLRandom.java create mode 100644 libjava/classpath/gnu/javax/net/ssl/provider/SSLServerSocketFactoryImpl.java create mode 100644 libjava/classpath/gnu/javax/net/ssl/provider/SSLServerSocketImpl.java create mode 100644 libjava/classpath/gnu/javax/net/ssl/provider/SSLSocketFactoryImpl.java create mode 100644 libjava/classpath/gnu/javax/net/ssl/provider/SSLSocketImpl.java create mode 100644 libjava/classpath/gnu/javax/net/ssl/provider/SSLv3HMacMD5Impl.java create mode 100644 libjava/classpath/gnu/javax/net/ssl/provider/SSLv3HMacSHAImpl.java create mode 100644 libjava/classpath/gnu/javax/net/ssl/provider/ServerDHE_PSKParameters.java create mode 100644 libjava/classpath/gnu/javax/net/ssl/provider/ServerDHParams.java create mode 100644 libjava/classpath/gnu/javax/net/ssl/provider/ServerHandshake.java create mode 100644 libjava/classpath/gnu/javax/net/ssl/provider/ServerHello.java create mode 100644 libjava/classpath/gnu/javax/net/ssl/provider/ServerHelloBuilder.java create mode 100644 libjava/classpath/gnu/javax/net/ssl/provider/ServerHelloDone.java create mode 100644 libjava/classpath/gnu/javax/net/ssl/provider/ServerKeyExchange.java create mode 100644 libjava/classpath/gnu/javax/net/ssl/provider/ServerKeyExchangeBuilder.java create mode 100644 libjava/classpath/gnu/javax/net/ssl/provider/ServerKeyExchangeParams.java create mode 100644 libjava/classpath/gnu/javax/net/ssl/provider/ServerNameList.java create mode 100644 libjava/classpath/gnu/javax/net/ssl/provider/ServerPSKParameters.java create mode 100644 libjava/classpath/gnu/javax/net/ssl/provider/ServerRSAParams.java create mode 100644 libjava/classpath/gnu/javax/net/ssl/provider/ServerRSA_PSKParameters.java create mode 100644 libjava/classpath/gnu/javax/net/ssl/provider/SessionImpl.java create mode 100644 libjava/classpath/gnu/javax/net/ssl/provider/Signature.java create mode 100644 libjava/classpath/gnu/javax/net/ssl/provider/SignatureAlgorithm.java create mode 100644 libjava/classpath/gnu/javax/net/ssl/provider/SimpleSessionContext.java create mode 100644 libjava/classpath/gnu/javax/net/ssl/provider/TLSHMac.java create mode 100644 libjava/classpath/gnu/javax/net/ssl/provider/TLSRandom.java create mode 100644 libjava/classpath/gnu/javax/net/ssl/provider/TruncatedHMAC.java create mode 100644 libjava/classpath/gnu/javax/net/ssl/provider/TrustedAuthorities.java create mode 100644 libjava/classpath/gnu/javax/net/ssl/provider/UnresolvedExtensionValue.java create mode 100644 libjava/classpath/gnu/javax/net/ssl/provider/Util.java create mode 100644 libjava/classpath/gnu/javax/net/ssl/provider/X500PrincipalList.java create mode 100644 libjava/classpath/gnu/javax/net/ssl/provider/X509KeyManagerFactory.java create mode 100644 libjava/classpath/gnu/javax/net/ssl/provider/X509TrustManagerFactory.java (limited to 'libjava/classpath/gnu/javax/net/ssl/provider') diff --git a/libjava/classpath/gnu/javax/net/ssl/provider/AbstractHandshake.java b/libjava/classpath/gnu/javax/net/ssl/provider/AbstractHandshake.java new file mode 100644 index 000000000..bf03ed77f --- /dev/null +++ b/libjava/classpath/gnu/javax/net/ssl/provider/AbstractHandshake.java @@ -0,0 +1,1205 @@ +/* AbstractHandshake.java -- abstract handshake handler. + 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 gnu.java.security.action.GetSecurityPropertyAction; +import gnu.java.security.prng.IRandom; +import gnu.java.security.prng.LimitReachedException; +import gnu.java.security.util.ByteArray; +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.KeyManagementException; +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.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.SecretKey; +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 + * subclasses of this class (one for the server, one for the client) + * handle the HANDSHAKE content-type in communications. + */ +public abstract class AbstractHandshake +{ + protected static final SystemLogger logger = SystemLogger.SYSTEM; + + /** + * "server finished" -- TLS 1.0 and later + */ + protected static final byte[] SERVER_FINISHED + = new byte[] { + 115, 101, 114, 118, 101, 114, 32, 102, 105, 110, 105, 115, + 104, 101, 100 + }; + + /** + * "client finished" -- TLS 1.0 and later + */ + protected static final byte[] CLIENT_FINISHED + = new byte[] { + 99, 108, 105, 101, 110, 116, 32, 102, 105, 110, 105, 115, + 104, 101, 100 + }; + + /** + * "key expansion" -- TLS 1.0 and later + */ + private static final byte[] KEY_EXPANSION = + new byte[] { 107, 101, 121, 32, 101, 120, 112, + 97, 110, 115, 105, 111, 110 }; + + /** + * "master secret" -- TLS 1.0 and later + */ + private static final byte[] MASTER_SECRET + = new byte[] { + 109, 97, 115, 116, 101, 114, 32, 115, 101, 99, 114, 101, 116 + }; + + /** + * "client write key" -- TLS 1.0 exportable whitener. + */ + private static final byte[] CLIENT_WRITE_KEY + = new byte[] { + 99, 108, 105, 101, 110, 116, 32, 119, 114, 105, 116, 101, 32, 107, + 101, 121 + }; + + /** + * "server write key" -- TLS 1.0 exportable whitener. + */ + private static final byte[] SERVER_WRITE_KEY + = new byte[] { + 115, 101, 114, 118, 101, 114, 32, 119, 114, 105, 116, 101, 32, 107, + 101, 121 + }; + + private static final byte[] IV_BLOCK + = new byte[] { + 73, 86, 32, 98, 108, 111, 99, 107 + }; + + /** + * SSL 3.0; the string "CLNT" + */ + private static final byte[] SENDER_CLIENT + = new byte[] { 0x43, 0x4C, 0x4E, 0x54 }; + + /** + * SSL 3.0; the string "SRVR" + */ + private static final byte[] SENDER_SERVER + = new byte[] { 0x53, 0x52, 0x56, 0x52 }; + + /** + * SSL 3.0; the value 0x36 40 (for SHA-1 hashes) or 48 (for MD5 hashes) + * times. + */ + protected static final byte[] PAD1 = new byte[48]; + + /** + * SSL 3.0; the value 0x5c 40 (for SHA-1 hashes) or 48 (for MD5 hashes) + * times. + */ + protected static final byte[] PAD2 = new byte[48]; + + static + { + Arrays.fill(PAD1, SSLHMac.PAD1); + Arrays.fill(PAD2, SSLHMac.PAD2); + } + + /** + * The currently-read handshake messages. There may be zero, or + * multiple, handshake messages in this buffer. + */ + protected ByteBuffer handshakeBuffer; + + /** + * The offset into `handshakeBuffer' where the first unread + * handshake message resides. + */ + protected int handshakeOffset; + + protected MessageDigest sha; + protected MessageDigest md5; + + protected final SSLEngineImpl engine; + protected KeyAgreement keyAgreement; + protected byte[] preMasterSecret; + protected InputSecurityParameters inParams; + protected OutputSecurityParameters outParams; + protected LinkedList 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(); + } + + /** + * Handles the next input message in the handshake. This is called + * in response to a call to {@link javax.net.ssl.SSLEngine#unwrap} + * for a message with content-type HANDSHAKE. + * + * @param record The input record. The callee should not assume that + * the record's buffer is writable, and should not try to use it for + * output or temporary storage. + * @return An {@link SSLEngineResult} describing the result. + */ + 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; + + // Try to read another... + if (!pollHandshake(fragment)) + return HandshakeStatus.NEED_UNWRAP; + + while (hasMessage() && status != HandshakeStatus.NEED_WRAP) + { + int pos = handshakeOffset; + status = implHandleInput(); + int len = handshakeOffset - pos; + if (len == 0) + { + // Don't bother; the impl is just telling us to go around + // again. + continue; + } + if (doHash()) + { + if (Debug.DEBUG) + logger.logv(Component.SSL_HANDSHAKE, "hashing output\n{0}", + Util.hexDump((ByteBuffer) handshakeBuffer + .duplicate().position(pos) + .limit(pos+len), " >> ")); + sha.update((ByteBuffer) handshakeBuffer.duplicate() + .position(pos).limit(pos+len)); + md5.update((ByteBuffer) handshakeBuffer.duplicate() + .position(pos).limit(pos+len)); + } + } + return status; + } + + /** + * Called to process more handshake data. This method will be called + * repeatedly while there is remaining handshake data, and while the + * status is + * @return + * @throws SSLException + */ + protected abstract HandshakeStatus implHandleInput() + throws SSLException; + + /** + * Produce more handshake output. This is called in response to a + * call to {@link javax.net.ssl.SSLEngine#wrap}, when the handshake + * is still in progress. + * + * @param record The output record; the callee should put its output + * handshake message (or a part of it) in the argument's + * fragment, and should set the record length + * appropriately. + * @return An {@link SSLEngineResult} describing the result. + */ + 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()) + { + if (Debug.DEBUG) + logger.logv(Component.SSL_HANDSHAKE, "hashing output:\n{0}", + Util.hexDump((ByteBuffer) fragment.duplicate().flip().position(orig), " >> ")); + sha.update((ByteBuffer) fragment.duplicate().flip().position(orig)); + md5.update((ByteBuffer) fragment.duplicate().flip().position(orig)); + } + return status; + } + + /** + * Called to implement the underlying output handling. The callee should + * attempt to fill the given buffer as much as it can; this can include + * multiple, and even partial, handshake messages. + * + * @param fragment The buffer the callee should write handshake messages to. + * @return The new status of the handshake. + * @throws SSLException If an error occurs processing the output message. + */ + protected abstract SSLEngineResult.HandshakeStatus implHandleOutput (ByteBuffer fragment) + throws SSLException; + + /** + * Return a new instance of input security parameters, initialized with + * the session key. It is, of course, only valid to invoke this method + * once the handshake is complete, and the session keys established. + * + *

In the presence of a well-behaving peer, this should be called once + * the ChangeCipherSpec message is recieved. + * + * @return The input parameters for the newly established session. + * @throws SSLException If the handshake is not complete. + */ + final InputSecurityParameters getInputParams() throws SSLException + { + checkKeyExchange(); + return inParams; + } + + /** + * Return a new instance of output security parameters, initialized with + * the session key. This should be called after the + * ChangeCipherSpec message is sent to the peer. + * + * @return The output parameters for the newly established session. + * @throws SSLException If the handshake is not complete. + */ + 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. + * This should be the same value as returned by the previous call + * to {@link #implHandleOutput(ByteBuffer)} or {@link + * #implHandleInput(ByteBuffer)}. + * + * @return The current handshake status. + */ + abstract HandshakeStatus status(); + + /** + * Check if the key exchange completed successfully, throwing an exception + * if not. + * + *

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. + * + * @param hello The hello message. + */ + abstract void handleV2Hello(ByteBuffer hello) throws SSLException; + + /** + * Attempt to read the next handshake message from the given + * record. If only a partial handshake message is available, then + * this method saves the incoming bytes and returns false. If a + * complete handshake is read, or if there was one buffered in the + * handshake buffer, this method returns true, and `handshakeBuffer' + * can be used to read the handshake. + * + * @param record The input record. + * @return True if a complete handshake is present in the buffer; + * false if only a partial one. + */ + protected boolean pollHandshake (final ByteBuffer fragment) + { + // Allocate space for the new fragment. + if (handshakeBuffer == null + || handshakeBuffer.remaining() < fragment.remaining()) + { + // We need space for anything still unread in the handshake + // buffer... + int len = ((handshakeBuffer == null) ? 0 + : handshakeBuffer.position() - handshakeOffset); + + // Plus room for the incoming record. + len += fragment.remaining(); + reallocateBuffer(len); + } + + if (Debug.DEBUG) + logger.logv(Component.SSL_HANDSHAKE, "inserting {0} into {1}", + fragment, handshakeBuffer); + + // Put the fragment into the buffer. + handshakeBuffer.put(fragment); + + return hasMessage(); + } + + protected boolean doHash() + { + return true; + } + + /** + * Tell if the handshake buffer currently has a full handshake + * message. + */ + protected boolean hasMessage() + { + if (handshakeBuffer == null) + return false; + ByteBuffer tmp = handshakeBuffer.duplicate(); + tmp.flip(); + tmp.position(handshakeOffset); + if (Debug.DEBUG) + logger.logv(Component.SSL_HANDSHAKE, "current buffer: {0}; test buffer {1}", + handshakeBuffer, tmp); + if (tmp.remaining() < 4) + return false; + Handshake handshake = new Handshake(tmp.slice()); + if (Debug.DEBUG) + logger.logv(Component.SSL_HANDSHAKE, "handshake len:{0} remaining:{1}", + handshake.length(), tmp.remaining()); + return (handshake.length() <= tmp.remaining() - 4); + } + + /** + * Reallocate the handshake buffer so it can hold `totalLen' + * bytes. The smallest buffer allocated is 1024 bytes, and the size + * doubles from there until the buffer is sufficiently large. + */ + private void reallocateBuffer (final int totalLen) + { + int len = handshakeBuffer == null ? -1 + : handshakeBuffer.capacity() - (handshakeBuffer.limit() - handshakeOffset); + if (len >= totalLen) + { + // Big enough; no need to reallocate; but maybe shift the contents + // down. + if (handshakeOffset > 0) + { + handshakeBuffer.flip().position(handshakeOffset); + handshakeBuffer.compact(); + handshakeOffset = 0; + } + return; + } + + // Start at 1K (probably the system's page size). Double the size + // from there. + len = 1024; + while (len < totalLen) + len = len << 1; + ByteBuffer newBuf = ByteBuffer.allocate (len); + + // Copy the unread bytes from the old buffer. + if (handshakeBuffer != null) + { + handshakeBuffer.flip (); + handshakeBuffer.position(handshakeOffset); + newBuf.put(handshakeBuffer); + } + handshakeBuffer = newBuf; + + // We just put only unread handshake messages in the new buffer; + // the offset of the next one is now zero. + handshakeOffset = 0; + } + + /** + * Generate a certificate verify message for SSLv3. In SSLv3, a different + * algorithm was used to generate this value was subtly different than + * that used in TLSv1.0 and later. In TLSv1.0 and later, this value is + * just the digest over the handshake messages. + * + *

SSLv3 uses the algorithm: + * + *

+CertificateVerify.signature.md5_hash
+  MD5(master_secret + pad_2 +
+      MD5(handshake_messages + master_secret + pad_1));
+Certificate.signature.sha_hash
+  SHA(master_secret + pad_2 +
+      SHA(handshake_messages + master_secret + pad_1));
+ * + * @param md5 The running MD5 hash of the handshake. + * @param sha The running SHA-1 hash of the handshake. + * @param session The current session being negotiated. + * @return The computed to-be-signed value. + */ + protected byte[] genV3CertificateVerify(MessageDigest md5, + MessageDigest sha, + SessionImpl session) + { + byte[] md5value = null; + if (session.suite.signatureAlgorithm() == SignatureAlgorithm.RSA) + { + md5.update(session.privateData.masterSecret); + md5.update(PAD1, 0, 48); + byte[] tmp = md5.digest(); + md5.reset(); + md5.update(session.privateData.masterSecret); + md5.update(PAD2, 0, 48); + md5.update(tmp); + md5value = md5.digest(); + } + + sha.update(session.privateData.masterSecret); + sha.update(PAD1, 0, 40); + byte[] tmp = sha.digest(); + sha.reset(); + sha.update(session.privateData.masterSecret); + sha.update(PAD2, 0, 40); + sha.update(tmp); + byte[] shavalue = sha.digest(); + + if (md5value != null) + return Util.concat(md5value, shavalue); + + return shavalue; + } + + /** + * Generate the session keys from the computed master secret. + * + * @param clientRandom The client's nonce. + * @param serverRandom The server's nonce. + * @param session The session being established. + * @return The derived keys. + */ + protected byte[][] generateKeys(Random clientRandom, Random serverRandom, + SessionImpl session) + { + int maclen = 20; // SHA-1. + if (session.suite.macAlgorithm() == MacAlgorithm.MD5) + maclen = 16; + int ivlen = 0; + if (session.suite.cipherAlgorithm() == CipherAlgorithm.DES + || session.suite.cipherAlgorithm() == CipherAlgorithm.DESede) + ivlen = 8; + if (session.suite.cipherAlgorithm() == CipherAlgorithm.AES) + ivlen = 16; + int keylen = session.suite.keyLength(); + + byte[][] keys = new byte[6][]; + keys[0] = new byte[maclen]; // client_write_MAC_secret + keys[1] = new byte[maclen]; // server_write_MAC_secret + keys[2] = new byte[keylen]; // client_write_key + keys[3] = new byte[keylen]; // server_write_key + keys[4] = new byte[ivlen]; // client_write_iv + keys[5] = new byte[ivlen]; // server_write_iv + + IRandom prf = null; + if (session.version == ProtocolVersion.SSL_3) + { + byte[] seed = new byte[clientRandom.length() + + serverRandom.length()]; + serverRandom.buffer().get(seed, 0, serverRandom.length()); + clientRandom.buffer().get(seed, serverRandom.length(), + clientRandom.length()); + prf = new SSLRandom(); + HashMap attr = new HashMap(2); + attr.put(SSLRandom.SECRET, session.privateData.masterSecret); + attr.put(SSLRandom.SEED, seed); + prf.init(attr); + } + else + { + byte[] seed = new byte[KEY_EXPANSION.length + + clientRandom.length() + + serverRandom.length()]; + System.arraycopy(KEY_EXPANSION, 0, seed, 0, KEY_EXPANSION.length); + serverRandom.buffer().get(seed, KEY_EXPANSION.length, + serverRandom.length()); + clientRandom.buffer().get(seed, (KEY_EXPANSION.length + + serverRandom.length()), + clientRandom.length()); + + prf = new TLSRandom(); + HashMap attr = new HashMap(2); + attr.put(TLSRandom.SECRET, session.privateData.masterSecret); + attr.put(TLSRandom.SEED, seed); + prf.init(attr); + } + + try + { + prf.nextBytes(keys[0], 0, keys[0].length); + prf.nextBytes(keys[1], 0, keys[1].length); + prf.nextBytes(keys[2], 0, keys[2].length); + prf.nextBytes(keys[3], 0, keys[3].length); + + if (session.suite.isExportable()) + { + if (session.version == ProtocolVersion.SSL_3) + { + MessageDigest md5 = MessageDigest.getInstance("MD5"); + md5.update(clientRandom.buffer()); + md5.update(serverRandom.buffer()); + byte[] d = md5.digest(); + System.arraycopy(d, 0, keys[4], 0, keys[4].length); + + md5.reset(); + md5.update(serverRandom.buffer()); + md5.update(clientRandom.buffer()); + d = md5.digest(); + System.arraycopy(d, 0, keys[5], 0, keys[5].length); + + md5.reset(); + md5.update(keys[2]); + md5.update(clientRandom.buffer()); + md5.update(serverRandom.buffer()); + keys[2] = Util.trim(md5.digest(), 8); + + md5.reset(); + md5.update(keys[3]); + md5.update(serverRandom.buffer()); + md5.update(clientRandom.buffer()); + keys[3] = Util.trim(md5.digest(), 8); + } + else + { + TLSRandom prf2 = new TLSRandom(); + HashMap attr = new HashMap(2); + attr.put(TLSRandom.SECRET, keys[2]); + byte[] seed = new byte[CLIENT_WRITE_KEY.length + + clientRandom.length() + + serverRandom.length()]; + System.arraycopy(CLIENT_WRITE_KEY, 0, seed, 0, + CLIENT_WRITE_KEY.length); + clientRandom.buffer().get(seed, CLIENT_WRITE_KEY.length, + clientRandom.length()); + serverRandom.buffer().get(seed, CLIENT_WRITE_KEY.length + + clientRandom.length(), + serverRandom.length()); + attr.put(TLSRandom.SEED, seed); + prf2.init(attr); + keys[2] = new byte[8]; + prf2.nextBytes(keys[2], 0, keys[2].length); + + attr.put(TLSRandom.SECRET, keys[3]); + seed = new byte[SERVER_WRITE_KEY.length + + serverRandom.length() + + clientRandom.length()]; + System.arraycopy(SERVER_WRITE_KEY, 0, seed, 0, + SERVER_WRITE_KEY.length); + serverRandom.buffer().get(seed, SERVER_WRITE_KEY.length, + serverRandom.length()); + clientRandom.buffer().get(seed, SERVER_WRITE_KEY.length + + serverRandom.length(), + + clientRandom.length()); + attr.put(TLSRandom.SEED, seed); + prf2.init(attr); + keys[3] = new byte[8]; + prf2.nextBytes(keys[3], 0, keys[3].length); + + attr.put(TLSRandom.SECRET, new byte[0]); + seed = new byte[IV_BLOCK.length + + clientRandom.length() + + serverRandom.length()]; + System.arraycopy(IV_BLOCK, 0, seed, 0, IV_BLOCK.length); + clientRandom.buffer().get(seed, IV_BLOCK.length, + clientRandom.length()); + serverRandom.buffer().get(seed, IV_BLOCK.length + + clientRandom.length(), + serverRandom.length()); + attr.put(TLSRandom.SEED, seed); + prf2.init(attr); + prf2.nextBytes(keys[4], 0, keys[4].length); + prf2.nextBytes(keys[5], 0, keys[5].length); + } + } + else + { + prf.nextBytes(keys[4], 0, keys[4].length); + prf.nextBytes(keys[5], 0, keys[5].length); + } + } + catch (LimitReachedException lre) + { + // Won't happen with our implementation. + throw new Error(lre); + } + catch (NoSuchAlgorithmException nsae) + { + throw new Error(nsae); + } + + if (Debug.DEBUG_KEY_EXCHANGE) + logger.logv(Component.SSL_KEY_EXCHANGE, + "keys generated;\n [0]: {0}\n [1]: {1}\n [2]: {2}\n" + + " [3]: {3}\n [4]: {4}\n [5]: {5}", + Util.toHexString(keys[0], ':'), + Util.toHexString(keys[1], ':'), + Util.toHexString(keys[2], ':'), + Util.toHexString(keys[3], ':'), + Util.toHexString(keys[4], ':'), + Util.toHexString(keys[5], ':')); + return keys; + } + + /** + * Generate a "finished" message. The hashes passed in are modified + * by this function, so they should be clone copies of the digest if + * the hash function needs to be used more. + * + * @param md5 The MD5 computation. + * @param sha The SHA-1 computation. + * @param isClient Whether or not the client-side finished message is + * being computed. + * @param session The current session. + * @return A byte buffer containing the computed finished message. + */ + protected ByteBuffer generateFinished(MessageDigest md5, + MessageDigest sha, + boolean isClient, + SessionImpl session) + { + ByteBuffer finishedBuffer = null; + if (session.version.compareTo(ProtocolVersion.TLS_1) >= 0) + { + finishedBuffer = ByteBuffer.allocate(12); + TLSRandom prf = new TLSRandom(); + byte[] md5val = md5.digest(); + byte[] shaval = sha.digest(); + if (Debug.DEBUG) + logger.logv(Component.SSL_HANDSHAKE, "finished md5:{0} sha:{1}", + Util.toHexString(md5val, ':'), + Util.toHexString(shaval, ':')); + byte[] seed = new byte[CLIENT_FINISHED.length + + md5val.length + + shaval.length]; + if (isClient) + System.arraycopy(CLIENT_FINISHED, 0, seed, 0, CLIENT_FINISHED.length); + else + System.arraycopy(SERVER_FINISHED, 0, seed, 0, SERVER_FINISHED.length); + System.arraycopy(md5val, 0, + seed, CLIENT_FINISHED.length, + md5val.length); + System.arraycopy(shaval, 0, + seed, CLIENT_FINISHED.length + md5val.length, + shaval.length); + HashMap params = new HashMap(2); + params.put(TLSRandom.SECRET, session.privateData.masterSecret); + params.put(TLSRandom.SEED, seed); + prf.init(params); + byte[] buf = new byte[12]; + prf.nextBytes(buf, 0, buf.length); + finishedBuffer.put(buf).position(0); + } + else + { + // The SSLv3 algorithm is: + // + // enum { client(0x434C4E54), server(0x53525652) } Sender; + // + // struct { + // opaque md5_hash[16]; + // opaque sha_hash[20]; + // } Finished; + // + // md5_hash MD5(master_secret + pad2 + + // MD5(handshake_messages + Sender + + // master_secret + pad1)); + // sha_hash SHA(master_secret + pad2 + + // SHA(handshake_messages + Sender + + // master_secret + pad1)); + // + + finishedBuffer = ByteBuffer.allocate(36); + + md5.update(isClient ? SENDER_CLIENT : SENDER_SERVER); + md5.update(session.privateData.masterSecret); + md5.update(PAD1); + + byte[] tmp = md5.digest(); + md5.reset(); + md5.update(session.privateData.masterSecret); + md5.update(PAD2); + md5.update(tmp); + finishedBuffer.put(md5.digest()); + + sha.update(isClient ? SENDER_CLIENT : SENDER_SERVER); + sha.update(session.privateData.masterSecret); + sha.update(PAD1, 0, 40); + + tmp = sha.digest(); + sha.reset(); + sha.update(session.privateData.masterSecret); + sha.update(PAD2, 0, 40); + sha.update(tmp); + finishedBuffer.put(sha.digest()).position(0); + } + return finishedBuffer; + } + + protected void initDiffieHellman(DHPrivateKey dhKey, SecureRandom random) + throws SSLException + { + try + { + keyAgreement = KeyAgreement.getInstance("DH"); + keyAgreement.init(dhKey, random); + } + catch (InvalidKeyException ike) + { + throw new SSLException(ike); + } + catch (NoSuchAlgorithmException nsae) + { + throw new SSLException(nsae); + } + } + + protected void generateMasterSecret(Random clientRandom, + Random serverRandom, + 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)); + + if (session.version == ProtocolVersion.SSL_3) + { + try + { + MessageDigest _md5 = MessageDigest.getInstance("MD5"); + MessageDigest _sha = MessageDigest.getInstance("SHA"); + session.privateData.masterSecret = new byte[48]; + + _sha.update((byte) 'A'); + _sha.update(preMasterSecret); + _sha.update(clientRandom.buffer()); + _sha.update(serverRandom.buffer()); + _md5.update(preMasterSecret); + _md5.update(_sha.digest()); + _md5.digest(session.privateData.masterSecret, 0, 16); + + _sha.update((byte) 'B'); + _sha.update((byte) 'B'); + _sha.update(preMasterSecret); + _sha.update(clientRandom.buffer()); + _sha.update(serverRandom.buffer()); + _md5.update(preMasterSecret); + _md5.update(_sha.digest()); + _md5.digest(session.privateData.masterSecret, 16, 16); + + _sha.update((byte) 'C'); + _sha.update((byte) 'C'); + _sha.update((byte) 'C'); + _sha.update(preMasterSecret); + _sha.update(clientRandom.buffer()); + _sha.update(serverRandom.buffer()); + _md5.update(preMasterSecret); + _md5.update(_sha.digest()); + _md5.digest(session.privateData.masterSecret, 32, 16); + } + catch (DigestException de) + { + throw new SSLException(de); + } + catch (NoSuchAlgorithmException nsae) + { + throw new SSLException(nsae); + } + } + else // TLSv1.0 and later + { + byte[] seed = new byte[clientRandom.length() + + serverRandom.length() + + MASTER_SECRET.length]; + System.arraycopy(MASTER_SECRET, 0, seed, 0, MASTER_SECRET.length); + clientRandom.buffer().get(seed, MASTER_SECRET.length, + clientRandom.length()); + serverRandom.buffer().get(seed, + MASTER_SECRET.length + clientRandom.length(), + serverRandom.length()); + TLSRandom prf = new TLSRandom(); + HashMap attr = new HashMap(2); + attr.put(TLSRandom.SECRET, preMasterSecret); + attr.put(TLSRandom.SEED, seed); + prf.init(attr); + + session.privateData.masterSecret = new byte[48]; + prf.nextBytes(session.privateData.masterSecret, 0, 48); + } + + if (Debug.DEBUG_KEY_EXCHANGE) + logger.log(Component.SSL_KEY_EXCHANGE, "master_secret: {0}", + new ByteArray(session.privateData.masterSecret)); + + // Wipe out the preMasterSecret. + 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); + } + } + + protected void generatePSKSecret(String identity, byte[] otherkey, + boolean isClient) + throws SSLException + { + SecretKey key = null; + try + { + key = engine.contextImpl.pskManager.getKey(identity); + } + catch (KeyManagementException kme) + { + } + if (key != null) + { + byte[] keyb = key.getEncoded(); + if (otherkey == null) + { + otherkey = new byte[keyb.length]; + } + preMasterSecret = new byte[otherkey.length + keyb.length + 4]; + preMasterSecret[0] = (byte) (otherkey.length >>> 8); + preMasterSecret[1] = (byte) otherkey.length; + System.arraycopy(otherkey, 0, preMasterSecret, 2, otherkey.length); + preMasterSecret[otherkey.length + 2] + = (byte) (keyb.length >>> 8); + preMasterSecret[otherkey.length + 3] + = (byte) keyb.length; + System.arraycopy(keyb, 0, preMasterSecret, + otherkey.length + 4, keyb.length); + } + else + { + // Generate a random, fake secret. + preMasterSecret = new byte[8]; + preMasterSecret[1] = 2; + preMasterSecret[5] = 2; + preMasterSecret[6] = (byte) engine.session().random().nextInt(); + preMasterSecret[7] = (byte) engine.session().random().nextInt(); + } + + if (Debug.DEBUG_KEY_EXCHANGE) + logger.logv(Component.SSL_KEY_EXCHANGE, "PSK identity {0} key {1}", + identity, key); + + generateMasterSecret(clientRandom, serverRandom, + engine.session()); + byte[][] keys = generateKeys(clientRandom, serverRandom, + engine.session()); + setupSecurityParameters(keys, isClient, engine, compression); + } + + protected class DHPhase extends DelegatedTask + { + private final DHPublicKey key; + private final boolean full; + + protected DHPhase(DHPublicKey key) + { + this(key, true); + } + + protected DHPhase(DHPublicKey key, boolean full) + { + this.key = key; + this.full = full; + } + + protected void implRun() throws InvalidKeyException, SSLException + { + keyAgreement.doPhase(key, true); + preMasterSecret = keyAgreement.generateSecret(); + if (full) + { + generateMasterSecret(clientRandom, serverRandom, engine.session()); + byte[][] keys = generateKeys(clientRandom, serverRandom, engine.session()); + setupSecurityParameters(keys, engine.getUseClientMode(), engine, compression); + } + } + } + + protected class CertVerifier extends DelegatedTask + { + 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() + { + 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 class DHE_PSKGen extends DelegatedTask + { + private final DHPublicKey dhKey; + private final SecretKey psKey; + private final boolean isClient; + + protected DHE_PSKGen(DHPublicKey dhKey, SecretKey psKey, boolean isClient) + { + this.dhKey = dhKey; + this.psKey = psKey; + this.isClient = isClient; + } + + /* (non-Javadoc) + * @see gnu.javax.net.ssl.provider.DelegatedTask#implRun() + */ + @Override protected void implRun() throws Throwable + { + keyAgreement.doPhase(dhKey, true); + byte[] dhSecret = keyAgreement.generateSecret(); + byte[] psSecret = null; + if (psKey != null) + psSecret = psKey.getEncoded(); + else + { + psSecret = new byte[8]; + engine.session().random().nextBytes(psSecret); + } + + preMasterSecret = new byte[dhSecret.length + psSecret.length + 4]; + preMasterSecret[0] = (byte) (dhSecret.length >>> 8); + preMasterSecret[1] = (byte) dhSecret.length; + System.arraycopy(dhSecret, 0, preMasterSecret, 2, dhSecret.length); + preMasterSecret[dhSecret.length + 2] = (byte) (psSecret.length >>> 8); + preMasterSecret[dhSecret.length + 3] = (byte) psSecret.length; + System.arraycopy(psSecret, 0, preMasterSecret, dhSecret.length + 4, + psSecret.length); + + generateMasterSecret(clientRandom, serverRandom, engine.session()); + byte[][] keys = generateKeys(clientRandom, serverRandom, engine.session()); + setupSecurityParameters(keys, isClient, engine, compression); + } + } +} diff --git a/libjava/classpath/gnu/javax/net/ssl/provider/Alert.java b/libjava/classpath/gnu/javax/net/ssl/provider/Alert.java new file mode 100644 index 000000000..0ceb96bbb --- /dev/null +++ b/libjava/classpath/gnu/javax/net/ssl/provider/Alert.java @@ -0,0 +1,288 @@ +/* Alert.java -- SSL Alert message. + 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.PrintWriter; +import java.io.StringWriter; + +import java.nio.ByteBuffer; + +/** + * An alert message in the SSL protocol. Alerts are sent both as warnings + * which may allow execution to continue, or they may be fatal, which will + * halt this session. An alert object is composed of two enums -- the level, + * which indicates the seriousness of the alert, and the description, which + * indicates the reason for the alert. + * + *
+ * struct {
+ *   AlertLevel       level;
+ *   AlertDescription description;
+ * }
+ * 
+ */ +public final class Alert implements Constructed +{ + + // Fields. + // ------------------------------------------------------------------------- + + /** The underlying byte buffer. */ + private final ByteBuffer buffer; + + // Constructor. + // ------------------------------------------------------------------------- + + public Alert (final ByteBuffer buffer) + { + this.buffer = buffer; + } + + public Alert (final Level level, final Description description) + { + level.getClass (); + description.getClass (); + ByteBuffer b = ByteBuffer.allocate (2); + b.put (0, (byte) level.getValue ()); + b.put (1, (byte) description.getValue ()); + this.buffer = b.asReadOnlyBuffer (); + } + + // Instance methods. + // ------------------------------------------------------------------------- + + public int length () + { + return 2; + } + + byte[] getEncoded() + { + byte[] buf = new byte[2]; + buffer.position (0); + buffer.get (buf); + return buf; + } + + public Level level() + { + return Level.forInteger (buffer.get (0) & 0xFF); + } + + public Description description() + { + return Description.forInteger (buffer.get (1) & 0xFF); + } + + public void setLevel (final Level level) + { + buffer.put (0, (byte) level.getValue ()); + } + + public void setDescription (final Description description) + { + buffer.put (1, (byte) description.getValue ()); + } + + public boolean equals (Object o) + { + if (!(o instanceof Alert)) + return false; + Alert that = (Alert) o; + return that.buffer.position (0).equals (buffer.position (0)); + } + + public int hashCode () + { + return buffer.getShort (0) & 0xFFFF; + } + + public String toString() + { + return toString (null); + } + + public String toString (final String prefix) + { + StringWriter str = new StringWriter (); + PrintWriter out = new PrintWriter (str); + if (prefix != null) out.print (prefix); + out.println ("struct {"); + if (prefix != null) out.print (prefix); + out.print (" level: "); + out.print (level ()); + out.println (";"); + if (prefix != null) out.print (prefix); + out.print (" description: "); + out.print (description ()); + out.println (";"); + if (prefix != null) out.print (prefix); + out.print ("} Alert;"); + return str.toString (); + } + + // Enumerations. + // ------------------------------------------------------------------------- + + /** + * The level enumeration. + * + *
+   * enum { warning(1), fatal(2), (255) } AlertLevel;
+   * 
+ */ + public static enum Level + { + + WARNING (1), FATAL (2); + + private final int value; + + private Level(int value) + { + this.value = value; + } + + public static Level forInteger (final int value) + { + switch (value & 0xFF) + { + case 1: return WARNING; + case 2: return FATAL; + default: throw new IllegalArgumentException ("invalid alert level: " + value); + } + } + + public int getValue() + { + return value; + } + } + + /** + * The description enumeration. + */ + public static enum Description + { + CLOSE_NOTIFY ( 0), + UNEXPECTED_MESSAGE ( 10), + BAD_RECORD_MAC ( 20), + DECRYPTION_FAILED ( 21), + RECORD_OVERFLOW ( 22), + DECOMPRESSION_FAILURE ( 30), + HANDSHAKE_FAILURE ( 40), + NO_CERTIFICATE ( 41), + BAD_CERTIFICATE ( 42), + UNSUPPORTED_CERTIFICATE ( 43), + CERTIFICATE_REVOKED ( 44), + CERTIFICATE_EXPIRED ( 45), + CERTIFICATE_UNKNOWN ( 46), + ILLEGAL_PARAMETER ( 47), + UNKNOWN_CA ( 48), + ACCESS_DENIED ( 49), + DECODE_ERROR ( 50), + DECRYPT_ERROR ( 51), + EXPORT_RESTRICTION ( 60), + PROTOCOL_VERSION ( 70), + INSUFFICIENT_SECURITY ( 71), + INTERNAL_ERROR ( 80), + USER_CANCELED ( 90), + NO_RENEGOTIATION (100), + UNSUPPORTED_EXTENSION (110), + CERTIFICATE_UNOBTAINABLE (111), + UNRECOGNIZED_NAME (112), + BAD_CERTIFICATE_STATUS_RESPONSE (113), + BAD_CERTIFICATE_HASH_VALUE (114), + UNKNOWN_SRP_USERNAME (120), + MISSING_SRP_USERNAME (121); + + private final int value; + + private Description(int value) + { + this.value = value; + } + + /** + * Return an alert description object based on the specified integer + * value. + * + * @param value The raw description value. + * @return The appropriate description object. + */ + public static Description forInteger (final int value) + { + switch (value & 0xFF) + { + case 0: return CLOSE_NOTIFY; + case 10: return UNEXPECTED_MESSAGE; + case 20: return BAD_RECORD_MAC; + case 21: return DECRYPTION_FAILED; + case 22: return RECORD_OVERFLOW; + case 30: return DECOMPRESSION_FAILURE; + case 40: return HANDSHAKE_FAILURE; + case 41: return NO_CERTIFICATE; + case 42: return BAD_CERTIFICATE; + case 43: return UNSUPPORTED_CERTIFICATE; + case 44: return CERTIFICATE_REVOKED; + case 45: return CERTIFICATE_EXPIRED; + case 46: return CERTIFICATE_UNKNOWN; + case 47: return ILLEGAL_PARAMETER; + case 48: return UNKNOWN_CA; + case 49: return ACCESS_DENIED; + case 50: return DECODE_ERROR; + case 51: return DECRYPT_ERROR; + case 60: return EXPORT_RESTRICTION; + case 70: return PROTOCOL_VERSION; + case 71: return INSUFFICIENT_SECURITY; + case 80: return INTERNAL_ERROR; + case 90: return USER_CANCELED; + case 100: return NO_RENEGOTIATION; + case 120: return UNKNOWN_SRP_USERNAME; + case 121: return MISSING_SRP_USERNAME; + default: throw new IllegalArgumentException("unknown alert description: " + value); + } + } + + public int getValue() + { + return value; + } + } +} diff --git a/libjava/classpath/gnu/javax/net/ssl/provider/AlertException.java b/libjava/classpath/gnu/javax/net/ssl/provider/AlertException.java new file mode 100644 index 000000000..90eaaf430 --- /dev/null +++ b/libjava/classpath/gnu/javax/net/ssl/provider/AlertException.java @@ -0,0 +1,101 @@ +/* AlertException.java -- exceptions generated by SSL alerts. + 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 javax.net.ssl.SSLException; + +/** + * An exception generated by an SSL alert. + */ +public class AlertException extends SSLException +{ + + // Fields. + // ------------------------------------------------------------------------- + + private final Alert alert; + private final boolean isLocal; + + // Constructor. + // ------------------------------------------------------------------------- + + public AlertException(Alert alert, boolean isLocal) + { + super(alert.description().toString()); + this.alert = alert; + this.isLocal = isLocal; + } + + public AlertException(Alert alert) + { + this(alert, true); + } + + public AlertException(Alert alert, boolean isLocal, Throwable cause) + { + super(alert.description().toString(), cause); + this.alert = alert; + this.isLocal = isLocal; + } + + public AlertException(Alert alert, Throwable cause) + { + this(alert, true, cause); + } + + // Instance methods. + // ------------------------------------------------------------------------- + + public String getMessage() + { + return alert.description() + ": " + + (isLocal ? "locally generated; " : "remotely generated; ") + + alert.level(); + } + + public Alert alert () + { + return alert; + } + + public boolean isLocal() + { + return isLocal; + } +} diff --git a/libjava/classpath/gnu/javax/net/ssl/provider/Builder.java b/libjava/classpath/gnu/javax/net/ssl/provider/Builder.java new file mode 100644 index 000000000..070c51b76 --- /dev/null +++ b/libjava/classpath/gnu/javax/net/ssl/provider/Builder.java @@ -0,0 +1,66 @@ +/* Builder.java -- builder interface for protocol objects. + 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; + +/** + * The base interface for classes that build SSL protocol objects. The + * general contract for Builder implementations is that they maintain a + * buffer that grows to fit the object being built; the allocated size of + * this buffer may be larger than the built object needs, but the general + * effort will be not to allocate too large a buffer. + * + *

Once the object is built, through various setters for + * the object's attributes, the final buffer may be retrieved with the + * {@link #buffer()} method. + * + * @author Casey Marshall (csm@gnu.org) + */ +public interface Builder extends Constructed +{ + /** + * Returns the final buffer, possibly containing the built object. The + * returned buffer will be "trimmed" to size: its position will be zero, + * and its limit and capacity set to the length of the built object. + * + * @return The underlying buffer. + */ + ByteBuffer buffer(); +} diff --git a/libjava/classpath/gnu/javax/net/ssl/provider/Certificate.java b/libjava/classpath/gnu/javax/net/ssl/provider/Certificate.java new file mode 100644 index 000000000..68de1304d --- /dev/null +++ b/libjava/classpath/gnu/javax/net/ssl/provider/Certificate.java @@ -0,0 +1,177 @@ +/* Certificate.java -- SSL certificate message. + 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.ByteArrayInputStream; +import java.io.PrintWriter; +import java.io.StringWriter; + +import java.nio.ByteBuffer; +import java.nio.ByteOrder; + +import java.security.NoSuchAlgorithmException; +import java.security.cert.CertificateException; +import java.security.cert.CertificateFactory; +import java.security.cert.X509Certificate; + +import java.util.Iterator; +import java.util.LinkedList; +import java.util.List; + +/** + * The certificate object. This is used by both the client and the server + * to send their certificates (if any) to one another. + * + *

opaque ASN.1Cert<1..2^24-1>;
+
+struct {
+  ASN.1Cert certificate_list<0..2^24-1>;
+} Certificate;
+ * + * @author Casey Marshall (csm@gnu.org) + */ +public class Certificate implements Handshake.Body +{ + + // Fields. + // ------------------------------------------------------------------------- + + protected ByteBuffer buffer; + protected final CertificateType type; + + // Constructors. + // ------------------------------------------------------------------------- + + public Certificate (final ByteBuffer buffer, final CertificateType type) + { + buffer.getClass (); + type.getClass (); + this.buffer = buffer.duplicate().order(ByteOrder.BIG_ENDIAN); + this.type = type; + } + + // Instance methods. + // ------------------------------------------------------------------------- + + public int length () + { + return (((buffer.get (0) & 0xFF) << 24) + | buffer.getShort (1)) + 3; + } + + public List certificates () + throws CertificateException, NoSuchAlgorithmException + { + LinkedList list + = new LinkedList(); + CertificateFactory factory = CertificateFactory.getInstance(type.toString()); + int length = (((buffer.get(0) & 0xFF) << 16) + | (buffer.getShort(1) & 0xFFFF)); + ByteBuffer b = (ByteBuffer) buffer.duplicate().position(3); + for (int i = 3; i < length; ) + { + int length2 = (((b.get () & 0xFF) << 16) + | (b.getShort () & 0xFFFF)); + byte[] buf = new byte[length2]; + b.position(i+3); + b.get (buf); + list.add(factory.generateCertificate (new ByteArrayInputStream (buf))); + i += length2 + 3; + b.position(i); + } + return list; + } + + public String toString () + { + return toString (null); + } + + public String toString (final String prefix) + { + StringWriter str = new StringWriter(); + PrintWriter out = new PrintWriter(str); + if (prefix != null) + out.print (prefix); + out.println ("struct {"); + try + { + List certs = certificates (); + if (prefix != null) + out.print (prefix); + out.print (" certificateList: ["); + out.print (certs.size ()); + out.println ("] {"); + for (Iterator it = certs.iterator (); it.hasNext (); ) + { + java.security.cert.Certificate cert = + (java.security.cert.Certificate) it.next (); + if (prefix != null) + out.print (prefix); + out.print (" "); + if (cert instanceof X509Certificate) + out.print (((X509Certificate) cert).getSubjectDN ()); + else + out.print (cert); + out.println (";"); + } + if (prefix != null) + out.print (prefix); + out.println (" };"); + } + catch (CertificateException ce) + { + if (prefix != null) + out.print (prefix); + out.print (" "); + out.print (ce); + out.println (";"); + } + catch (NoSuchAlgorithmException nsae) + { + if (prefix != null) + out.print (prefix); + out.print (" "); + out.print (nsae); + out.println (";"); + } + out.print ("} Certificate;"); + return str.toString(); + } +} diff --git a/libjava/classpath/gnu/javax/net/ssl/provider/CertificateBuilder.java b/libjava/classpath/gnu/javax/net/ssl/provider/CertificateBuilder.java new file mode 100644 index 000000000..1126e6fcc --- /dev/null +++ b/libjava/classpath/gnu/javax/net/ssl/provider/CertificateBuilder.java @@ -0,0 +1,94 @@ +/* CertificateBuilder.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.ByteArrayOutputStream; +import java.io.IOException; +import java.nio.ByteBuffer; +import java.util.List; +import java.security.cert.CertificateException; + +/** + * Builder for {@link Certificate} objects. + * + * @author Casey Marshall (csm@gnu.org) + */ +public class CertificateBuilder extends Certificate implements Builder +{ + public CertificateBuilder(final CertificateType certType) + { + super(ByteBuffer.allocate(1024), certType); + } + + /* (non-Javadoc) + * @see gnu.javax.net.ssl.provider.Builder#buffer() + */ + public ByteBuffer buffer() + { + return ((ByteBuffer) buffer.duplicate().position(0).limit(length())).slice(); + } + + public void setCertificates (final List certificates) + throws CertificateException + { + ByteArrayOutputStream out = new ByteArrayOutputStream(1024); + for (java.security.cert.Certificate cert : certificates) + { + byte[] encoded = cert.getEncoded(); + out.write((encoded.length >>> 16) & 0xFF); + out.write((encoded.length >>> 8) & 0xFF); + out.write( encoded.length & 0xFF); + try + { + out.write(encoded); + } + catch (IOException shouldNotHappen) + { + // ignore; this is a ByteArrayOutputStream. + } + } + byte[] certs = out.toByteArray(); + // There is only one field in Certificate; so it is easy to reallocate. + if (buffer.capacity() < certs.length + 3) + buffer = ByteBuffer.allocate(certs.length + 3); + buffer.put(0, (byte) (certs.length >>> 16)); + buffer.putShort(1, (short) certs.length); + ((ByteBuffer) buffer.duplicate().position(3)).put(certs); + } +} diff --git a/libjava/classpath/gnu/javax/net/ssl/provider/CertificateRequest.java b/libjava/classpath/gnu/javax/net/ssl/provider/CertificateRequest.java new file mode 100644 index 000000000..fd9d65be5 --- /dev/null +++ b/libjava/classpath/gnu/javax/net/ssl/provider/CertificateRequest.java @@ -0,0 +1,155 @@ +/* CertificateRequest.java -- SSL CertificateRequest message. + 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.PrintWriter; +import java.io.StringWriter; + +import java.nio.ByteBuffer; +import java.nio.ByteOrder; + +/** + * A request by the server for a client certificate. + * + *
+struct
+{
+  ClientCertificateType certificate_types<1..2^8-1>;
+  DistinguishedName certificate_authorities<3..2^16-1>;
+} CertificateRequest;
+
+ */ +public class CertificateRequest implements Handshake.Body +{ + + // Fields. + // ------------------------------------------------------------------------- + + protected ByteBuffer buffer; + + // Constructor. + // ------------------------------------------------------------------------- + + public CertificateRequest(final ByteBuffer buffer) + { + this.buffer = buffer.duplicate().order(ByteOrder.BIG_ENDIAN); + } + + // Instance methods. + // ------------------------------------------------------------------------- + + public int length () + { + int o1 = (buffer.get (0) & 0xFF) + 1; + return o1 + (buffer.getShort (o1) & 0xFFFF) + 2; + } + + public ClientCertificateTypeList types () + { + return new ClientCertificateTypeList(buffer.duplicate()); + } + + public X500PrincipalList authorities () + { + int offset = (buffer.get (0) & 0xFF) + 1; + return new X500PrincipalList (((ByteBuffer) buffer.position(offset)).slice()); + } + + public String toString() + { + return toString (null); + } + + public String toString (final String prefix) + { + StringWriter str = new StringWriter(); + PrintWriter out = new PrintWriter(str); + String subprefix = " "; + if (prefix != null) subprefix = prefix + " "; + if (prefix != null) out.print (prefix); + out.println("struct {"); + if (prefix != null) out.print (prefix); + out.println (" types ="); + out.println (types ().toString (subprefix)); + if (prefix != null) out.print (prefix); + out.println(" authorities ="); + out.println (authorities ().toString (subprefix)); + if (prefix != null) out.print (prefix); + out.print ("} CertificateRequest;"); + return str.toString(); + } + + public static enum ClientCertificateType + { + RSA_SIGN (1), + DSS_SIGN (2), + RSA_FIXED_DH (3), + DSS_FIXED_DH (4); + + private final int value; + + // Constructor. + // ----------------------------------------------------------------------- + + private ClientCertificateType (final int value) + { + this.value = value; + } + + // Class method. + // ----------------------------------------------------------------------- + + static ClientCertificateType forValue (final int value) + { + switch (value) + { + case 1: return RSA_SIGN; + case 2: return DSS_SIGN; + case 3: return RSA_FIXED_DH; + case 4: return DSS_FIXED_DH; + default: throw new IllegalArgumentException("unknown client certificate type: " + value); + } + } + + public int getValue() + { + return value; + } + } +} diff --git a/libjava/classpath/gnu/javax/net/ssl/provider/CertificateRequestBuilder.java b/libjava/classpath/gnu/javax/net/ssl/provider/CertificateRequestBuilder.java new file mode 100644 index 000000000..f32c52acf --- /dev/null +++ b/libjava/classpath/gnu/javax/net/ssl/provider/CertificateRequestBuilder.java @@ -0,0 +1,111 @@ +/* CertificateRequestBuilder.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.ByteArrayOutputStream; +import java.io.IOException; +import java.nio.ByteBuffer; +import java.util.List; + +import javax.security.auth.x500.X500Principal; + +/** + * Builder for {@link CertificateRequest} objects. + * + * @author Casey Marshall (csm@gnu.org) + */ +public class CertificateRequestBuilder extends CertificateRequest + implements Builder +{ + public CertificateRequestBuilder() + { + super(ByteBuffer.allocate(1024)); + } + + /* (non-Javadoc) + * @see gnu.javax.net.ssl.provider.Builder#buffer() + */ + public ByteBuffer buffer() + { + return ((ByteBuffer) buffer.duplicate().limit(length())).slice(); + } + + public void setTypes(List types) + { + ensureCapacity(types.size() + 3); + buffer.put(0, (byte) types.size()); + ByteBuffer b = (ByteBuffer) buffer.duplicate().position(1); + for (ClientCertificateType type : types) + b.put((byte) type.getValue()); + } + + public void setAuthorities(List authorities) + { + ByteArrayOutputStream out = new ByteArrayOutputStream(1024); + for (X500Principal auth : authorities) + { + byte[] encoded = auth.getEncoded(); + out.write((encoded.length >>> 8) & 0xFF); + out.write( encoded.length & 0xFF); + try + { + out.write(encoded); + } + catch (IOException ignored) + { + // Ignored; we use a ByteArrayOutputStream. + } + } + byte[] auths = out.toByteArray(); + int typesLen = 1 + (buffer.get(0) & 0xFF); + int len = typesLen + auths.length + 2; + ensureCapacity(len); + buffer.putShort(typesLen, (short) auths.length); + ((ByteBuffer) buffer.duplicate().position(typesLen + 2)).put(auths); + } + + public void ensureCapacity(final int capacity) + { + if (buffer.capacity() >= capacity) + return; + ByteBuffer newBuffer = ByteBuffer.allocate(capacity); + newBuffer.duplicate().put(buffer); + buffer = newBuffer; + } +} diff --git a/libjava/classpath/gnu/javax/net/ssl/provider/CertificateStatusRequest.java b/libjava/classpath/gnu/javax/net/ssl/provider/CertificateStatusRequest.java new file mode 100644 index 000000000..e66373620 --- /dev/null +++ b/libjava/classpath/gnu/javax/net/ssl/provider/CertificateStatusRequest.java @@ -0,0 +1,272 @@ +/* CertificateStatusRequest.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.javax.net.ssl.provider.Extension.Value; + +import java.io.PrintWriter; +import java.io.StringWriter; +import java.nio.ByteBuffer; +import java.util.Iterator; +import java.util.List; +import java.util.NoSuchElementException; + +/** + *
+struct {
+  CertificateStatusType status_type;
+  select (status_type) {
+    case ocsp: OCSPStatusRequest;
+  } request;
+} CertificateStatusRequest;
+
+enum { ocsp(1), (255) } CertificateStatusType;
+
+struct {
+  ResponderID responder_id_list<0..2^16-1>;
+  Extensions  request_extensions;
+} OCSPStatusRequest;
+
+opaque ResponderID<1..2^16-1>;
+opaque Extensions<0..2^16-1>;
+ * + * @author csm + */ +public class CertificateStatusRequest extends Value implements Iterable +{ + private ByteBuffer buffer; + + public CertificateStatusRequest(final ByteBuffer buffer) + { + this.buffer = buffer; + } + + public CertificateStatusRequest(CertificateStatusType type, + List responderIdList, + byte[] requestExtensions) + { + if (type != CertificateStatusType.OCSP) + throw new IllegalArgumentException(); + int length = 3; + int idsLength = 0; + for (byte[] responderId : responderIdList) + { + length += 2 + responderId.length; + idsLength += 2 + responderId.length; + } + length += 2 + requestExtensions.length; + buffer = ByteBuffer.allocate(length); + buffer.put((byte) 1); + buffer.putShort((short) idsLength); + for (byte[] responderId : responderIdList) + buffer.putShort((short) responderId.length).put(responderId); + buffer.putShort((short) requestExtensions.length); + buffer.put(requestExtensions); + buffer.rewind(); + } + + public int length() + { + int l = 3 + (buffer.getShort(1) & 0xFFFF); + return l + (buffer.getShort(l) & 0xFFFF) + 2; + } + + public ByteBuffer buffer() + { + return (ByteBuffer) buffer.duplicate().limit(length()); + } + + public CertificateStatusType statusType() + { + int x = buffer.get(0) & 0xFF; + if (x == 1) + return CertificateStatusType.OCSP; + throw new IllegalArgumentException ("invalid type: " + x); + } + + public int size() + { + int len = buffer.getShort(1) & 0xFFFF; + int n = 0; + for (int i = 3; i < len; ) + { + int l = buffer.getShort(i); + i += l + 2; + n++; + } + return n; + } + + public byte[] responderId(int index) + { + int len = buffer.getShort(1) & 0xFFFF; + int n = 0; + int i = 3; + while (i < len && n <= index) + { + int l = buffer.getShort(i) & 0xFFFF; + if (n == index) + { + byte[] b = new byte[l]; + ((ByteBuffer) buffer.duplicate().position(i+2)).get(b); + return b; + } + i += l + 2; + n++; + } + throw new IndexOutOfBoundsException(); + } + + public byte[] requestExtensions() + { + int l = 2 + (buffer.getShort(0) & 0xFFFF); + int ll = buffer.getShort(l) & 0xFFFF; + byte[] b = new byte[ll]; + ((ByteBuffer) buffer.duplicate().position(ll+2)).get(b); + return b; + } + + public void setStatusType(CertificateStatusType type) + { + buffer.put(0, (byte) type.value); + } + + public void setRequestIdListLength(int newLength) + { + if (newLength < 0 || newLength > 0xFFFF) + throw new IllegalArgumentException("length out of range"); + buffer.putShort(1, (short) newLength); + } + + public void putRequestId(int index, byte[] id) + { + if (id.length > 0xFFFF) + throw new IllegalArgumentException("request ID too large"); + int len = buffer.getShort(1) & 0xFFFF; + int n = 0; + int i = 3; + while (i < len && n < index) + { + int l = buffer.getShort(i) & 0xFFFF; + i += l + 2; + n++; + } + if (n < index) + throw new IndexOutOfBoundsException(); + buffer.putShort(i, (short) id.length); + ((ByteBuffer) buffer.duplicate().position(i)).put(id); + } + + public void setRequestExtensions(int index, byte[] ext) + { + if (ext.length > 0xFFFF) + throw new IllegalArgumentException("exceptions too large"); + int off = 3 + (buffer.getShort(1) & 0xFFFF); + buffer.putShort(off, (short) ext.length); + ((ByteBuffer) buffer.duplicate().position(off+2)).put(ext); + } + + public Iterator iterator() + { + return new ResponderIdIterator(); + } + + public String toString() + { + return toString(null); + } + + public String toString(String prefix) + { + StringWriter str = new StringWriter(); + PrintWriter out = new PrintWriter(str); + if (prefix != null) out.print(prefix); + out.println("struct {"); + if (prefix != null) out.print(prefix); + out.print(" status_type = "); + out.print(statusType()); + out.println(";"); + String subprefix = " "; + if (prefix != null) subprefix = prefix + subprefix; + if (prefix != null) out.print(prefix); + out.println(" responder_id_list = {"); + for (byte[] b : this) + out.print(Util.hexDump(b, subprefix)); + if (prefix != null) out.print(prefix); + out.println(" };"); + if (prefix != null) out.print(prefix); + out.println(" request_extensions ="); + out.print(Util.hexDump(requestExtensions(), subprefix)); + if (prefix != null) out.print(prefix); + out.print("} CertificateStatus;"); + return str.toString(); + } + + public class ResponderIdIterator implements Iterator + { + private int index; + + public ResponderIdIterator() + { + index = 0; + } + + public byte[] next() throws NoSuchElementException + { + try + { + return responderId(index++); + } + catch (IndexOutOfBoundsException ioobe) + { + throw new NoSuchElementException(); + } + } + + public boolean hasNext() + { + return index < size(); + } + + public void remove() + { + throw new UnsupportedOperationException(); + } + } +} diff --git a/libjava/classpath/gnu/javax/net/ssl/provider/CertificateStatusType.java b/libjava/classpath/gnu/javax/net/ssl/provider/CertificateStatusType.java new file mode 100644 index 000000000..0d52b2778 --- /dev/null +++ b/libjava/classpath/gnu/javax/net/ssl/provider/CertificateStatusType.java @@ -0,0 +1,13 @@ +package gnu.javax.net.ssl.provider; + +public enum CertificateStatusType +{ + OCSP (1); + + public final int value; + + private CertificateStatusType (final int value) + { + this.value = value; + } +} diff --git a/libjava/classpath/gnu/javax/net/ssl/provider/CertificateType.java b/libjava/classpath/gnu/javax/net/ssl/provider/CertificateType.java new file mode 100644 index 000000000..ecba21b63 --- /dev/null +++ b/libjava/classpath/gnu/javax/net/ssl/provider/CertificateType.java @@ -0,0 +1,62 @@ +/* CertificateType.java -- the certificate type extension. + 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; + +public enum CertificateType +{ + X509 (0), + OPEN_PGP (1); + + private final int value; + + private CertificateType(int value) + { + this.value = value; + } + + public static CertificateType forValue (final int value) + { + switch (value) + { + case 0: return X509; + case 1: return OPEN_PGP; + default: throw new IllegalArgumentException ("unknown certificate type: " + value); + } + } +} diff --git a/libjava/classpath/gnu/javax/net/ssl/provider/CertificateURL.java b/libjava/classpath/gnu/javax/net/ssl/provider/CertificateURL.java new file mode 100644 index 000000000..737efcacd --- /dev/null +++ b/libjava/classpath/gnu/javax/net/ssl/provider/CertificateURL.java @@ -0,0 +1,388 @@ +/* CertificateURL.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.javax.net.ssl.provider.Extension.Value; + +import java.io.PrintWriter; +import java.io.StringWriter; +import java.nio.ByteBuffer; +import java.nio.ByteOrder; +import java.nio.CharBuffer; +import java.nio.charset.Charset; +import java.nio.charset.CharsetEncoder; +import java.util.List; +import java.util.NoSuchElementException; + +/** + * The CertificateURL extension value. + * + *
+enum {
+  individual_certs(0), pkipath(1), (255)
+} CertChainType;
+
+enum {
+  false(0), true(1)
+} Boolean;
+
+struct {
+  CertChainType type;
+  URLAndOptionalHash url_and_hash_list<1..2^16-1>;
+} CertificateURL;
+
+struct {
+  opaque url<1..2^16-1>;
+  Boolean hash_present;
+  select (hash_present) {
+    case false: struct {};
+    case true: SHA1Hash;
+  } hash;
+} URLAndOptionalHash;
+
+opaque SHA1Hash[20];
+ * + * @author csm + * + */ +public class CertificateURL extends Value implements Iterable +{ + private ByteBuffer buffer; + + public CertificateURL(final ByteBuffer buffer) + { + this.buffer = buffer; + } + + public CertificateURL(CertChainType type, List urls) + { + int length = 3; + for (URLAndOptionalHash url : urls) + length += url.length(); + buffer = ByteBuffer.allocate(length); + buffer.put((byte) type.getValue()); + buffer.putShort((short) (length - 1)); + for (URLAndOptionalHash url : urls) + buffer.put(url.buffer()); + buffer.rewind(); + } + + public int length() + { + return 3 + (buffer.getShort(1) & 0xFFFF); + } + + public ByteBuffer buffer() + { + return (ByteBuffer) buffer.duplicate().limit(length()); + } + + public CertChainType type() + { + switch (buffer.get(0)) + { + case 0: return CertChainType.INDIVIDUAL_CERTS; + case 1: return CertChainType.PKIPATH; + } + throw new IllegalArgumentException("unknown certificate URL type"); + } + + public int size() + { + int len = buffer.getShort(1) & 0xFFFF; + int n = 0; + for (int i = 3; i < len; ) + { + URLAndOptionalHash u + = new URLAndOptionalHash((ByteBuffer) buffer.duplicate().position(i)); + int l = u.length(); + i += l; + n++; + } + return n; + } + + public URLAndOptionalHash get(int index) + { + int len = buffer.getShort(1) & 0xFFFF; + int n = 0; + int l = 0; + int i; + for (i = 3; i < len && n < index; ) + { + URLAndOptionalHash u + = new URLAndOptionalHash((ByteBuffer) buffer.duplicate().position(i)); + l = u.length(); + i += l; + n++; + } + if (n < index) + throw new IndexOutOfBoundsException(); + return new URLAndOptionalHash(((ByteBuffer) buffer.duplicate().position(i).limit(i+l)).slice()); + } + + public void set(int index, URLAndOptionalHash url) + { + int len = buffer.getShort(1) & 0xFFFF; + int n = 0; + int i; + for (i = 3; i < len && n < index-1; ) + { + URLAndOptionalHash u + = new URLAndOptionalHash((ByteBuffer) buffer.duplicate().position(i)); + int l = u.length(); + i += l; + n++; + } + if (n < index - 1) + throw new IndexOutOfBoundsException(); + int l = url.urlLength(); + buffer.putShort(i, (short) l); + ((ByteBuffer) buffer.duplicate().position(i+2)).put(url.urlBuffer()); + buffer.put(i+l+2, (byte) (url.hashPresent() ? 1 : 0)); + if (url.hashPresent()) + ((ByteBuffer) buffer.duplicate().position(i+l+3)).put (url.sha1Hash()); + } + + public void setLength(final int length) + { + if (length < 0 || length > 65535) + throw new IllegalArgumentException("length must be between 0 and 65535"); + buffer.putShort(1, (short) length); + } + + public String toString() + { + return toString(null); + } + + public String toString(String prefix) + { + StringWriter str = new StringWriter(); + PrintWriter out = new PrintWriter(str); + if (prefix != null) out.print(prefix); + out.println ("struct {"); + if (prefix != null) out.print(prefix); + out.print(" type = "); + out.print(type()); + out.println(";"); + if (prefix != null) out.print(prefix); + out.println(" url_and_hash_list = {"); + String subprefix = " "; + if (prefix != null) subprefix = prefix + subprefix; + for (URLAndOptionalHash url : this) + { + out.println(url.toString(subprefix)); + } + if (prefix != null) out.print(prefix); + out.println(" };"); + if (prefix != null) out.print(prefix); + out.print("} CertificateURL;"); + return str.toString(); + } + + public java.util.Iterator iterator() + { + return new Iterator(); + } + + public class Iterator implements java.util.Iterator + { + private int index; + + public Iterator() + { + index = 0; + } + + public URLAndOptionalHash next() throws NoSuchElementException + { + try + { + return get(index++); + } + catch (IndexOutOfBoundsException ioobe) + { + throw new NoSuchElementException(); + } + } + + public boolean hasNext() + { + return index < size(); + } + + public void remove() + { + throw new UnsupportedOperationException(); + } + } + + public static enum CertChainType + { + INDIVIDUAL_CERTS (0), PKIPATH (1); + + private final int value; + + private CertChainType (final int value) + { + this.value = value; + } + + public int getValue() + { + return value; + } + } + + public static class URLAndOptionalHash implements Builder, Constructed + { + private ByteBuffer buffer; + + public URLAndOptionalHash (final ByteBuffer buffer) + { + this.buffer = buffer.duplicate().order(ByteOrder.BIG_ENDIAN); + } + + public URLAndOptionalHash(String url) + { + this(url, null); + } + + public URLAndOptionalHash(String url, byte[] hash) + { + if (hash != null && hash.length < 20) + throw new IllegalArgumentException(); + int length = 3 + url.length(); + if (hash != null) + length += 20; + buffer = ByteBuffer.allocate(length); + buffer.putShort((short) url.length()); + Charset cs = Charset.forName("US-ASCII"); + CharsetEncoder ascii = cs.newEncoder(); + ascii.encode(CharBuffer.wrap(url), buffer, true); + buffer.put((byte) (hash != null ? 1 : 0)); + if (hash != null) + buffer.put(hash, 0, 20); + buffer.rewind(); + } + + public int length() + { + return ((buffer.getShort(0) & 0xFFFF) + + (hashPresent() ? 23 : 3)); + } + + public ByteBuffer buffer() + { + return (ByteBuffer) buffer.duplicate().limit(length()); + } + + public String url() + { + Charset cs = Charset.forName("ASCII"); + return cs.decode(urlBuffer()).toString(); + } + + public int urlLength() + { + return buffer.getShort(0) & 0xFFFF; + } + + public ByteBuffer urlBuffer() + { + int len = urlLength(); + return ((ByteBuffer) buffer.duplicate().position(2).limit(2+len)).slice(); + } + + public boolean hashPresent() + { + int i = (buffer.getShort(0) & 0xFFFF) + 2; + byte b = buffer.get(i); + if (b == 0) + return false; + if (b == 1) + return true; + throw new IllegalArgumentException("expecting 0 or 1: " + (b & 0xFF)); + } + + public byte[] sha1Hash() + { + int i = (buffer.getShort(0) & 0xFFFF) + 2; + byte b = buffer.get(i); + if (b == 0) + return null; + byte[] buf = new byte[20]; + ((ByteBuffer) buffer.duplicate().position(i+1)).get(buf); + return buf; + } + + public String toString() + { + return toString(null); + } + + public String toString(final String prefix) + { + StringWriter str = new StringWriter(); + PrintWriter out = new PrintWriter(str); + if (prefix != null) out.print(prefix); + out.println("struct {"); + if (prefix != null) out.print(prefix); + out.print(" url = "); + out.print(url()); + out.println(";"); + boolean has_hash = hashPresent(); + if (prefix != null) out.print(prefix); + out.print(" hash_present = "); + out.print(has_hash); + out.println(";"); + if (has_hash) + { + if (prefix != null) out.print(prefix); + out.print(" sha1Hash = "); + out.print(Util.toHexString(sha1Hash(), ':')); + out.println(";"); + } + if (prefix != null) out.print(prefix); + out.print("} URLAndOptionalHash;"); + return str.toString(); + } + } +} diff --git a/libjava/classpath/gnu/javax/net/ssl/provider/CertificateVerify.java b/libjava/classpath/gnu/javax/net/ssl/provider/CertificateVerify.java new file mode 100644 index 000000000..dfa5f6028 --- /dev/null +++ b/libjava/classpath/gnu/javax/net/ssl/provider/CertificateVerify.java @@ -0,0 +1,83 @@ +/* CertificateVerify.java -- SSL CertificateVerify message. + 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.PrintWriter; +import java.io.StringWriter; +import java.nio.ByteBuffer; + +public class CertificateVerify extends Signature implements Handshake.Body +{ + + // Contstructor. + // ------------------------------------------------------------------------- + + public CertificateVerify(final ByteBuffer buffer, final SignatureAlgorithm sigAlg) + { + super(buffer, sigAlg); + } + + public CertificateVerify(final byte[] sigVal, final SignatureAlgorithm sigAlg) + { + super(sigVal, sigAlg); + } + + // Instance method. + // ------------------------------------------------------------------------- + + public String toString() + { + return toString (null); + } + + public String toString (final String prefix) + { + StringWriter str = new StringWriter (); + PrintWriter out = new PrintWriter (str); + if (prefix != null) out.print (prefix); + out.println("struct {"); + String subprefix = " "; + if (prefix != null) + subprefix = prefix + subprefix; + out.println (super.toString (subprefix)); + if (prefix != null) out.print (prefix); + out.print ("} CertificateVerify;"); + return str.toString(); + } +} diff --git a/libjava/classpath/gnu/javax/net/ssl/provider/CipherAlgorithm.java b/libjava/classpath/gnu/javax/net/ssl/provider/CipherAlgorithm.java new file mode 100644 index 000000000..98e05af31 --- /dev/null +++ b/libjava/classpath/gnu/javax/net/ssl/provider/CipherAlgorithm.java @@ -0,0 +1,47 @@ +/* CipherAlgorithm.java -- Cipher algorithm enumeration. + 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; + +/** + * The set of cipher algorithms we support. + */ +public enum CipherAlgorithm +{ + NULL, RC4, DES, DESede, CAST5, AES +} diff --git a/libjava/classpath/gnu/javax/net/ssl/provider/CipherSuite.java b/libjava/classpath/gnu/javax/net/ssl/provider/CipherSuite.java new file mode 100644 index 000000000..1c5923129 --- /dev/null +++ b/libjava/classpath/gnu/javax/net/ssl/provider/CipherSuite.java @@ -0,0 +1,837 @@ +/* CipherSuite.java -- Supported cipher suites. + 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.java.security.action.GetSecurityPropertyAction; + +import java.io.IOException; +import java.io.OutputStream; + +import java.nio.ByteBuffer; + +import java.security.AccessController; +import java.security.NoSuchAlgorithmException; +import java.security.NoSuchProviderException; + +import java.util.HashMap; +import java.util.LinkedList; +import java.util.List; + +import javax.crypto.Cipher; +import javax.crypto.Mac; +import javax.crypto.NoSuchPaddingException; +import javax.crypto.NullCipher; + +public final class CipherSuite implements Constructed +{ + + // Constants and fields. + // ------------------------------------------------------------------------- + + private static final List tlsSuiteNames = new LinkedList(); + private static final HashMap namesToSuites = new HashMap(); + + // Core TLS cipher suites. + public static final CipherSuite TLS_NULL_WITH_NULL_NULL = + new CipherSuite (CipherAlgorithm.NULL, + KeyExchangeAlgorithm.NONE, + SignatureAlgorithm.ANONYMOUS, + MacAlgorithm.NULL, 0, 0x00, 0x00, + "TLS_NULL_WITH_NULL_NULL"); + public static final CipherSuite TLS_RSA_WITH_NULL_MD5 = + new CipherSuite (CipherAlgorithm.NULL, + KeyExchangeAlgorithm.RSA, + SignatureAlgorithm.RSA, + MacAlgorithm.MD5, 0, 0x00, 0x01, + "TLS_RSA_WITH_NULL_MD5"); + public static final CipherSuite TLS_RSA_WITH_NULL_SHA = + new CipherSuite (CipherAlgorithm.NULL, + KeyExchangeAlgorithm.RSA, + SignatureAlgorithm.RSA, + MacAlgorithm.SHA, 0, 0x00, 0x02, + "TLS_RSA_WITH_NULL_SHA"); + public static final CipherSuite TLS_RSA_EXPORT_WITH_RC4_40_MD5 = + new CipherSuite (CipherAlgorithm.RC4, + KeyExchangeAlgorithm.RSA, + SignatureAlgorithm.RSA, + MacAlgorithm.MD5, 5, 0x00, 0x03, + "TLS_RSA_EXPORT_WITH_RC4_40_MD5"); + public static final CipherSuite TLS_RSA_WITH_RC4_128_MD5 = + new CipherSuite (CipherAlgorithm.RC4, + KeyExchangeAlgorithm.RSA, + SignatureAlgorithm.RSA, + MacAlgorithm.MD5, 16, 0x00, 0x04, + "TLS_RSA_WITH_RC4_128_MD5"); + public static final CipherSuite TLS_RSA_WITH_RC4_128_SHA = + new CipherSuite (CipherAlgorithm.RC4, + KeyExchangeAlgorithm.RSA, + SignatureAlgorithm.RSA, + MacAlgorithm.SHA, 16, 0x00, 0x05, + "TLS_RSA_WITH_RC4_128_SHA"); + public static final CipherSuite TLS_RSA_EXPORT_WITH_DES40_CBC_SHA = + new CipherSuite (CipherAlgorithm.DES, + KeyExchangeAlgorithm.RSA, + SignatureAlgorithm.RSA, + MacAlgorithm.SHA, 5, 0x00, 0x08, + "TLS_RSA_EXPORT_WITH_DES40_CBC_SHA"); + public static final CipherSuite TLS_RSA_WITH_DES_CBC_SHA = + new CipherSuite (CipherAlgorithm.DES, + KeyExchangeAlgorithm.RSA, + SignatureAlgorithm.RSA, + MacAlgorithm.SHA, 8, 0x00, 0x09, + "TLS_RSA_WITH_DES_CBC_SHA"); + public static final CipherSuite TLS_RSA_WITH_3DES_EDE_CBC_SHA = + new CipherSuite (CipherAlgorithm.DESede, + KeyExchangeAlgorithm.RSA, + SignatureAlgorithm.RSA, + MacAlgorithm.SHA, 24, 0x00, 0x0A, + "TLS_RSA_WITH_3DES_EDE_CBC_SHA"); + public static final CipherSuite TLS_DH_DSS_EXPORT_WITH_DES40_CBC_SHA = + new CipherSuite (CipherAlgorithm.DES, + KeyExchangeAlgorithm.DH_DSS, + SignatureAlgorithm.ANONYMOUS, + MacAlgorithm.SHA, 5, 0x00, 0x0B, + "TLS_DH_DSS_EXPORT_WITH_DES40_CBC_SHA"); + public static final CipherSuite TLS_DH_DSS_WITH_DES_CBC_SHA = + new CipherSuite (CipherAlgorithm.DES, + KeyExchangeAlgorithm.DH_DSS, + SignatureAlgorithm.ANONYMOUS, + MacAlgorithm.SHA, 8, 0x00, 0x0C, + "TLS_DH_DSS_WITH_DES_CBC_SHA"); + public static final CipherSuite TLS_DH_DSS_WITH_3DES_EDE_CBC_SHA = + new CipherSuite (CipherAlgorithm.DESede, + KeyExchangeAlgorithm.DH_DSS, + SignatureAlgorithm.ANONYMOUS, + MacAlgorithm.SHA, 24, 0x00, 0x0D, + "TLS_DH_DSS_WITH_3DES_EDE_CBC_SHA"); + public static final CipherSuite TLS_DH_RSA_EXPORT_WITH_DES40_CBC_SHA = + new CipherSuite (CipherAlgorithm.DES, + KeyExchangeAlgorithm.DH_RSA, + SignatureAlgorithm.ANONYMOUS, + MacAlgorithm.SHA, 5, 0x00, 0x0E, + "TLS_DH_RSA_EXPORT_WITH_DES40_CBC_SHA"); + public static final CipherSuite TLS_DH_RSA_WITH_DES_CBC_SHA = + new CipherSuite (CipherAlgorithm.DES, + KeyExchangeAlgorithm.DH_RSA, + SignatureAlgorithm.ANONYMOUS, + MacAlgorithm.SHA, 8, 0x00, 0x0F, + "TLS_DH_RSA_WITH_DES_CBC_SHA"); + public static final CipherSuite TLS_DH_RSA_WITH_3DES_EDE_CBC_SHA = + new CipherSuite (CipherAlgorithm.DESede, + KeyExchangeAlgorithm.DH_RSA, + SignatureAlgorithm.ANONYMOUS, + MacAlgorithm.SHA, 24, 0x00, 0x10, + "TLS_DH_RSA_WITH_3DES_EDE_CBC_SHA"); + public static final CipherSuite TLS_DHE_DSS_EXPORT_WITH_DES40_CBC_SHA = + new CipherSuite (CipherAlgorithm.DES, + KeyExchangeAlgorithm.DHE_DSS, true, + SignatureAlgorithm.DSA, + MacAlgorithm.SHA, 5, 0x00, 0x11, + "TLS_DHE_DSS_EXPORT_WITH_DES40_CBC_SHA"); + public static final CipherSuite TLS_DHE_DSS_WITH_DES_CBC_SHA = + new CipherSuite (CipherAlgorithm.DES, + KeyExchangeAlgorithm.DHE_DSS, true, + SignatureAlgorithm.DSA, + MacAlgorithm.SHA, 8, 0x00, 0x12, + "TLS_DHE_DSS_WITH_DES_CBC_SHA"); + public static final CipherSuite TLS_DHE_DSS_WITH_3DES_EDE_CBC_SHA = + new CipherSuite (CipherAlgorithm.DESede, + KeyExchangeAlgorithm.DHE_DSS, true, + SignatureAlgorithm.DSA, + MacAlgorithm.SHA, 24, 0x00, 0x13, + "TLS_DHE_DSS_WITH_3DES_EDE_CBC_SHA"); + public static final CipherSuite TLS_DHE_RSA_EXPORT_WITH_DES40_CBC_SHA = + new CipherSuite (CipherAlgorithm.DES, + KeyExchangeAlgorithm.DHE_RSA, true, + SignatureAlgorithm.RSA, + MacAlgorithm.SHA, 5, 0x00, 0x14, + "TLS_DHE_RSA_EXPORT_WITH_DES40_CBC_SHA"); + public static final CipherSuite TLS_DHE_RSA_WITH_DES_CBC_SHA = + new CipherSuite (CipherAlgorithm.DES, + KeyExchangeAlgorithm.DHE_RSA, true, + SignatureAlgorithm.RSA, + MacAlgorithm.SHA, 8, 0x00, 0x15, + "TLS_DHE_RSA_WITH_DES_CBC_SHA"); + public static final CipherSuite TLS_DHE_RSA_WITH_3DES_EDE_CBC_SHA = + new CipherSuite (CipherAlgorithm.DESede, + KeyExchangeAlgorithm.DHE_RSA, true, + SignatureAlgorithm.RSA, + MacAlgorithm.SHA, 24, 0x00, 0x16, + "TLS_DHE_RSA_WITH_3DES_EDE_CBC_SHA"); + + // AES CipherSuites. + public static final CipherSuite TLS_RSA_WITH_AES_128_CBC_SHA = + new CipherSuite (CipherAlgorithm.AES, + KeyExchangeAlgorithm.RSA, + SignatureAlgorithm.RSA, + MacAlgorithm.SHA, 16, 0x00, 0x2F, + "TLS_RSA_WITH_AES_128_CBC_SHA"); + public static final CipherSuite TLS_DH_DSS_WITH_AES_128_CBC_SHA = + new CipherSuite (CipherAlgorithm.AES, + KeyExchangeAlgorithm.DH_DSS, + SignatureAlgorithm.ANONYMOUS, + MacAlgorithm.SHA, 16, 0x00, 0x30, + "TLS_DH_DSS_WITH_AES_128_CBC_SHA"); + public static final CipherSuite TLS_DH_RSA_WITH_AES_128_CBC_SHA = + new CipherSuite (CipherAlgorithm.AES, + KeyExchangeAlgorithm.DH_RSA, + SignatureAlgorithm.ANONYMOUS, + MacAlgorithm.SHA, 16, 0x00, 0x31, + "TLS_DH_RSA_WITH_AES_128_CBC_SHA"); + public static final CipherSuite TLS_DHE_DSS_WITH_AES_128_CBC_SHA = + new CipherSuite (CipherAlgorithm.AES, + KeyExchangeAlgorithm.DHE_DSS, true, + SignatureAlgorithm.DSA, + MacAlgorithm.SHA, 16, 0x00, 0x32, + "TLS_DHE_DSS_WITH_AES_128_CBC_SHA"); + public static final CipherSuite TLS_DHE_RSA_WITH_AES_128_CBC_SHA = + new CipherSuite (CipherAlgorithm.AES, + KeyExchangeAlgorithm.DHE_RSA, true, + SignatureAlgorithm.RSA, + MacAlgorithm.SHA, 16, 0x00, 0x33, + "TLS_DHE_RSA_WITH_AES_128_CBC_SHA"); + public static final CipherSuite TLS_RSA_WITH_AES_256_CBC_SHA = + new CipherSuite (CipherAlgorithm.AES, + KeyExchangeAlgorithm.RSA, + SignatureAlgorithm.ANONYMOUS, + MacAlgorithm.SHA, 32, 0x00, 0x35, + "TLS_RSA_WITH_AES_256_CBC_SHA"); + public static final CipherSuite TLS_DH_DSS_WITH_AES_256_CBC_SHA = + new CipherSuite (CipherAlgorithm.AES, + KeyExchangeAlgorithm.DH_DSS, + SignatureAlgorithm.ANONYMOUS, + MacAlgorithm.SHA, 32, 0x00, 0x36, + "TLS_DH_DSS_WITH_AES_256_CBC_SHA"); + public static final CipherSuite TLS_DH_RSA_WITH_AES_256_CBC_SHA = + new CipherSuite (CipherAlgorithm.AES, + KeyExchangeAlgorithm.DH_RSA, + SignatureAlgorithm.ANONYMOUS, + MacAlgorithm.SHA, 32, 0x00, 0x37, + "TLS_DH_RSA_WITH_AES_256_CBC_SHA"); + public static final CipherSuite TLS_DHE_DSS_WITH_AES_256_CBC_SHA = + new CipherSuite (CipherAlgorithm.AES, + KeyExchangeAlgorithm.DHE_DSS, true, + SignatureAlgorithm.DSA, + MacAlgorithm.SHA, 32, 0x00, 0x38, + "TLS_DHE_DSS_WITH_AES_256_CBC_SHA"); + public static final CipherSuite TLS_DHE_RSA_WITH_AES_256_CBC_SHA = + new CipherSuite (CipherAlgorithm.AES, + KeyExchangeAlgorithm.DHE_RSA, true, + SignatureAlgorithm.RSA, + MacAlgorithm.SHA, 32, 0x00, 0x39, + "TLS_DHE_RSA_WITH_AES_256_CBC_SHA"); + + // Secure remote password (SRP) ciphersuites + // Actual ID values are TBD, so these are omitted until they are specified. + /*public static final CipherSuite TLS_SRP_SHA_WITH_3DES_EDE_CBC_SHA = + new CipherSuite (CipherAlgorithm.DESede, + KeyExchangeAlgorithm.SRP, + SignatureAlgorithm.ANONYMOUS, + MacAlgorithm.SHA, 24, 0x00, 0x50, + "TLS_SRP_SHA_WITH_3DES_EDE_CBC_SHA"); + public static final CipherSuite TLS_SRP_SHA_RSA_WITH_3DES_EDE_CBC_SHA = + new CipherSuite (CipherAlgorithm.DESede, + KeyExchangeAlgorithm.SRP, + SignatureAlgorithm.RSA, + MacAlgorithm.SHA, 24, 0x00, 0x51, + "TLS_SRP_SHA_RSA_WITH_3DES_EDE_CBC_SHA"); + public static final CipherSuite TLS_SRP_SHA_DSS_WITH_3DES_EDE_CBC_SHA = + new CipherSuite (CipherAlgorithm.DESede, + KeyExchangeAlgorithm.SRP, + SignatureAlgorithm.DSA, + MacAlgorithm.SHA, 24, 0x00, 0x52, + "TLS_SRP_SHA_DSS_WITH_3DES_EDE_CBC_SHA"); + public static final CipherSuite TLS_SRP_SHA_WITH_AES_128_CBC_SHA = + new CipherSuite (CipherAlgorithm.AES, + KeyExchangeAlgorithm.SRP, + SignatureAlgorithm.ANONYMOUS, + MacAlgorithm.SHA, 16, 0x00, 0x53, + "TLS_SRP_SHA_WITH_AES_128_CBC_SHA"); + public static final CipherSuite TLS_SRP_SHA_RSA_WITH_AES_128_CBC_SHA = + new CipherSuite (CipherAlgorithm.AES, + KeyExchangeAlgorithm.SRP, + SignatureAlgorithm.RSA, + MacAlgorithm.SHA, 16, 0x00, 0x54, + "TLS_SRP_SHA_RSA_WITH_AES_128_CBC_SHA"); + public static final CipherSuite TLS_SRP_SHA_DSS_WITH_AES_128_CBC_SHA = + new CipherSuite (CipherAlgorithm.AES, + KeyExchangeAlgorithm.SRP, + SignatureAlgorithm.DSA, + MacAlgorithm.SHA, 16, 0x00, 0x55, + "TLS_SRP_SHA_DSS_WITH_AES_128_CBC_SHA"); + public static final CipherSuite TLS_SRP_SHA_WITH_AES_256_CBC_SHA = + new CipherSuite (CipherAlgorithm.AES, + KeyExchangeAlgorithm.SRP, + SignatureAlgorithm.ANONYMOUS, + MacAlgorithm.SHA, 32, 0x00, 0x56, + "TLS_SRP_SHA_WITH_AES_256_CBC_SHA"); + public static final CipherSuite TLS_SRP_SHA_RSA_WITH_AES_256_CBC_SHA = + new CipherSuite (CipherAlgorithm.AES, + KeyExchangeAlgorithm.SRP, + SignatureAlgorithm.RSA, + MacAlgorithm.SHA, 32, 0x00, 0x57, + "TLS_SRP_SHA_RSA_WITH_AES_256_CBC_SHA"); + public static final CipherSuite TLS_SRP_SHA_DSS_WITH_AES_256_CBC_SHA = + new CipherSuite (CipherAlgorithm.AES, + KeyExchangeAlgorithm.SRP, + SignatureAlgorithm.DSA, + MacAlgorithm.SHA, 32, 0x00, 0x58, + "TLS_SRP_SHA_DSS_WITH_AES_256_CBC_SHA");*/ + + // Pre-shared key suites. + public static final CipherSuite TLS_PSK_WITH_RC4_128_SHA = + new CipherSuite(CipherAlgorithm.RC4, + KeyExchangeAlgorithm.PSK, + SignatureAlgorithm.ANONYMOUS, + MacAlgorithm.SHA, 16, 0x00, 0x8A, + "TLS_PSK_WITH_RC4_128_SHA"); + public static final CipherSuite TLS_PSK_WITH_3DES_EDE_CBC_SHA = + new CipherSuite(CipherAlgorithm.DESede, + KeyExchangeAlgorithm.PSK, + SignatureAlgorithm.ANONYMOUS, + MacAlgorithm.SHA, 24, 0x00, 0x8B, + "TLS_PSK_WITH_3DES_EDE_CBC_SHA"); + public static final CipherSuite TLS_PSK_WITH_AES_128_CBC_SHA = + new CipherSuite(CipherAlgorithm.AES, + KeyExchangeAlgorithm.PSK, + SignatureAlgorithm.ANONYMOUS, + MacAlgorithm.SHA, 16, 0x00, 0x8C, + "TLS_PSK_WITH_AES_128_CBC_SHA"); + public static final CipherSuite TLS_PSK_WITH_AES_256_CBC_SHA = + new CipherSuite(CipherAlgorithm.AES, + KeyExchangeAlgorithm.PSK, + SignatureAlgorithm.ANONYMOUS, + MacAlgorithm.SHA, 32, 0x00, 0x8D, + "TLS_PSK_WITH_AES_256_CBC_SHA"); + + public static final CipherSuite TLS_DHE_PSK_WITH_RC4_128_SHA = + new CipherSuite(CipherAlgorithm.RC4, + KeyExchangeAlgorithm.DHE_PSK, true, + SignatureAlgorithm.ANONYMOUS, + MacAlgorithm.SHA, 16, 0x00, 0x8E, + "TLS_DHE_PSK_WITH_RC4_128_SHA"); + public static final CipherSuite TLS_DHE_PSK_WITH_3DES_EDE_CBC_SHA = + new CipherSuite(CipherAlgorithm.DESede, + KeyExchangeAlgorithm.DHE_PSK, true, + SignatureAlgorithm.ANONYMOUS, + MacAlgorithm.SHA, 24, 0x00, 0x8F, + "TLS_DHE_PSK_WITH_3DES_EDE_CBC_SHA"); + public static final CipherSuite TLS_DHE_PSK_WITH_AES_128_CBC_SHA = + new CipherSuite(CipherAlgorithm.AES, + KeyExchangeAlgorithm.DHE_PSK, true, + SignatureAlgorithm.ANONYMOUS, + MacAlgorithm.SHA, 16, 0x00, 0x90, + "TLS_DHE_PSK_WITH_AES_128_CBC_SHA"); + public static final CipherSuite TLS_DHE_PSK_WITH_AES_256_CBC_SHA = + new CipherSuite(CipherAlgorithm.AES, + KeyExchangeAlgorithm.DHE_PSK, true, + SignatureAlgorithm.ANONYMOUS, + MacAlgorithm.SHA, 32, 0x00, 0x91, + "TLS_DHE_PSK_WITH_AES_256_CBC_SHA"); + + public static final CipherSuite TLS_RSA_PSK_WITH_RC4_128_SHA = + new CipherSuite(CipherAlgorithm.RC4, + KeyExchangeAlgorithm.RSA_PSK, + SignatureAlgorithm.ANONYMOUS, + MacAlgorithm.SHA, 16, 0x00, 0x92, + "TLS_RSA_PSK_WITH_RC4_128_SHA"); + public static final CipherSuite TLS_RSA_PSK_WITH_3DES_EDE_CBC_SHA = + new CipherSuite(CipherAlgorithm.DESede, + KeyExchangeAlgorithm.RSA_PSK, + SignatureAlgorithm.ANONYMOUS, + MacAlgorithm.SHA, 24, 0x00, 0x93, + "TLS_RSA_PSK_WITH_3DES_EDE_CBC_SHA"); + public static final CipherSuite TLS_RSA_PSK_WITH_AES_128_CBC_SHA = + new CipherSuite(CipherAlgorithm.AES, + KeyExchangeAlgorithm.RSA_PSK, + SignatureAlgorithm.ANONYMOUS, + MacAlgorithm.SHA, 16, 0x00, 0x94, + "TLS_RSA_PSK_WITH_AES_128_CBC_SHA"); + public static final CipherSuite TLS_RSA_PSK_WITH_AES_256_CBC_SHA = + new CipherSuite(CipherAlgorithm.AES, + KeyExchangeAlgorithm.RSA_PSK, + SignatureAlgorithm.ANONYMOUS, + MacAlgorithm.SHA, 32, 0x00, 0x95, + "TLS_RSA_PSK_WITH_AES_256_CBC_SHA"); + + // Ciphersuites from the OpenPGP extension draft. + // These disappeared from a more recent draft. +/* public static final CipherSuite TLS_DHE_DSS_WITH_CAST_128_CBC_SHA = + new CipherSuite (CipherAlgorithm.CAST5, + KeyExchangeAlgorithm.DIFFIE_HELLMAN, true, + SignatureAlgorithm.DSA, + MacAlgorithm.SHA, 16, 0x00, 0x70, + "TLS_DHE_DSS_WITH_CAST_128_CBC_SHA"); + public static final CipherSuite TLS_DHE_DSS_WITH_CAST_128_CBC_RMD = + new CipherSuite (CipherAlgorithm.CAST5, + KeyExchangeAlgorithm.DIFFIE_HELLMAN, true, + SignatureAlgorithm.DSA, + MacAlgorithm.HMAC_RMD, 16, 0x00, 0x71, + "TLS_DHE_DSS_WITH_CAST_128_CBC_RMD"); + public static final CipherSuite TLS_DHE_DSS_WITH_3DES_EDE_CBC_RMD = + new CipherSuite (CipherAlgorithm.DESede, + KeyExchangeAlgorithm.DIFFIE_HELLMAN, true, + SignatureAlgorithm.DSA, + MacAlgorithm.HMAC_RMD, 24, 0x00, 0x72, + "TLS_DHE_DSS_WITH_3DES_EDE_CBC_RMD"); + public static final CipherSuite TLS_DHE_DSS_WITH_AES_128_CBC_RMD = + new CipherSuite (CipherAlgorithm.AES, + KeyExchangeAlgorithm.DIFFIE_HELLMAN, true, + SignatureAlgorithm.DSA, + MacAlgorithm.HMAC_RMD, 16, 0x00, 0x73, + "TLS_DHE_DSS_WITH_AES_128_CBC_RMD"); + public static final CipherSuite TLS_DHE_DSS_WITH_AES_256_CBC_RMD = + new CipherSuite (CipherAlgorithm.AES, + KeyExchangeAlgorithm.DIFFIE_HELLMAN, true, + SignatureAlgorithm.DSA, + MacAlgorithm.HMAC_RMD, 32, 0x00, 0x74, + "TLS_DHE_DSS_WITH_AES_256_CBC_RMD"); + public static final CipherSuite TLS_DHE_RSA_WITH_CAST_128_CBC_SHA = + new CipherSuite (CipherAlgorithm.CAST5, + KeyExchangeAlgorithm.DIFFIE_HELLMAN, true, + SignatureAlgorithm.RSA, + MacAlgorithm.SHA, 16, 0x00, 0x75, + "TLS_DHE_RSA_WITH_CAST_128_CBC_SHA"); + public static final CipherSuite TLS_DHE_RSA_WITH_CAST_128_CBC_RMD = + new CipherSuite (CipherAlgorithm.CAST5, + KeyExchangeAlgorithm.DIFFIE_HELLMAN, true, + SignatureAlgorithm.RSA, + MacAlgorithm.HMAC_RMD, 16, 0x00, 0x76, + "TLS_DHE_RSA_WITH_CAST_128_CBC_RMD"); + public static final CipherSuite TLS_DHE_RSA_WITH_3DES_EDE_CBC_RMD = + new CipherSuite (CipherAlgorithm.DESede, + KeyExchangeAlgorithm.DIFFIE_HELLMAN, true, + SignatureAlgorithm.RSA, + MacAlgorithm.HMAC_RMD, 24, 0x00, 0x77, + "TLS_DHE_RSA_WITH_3DES_EDE_CBC_RMD"); + public static final CipherSuite TLS_DHE_RSA_WITH_AES_128_CBC_RMD = + new CipherSuite (CipherAlgorithm.AES, + KeyExchangeAlgorithm.DIFFIE_HELLMAN, true, + SignatureAlgorithm.RSA, + MacAlgorithm.HMAC_RMD, 16, 0x00, 0x78, + "TLS_DHE_RSA_WITH_AES_128_CBC_RMD"); + public static final CipherSuite TLS_DHE_RSA_WITH_AES_256_CBC_RMD = + new CipherSuite (CipherAlgorithm.AES, + KeyExchangeAlgorithm.DIFFIE_HELLMAN, true, + SignatureAlgorithm.RSA, + MacAlgorithm.HMAC_RMD, 32, 0x00, 0x79, + "TLS_DHE_RSA_WITH_AES_256_CBC_RMD"); + public static final CipherSuite TLS_RSA_WITH_CAST_128_CBC_SHA = + new CipherSuite (CipherAlgorithm.CAST5, + KeyExchangeAlgorithm.RSA, + SignatureAlgorithm.RSA, + MacAlgorithm.SHA, 16, 0x00, 0x7A, + "TLS_RSA_WITH_CAST_128_CBC_SHA"); + public static final CipherSuite TLS_RSA_WITH_CAST_128_CBC_RMD = + new CipherSuite (CipherAlgorithm.CAST5, + KeyExchangeAlgorithm.RSA, + SignatureAlgorithm.RSA, + MacAlgorithm.HMAC_RMD, 16, 0x00, 0x7B, + "TLS_RSA_WITH_CAST_128_CBC_RMD"); + public static final CipherSuite TLS_RSA_WITH_3DES_EDE_CBC_RMD = + new CipherSuite (CipherAlgorithm.DESede, + KeyExchangeAlgorithm.RSA, + SignatureAlgorithm.RSA, + MacAlgorithm.HMAC_RMD, 24, 0x00, 0x7C, + "TLS_RSA_WITH_3DES_EDE_CBC_RMD"); + public static final CipherSuite TLS_RSA_WITH_AES_128_CBC_RMD = + new CipherSuite (CipherAlgorithm.AES, + KeyExchangeAlgorithm.RSA, + SignatureAlgorithm.RSA, + MacAlgorithm.HMAC_RMD, 16, 0x00, 0x7D, + "TLS_RSA_WITH_AES_128_CBC_RMD"); + public static final CipherSuite TLS_RSA_WITH_AES_256_CBC_RMD = + new CipherSuite (CipherAlgorithm.AES, + KeyExchangeAlgorithm.RSA, + SignatureAlgorithm.RSA, + MacAlgorithm.HMAC_RMD, 32, 0x00, 0x7E, + "TLS_RSA_WITH_AES_256_CBC_RMD"); */ + + private final CipherAlgorithm cipherAlgorithm; + private final KeyExchangeAlgorithm keyExchangeAlgorithm; + private final SignatureAlgorithm signatureAlgorithm; + private final MacAlgorithm macAlgorithm; + private final boolean ephemeralDH; + private final boolean exportable; + private final boolean isStream; + private final int keyLength; + private final byte[] id; + private final String name; + private final boolean isResolved; + + // Constructors. + // ------------------------------------------------------------------------- + + private CipherSuite (final CipherAlgorithm cipherAlgorithm, + final KeyExchangeAlgorithm keyExchangeAlgorithm, + final SignatureAlgorithm signatureAlgorithm, + final MacAlgorithm macAlgorithm, + final int keyLength, + final int id1, + final int id2, + final String name) + { + this (cipherAlgorithm, keyExchangeAlgorithm, false, signatureAlgorithm, + macAlgorithm, keyLength, id1, id2, name); + } + + private CipherSuite (final CipherAlgorithm cipherAlgorithm, + final KeyExchangeAlgorithm keyExchangeAlgorithm, + final boolean ephemeralDH, + final SignatureAlgorithm signatureAlgorithm, + final MacAlgorithm macAlgorithm, + final int keyLength, + final int id1, + final int id2, + final String name) + { + this.cipherAlgorithm = cipherAlgorithm; + this.keyExchangeAlgorithm = keyExchangeAlgorithm; + this.ephemeralDH = ephemeralDH; + this.signatureAlgorithm = signatureAlgorithm; + this.macAlgorithm = macAlgorithm; + this.exportable = keyLength <= 5; + this.isStream = (cipherAlgorithm == CipherAlgorithm.NULL + || cipherAlgorithm == CipherAlgorithm.RC4); + this.keyLength = keyLength; + this.id = new byte[] { (byte) id1, (byte) id2 }; + this.name = name.intern(); + namesToSuites.put(name, this); + if (name.startsWith("TLS")) + { + tlsSuiteNames.add(name); + } + isResolved = true; + } + + private CipherSuite(byte[] id) + { + cipherAlgorithm = null; + keyExchangeAlgorithm = null; + signatureAlgorithm = null; + macAlgorithm = null; + ephemeralDH = false; + exportable = false; + isStream = false; + keyLength = 0; + this.id = id; + name = null; + isResolved = false; + } + + // Class methods. + // ------------------------------------------------------------------------- + + /** + * Returns the cipher suite for the given name, or null if there is no + * such suite. + * + * @return The named cipher suite. + */ + public static CipherSuite forName(String name) + { + if (name.startsWith("SSL_")) + name = "TLS_" + name.substring(4); + return namesToSuites.get(name); + } + + public static CipherSuite forValue(final short raw_value) + { + byte[] b = new byte[] { (byte) (raw_value >>> 8), (byte) raw_value }; + return new CipherSuite(b).resolve(); + } + + public static List availableSuiteNames() + { + return tlsSuiteNames; + } + + // Intance methods. + // ------------------------------------------------------------------------- + + public CipherAlgorithm cipherAlgorithm () + { + return cipherAlgorithm; + } + + public Cipher cipher () throws NoSuchAlgorithmException, NoSuchPaddingException + { + if (cipherAlgorithm == null) + throw new NoSuchAlgorithmException (toString () + ": unresolved cipher suite"); + if (cipherAlgorithm == CipherAlgorithm.NULL) + return new NullCipher (); + + String alg = null; + if (cipherAlgorithm == CipherAlgorithm.RC4) + alg = "RC4"; + else + alg = cipherAlgorithm + "/CBC/NoPadding"; + GetSecurityPropertyAction gspa = + new GetSecurityPropertyAction ("jessie.jce.provider"); + final String provider = (String) AccessController.doPrivileged (gspa); + if (provider != null) + { + try + { + return Cipher.getInstance (alg, provider); + } + catch (NoSuchProviderException nspe) + { + } + } + return Cipher.getInstance (alg); + } + + public MacAlgorithm macAlgorithm () + { + return macAlgorithm; + } + + public Mac mac(ProtocolVersion version) throws NoSuchAlgorithmException + { + if (macAlgorithm == null) + throw new NoSuchAlgorithmException(toString() + ": unresolved cipher suite"); + if (macAlgorithm == MacAlgorithm.NULL) + return null; + + String macAlg = null; + if (version == ProtocolVersion.SSL_3) + { + macAlg = "SSLv3HMac-" + macAlgorithm; + } + else + { + if (macAlgorithm == MacAlgorithm.MD5) + macAlg = "HMac-MD5"; + if (macAlgorithm == MacAlgorithm.SHA) + macAlg = "HMac-SHA1"; + } + + GetSecurityPropertyAction gspa = + new GetSecurityPropertyAction ("jessie.jce.provider"); + final String provider = AccessController.doPrivileged (gspa); + if (provider != null) + { + try + { + return Mac.getInstance(macAlg, provider); + } + catch (NoSuchProviderException nspe) + { + // Ignore; try any installed provider. + } + } + return Mac.getInstance(macAlg); + } + + public SignatureAlgorithm signatureAlgorithm () + { + return signatureAlgorithm; + } + + public KeyExchangeAlgorithm keyExchangeAlgorithm () + { + return keyExchangeAlgorithm; + } + + public boolean isEphemeralDH () + { + return ephemeralDH; + } + + public int length () + { + return 2; + } + + public void write(OutputStream out) throws IOException + { + out.write(id); + } + + public void put (final ByteBuffer buf) + { + buf.put (id); + } + + public CipherSuite resolve() + { + if (id[0] == 0x00) switch (id[1] & 0xFF) + { + case 0x00: return TLS_NULL_WITH_NULL_NULL; + case 0x01: return TLS_RSA_WITH_NULL_MD5; + case 0x02: return TLS_RSA_WITH_NULL_SHA; + case 0x03: return TLS_RSA_EXPORT_WITH_RC4_40_MD5; + case 0x04: return TLS_RSA_WITH_RC4_128_MD5; + case 0x05: return TLS_RSA_WITH_RC4_128_SHA; + case 0x08: return TLS_RSA_EXPORT_WITH_DES40_CBC_SHA; + case 0x09: return TLS_RSA_WITH_DES_CBC_SHA; + case 0x0A: return TLS_RSA_WITH_3DES_EDE_CBC_SHA; + case 0x0B: return TLS_DH_DSS_EXPORT_WITH_DES40_CBC_SHA; + case 0x0C: return TLS_DH_DSS_WITH_DES_CBC_SHA; + case 0x0D: return TLS_DH_DSS_WITH_3DES_EDE_CBC_SHA; + case 0x0E: return TLS_DH_RSA_EXPORT_WITH_DES40_CBC_SHA; + case 0x0F: return TLS_DH_RSA_WITH_DES_CBC_SHA; + case 0x10: return TLS_DH_RSA_WITH_3DES_EDE_CBC_SHA; + case 0x11: return TLS_DHE_DSS_EXPORT_WITH_DES40_CBC_SHA; + case 0x12: return TLS_DHE_DSS_WITH_DES_CBC_SHA; + case 0x13: return TLS_DHE_DSS_WITH_3DES_EDE_CBC_SHA; + case 0x14: return TLS_DHE_RSA_EXPORT_WITH_DES40_CBC_SHA; + case 0x15: return TLS_DHE_RSA_WITH_DES_CBC_SHA; + case 0x16: return TLS_DHE_RSA_WITH_3DES_EDE_CBC_SHA; + case 0x2F: return TLS_RSA_WITH_AES_128_CBC_SHA; + case 0x30: return TLS_DH_DSS_WITH_AES_128_CBC_SHA; + case 0x31: return TLS_DH_RSA_WITH_AES_128_CBC_SHA; + case 0x32: return TLS_DHE_DSS_WITH_AES_128_CBC_SHA; + case 0x33: return TLS_DHE_RSA_WITH_AES_128_CBC_SHA; + case 0x35: return TLS_RSA_WITH_AES_256_CBC_SHA; + case 0x36: return TLS_DH_DSS_WITH_AES_256_CBC_SHA; + case 0x37: return TLS_DH_RSA_WITH_AES_256_CBC_SHA; + case 0x38: return TLS_DHE_DSS_WITH_AES_256_CBC_SHA; + case 0x39: return TLS_DHE_RSA_WITH_AES_256_CBC_SHA; + /*case 0x50: return TLS_SRP_SHA_WITH_3DES_EDE_CBC_SHA; + case 0x51: return TLS_SRP_SHA_RSA_WITH_3DES_EDE_CBC_SHA; + case 0x52: return TLS_SRP_SHA_DSS_WITH_3DES_EDE_CBC_SHA; + case 0x53: return TLS_SRP_SHA_WITH_AES_128_CBC_SHA; + case 0x54: return TLS_SRP_SHA_RSA_WITH_AES_128_CBC_SHA; + case 0x55: return TLS_SRP_SHA_DSS_WITH_AES_128_CBC_SHA; + case 0x56: return TLS_SRP_SHA_WITH_AES_256_CBC_SHA; + case 0x57: return TLS_SRP_SHA_RSA_WITH_AES_256_CBC_SHA; + case 0x58: return TLS_SRP_SHA_DSS_WITH_AES_256_CBC_SHA; + case 0x70: return TLS_DHE_DSS_WITH_CAST_128_CBC_SHA; + case 0x71: return TLS_DHE_DSS_WITH_CAST_128_CBC_RMD; + case 0x72: return TLS_DHE_DSS_WITH_3DES_EDE_CBC_RMD; + case 0x73: return TLS_DHE_DSS_WITH_AES_128_CBC_RMD; + case 0x74: return TLS_DHE_DSS_WITH_AES_256_CBC_RMD; + case 0x75: return TLS_DHE_RSA_WITH_CAST_128_CBC_SHA; + case 0x76: return TLS_DHE_RSA_WITH_CAST_128_CBC_RMD; + case 0x77: return TLS_DHE_RSA_WITH_3DES_EDE_CBC_RMD; + case 0x78: return TLS_DHE_RSA_WITH_AES_128_CBC_RMD; + case 0x79: return TLS_DHE_RSA_WITH_AES_256_CBC_RMD; + case 0x7A: return TLS_RSA_WITH_CAST_128_CBC_SHA; + case 0x7B: return TLS_RSA_WITH_CAST_128_CBC_RMD; + case 0x7C: return TLS_RSA_WITH_3DES_EDE_CBC_RMD; + case 0x7D: return TLS_RSA_WITH_AES_128_CBC_RMD; + case 0x7E: return TLS_RSA_WITH_AES_256_CBC_RMD;*/ + case 0x8A: return TLS_PSK_WITH_RC4_128_SHA; + case 0x8B: return TLS_PSK_WITH_3DES_EDE_CBC_SHA; + case 0x8C: return TLS_PSK_WITH_AES_128_CBC_SHA; + case 0x8D: return TLS_PSK_WITH_AES_256_CBC_SHA; + case 0x8E: return TLS_DHE_PSK_WITH_RC4_128_SHA; + case 0x8F: return TLS_DHE_PSK_WITH_3DES_EDE_CBC_SHA; + case 0x90: return TLS_DHE_PSK_WITH_AES_128_CBC_SHA; + case 0x91: return TLS_DHE_PSK_WITH_AES_256_CBC_SHA; + case 0x92: return TLS_RSA_PSK_WITH_RC4_128_SHA; + case 0x93: return TLS_RSA_PSK_WITH_3DES_EDE_CBC_SHA; + case 0x94: return TLS_RSA_PSK_WITH_AES_128_CBC_SHA; + case 0x95: return TLS_RSA_PSK_WITH_AES_256_CBC_SHA; + } + return this; + } + + public boolean isResolved() + { + return isResolved; + } + + public int keyLength() + { + return keyLength; + } + + public boolean isExportable() + { + return exportable; + } + + public boolean isStreamCipher() + { + return isStream; + } + +// String getAuthType() +// { +// if (keyExchangeAlgorithm == KeyExchangeAlgorithm.RSA) +// { +// if (isExportable()) +// { +// return "RSA_EXPORT"; +// } +// return "RSA"; +// } +// return kexName + "_" + sigName; +// } + + public byte[] id() + { + return id; + } + + public boolean equals(Object o) + { + if (!(o instanceof CipherSuite)) + { + return false; + } + if (o == this) + return true; + byte[] id = ((CipherSuite) o).id(); + return (id[0] == this.id[0] && + id[1] == this.id[1]); + } + + public int hashCode() + { + return 0xFFFF0000 | (id[0] & 0xFF) << 8 | (id[1] & 0xFF); + } + + public String toString (String prefix) + { + return toString (); + } + + public String toString() + { + if (name == null) + { + return "{ " + (id[0] & 0xFF) + ", " + (id[1] & 0xFF) + " }"; + } + return name; + } +} diff --git a/libjava/classpath/gnu/javax/net/ssl/provider/CipherSuiteList.java b/libjava/classpath/gnu/javax/net/ssl/provider/CipherSuiteList.java new file mode 100644 index 000000000..a12304698 --- /dev/null +++ b/libjava/classpath/gnu/javax/net/ssl/provider/CipherSuiteList.java @@ -0,0 +1,283 @@ +/* CipherSuiteList.java -- A list of cipher suites. + 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.PrintWriter; +import java.io.StringWriter; + +import java.nio.ByteBuffer; +import java.util.ConcurrentModificationException; +import java.util.ListIterator; +import java.util.NoSuchElementException; + +public final class CipherSuiteList implements Iterable +{ + private final ByteBuffer buffer; + private final ProtocolVersion version; + private int modCount; + + public CipherSuiteList (final ByteBuffer buffer) + { + this (buffer, ProtocolVersion.SSL_3); + } + + public CipherSuiteList (final ByteBuffer buffer, final ProtocolVersion version) + { + this.version = version; + this.buffer = buffer; + modCount = 0; + } + + /** + * Return the number of elements in this list. + * + * @return The size of this list. + */ + public int size () + { + return (buffer.getShort (0) & 0xFFFF) >>> 1; + } + + /** + * Get the cipher suite at the specified index. + * + * @param index The index of the suite to get. + * @return The cipher suite at that index. + * @throws IndexOutOfBoundsException If the index is negative or is + * not less than {@link size()}. + */ + public CipherSuite get (final int index) + { + int size = size (); + if (index < 0 || index >= size) + throw new IndexOutOfBoundsException ("limit: " + size + + "; requested: " + index); + return CipherSuite.forValue(buffer.getShort(2 + (index << 1))).resolve(); + } + + /** + * Set the CipherSuite at the specified index. The list must have + * sufficient size to hold the element (that is, index <= + * size ()). + * + * @param index The index to put the suite. + * @param suite The CipherSuite object. + * @throws IndexOutOfBoundsException If index is not + * less than @{link #size()}, or if it is negative. + * @throws NullPointerException If suite is + * null. + * @throws java.nio.ReadOnlyBufferException If the underlying buffer + * is not writable. + */ + public void put (final int index, final CipherSuite suite) + { + int size = size (); + if (index < 0 || index >= size) + throw new IndexOutOfBoundsException ("limit: " + size + + "; requested: " + index); + buffer.position (2 + (index << 1)); + buffer.put (suite.id ()); + modCount++; + } + + /** + * Sets the size of this list. You must call this if you are adding + * elements to the list; calling {@link + * #put(int,gnu.jessie.provider.CipherSuite)} does not expand the + * list size (the same goes for removing elements, as there is no + * remove method). + * + * @param newSize The new size of this list. + * @throws IllegalArgumentException If the new size is negative or + * greater than 32767, or if there is insufficient space for that + * many elements in the underlying buffer. + * @throws java.nio.ReadOnlyBufferException If the underlying buffer + * is not writable. + */ + public void setSize (final int newSize) + { + if (newSize < 0 || newSize > 32767) + throw new IllegalArgumentException ("size must be between 0 and 32767"); + if ((newSize << 1) + 2 > buffer.capacity ()) + throw new IllegalArgumentException ("limit: " + buffer.capacity () + + "; requested: " + newSize); + buffer.putShort (0, (short) (newSize << 1)); + modCount++; + } + + public String toString () + { + return toString (null); + } + + public String toString (final String prefix) + { + StringWriter str = new StringWriter (); + PrintWriter out = new PrintWriter (str); + if (prefix != null) + out.print (prefix); + out.print ("["); + out.print (size ()); + out.println ("] {"); + for (Iterator it = new Iterator (); it.hasNext (); ) + { + CipherSuite suite = (CipherSuite) it.next (); + if (prefix != null) + out.print (prefix); + out.print (" "); + out.print (suite); + if (it.hasNext ()) + out.print (","); + out.println (); + } + if (prefix != null) + out.print (prefix); + out.print ("};"); + return str.toString (); + } + + public boolean equals (Object o) + { + if (!(o instanceof CipherSuiteList)) + return false; + CipherSuiteList that = (CipherSuiteList) o; + + if (size () != that.size ()) + return false; + + for (Iterator it1 = new Iterator (), it2 = that.new Iterator (); + it1.hasNext () && it2.hasNext (); ) + { + if (!it1.next ().equals (it2.next ())) + return false; + } + return true; + } + + public java.util.Iterator iterator () + { + return new Iterator (); + } + + /** + * An iterator for the elements in this list. The iterator supports + * only the set method out of the optional methods, + * because elements in a CipherSuiteList may not be removed or + * added; only the size of the list can be changed, and elements at + * a specific index changed. + */ + public class Iterator implements ListIterator + { + private final int modCount; + private int index; + + Iterator () + { + this.modCount = CipherSuiteList.this.modCount; + index = 0; + } + + public void add (CipherSuite cs) + { + throw new UnsupportedOperationException (); + } + + public boolean hasNext () + { + return (index < size ()); + } + + public boolean hasPrevious () + { + return (index > 0); + } + + public CipherSuite next () throws NoSuchElementException + { + if (modCount != CipherSuiteList.this.modCount) + throw new ConcurrentModificationException (); + try + { + return get (index++); + } + catch (IndexOutOfBoundsException ioobe) + { + throw new NoSuchElementException (); + } + } + + public int nextIndex () + { + if (hasNext ()) + return (index + 1); + return -1; + } + + public CipherSuite previous () throws NoSuchElementException + { + if (index == 0) + throw new NoSuchElementException (); + if (modCount != CipherSuiteList.this.modCount) + throw new ConcurrentModificationException (); + try + { + return get (--index); + } + catch (IndexOutOfBoundsException ioobe) // on empty list + { + throw new NoSuchElementException (); + } + } + + public int previousIndex () + { + return (index - 1); + } + + public void remove () + { + throw new UnsupportedOperationException (); + } + + public void set (final CipherSuite cs) + { + put (index, cs); + } + } +} diff --git a/libjava/classpath/gnu/javax/net/ssl/provider/ClientCertificateTypeList.java b/libjava/classpath/gnu/javax/net/ssl/provider/ClientCertificateTypeList.java new file mode 100644 index 000000000..4dd64f09f --- /dev/null +++ b/libjava/classpath/gnu/javax/net/ssl/provider/ClientCertificateTypeList.java @@ -0,0 +1,227 @@ +/* ClientCertificateTypeList.java -- A list of certificate types. + 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.javax.net.ssl.provider.CertificateRequest.ClientCertificateType; + +import java.io.PrintWriter; +import java.io.StringWriter; + +import java.nio.ByteBuffer; + +import java.util.ConcurrentModificationException; +import java.util.ListIterator; +import java.util.NoSuchElementException; + +public class ClientCertificateTypeList implements Iterable +{ + private final ByteBuffer buffer; + private int modCount; + + public ClientCertificateTypeList (final ByteBuffer buffer) + { + this.buffer = buffer; + modCount = 0; + } + + public int size () + { + return (buffer.get (0) & 0xFF); + } + + public CertificateRequest.ClientCertificateType get (final int index) + { + int size = size (); + if (index < 0 || index >= size) + throw new IndexOutOfBoundsException ("limit: " + size + + "; requested: " + index); + return CertificateRequest.ClientCertificateType.forValue + (buffer.get (index + 1) & 0xFF); + } + + public java.util.Iterator iterator() + { + return new Iterator(); + } + + public void put (final int index, final CertificateRequest.ClientCertificateType type) + { + int size = size (); + if (index < 0 || index >= size) + throw new IndexOutOfBoundsException ("limit: " + size + + "; requested: " + index); + buffer.put (index + 1, (byte) type.getValue ()); + modCount++; + } + + public void setSize (final int newSize) + { + if (newSize < 0 || newSize > 255) + throw new IllegalArgumentException ("size must be between 0 and 255"); + if (newSize + 1 > buffer.capacity ()) + throw new IllegalArgumentException ("limit: " + (buffer.capacity () - 1) + + "; requested: " + newSize); + buffer.put (0, (byte) newSize); + modCount++; + } + + public String toString () + { + return toString (null); + } + + public String toString (final String prefix) + { + StringWriter str = new StringWriter (); + PrintWriter out = new PrintWriter (str); + if (prefix != null) out.print (prefix); + out.print ("["); + out.print (size ()); + out.println ("] {"); + for (Iterator it = new Iterator (); it.hasNext (); ) + { + if (prefix != null) out.print (prefix); + out.print (" "); + out.print (it.next ()); + if (it.hasNext ()) + out.print (","); + out.println (); + } + if (prefix != null) out.print (prefix); + out.println ("};"); + return str.toString (); + } + + public boolean equals (Object o) + { + if (!(o instanceof ClientCertificateTypeList)) + return false; + ClientCertificateTypeList that = (ClientCertificateTypeList) o; + + if (size () != that.size ()) + return false; + + for (Iterator it1 = new Iterator (), it2 = that.new Iterator (); + it1.hasNext () && it2.hasNext (); ) + { + if (!it1.next ().equals (it2.next ())) + return false; + } + return true; + } + + public class Iterator implements ListIterator + { + private int index; + private final int modCount; + + Iterator () + { + index = 0; + modCount = ClientCertificateTypeList.this.modCount; + } + + public void add (CertificateRequest.ClientCertificateType type) + { + throw new UnsupportedOperationException (); + } + + public boolean hasNext () + { + return (index < size ()); + } + + public boolean hasPrevious () + { + return (index > 0); + } + + public CertificateRequest.ClientCertificateType next () throws NoSuchElementException + { + if (modCount != ClientCertificateTypeList.this.modCount) + throw new ConcurrentModificationException (); + try + { + return get (index++); + } + catch (IndexOutOfBoundsException ioobe) + { + throw new NoSuchElementException (); + } + } + + public int nextIndex () + { + if (hasNext ()) + return (index + 1); + return -1; + } + + public CertificateRequest.ClientCertificateType previous () throws NoSuchElementException + { + if (index == 0) + throw new NoSuchElementException (); + if (modCount != ClientCertificateTypeList.this.modCount) + throw new ConcurrentModificationException (); + try + { + return get (--index); + } + catch (IndexOutOfBoundsException ioobe) + { + throw new NoSuchElementException (); + } + } + + public int previousIndex () + { + return (index - 1); + } + + public void remove () + { + throw new UnsupportedOperationException (); + } + + public void set (final CertificateRequest.ClientCertificateType type) + { + put (index, type); + } + } +} diff --git a/libjava/classpath/gnu/javax/net/ssl/provider/ClientDHE_PSKParameters.java b/libjava/classpath/gnu/javax/net/ssl/provider/ClientDHE_PSKParameters.java new file mode 100644 index 000000000..e2362e029 --- /dev/null +++ b/libjava/classpath/gnu/javax/net/ssl/provider/ClientDHE_PSKParameters.java @@ -0,0 +1,122 @@ +/* ClientDHE_PSKParameters.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; +import java.nio.charset.Charset; + +/** + *
+      struct {
+          select (KeyExchangeAlgorithm) {
+              /* other cases for rsa, diffie_hellman, etc. */
+              case diffie_hellman_psk:   /* NEW */
+                  opaque psk_identity<0..2^16-1>;
+                  ClientDiffieHellmanPublic public;
+          } exchange_keys;
+      } ClientKeyExchange;
+ * + * @author Casey Marshall (csm@gnu.org) + */ +public class ClientDHE_PSKParameters extends ExchangeKeys implements Builder, Constructed +{ + public ClientDHE_PSKParameters(ByteBuffer buffer) + { + super(buffer); + } + + public ClientDHE_PSKParameters(String identity, ClientDiffieHellmanPublic dh) + { + super(null); + Charset utf8 = Charset.forName("UTF-8"); + ByteBuffer idBuf = utf8.encode(identity); + buffer = ByteBuffer.allocate(2 + idBuf.remaining() + dh.length()); + buffer.putShort((short) idBuf.remaining()); + buffer.put(idBuf); + buffer.put(dh.buffer()); + buffer.rewind(); + } + + /* (non-Javadoc) + * @see gnu.javax.net.ssl.provider.Builder#buffer() + */ + public ByteBuffer buffer() + { + return (ByteBuffer) buffer.duplicate().rewind().limit(length()); + } + + private int identityLength() + { + return (buffer.getShort(0) & 0xFFFF) + 2; + } + + public String identity() + { + Charset utf8 = Charset.forName("UTF-8"); + return utf8.decode((ByteBuffer) buffer.duplicate().position(2).limit + (identityLength())).toString(); + } + + /* (non-Javadoc) + * @see gnu.javax.net.ssl.provider.Constructed#length() + */ + public int length() + { + int length = (buffer.getShort(0) & 0xFFFF) + 2; + // XXX always explicit? + length += (buffer.getShort(length) & 0xFFFF) + 2; + return length; + } + + public ClientDiffieHellmanPublic params() + { + return new ClientDiffieHellmanPublic(((ByteBuffer) buffer.duplicate() + .position(identityLength()).limit(length())).slice()); + } + + /* (non-Javadoc) + * @see gnu.javax.net.ssl.provider.Constructed#toString(java.lang.String) + */ + public String toString(String prefix) + { + // TODO Auto-generated method stub + return null; + } + +} diff --git a/libjava/classpath/gnu/javax/net/ssl/provider/ClientDiffieHellmanPublic.java b/libjava/classpath/gnu/javax/net/ssl/provider/ClientDiffieHellmanPublic.java new file mode 100644 index 000000000..393313a2f --- /dev/null +++ b/libjava/classpath/gnu/javax/net/ssl/provider/ClientDiffieHellmanPublic.java @@ -0,0 +1,129 @@ +/* ClientDiffieHellmanPublic.java -- Client Diffie-Hellman value. + 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.PrintWriter; +import java.io.StringWriter; + +import java.math.BigInteger; + +import java.nio.ByteBuffer; + +/** + * The client's explicit Diffie Hellman value. + * + *
+struct {
+  select (PublicValueEncoding) {
+    case implicit: struct { };
+    case explicit: opaque dh_Yc<1..2^16-1>;
+  } dh_public;
+} ClientDiffieHellmanPublic;
+ */ +public class ClientDiffieHellmanPublic extends ExchangeKeys implements Builder +{ + public ClientDiffieHellmanPublic(final ByteBuffer 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(); + } + + public ByteBuffer buffer() + { + return (ByteBuffer) buffer.duplicate().rewind().limit(length()); + } + + public BigInteger publicValue() + { + int len = length() - 2; + byte[] b = new byte[len]; + buffer.position(2); + buffer.get(b); + buffer.rewind(); + return new BigInteger(1, b); + } + + public void setPublicValue(final BigInteger Yc) + { + 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) + 2; + } + + public String toString () + { + return toString (null); + } + + public String toString (final String prefix) + { + StringWriter str = new StringWriter (); + PrintWriter out = new PrintWriter (str); + if (prefix != null) out.print (prefix); + out.println ("struct {"); + if (prefix != null) out.print (prefix); + out.print (" dh_Yc = "); + out.print (publicValue ().toString (16)); + out.println (';'); + if (prefix != null) out.print (prefix); + out.print ("} ClientDiffieHellmanPublic;"); + return str.toString (); + } +} diff --git a/libjava/classpath/gnu/javax/net/ssl/provider/ClientHandshake.java b/libjava/classpath/gnu/javax/net/ssl/provider/ClientHandshake.java new file mode 100644 index 000000000..c938e284a --- /dev/null +++ b/libjava/classpath/gnu/javax/net/ssl/provider/ClientHandshake.java @@ -0,0 +1,1153 @@ +/* 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 static gnu.javax.net.ssl.provider.KeyExchangeAlgorithm.*; + +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 gnu.javax.net.ssl.provider.ServerNameList.NameType; +import gnu.javax.net.ssl.provider.ServerNameList.ServerName; + +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.Collections; +import java.util.LinkedList; +import java.util.List; +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.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; + private MaxFragmentLength maxFragmentLengthSent; + private boolean truncatedHMacSent; + private ProtocolVersion sentVersion; + + // 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()); + } + ExtensionList extensions = hello.extensions(); + if (extensions != null) + { + for (Extension extension : extensions) + { + Extension.Type type = extension.type(); + if (type == null) + continue; + switch (type) + { + case MAX_FRAGMENT_LENGTH: + MaxFragmentLength mfl + = (MaxFragmentLength) extension.value(); + if (maxFragmentLengthSent == mfl) + engine.session().setApplicationBufferSize(mfl.maxLength()); + break; + + case TRUNCATED_HMAC: + if (truncatedHMacSent) + engine.session().setTruncatedMac(true); + break; + } + } + } + + KeyExchangeAlgorithm kex = engine.session().suite.keyExchangeAlgorithm(); + if (continuedSession) + { + byte[][] keys = generateKeys(clientRandom, serverRandom, + engine.session()); + setupSecurityParameters(keys, true, engine, compression); + state = READ_FINISHED; + } + else if (kex == RSA || kex == DH_DSS || kex == DH_RSA + || kex == DHE_DSS || kex == DHE_RSA || kex == RSA_PSK) + state = READ_CERTIFICATE; + else if (kex == DH_anon || kex == PSK || kex == DHE_PSK) + state = READ_SERVER_KEY_EXCHANGE; + else + state = READ_CERTIFICATE_REQUEST; + } + 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. + KeyExchangeAlgorithm kea = engine.session().suite.keyExchangeAlgorithm(); + if (kea == RSA || kea == RSA_PSK) + { + keyExchange = new RSAGen(kea == RSA); + tasks.add(keyExchange); + if (kea == RSA) + state = READ_CERTIFICATE_REQUEST; + else + state = READ_SERVER_KEY_EXCHANGE; + } + else + state = READ_SERVER_KEY_EXCHANGE; + } + break; + + // Server Key Exchange. + case READ_SERVER_KEY_EXCHANGE: + { + CipherSuite s = engine.session().suite; + KeyExchangeAlgorithm kexalg = s.keyExchangeAlgorithm(); + // XXX also SRP. + if (kexalg != DHE_DSS && kexalg != DHE_RSA && kexalg != DH_anon + && kexalg != DHE_PSK && kexalg != PSK && kexalg != RSA_PSK) + throw new AlertException(new Alert(Level.FATAL, + Description.UNEXPECTED_MESSAGE)); + + if (handshake.type() != Handshake.Type.SERVER_KEY_EXCHANGE) + { + if (kexalg != RSA_PSK && kexalg != PSK) + throw new AlertException(new Alert(Level.FATAL, + Description.UNEXPECTED_MESSAGE)); + state = READ_CERTIFICATE_REQUEST; + return HandshakeStatus.NEED_UNWRAP; + } + + ServerKeyExchange skex = (ServerKeyExchange) handshake.body(); + ByteBuffer paramsBuffer = null; + if (kexalg == DHE_DSS || kexalg == DHE_RSA || kexalg == DH_anon) + { + 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 (kexalg == DHE_DSS || kexalg == DHE_RSA || kexalg == DH_anon) + { + 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, true); + tasks.add(keyExchange); + } + if (kexalg == DHE_PSK) + { + ServerDHE_PSKParameters pskParams = (ServerDHE_PSKParameters) + skex.params(); + ServerDHParams dhParams = pskParams.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, false); + 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 typeList = new LinkedList(); + for (ClientCertificateType t : types) + typeList.add(t.name()); + + X500PrincipalList issuers = req.authorities(); + LinkedList issuerList = new LinkedList(); + 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()); + sentVersion = chooseVersion(); + hello.setVersion(sentVersion); + 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(); + if (enableExtensions()) + { + List extensions = new LinkedList(); + MaxFragmentLength fraglen = maxFragmentLength(); + if (fraglen != null) + { + extensions.add(new Extension(Extension.Type.MAX_FRAGMENT_LENGTH, + fraglen)); + maxFragmentLengthSent = fraglen; + } + + String host = engine.getPeerHost(); + if (host != null) + { + ServerName name + = new ServerName(NameType.HOST_NAME, host); + ServerNameList names + = new ServerNameList(Collections.singletonList(name)); + extensions.add(new Extension(Extension.Type.SERVER_NAME, + names)); + } + + if (truncatedHMac()) + { + extensions.add(new Extension(Extension.Type.TRUNCATED_HMAC, + new TruncatedHMAC())); + truncatedHMacSent = true; + } + + ExtensionList elist = new ExtensionList(extensions); + hello.setExtensions(elist.buffer()); + } + else + hello.setDisableExtensions(true); + + if (Debug.DEBUG) + logger.logv(Component.SSL_HANDSHAKE, "{0}", hello); + + 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 == DHE_DSS || kea == DHE_RSA || kea == DH_anon + || kea == DH_DSS || kea == DH_RSA) + { + assert(dhPair != null); + DHPublicKey pubkey = (DHPublicKey) dhPair.getPublic(); + ClientDiffieHellmanPublic pub + = new ClientDiffieHellmanPublic(pubkey.getY()); + ckex.setExchangeKeys(pub.buffer()); + } + if (kea == RSA || kea == RSA_PSK) + { + 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); + if (kea == RSA) + ckex.setExchangeKeys(epms.buffer()); + else + { + String identity = getPSKIdentity(); + if (identity == null) + throw new SSLException("no pre-shared-key identity;" + + " set the security property" + + " \"jessie.client.psk.identity\""); + ClientRSA_PSKParameters params = + new ClientRSA_PSKParameters(identity, epms.buffer()); + ckex.setExchangeKeys(params.buffer()); + generatePSKSecret(identity, preMasterSecret, true); + } + } + if (kea == DHE_PSK) + { + assert(keyExchange instanceof ClientDHGen); + assert(dhPair != null); + String identity = getPSKIdentity(); + if (identity == null) + throw new SSLException("no pre-shared key identity; set" + + " the security property" + + " \"jessie.client.psk.identity\""); + DHPublicKey pubkey = (DHPublicKey) dhPair.getPublic(); + ClientDHE_PSKParameters params = + new ClientDHE_PSKParameters(identity, + new ClientDiffieHellmanPublic(pubkey.getY())); + ckex.setExchangeKeys(params.buffer()); + generatePSKSecret(identity, preMasterSecret, true); + } + if (kea == PSK) + { + String identity = getPSKIdentity(); + if (identity == null) + throw new SSLException("no pre-shared key identity; set" + + " the security property" + + " \"jessie.client.psk.identity\""); + generatePSKSecret(identity, null, true); + ClientPSKParameters params = new ClientPSKParameters(identity); + ckex.setExchangeKeys(params.buffer()); + } + if (kea == NONE) + { + Inflater inflater = null; + Deflater deflater = null; + if (compression == CompressionMethod.ZLIB) + { + inflater = new Inflater(); + deflater = new Deflater(); + } + inParams = new InputSecurityParameters(null, null, inflater, + engine.session(), + engine.session().suite); + outParams = new OutputSecurityParameters(null, null, deflater, + engine.session(), + engine.session().suite); + engine.session().privateData.masterSecret = new byte[0]; + } + + 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() & 0xFFFFFF)); + 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(md5, sha); + 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: + { + assert(genCertVerify != null); + assert(genCertVerify.hasRun()); + CertificateVerify verify = new CertificateVerify(genCertVerify.signed(), + engine.session().suite.signatureAlgorithm()); + + outBuffer = verify.buffer(); + fragment.putInt((Handshake.Type.CERTIFICATE_VERIFY.getValue() << 24) + | (verify.length() & 0xFFFFFF)); + int l = Math.min(fragment.remaining(), outBuffer.remaining()); + fragment.put((ByteBuffer) outBuffer.duplicate().limit(outBuffer.position() + l)); + outBuffer.position(outBuffer.position() + l); + + // XXX This is a potential problem: we may not have drained + // outBuffer, but set the changeCipherSpec toggle. + 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 getSuites() throws SSLException + { + List suites = new LinkedList(); + 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 getCompressionMethods() + { + List methods = new LinkedList(); + GetSecurityPropertyAction gspa = new GetSecurityPropertyAction("jessie.enable.compression"); + if (Boolean.valueOf(AccessController.doPrivileged(gspa))) + methods.add(CompressionMethod.ZLIB); + methods.add(CompressionMethod.NULL); + return methods; + } + + private boolean enableExtensions() + { + GetSecurityPropertyAction action + = new GetSecurityPropertyAction("jessie.client.enable.extensions"); + return Boolean.valueOf(AccessController.doPrivileged(action)); + } + + private MaxFragmentLength maxFragmentLength() + { + GetSecurityPropertyAction action + = new GetSecurityPropertyAction("jessie.client.maxFragmentLength"); + String s = AccessController.doPrivileged(action); + if (s != null) + { + try + { + int len = Integer.parseInt(s); + switch (len) + { + case 9: + case (1 << 9): return MaxFragmentLength.LEN_2_9; + case 10: + case (1 << 10): return MaxFragmentLength.LEN_2_10; + case 11: + case (1 << 11): return MaxFragmentLength.LEN_2_11; + case 12: + case (1 << 12): return MaxFragmentLength.LEN_2_12; + } + } + catch (NumberFormatException nfe) + { + } + } + return null; + } + + private boolean truncatedHMac() + { + GetSecurityPropertyAction action + = new GetSecurityPropertyAction("jessie.client.truncatedHMac"); + return Boolean.valueOf(AccessController.doPrivileged(action)); + } + + private String getPSKIdentity() + { + GetSecurityPropertyAction action + = new GetSecurityPropertyAction("jessie.client.psk.identity"); + return AccessController.doPrivileged(action); + } + + // 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; + private final boolean full; + + ClientDHGen(DHPublicKey serverKey, DHParameterSpec params, boolean full) + { + this.serverKey = serverKey; + this.params = params; + this.full = full; + } + + 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()); + + initDiffieHellman((DHPrivateKey) dhPair.getPrivate(), engine.session().random()); + + // We have enough info to do the full key exchange; so let's do it. + DHPhase phase = new DHPhase(serverKey, full); + phase.run(); + if (phase.thrown() != null) + throw new SSLException(phase.thrown()); + } + + DHPublicKey serverKey() + { + return serverKey; + } + } + + class CertLoader extends DelegatedTask + { + private final List keyTypes; + private final List issuers; + + CertLoader(List keyTypes, List 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; + private final boolean full; + + RSAGen() + { + this(true); + } + + RSAGen(boolean full) + { + this.full = full; + } + + 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) sentVersion.major(); + preMasterSecret[1] = (byte) sentVersion.minor(); + Cipher rsa = Cipher.getInstance("RSA"); + java.security.cert.Certificate cert + = engine.session().getPeerCertificates()[0]; + if (cert instanceof X509Certificate) + { + boolean[] keyUsage = ((X509Certificate) cert).getKeyUsage(); + if (keyUsage != null && !keyUsage[2]) + throw new InvalidKeyException("certificate's keyUsage does not permit keyEncipherment"); + } + rsa.init(Cipher.ENCRYPT_MODE, cert.getPublicKey()); + encryptedPreMasterSecret = rsa.doFinal(preMasterSecret); + + // Generate our session keys, because we can. + if (full) + { + 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 + { + private final MessageDigest md5, sha; + private byte[] signed; + + GenCertVerify(MessageDigest md5, MessageDigest sha) + { + try + { + this.md5 = (MessageDigest) md5.clone(); + this.sha = (MessageDigest) sha.clone(); + } + catch (CloneNotSupportedException cnse) + { + // Our message digests *should* be cloneable. + throw new Error(cnse); + } + } + + public void implRun() + throws InvalidKeyException, NoSuchAlgorithmException, SignatureException + { + byte[] toSign; + if (engine.session().version == ProtocolVersion.SSL_3) + { + toSign = genV3CertificateVerify(md5, sha, engine.session()); + } + else + { + if (engine.session().suite.signatureAlgorithm() == SignatureAlgorithm.RSA) + toSign = Util.concat(md5.digest(), sha.digest()); + else + toSign = sha.digest(); + } + + java.security.Signature sig = + java.security.Signature.getInstance(engine.session().suite.signatureAlgorithm().name()); + sig.initSign(privateKey); + sig.update(toSign); + signed = sig.sign(); + } + + byte[] signed() + { + return signed; + } + } +} diff --git a/libjava/classpath/gnu/javax/net/ssl/provider/ClientHello.java b/libjava/classpath/gnu/javax/net/ssl/provider/ClientHello.java new file mode 100644 index 000000000..a58dc5d7a --- /dev/null +++ b/libjava/classpath/gnu/javax/net/ssl/provider/ClientHello.java @@ -0,0 +1,240 @@ +/* ClientHello.java -- SSL ClientHello message. + 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.PrintWriter; +import java.io.StringWriter; + +import java.nio.ByteBuffer; +import java.nio.ByteOrder; + +/** + * A ClientHello handshake message. + * + *
+struct
+{
+  ProtocolVersion   client_version;                // 2
+  Random            random;                        // 32
+  SessionID         session_id;                    // 1 + 0..32
+  CipherSuite       cipher_suites<2..2^16-1>
+  CompressionMethod compression_methods<1..2^8-1>
+  Extension         client_hello_extension_list<0..2^16-1>
+} ClientHello;
+
+ */ +public class ClientHello implements Handshake.Body +{ + + // Fields. + // ------------------------------------------------------------------------- + + // To help track offsets into the message: + // The location of the 'random' field. + protected static final int RANDOM_OFFSET = 2; + // The location of the sesion_id length. + protected static final int SESSID_OFFSET = 32 + RANDOM_OFFSET; + // The location of the session_id bytes (if any). + protected static final int SESSID_OFFSET2 = SESSID_OFFSET + 1; + + protected ByteBuffer buffer; + protected boolean disableExtensions; + + // Constructor. + // ------------------------------------------------------------------------- + + public ClientHello (final ByteBuffer buffer) + { + this.buffer = buffer.duplicate().order(ByteOrder.BIG_ENDIAN); + disableExtensions = false; + } + + // Instance methods. + // ------------------------------------------------------------------------- + + public int length() + { + int len = SESSID_OFFSET2 + buffer.get(SESSID_OFFSET); + len += (buffer.getShort(len) & 0xFFFF) + 2; + len += (buffer.get(len) & 0xFF) + 1; + if (!disableExtensions && len + 1 < buffer.capacity()) + len += (buffer.getShort(len) & 0xFFFF) + 2; + return len; + } + + /** + * Gets the protocol version field. + * + * @return The protocol version field. + */ + public ProtocolVersion version() + { + return ProtocolVersion.getInstance (buffer.getShort (0)); + } + + /** + * Gets the SSL nonce. + * + * @return The nonce. + */ + public Random random() + { + ByteBuffer randomBuf = + ((ByteBuffer) buffer.duplicate ().position (RANDOM_OFFSET) + .limit (SESSID_OFFSET)).slice (); + return new Random (randomBuf); + } + + public byte[] sessionId() + { + int idlen = buffer.get (SESSID_OFFSET) & 0xFF; + byte[] sessionId = new byte[idlen]; + buffer.position (SESSID_OFFSET2); + buffer.get (sessionId); + return sessionId; + } + + public CipherSuiteList cipherSuites() + { + int offset = getCipherSuitesOffset (); + + // We give the CipherSuiteList all the remaining bytes to play with, + // since this might be an in-construction packet that will fill in + // the length field itself. + ByteBuffer listBuf = ((ByteBuffer) buffer.duplicate ().position (offset) + .limit (buffer.capacity ())).slice (); + return new CipherSuiteList (listBuf, version ()); + } + + public CompressionMethodList compressionMethods() + { + int offset = getCompressionMethodsOffset (); + ByteBuffer listBuf = ((ByteBuffer) buffer.duplicate ().position (offset) + .limit (buffer.capacity ())).slice (); + return new CompressionMethodList (listBuf); + } + + public boolean hasExtensions() + { + int offset = getExtensionsOffset(); + return (offset + 1 < buffer.limit()); + } + + public ExtensionList extensions() + { + int offset = getExtensionsOffset (); + if (offset + 1 >= buffer.limit()) + return null; + int len = buffer.getShort(offset) & 0xFFFF; + if (len == 0) + len = buffer.limit() - offset - 2; + ByteBuffer ebuf = ((ByteBuffer) buffer.duplicate().position(offset) + .limit(offset + len + 2)).slice (); + return new ExtensionList(ebuf); + } + + public int extensionsLength() + { + if (hasExtensions()) + return 0; + return buffer.getShort(getExtensionsOffset()) & 0xFFFF; + } + + protected int getCipherSuitesOffset () + { + return (SESSID_OFFSET2 + (buffer.get (SESSID_OFFSET) & 0xFF)); + } + + protected int getCompressionMethodsOffset () + { + int csOffset = getCipherSuitesOffset (); + int csLen = buffer.getShort (csOffset) & 0xFFFF; + return csOffset + csLen + 2; + } + + protected int getExtensionsOffset () + { + int cmOffset = getCompressionMethodsOffset (); + return (buffer.get (cmOffset) & 0xFF) + cmOffset + 1; + } + + public String toString () + { + return toString (null); + } + + public String toString (final String prefix) + { + StringWriter str = new StringWriter (); + PrintWriter out = new PrintWriter (str); + String subprefix = " "; + if (prefix != null) + subprefix += prefix; + if (prefix != null) + out.print (prefix); + out.println ("struct {"); + if (prefix != null) + out.print (prefix); + out.print (" version: "); + out.print (version ()); + out.println (";"); + out.print (subprefix); + out.println ("random:"); + out.print (random ().toString (subprefix)); + if (prefix != null) + out.print (prefix); + out.print (" sessionId: "); + out.print (Util.toHexString (sessionId (), ':')); + out.println (";"); + out.print (subprefix); + out.println ("cipher_suites:"); + out.println (cipherSuites ().toString (subprefix)); + out.print (subprefix); + out.println ("compression_methods:"); + out.println (compressionMethods ().toString (subprefix)); + out.print (subprefix); + out.print ("extensions: "); + ExtensionList el = extensions(); + out.println (el != null ? el.toString(subprefix+" ") : "(nil)"); + if (prefix != null) + out.print (prefix); + out.print ("} ClientHello;"); + return str.toString(); + } +} diff --git a/libjava/classpath/gnu/javax/net/ssl/provider/ClientHelloBuilder.java b/libjava/classpath/gnu/javax/net/ssl/provider/ClientHelloBuilder.java new file mode 100644 index 000000000..90405c45b --- /dev/null +++ b/libjava/classpath/gnu/javax/net/ssl/provider/ClientHelloBuilder.java @@ -0,0 +1,137 @@ +/* ClientHelloBuilder.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; +import java.util.List; + +/** + * Builder for {@link ClientHello} objects. + * + * @author Casey Marshall (csm@gnu.org) + */ +public class ClientHelloBuilder extends ClientHello implements Builder +{ + public ClientHelloBuilder() + { + super(ByteBuffer.allocate(256)); + } + + /* (non-Javadoc) + * @see gnu.javax.net.ssl.provider.Builder#buffer() + */ + public ByteBuffer buffer() + { + return (ByteBuffer) buffer.duplicate().position(0).limit(length()); + } + + public void setVersion(final ProtocolVersion version) + { + ensureCapacity(2); + buffer.putShort(0, (short) version.rawValue ()); + } + + public void setSessionId (final byte[] buffer) + { + setSessionId(buffer, 0, buffer.length); + } + + public void setSessionId (final byte[] buffer, final int offset, final int length) + { + ensureCapacity(SESSID_OFFSET2 + length); + int len = Math.min (32, length); + this.buffer.put (SESSID_OFFSET, (byte) len); + this.buffer.position (SESSID_OFFSET2); + this.buffer.put (buffer, offset, len); + } + + public void setCipherSuites(List suites) + { + int off = getCipherSuitesOffset(); + ensureCapacity(off + (2 * suites.size()) + 2); + buffer.putShort(off, (short) (suites.size() * 2)); + int i = 2; + for (CipherSuite suite : suites) + { + ((ByteBuffer) buffer.duplicate().position(off+i)).put(suite.id()); + i += 2; + } + } + + public void setCompressionMethods(List methods) + { + int off = getCompressionMethodsOffset(); + ensureCapacity(off + methods.size() + 1); + buffer.put(off, (byte) methods.size()); + for (CompressionMethod method : methods) + buffer.put(++off, (byte) method.getValue()); + } + + public void setExtensionsLength (final int length) + { + if (length < 0 || length > 16384) + throw new IllegalArgumentException("length must be nonnegative and not exceed 16384"); + int needed = getExtensionsOffset() + 2 + length; + if (buffer.capacity() < needed) + ensureCapacity(needed); + buffer.putShort(getExtensionsOffset(), (short) length); + } + + public void setExtensions(ByteBuffer extensions) + { + int elen = extensions.getShort(0) & 0xFFFF; + setExtensionsLength(elen); + ((ByteBuffer) buffer.duplicate().position(getExtensionsOffset())).put(extensions); + } + + public void setDisableExtensions(boolean disableExtensions) + { + this.disableExtensions = disableExtensions; + } + + public void ensureCapacity(final int length) + { + if (buffer.capacity() >= length) + return; + ByteBuffer newBuf = ByteBuffer.allocate(length); + newBuf.put((ByteBuffer) buffer.position(0)); + newBuf.position(0); + this.buffer = newBuf; + } +} diff --git a/libjava/classpath/gnu/javax/net/ssl/provider/ClientHelloV2.java b/libjava/classpath/gnu/javax/net/ssl/provider/ClientHelloV2.java new file mode 100644 index 000000000..6009d52a3 --- /dev/null +++ b/libjava/classpath/gnu/javax/net/ssl/provider/ClientHelloV2.java @@ -0,0 +1,158 @@ +/* ClientHelloV2.java -- a hello message from SSLv2. + 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.PrintWriter; +import java.io.StringWriter; +import java.nio.ByteBuffer; +import java.nio.ByteOrder; +import java.util.ArrayList; +import java.util.List; + +/** + * A client hello message from SSLv2. In SSLv3 and later, clients can + * send an SSLv2 client hello message, but set the protocol version + * for a later version. + * + *

The format of a version 2 client hello is: + * + *

+    char MSG-CLIENT-HELLO          // equals 1
+    char CLIENT-VERSION-MSB
+    char CLIENT-VERSION-LSB
+    char CIPHER-SPECS-LENGTH-MSB
+    char CIPHER-SPECS-LENGTH-LSB
+    char SESSION-ID-LENGTH-MSB
+    char SESSION-ID-LENGTH-LSB
+    char CHALLENGE-LENGTH-MSB
+    char CHALLENGE-LENGTH-LSB
+    char CIPHER-SPECS-DATA[(MSB<<8)|LSB]
+    char SESSION-ID-DATA[(MSB<<8)|LSB]
+    char CHALLENGE-DATA[(MSB<<8)|LSB]
+ */ +class ClientHelloV2 implements Constructed +{ + private final ByteBuffer buffer; + + ClientHelloV2 (final ByteBuffer buffer) + { + this.buffer = buffer.duplicate().order(ByteOrder.BIG_ENDIAN); + } + + public int length () + { + return 9 + cipherSpecsLength () + sessionIdLength () + challengeLength (); + } + + ProtocolVersion version () + { + return ProtocolVersion.getInstance (buffer.getShort (1)); + } + + int cipherSpecsLength () + { + return buffer.getShort (3) & 0xFFFF; + } + + int sessionIdLength () + { + return buffer.getShort (5) & 0xFFFF; + } + + int challengeLength () + { + return buffer.getShort (7) & 0xFFFF; + } + + public List cipherSpecs () + { + int n = cipherSpecsLength (); + List l = new ArrayList(n / 3); + ByteBuffer b = (ByteBuffer) buffer.duplicate ().position (9); + for (int i = 0; i < n; i += 3) + { + if (b.get () == 0) + l.add (CipherSuite.forValue(b.getShort()).resolve()); + else + b.getShort (); + } + return l; + } + + byte[] sessionId () + { + byte[] id = new byte[sessionIdLength ()]; + ((ByteBuffer) buffer.duplicate ().position (9 + cipherSpecsLength ())).get (id); + return id; + } + + byte[] challenge () + { + byte[] challenge = new byte[challengeLength ()]; + ((ByteBuffer) buffer.duplicate ().position (9 + cipherSpecsLength () + sessionIdLength ())).get (challenge); + return challenge; + } + + public String toString () + { + return toString (null); + } + + public String toString (String prefix) + { + StringWriter str = new StringWriter (); + PrintWriter out = new PrintWriter (str); + + if (prefix != null) out.print (prefix); + out.println ("CLIENT-HELLO-MSG"); + if (prefix != null) out.print (prefix); + out.print (" version: "); + out.println (version ()); + if (prefix != null) out.print (prefix); + out.println (" suites: "); + out.println (cipherSpecs ()); + if (prefix != null) out.print (prefix); + out.print (" sessionId: "); + out.println (Util.toHexString (sessionId (), ':')); + if (prefix != null) out.print (prefix); + out.print (" challenge: "); + out.println (Util.toHexString (challenge (), ':')); + return str.toString (); + } +} diff --git a/libjava/classpath/gnu/javax/net/ssl/provider/ClientKeyExchange.java b/libjava/classpath/gnu/javax/net/ssl/provider/ClientKeyExchange.java new file mode 100644 index 000000000..2006e7385 --- /dev/null +++ b/libjava/classpath/gnu/javax/net/ssl/provider/ClientKeyExchange.java @@ -0,0 +1,132 @@ +/* ClientKeyExchange.java -- SSL ClientKeyExchange message. + 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.PrintWriter; +import java.io.StringWriter; + +import java.nio.ByteBuffer; +import java.nio.ByteOrder; + +/** + * The client key exchange message. + * + *
+struct {
+  select (KeyExchangeAlgorithm) {
+    case rsa: EncryptedPreMasterSecret;
+    case diffie_hellman: ClientDiffieHellmanPublic;
+  } exchange_keys;
+} ClientKeyExchange;
+ */ +public class ClientKeyExchange implements Handshake.Body +{ + + // Fields. + // ------------------------------------------------------------------------- + + protected ByteBuffer buffer; + protected final CipherSuite suite; + protected final ProtocolVersion version; + + // Constructors. + // ------------------------------------------------------------------------- + + public ClientKeyExchange (final ByteBuffer buffer, final CipherSuite suite, + final ProtocolVersion version) + { + suite.getClass(); + version.getClass (); + this.buffer = buffer.duplicate().order(ByteOrder.BIG_ENDIAN); + this.suite = suite; + this.version = version; + } + + // Instance methods. + // ------------------------------------------------------------------------- + + public ExchangeKeys exchangeKeys () + { + KeyExchangeAlgorithm alg = suite.keyExchangeAlgorithm(); + if (alg == KeyExchangeAlgorithm.RSA) + return new EncryptedPreMasterSecret(buffer, version); + else if (alg == KeyExchangeAlgorithm.DH_anon + || alg == KeyExchangeAlgorithm.DHE_DSS + || alg == KeyExchangeAlgorithm.DHE_RSA) + return new ClientDiffieHellmanPublic(buffer.duplicate()); + else if (alg == KeyExchangeAlgorithm.DHE_PSK) + return new ClientDHE_PSKParameters(buffer.duplicate()); + else if (alg == KeyExchangeAlgorithm.PSK) + return new ClientPSKParameters(buffer.duplicate()); + else if (alg == KeyExchangeAlgorithm.RSA_PSK) + return new ClientRSA_PSKParameters(buffer.duplicate()); + else if (alg == KeyExchangeAlgorithm.NONE) + return new EmptyExchangeKeys(); + throw new IllegalArgumentException("unsupported key exchange: " + alg); + } + + public int length() + { + if (suite.keyExchangeAlgorithm() == KeyExchangeAlgorithm.NONE) + return 0; + return exchangeKeys().length(); + } + + public String toString () + { + return toString (null); + } + + public String toString (final String prefix) + { + StringWriter str = new StringWriter(); + PrintWriter out = new PrintWriter(str); + if (prefix != null) + out.print (prefix); + out.println("struct {"); + String subprefix = " "; + if (prefix != null) + subprefix = prefix + subprefix; + out.println (exchangeKeys ().toString (subprefix)); + if (prefix != null) + out.print (prefix); + out.println("} ClientKeyExchange;"); + return str.toString(); + } +} diff --git a/libjava/classpath/gnu/javax/net/ssl/provider/ClientKeyExchangeBuilder.java b/libjava/classpath/gnu/javax/net/ssl/provider/ClientKeyExchangeBuilder.java new file mode 100644 index 000000000..a43873510 --- /dev/null +++ b/libjava/classpath/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/libjava/classpath/gnu/javax/net/ssl/provider/ClientPSKParameters.java b/libjava/classpath/gnu/javax/net/ssl/provider/ClientPSKParameters.java new file mode 100644 index 000000000..22c6333e9 --- /dev/null +++ b/libjava/classpath/gnu/javax/net/ssl/provider/ClientPSKParameters.java @@ -0,0 +1,121 @@ +/* ClientPSKParameters.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.PrintWriter; +import java.io.StringWriter; +import java.nio.ByteBuffer; +import java.nio.CharBuffer; +import java.nio.charset.Charset; + +/** + *
+      struct {
+          select (KeyExchangeAlgorithm) {
+              /* other cases for rsa, diffie_hellman, etc. */
+              case psk:   /* NEW */
+                  opaque psk_identity<0..2^16-1>;
+          } exchange_keys;
+      } ClientKeyExchange;
+ * + * @author Casey Marshall (csm@gnu.org) + */ +public class ClientPSKParameters extends ExchangeKeys implements Builder, Constructed +{ + public ClientPSKParameters(ByteBuffer buffer) + { + super(buffer); + } + + public ClientPSKParameters(String identity) + { + super(null); + Charset utf8 = Charset.forName("UTF-8"); + ByteBuffer idBuf = utf8.encode(CharBuffer.wrap(identity)); + buffer = ByteBuffer.allocate(idBuf.remaining() + 2); + buffer.putShort((short) idBuf.remaining()); + buffer.put(idBuf); + buffer.rewind(); + } + + /* (non-Javadoc) + * @see gnu.javax.net.ssl.provider.Builder#buffer() + */ + public ByteBuffer buffer() + { + return (ByteBuffer) buffer.duplicate().rewind().limit(length()); + } + + /* (non-Javadoc) + * @see gnu.javax.net.ssl.provider.Constructed#length() + */ + public int length() + { + return (buffer.getShort(0) & 0xFFFF) + 2; + } + + public String identity() + { + Charset utf8 = Charset.forName("UTF-8"); + return utf8.decode((ByteBuffer) buffer.duplicate().position(2).limit(length())).toString(); + } + + public @Override String toString() + { + return toString(null); + } + + /* (non-Javadoc) + * @see gnu.javax.net.ssl.provider.Constructed#toString(java.lang.String) + */ + public String toString(String prefix) + { + StringWriter str = new StringWriter(); + PrintWriter out = new PrintWriter(str); + if (prefix != null) out.print(prefix); + out.println("struct {"); + if (prefix != null) out.print(prefix); + out.print(" identity = "); + out.print(identity()); + out.println(";"); + if (prefix != null) out.print(prefix); + out.print("} ClientPSKParameters;"); + return str.toString(); + } +} diff --git a/libjava/classpath/gnu/javax/net/ssl/provider/ClientRSA_PSKParameters.java b/libjava/classpath/gnu/javax/net/ssl/provider/ClientRSA_PSKParameters.java new file mode 100644 index 000000000..842e911d0 --- /dev/null +++ b/libjava/classpath/gnu/javax/net/ssl/provider/ClientRSA_PSKParameters.java @@ -0,0 +1,122 @@ +/* ClientRSA_PSKParameters.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.PrintWriter; +import java.io.StringWriter; +import java.nio.ByteBuffer; +import java.nio.charset.Charset; + +/** + * @author Casey Marshall (csm@gnu.org) + */ +public class ClientRSA_PSKParameters extends ExchangeKeys implements Builder, Constructed +{ + public ClientRSA_PSKParameters(ByteBuffer buffer) + { + super(buffer); + } + + public ClientRSA_PSKParameters(String identity, ByteBuffer epms) + { + super(null); + Charset utf8 = Charset.forName("UTF-8"); + ByteBuffer idBuf = utf8.encode(identity); + buffer = ByteBuffer.allocate(2 + idBuf.remaining() + epms.remaining()); + buffer.putShort((short) idBuf.remaining()); + buffer.put(idBuf); + buffer.put(epms); + buffer.rewind(); + } + + /* (non-Javadoc) + * @see gnu.javax.net.ssl.provider.Builder#buffer() + */ + public ByteBuffer buffer() + { + return (ByteBuffer) buffer.duplicate().rewind().limit(length()); + } + + public String identity() + { + Charset utf8 = Charset.forName("UTF-8"); + return utf8.decode((ByteBuffer) buffer.duplicate().position(2).limit + (identityLength())).toString(); + } + + private int identityLength() + { + return (buffer.getShort(0) & 0xFFFF) + 2; + } + + /* (non-Javadoc) + * @see gnu.javax.net.ssl.provider.Constructed#length() + */ + public int length() + { + return identityLength() + secret().length(); + } + + public EncryptedPreMasterSecret secret() + { + return new EncryptedPreMasterSecret + (((ByteBuffer) buffer.duplicate().position(identityLength()) + .limit(buffer.capacity())).slice(), ProtocolVersion.TLS_1); + } + + /* (non-Javadoc) + * @see gnu.javax.net.ssl.provider.Constructed#toString(java.lang.String) + */ + public String toString(String prefix) + { + StringWriter str = new StringWriter(); + PrintWriter out = new PrintWriter(str); + if (prefix != null) out.print(prefix); + out.println("struct {"); + if (prefix != null) out.print(prefix); + out.print(" identity = "); + out.print(identity()); + if (prefix != null) out.print(prefix); + out.println(" encrypted_pre_master_secret ="); + out.println(secret().toString(prefix != null ? prefix + " " : " ")); + if (prefix != null) out.print(prefix); + out.print("} ClientRSA_PSKParameters;"); + return str.toString(); + } +} diff --git a/libjava/classpath/gnu/javax/net/ssl/provider/CompressionMethod.java b/libjava/classpath/gnu/javax/net/ssl/provider/CompressionMethod.java new file mode 100644 index 000000000..3005dd9fc --- /dev/null +++ b/libjava/classpath/gnu/javax/net/ssl/provider/CompressionMethod.java @@ -0,0 +1,69 @@ +/* CompressionMethod.java -- The CompressionMethod enum. + 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; + +public enum CompressionMethod +{ + NULL (0), ZLIB(1); + + private final int value; + + private CompressionMethod(int value) + { + this.value = value; + } + + public static CompressionMethod getInstance (final int value) + { + switch (value & 0xFF) + { + case 0: return NULL; + case 1: return ZLIB; + + // Note: we can't throw an exception here, because we get these values + // over the wire, and need to just ignore ones we don't recognize. + default: return null; + } + } + + public int getValue() + { + return value; + } +} diff --git a/libjava/classpath/gnu/javax/net/ssl/provider/CompressionMethodList.java b/libjava/classpath/gnu/javax/net/ssl/provider/CompressionMethodList.java new file mode 100644 index 000000000..b57e0c6a6 --- /dev/null +++ b/libjava/classpath/gnu/javax/net/ssl/provider/CompressionMethodList.java @@ -0,0 +1,281 @@ +/* CompressionMethodList.java -- A list of compression methods. + 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.PrintWriter; +import java.io.StringWriter; + +import java.nio.ByteBuffer; + +import java.util.ConcurrentModificationException; +import java.util.ListIterator; +import java.util.NoSuchElementException; + +/** + * A basic list interface to a list of compression methods in an SSL + * packet. + */ +public final class CompressionMethodList implements Iterable +{ + private final ByteBuffer buffer; + private int modCount; + + public CompressionMethodList (final ByteBuffer buffer) + { + this.buffer = buffer; + modCount = 0; + } + + /** + * Return the number of elements in this list. + * + * @return The size of this list. + */ + public int size () + { + return (buffer.get (0) & 0xFF); + } + + /** + * Get the cipher suite at the specified index. + * + * @param index The index of the suite to get. + * @return The cipher suite at that index. + * @throws IndexOutOfBoundsException If the index is negative or is + * not less than {@link #size()}. + */ + public CompressionMethod get (final int index) + { + int size = size (); + if (index < 0 || index >= size) + throw new IndexOutOfBoundsException ("limit: " + size + + "; requested: " + index); + return CompressionMethod.getInstance (buffer.get (1 + index)); + } + + /** + * Set the CompressionMethod at the specified index. The list must + * have sufficient size to hold the element (that is, index + * <= size ()). + * + * @param index The index to put the suite. + * @param method The CompressionMethod object. + * @throws IndexOutOfBoundsException If index is not + * less than @{link #size()}, or if it is negative. + * @throws NullPointerException If suite is + * null. + * @throws java.nio.ReadOnlyBufferException If the underlying buffer + * is not writable. + */ + public void put (final int index, final CompressionMethod method) + { + int size = size (); + if (index < 0 || index >= size) + throw new IndexOutOfBoundsException ("limit: " + size + + "; requested: " + index); + buffer.position (1 + index); + buffer.put ((byte) method.getValue ()); + modCount++; + } + + /** + * Sets the size of this list. You must call this if you are adding + * elements to the list; calling {@link + * #put(int,gnu.jessie.provider.CipherSuite)} does not expand the + * list size (the same goes for removing elements, as there is no + * remove method). + * + * @param newSize The new size of this list. + * @throws IllegalArgumentException If the new size is negative or + * greater than 32767, or if there is insufficient space for that + * many elements in the underlying buffer. + * @throws java.nio.ReadOnlyBufferException If the underlying buffer + * is not writable. + */ + public void setSize (final int newSize) + { + if (newSize < 0 || newSize > 255) + throw new IllegalArgumentException ("size must be between 0 and 255"); + if (newSize + 1 > buffer.capacity ()) + throw new IllegalArgumentException ("limit: " + buffer.capacity () + + "; requested: " + newSize); + buffer.put (0, (byte) newSize); + modCount++; + } + + public String toString () + { + return toString (null); + } + + public String toString (final String prefix) + { + StringWriter str = new StringWriter (); + PrintWriter out = new PrintWriter (str); + if (prefix != null) + out.print (prefix); + out.print ("["); + out.print (size ()); + out.println ("] {"); + for (Iterator it = new Iterator (); it.hasNext (); ) + { + CompressionMethod method = (CompressionMethod) it.next (); + if (prefix != null) + out.print (prefix); + out.print (" "); + out.print (method); + if (it.hasNext ()) + out.print (","); + out.println (); + } + if (prefix != null) + out.print (prefix); + out.print ("};"); + return str.toString (); + } + + public boolean equals (Object o) + { + if (!(o instanceof CompressionMethodList)) + return false; + CompressionMethodList that = (CompressionMethodList) o; + + if (size () != that.size ()) + return false; + + for (Iterator it1 = new Iterator (), it2 = that.new Iterator (); + it1.hasNext () && it2.hasNext (); ) + { + if (!it1.next ().equals (it2.next ())) + return false; + } + return true; + } + + public java.util.Iterator iterator () + { + return new Iterator (); + } + + /** + * An iterator for the elements in this list. The iterator supports + * only the set method out of the optional methods, + * because elements in a CipherSuiteList may not be removed or + * added; only the size of the list can be changed, and elements at + * a specific index changed. + */ + public class Iterator implements ListIterator + { + private int index; + private final int modCount; + + Iterator () + { + index = 0; + modCount = CompressionMethodList.this.modCount; + } + + public void add (CompressionMethod cm) + { + throw new UnsupportedOperationException (); + } + + public boolean hasNext () + { + return (index < size ()); + } + + public boolean hasPrevious () + { + return (index > 0); + } + + public CompressionMethod next () throws NoSuchElementException + { + if (modCount != CompressionMethodList.this.modCount) + throw new ConcurrentModificationException (); + try + { + return get (index++); + } + catch (IndexOutOfBoundsException ioobe) + { + throw new NoSuchElementException (); + } + } + + public int nextIndex () + { + if (hasNext ()) + return (index + 1); + return -1; + } + + public CompressionMethod previous () throws NoSuchElementException + { + if (index == 0) + throw new NoSuchElementException (); + if (modCount != CompressionMethodList.this.modCount) + throw new ConcurrentModificationException (); + try + { + return get (--index); + } + catch (IndexOutOfBoundsException ioobe) // on empty list + { + throw new NoSuchElementException (); + } + } + + public int previousIndex () + { + return (index - 1); + } + + public void remove () + { + throw new UnsupportedOperationException (); + } + + public void set (final CompressionMethod cm) + { + put (index, cm); + } + } +} diff --git a/libjava/classpath/gnu/javax/net/ssl/provider/Constructed.java b/libjava/classpath/gnu/javax/net/ssl/provider/Constructed.java new file mode 100644 index 000000000..23ff68812 --- /dev/null +++ b/libjava/classpath/gnu/javax/net/ssl/provider/Constructed.java @@ -0,0 +1,86 @@ +/* Constructed.java -- Constructed type. + 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; + +/** + * The base interface to SSL constructed types. + * + *

Contract for ByteBuffer-based constructed types: + * + *

Most implementations of this interface supported by this library + * take a "view" of an underlying ByteBuffer. The general contract of + * such classes is that they will not modify the position or + * limit of the buffer when doing read operations. That is, the position + * of the underlying buffer should remain at 0 throughout the + * lifetime of the object, and the limit should be either set to the + * capacity of the buffer, or to the size of the object (in most cases, + * the length of the protocol object is determined by the contents of + * the object, so the limit isn't useful in such cases. Of course, if the + * limit is set to something other than the object's length, it must be + * larger than the object length). + * + *

Setter methods (usually in a class that implements the {@link Builder} + * interface) may modify the limit, but the general contract remains that + * the position remain at zero, and that the limit be at least as large as + * the object length. + * + *

Thus, very often the code will use absolute getters and setters + * for primitive types, or it will use the {@link java.nio.ByteBuffer#duplicate()} + * method, and sometimes the {@link java.nio.ByteBuffer#slice()} method, and + * will change the position or limit of the duplicate buffer. + */ +public interface Constructed +{ + /** + * Returns the total length, in bytes, of this structure. + * + * @return The length of this structure. + */ + int length(); + + /** + * Returns a printable representation of this structure, with the + * given prefix prepended to each line. + * + * @param prefix The prefix to prepend to each line of the + * output. This value may be null. + * @return A printable representation of this structure. + */ + String toString(String prefix); +} diff --git a/libjava/classpath/gnu/javax/net/ssl/provider/ContentType.java b/libjava/classpath/gnu/javax/net/ssl/provider/ContentType.java new file mode 100644 index 000000000..eaebebf4b --- /dev/null +++ b/libjava/classpath/gnu/javax/net/ssl/provider/ContentType.java @@ -0,0 +1,89 @@ +/* ContentType.java -- SSL record layer content type. + 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; + +/** + * The content type enumeration, which marks packets in the record layer. + * + *

+enum { change_cipher_spec(20), alert(21), handshake(22),
+       application_data(23), (255) } ContentType;
+ * + *

There is also a "pseudo" content type, client_hello_v2 + * (1), which is used for backwards compatibility with SSLv2. + * + * @author Casey Marshall (rsdio@metastatic.org) + */ +public enum ContentType +{ + + CLIENT_HELLO_V2 ( 1), + CHANGE_CIPHER_SPEC (20), + ALERT (21), + HANDSHAKE (22), + APPLICATION_DATA (23); + + private int value; + + // Constructors. + // ------------------------------------------------------------------------ + + private ContentType(int value) + { + this.value = value; + } + + static final ContentType forInteger (final int value) + { + switch (value & 0xFF) + { + case 1: return CLIENT_HELLO_V2; + case 20: return CHANGE_CIPHER_SPEC; + case 21: return ALERT; + case 22: return HANDSHAKE; + case 23: return APPLICATION_DATA; + default: return null; + } + } + + public int getValue() + { + return value; + } +} diff --git a/libjava/classpath/gnu/javax/net/ssl/provider/Debug.java b/libjava/classpath/gnu/javax/net/ssl/provider/Debug.java new file mode 100644 index 000000000..308ef67a0 --- /dev/null +++ b/libjava/classpath/gnu/javax/net/ssl/provider/Debug.java @@ -0,0 +1,66 @@ +/* Debug.java -- Jessie debug constants. + 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; + +/** + * Debug constants for Jessie. + * + * @author Casey Marshall (csm@gnu.org) + */ +public final class Debug +{ + /** + * Set to true to dump out traces of SSL connections to the system + * logger. + */ + public static final boolean DEBUG = true; + + /** + * Set to true to dump out info about the SSL key exchange. Since this + * MAY contain sensitive data, it is a separate value. + */ + public static final boolean DEBUG_KEY_EXCHANGE = true; + + /** + * Set to true to turn on dumping of decrypted packets. Since this will + * log potentially-sensitive information (i.e., decrypted messages), only + * enable this in debug scenarios. + */ + public static final boolean DEBUG_DECRYPTION = false; +} diff --git a/libjava/classpath/gnu/javax/net/ssl/provider/DelegatedTask.java b/libjava/classpath/gnu/javax/net/ssl/provider/DelegatedTask.java new file mode 100644 index 000000000..34fd39d19 --- /dev/null +++ b/libjava/classpath/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/libjava/classpath/gnu/javax/net/ssl/provider/DiffieHellman.java b/libjava/classpath/gnu/javax/net/ssl/provider/DiffieHellman.java new file mode 100644 index 000000000..5a5275712 --- /dev/null +++ b/libjava/classpath/gnu/javax/net/ssl/provider/DiffieHellman.java @@ -0,0 +1,289 @@ +/* DiffieHellman.java -- Diffie-Hellman key exchange. + 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.math.BigInteger; +import java.security.AccessController; + +import gnu.java.security.action.GetSecurityPropertyAction; +import gnu.javax.crypto.key.dh.GnuDHPrivateKey; + +/** + *

Simple implementation of two-party Diffie-Hellman key agreement.

+ * + *

The primes used in this class are from the following documents:

+ * + *
    + *
  • D. Harkins and D. Carrel, "The Internet Key Exchange (IKE)", RFC 2409.
  • + *
  • T. Kivinen and M. Kojo, "More Modular + * Exponential (MODP) Diffie-Hellman groups for Internet Key Exchange + * (IKE)", RFC + * 3526.
  • + * + * + *

    The generator for all these primes is 2.

    + */ +final class DiffieHellman +{ + + // Class method. + // ------------------------------------------------------------------------- + + /** + * Get the system's Diffie-Hellman parameters, in which g is 2 + * and p is determined by the property + * "jessie.keypool.dh.group". The default value for p + * is 18, corresponding to {@link #GROUP_18}. + */ + static GnuDHPrivateKey getParams() + { + BigInteger p = DiffieHellman.GROUP_5; + String group = AccessController.doPrivileged + (new GetSecurityPropertyAction("jessie.key.dh.group")); + if (group != null) + { + group = group.trim(); + if (group.equals("1")) + p = DiffieHellman.GROUP_1; + else if (group.equals("2")) + p = DiffieHellman.GROUP_2; + else if (group.equals("5")) + p = DiffieHellman.GROUP_5; + else if (group.equals("14")) + p = DiffieHellman.GROUP_14; + else if (group.equals("15")) + p = DiffieHellman.GROUP_15; + else if (group.equals("16")) + p = DiffieHellman.GROUP_16; + else if (group.equals("17")) + p = DiffieHellman.GROUP_17; + else if (group.equals("18")) + p = DiffieHellman.GROUP_18; + } + return new GnuDHPrivateKey(null, p, DH_G, null); + } + + // Constants. + // ------------------------------------------------------------------------- + + /** + * The generator for all Diffie Hellman groups below. + */ + static final BigInteger DH_G = BigInteger.valueOf(2L); + + /** + * p = 2^768 - 2 ^704 - 1 + 2^64 * { [2^638 pi] + 149686 } + */ + static final BigInteger GROUP_1 = new BigInteger("00" + + "FFFFFFFFFFFFFFFFC90FDAA22168C234C4C6628B80DC1CD1" + + "29024E088A67CC74020BBEA63B139B22514A08798E3404DD" + + "EF9519B3CD3A431B302B0A6DF25F14374FE1356D6D51C245" + + "E485B576625E7EC6F44C42E9A63A3620FFFFFFFFFFFFFFFF", 16); + + /** + * p = 2^1024 - 2^960 - 1 + 2^64 * { [2^894 pi] + 129093 } + */ + static final BigInteger GROUP_2 = new BigInteger("00" + + "FFFFFFFFFFFFFFFFC90FDAA22168C234C4C6628B80DC1CD1" + + "29024E088A67CC74020BBEA63B139B22514A08798E3404DD" + + "EF9519B3CD3A431B302B0A6DF25F14374FE1356D6D51C245" + + "E485B576625E7EC6F44C42E9A637ED6B0BFF5CB6F406B7ED" + + "EE386BFB5A899FA5AE9F24117C4B1FE649286651ECE65381" + + "FFFFFFFFFFFFFFFF", 16); + + /** + * This prime p = 2^1536 - 2^1472 - 1 + 2^64 * { [2^1406 pi] + 741804 }. + */ + static final BigInteger GROUP_5 = new BigInteger("00" + + "FFFFFFFFFFFFFFFFC90FDAA22168C234C4C6628B80DC1CD1" + + "29024E088A67CC74020BBEA63B139B22514A08798E3404DD" + + "EF9519B3CD3A431B302B0A6DF25F14374FE1356D6D51C245" + + "E485B576625E7EC6F44C42E9A637ED6B0BFF5CB6F406B7ED" + + "EE386BFB5A899FA5AE9F24117C4B1FE649286651ECE45B3D" + + "C2007CB8A163BF0598DA48361C55D39A69163FA8FD24CF5F" + + "83655D23DCA3AD961C62F356208552BB9ED529077096966D" + + "670C354E4ABC9804F1746C08CA237327FFFFFFFFFFFFFFFF", 16); + + /** + * p = 2^2048 - 2^1984 - 1 + 2^64 * { [2^1918 pi] + 124476 }. + */ + static final BigInteger GROUP_14 = new BigInteger("00" + + "FFFFFFFFFFFFFFFFC90FDAA22168C234C4C6628B80DC1CD1" + + "29024E088A67CC74020BBEA63B139B22514A08798E3404DD" + + "EF9519B3CD3A431B302B0A6DF25F14374FE1356D6D51C245" + + "E485B576625E7EC6F44C42E9A637ED6B0BFF5CB6F406B7ED" + + "EE386BFB5A899FA5AE9F24117C4B1FE649286651ECE45B3D" + + "C2007CB8A163BF0598DA48361C55D39A69163FA8FD24CF5F" + + "83655D23DCA3AD961C62F356208552BB9ED529077096966D" + + "670C354E4ABC9804F1746C08CA18217C32905E462E36CE3B" + + "E39E772C180E86039B2783A2EC07A28FB5C55DF06F4C52C9" + + "DE2BCBF6955817183995497CEA956AE515D2261898FA0510" + + "15728E5A8AACAA68FFFFFFFFFFFFFFFF", 16); + + /** + * p = 2^3072 - 2^3008 - 1 + 2^64 * { [2^2942 pi] + 1690314 }. + */ + static final BigInteger GROUP_15 = new BigInteger("00" + + "FFFFFFFFFFFFFFFFC90FDAA22168C234C4C6628B80DC1CD1" + + "29024E088A67CC74020BBEA63B139B22514A08798E3404DD" + + "EF9519B3CD3A431B302B0A6DF25F14374FE1356D6D51C245" + + "E485B576625E7EC6F44C42E9A637ED6B0BFF5CB6F406B7ED" + + "EE386BFB5A899FA5AE9F24117C4B1FE649286651ECE45B3D" + + "C2007CB8A163BF0598DA48361C55D39A69163FA8FD24CF5F" + + "83655D23DCA3AD961C62F356208552BB9ED529077096966D" + + "670C354E4ABC9804F1746C08CA18217C32905E462E36CE3B" + + "E39E772C180E86039B2783A2EC07A28FB5C55DF06F4C52C9" + + "DE2BCBF6955817183995497CEA956AE515D2261898FA0510" + + "15728E5A8AAAC42DAD33170D04507A33A85521ABDF1CBA64" + + "ECFB850458DBEF0A8AEA71575D060C7DB3970F85A6E1E4C7" + + "ABF5AE8CDB0933D71E8C94E04A25619DCEE3D2261AD2EE6B" + + "F12FFA06D98A0864D87602733EC86A64521F2B18177B200C" + + "BBE117577A615D6C770988C0BAD946E208E24FA074E5AB31" + + "43DB5BFCE0FD108E4B82D120A93AD2CAFFFFFFFFFFFFFFFF", 16); + + /** + * p = 2^4096 - 2^4032 - 1 + 2^64 * { [2^3966 pi] + 240904 }. + */ + static final BigInteger GROUP_16 = new BigInteger("00" + + "FFFFFFFFFFFFFFFFC90FDAA22168C234C4C6628B80DC1CD1" + + "29024E088A67CC74020BBEA63B139B22514A08798E3404DD" + + "EF9519B3CD3A431B302B0A6DF25F14374FE1356D6D51C245" + + "E485B576625E7EC6F44C42E9A637ED6B0BFF5CB6F406B7ED" + + "EE386BFB5A899FA5AE9F24117C4B1FE649286651ECE45B3D" + + "C2007CB8A163BF0598DA48361C55D39A69163FA8FD24CF5F" + + "83655D23DCA3AD961C62F356208552BB9ED529077096966D" + + "670C354E4ABC9804F1746C08CA18217C32905E462E36CE3B" + + "E39E772C180E86039B2783A2EC07A28FB5C55DF06F4C52C9" + + "DE2BCBF6955817183995497CEA956AE515D2261898FA0510" + + "15728E5A8AAAC42DAD33170D04507A33A85521ABDF1CBA64" + + "ECFB850458DBEF0A8AEA71575D060C7DB3970F85A6E1E4C7" + + "ABF5AE8CDB0933D71E8C94E04A25619DCEE3D2261AD2EE6B" + + "F12FFA06D98A0864D87602733EC86A64521F2B18177B200C" + + "BBE117577A615D6C770988C0BAD946E208E24FA074E5AB31" + + "43DB5BFCE0FD108E4B82D120A92108011A723C12A787E6D7" + + "88719A10BDBA5B2699C327186AF4E23C1A946834B6150BDA" + + "2583E9CA2AD44CE8DBBBC2DB04DE8EF92E8EFC141FBECAA6" + + "287C59474E6BC05D99B2964FA090C3A2233BA186515BE7ED" + + "1F612970CEE2D7AFB81BDD762170481CD0069127D5B05AA9" + + "93B4EA988D8FDDC186FFB7DC90A6C08F4DF435C934063199" + + "FFFFFFFFFFFFFFFF", 16); + + static final BigInteger GROUP_17 = new BigInteger("00" + + "FFFFFFFFFFFFFFFFC90FDAA22168C234C4C6628B80DC1CD129024E08" + + "8A67CC74020BBEA63B139B22514A08798E3404DDEF9519B3CD3A431B" + + "302B0A6DF25F14374FE1356D6D51C245E485B576625E7EC6F44C42E9" + + "A637ED6B0BFF5CB6F406B7EDEE386BFB5A899FA5AE9F24117C4B1FE6" + + "49286651ECE45B3DC2007CB8A163BF0598DA48361C55D39A69163FA8" + + "FD24CF5F83655D23DCA3AD961C62F356208552BB9ED529077096966D" + + "670C354E4ABC9804F1746C08CA18217C32905E462E36CE3BE39E772C" + + "180E86039B2783A2EC07A28FB5C55DF06F4C52C9DE2BCBF695581718" + + "3995497CEA956AE515D2261898FA051015728E5A8AAAC42DAD33170D" + + "04507A33A85521ABDF1CBA64ECFB850458DBEF0A8AEA71575D060C7D" + + "B3970F85A6E1E4C7ABF5AE8CDB0933D71E8C94E04A25619DCEE3D226" + + "1AD2EE6BF12FFA06D98A0864D87602733EC86A64521F2B18177B200C" + + "BBE117577A615D6C770988C0BAD946E208E24FA074E5AB3143DB5BFC" + + "E0FD108E4B82D120A92108011A723C12A787E6D788719A10BDBA5B26" + + "99C327186AF4E23C1A946834B6150BDA2583E9CA2AD44CE8DBBBC2DB" + + "04DE8EF92E8EFC141FBECAA6287C59474E6BC05D99B2964FA090C3A2" + + "233BA186515BE7ED1F612970CEE2D7AFB81BDD762170481CD0069127" + + "D5B05AA993B4EA988D8FDDC186FFB7DC90A6C08F4DF435C934028492" + + "36C3FAB4D27C7026C1D4DCB2602646DEC9751E763DBA37BDF8FF9406" + + "AD9E530EE5DB382F413001AEB06A53ED9027D831179727B0865A8918" + + "DA3EDBEBCF9B14ED44CE6CBACED4BB1BDB7F1447E6CC254B33205151" + + "2BD7AF426FB8F401378CD2BF5983CA01C64B92ECF032EA15D1721D03" + + "F482D7CE6E74FEF6D55E702F46980C82B5A84031900B1C9E59E7C97F" + + "BEC7E8F323A97A7E36CC88BE0F1D45B7FF585AC54BD407B22B4154AA" + + "CC8F6D7EBF48E1D814CC5ED20F8037E0A79715EEF29BE32806A1D58B" + + "B7C5DA76F550AA3D8A1FBFF0EB19CCB1A313D55CDA56C9EC2EF29632" + + "387FE8D76E3C0468043E8F663F4860EE12BF2D5B0B7474D6E694F91E" + + "6DCC4024FFFFFFFFFFFFFFFF", 16); + + /** + * p = 2^8192 - 2^8128 - 1 + 2^64 * { [2^8062 pi] + 4743158 }. + * + *

    This value, while quite large, is estimated to provide the equivalent + * cryptographic strength of a symmetric key between 190 and 320 bits. + */ + static final BigInteger GROUP_18 = new BigInteger("00" + + "FFFFFFFFFFFFFFFFC90FDAA22168C234C4C6628B80DC1CD1" + + "29024E088A67CC74020BBEA63B139B22514A08798E3404DD" + + "EF9519B3CD3A431B302B0A6DF25F14374FE1356D6D51C245" + + "E485B576625E7EC6F44C42E9A637ED6B0BFF5CB6F406B7ED" + + "EE386BFB5A899FA5AE9F24117C4B1FE649286651ECE45B3D" + + "C2007CB8A163BF0598DA48361C55D39A69163FA8FD24CF5F" + + "83655D23DCA3AD961C62F356208552BB9ED529077096966D" + + "670C354E4ABC9804F1746C08CA18217C32905E462E36CE3B" + + "E39E772C180E86039B2783A2EC07A28FB5C55DF06F4C52C9" + + "DE2BCBF6955817183995497CEA956AE515D2261898FA0510" + + "15728E5A8AAAC42DAD33170D04507A33A85521ABDF1CBA64" + + "ECFB850458DBEF0A8AEA71575D060C7DB3970F85A6E1E4C7" + + "ABF5AE8CDB0933D71E8C94E04A25619DCEE3D2261AD2EE6B" + + "F12FFA06D98A0864D87602733EC86A64521F2B18177B200C" + + "BBE117577A615D6C770988C0BAD946E208E24FA074E5AB31" + + "43DB5BFCE0FD108E4B82D120A92108011A723C12A787E6D7" + + "88719A10BDBA5B2699C327186AF4E23C1A946834B6150BDA" + + "2583E9CA2AD44CE8DBBBC2DB04DE8EF92E8EFC141FBECAA6" + + "287C59474E6BC05D99B2964FA090C3A2233BA186515BE7ED" + + "1F612970CEE2D7AFB81BDD762170481CD0069127D5B05AA9" + + "93B4EA988D8FDDC186FFB7DC90A6C08F4DF435C934028492" + + "36C3FAB4D27C7026C1D4DCB2602646DEC9751E763DBA37BD" + + "F8FF9406AD9E530EE5DB382F413001AEB06A53ED9027D831" + + "179727B0865A8918DA3EDBEBCF9B14ED44CE6CBACED4BB1B" + + "DB7F1447E6CC254B332051512BD7AF426FB8F401378CD2BF" + + "5983CA01C64B92ECF032EA15D1721D03F482D7CE6E74FEF6" + + "D55E702F46980C82B5A84031900B1C9E59E7C97FBEC7E8F3" + + "23A97A7E36CC88BE0F1D45B7FF585AC54BD407B22B4154AA" + + "CC8F6D7EBF48E1D814CC5ED20F8037E0A79715EEF29BE328" + + "06A1D58BB7C5DA76F550AA3D8A1FBFF0EB19CCB1A313D55C" + + "DA56C9EC2EF29632387FE8D76E3C0468043E8F663F4860EE" + + "12BF2D5B0B7474D6E694F91E6DBE115974A3926F12FEE5E4" + + "38777CB6A932DF8CD8BEC4D073B931BA3BC832B68D9DD300" + + "741FA7BF8AFC47ED2576F6936BA424663AAB639C5AE4F568" + + "3423B4742BF1C978238F16CBE39D652DE3FDB8BEFC848AD9" + + "22222E04A4037C0713EB57A81A23F0C73473FC646CEA306B" + + "4BCBC8862F8385DDFA9D4B7FA2C087E879683303ED5BDD3A" + + "062B3CF5B3A278A66D2A13F83F44F82DDF310EE074AB6A36" + + "4597E899A0255DC164F31CC50846851DF9AB48195DED7EA1" + + "B1D510BD7EE74D73FAF36BC31ECFA268359046F4EB879F92" + + "4009438B481C6CD7889A002ED5EE382BC9190DA6FC026E47" + + "9558E4475677E9AA9E3050E2765694DFC81F56E880B96E71" + + "60C980DD98EDD3DFFFFFFFFFFFFFFFFF", 16); + +} diff --git a/libjava/classpath/gnu/javax/net/ssl/provider/EmptyExchangeKeys.java b/libjava/classpath/gnu/javax/net/ssl/provider/EmptyExchangeKeys.java new file mode 100644 index 000000000..55b59998d --- /dev/null +++ b/libjava/classpath/gnu/javax/net/ssl/provider/EmptyExchangeKeys.java @@ -0,0 +1,77 @@ +/* EmptyExchangeKeys.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; + +/** + * @author Casey Marshall (csm@gnu.org) + */ +public class EmptyExchangeKeys + extends ExchangeKeys +{ + + public EmptyExchangeKeys() + { + super(ByteBuffer.allocate(0)); + } + + /* (non-Javadoc) + * @see gnu.javax.net.ssl.provider.Constructed#length() + */ + public int length() + { + return 0; + } + + public String toString() + { + return toString(null); + } + + /* (non-Javadoc) + * @see gnu.javax.net.ssl.provider.Constructed#toString(java.lang.String) + */ + public String toString(String prefix) + { + String ret = "struct { };"; + if (prefix != null) ret = prefix + ret; + return ret; + } +} diff --git a/libjava/classpath/gnu/javax/net/ssl/provider/EncryptedPreMasterSecret.java b/libjava/classpath/gnu/javax/net/ssl/provider/EncryptedPreMasterSecret.java new file mode 100644 index 000000000..a40223dd0 --- /dev/null +++ b/libjava/classpath/gnu/javax/net/ssl/provider/EncryptedPreMasterSecret.java @@ -0,0 +1,148 @@ +/* EncryptedPreMasterSecret.java -- RSA encrypted secret. + 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.PrintWriter; +import java.io.StringWriter; + +import java.nio.ByteBuffer; + +/** + * The client's RSA-encrypted pre-master secret. + * + *

    +struct {
    +  public-key-encrypted PreMasterSecret pre_master_secret;
    +} EncryptedPreMasterSecret;
    + */ +public final class EncryptedPreMasterSecret extends ExchangeKeys implements Builder +{ + private final ProtocolVersion version; + + public EncryptedPreMasterSecret(ByteBuffer buffer, ProtocolVersion version) + { + 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(); + } + + 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; + secret = new byte[len]; + buffer.position(2); + buffer.get(secret); + } + return secret; + } + + 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.rewind(); + } + else + { + buffer.putShort(0, (short) length); + buffer.position(2); + buffer.put(secret, offset, length); + buffer.rewind(); + } + } + + public int length () + { + if (version == ProtocolVersion.SSL_3) + { + return buffer.capacity(); + } + else + { + return (buffer.getShort(0) & 0xFFFF) + 2; + } + } + + public String toString () + { + return toString (null); + } + + public String toString (final String prefix) + { + StringWriter str = new StringWriter(); + PrintWriter out = new PrintWriter(str); + if (prefix != null) out.print(prefix); + out.println("struct {"); + if (prefix != null) out.print(prefix); + out.println(" pre_master_secret = "); + out.print(Util.hexDump(encryptedSecret(), prefix != null ? prefix + " " + : " ")); + if (prefix != null) out.print(prefix); + out.print("} EncryptedPreMasterSecret;"); + return str.toString(); + } +} diff --git a/libjava/classpath/gnu/javax/net/ssl/provider/ExchangeKeys.java b/libjava/classpath/gnu/javax/net/ssl/provider/ExchangeKeys.java new file mode 100644 index 000000000..a6664b856 --- /dev/null +++ b/libjava/classpath/gnu/javax/net/ssl/provider/ExchangeKeys.java @@ -0,0 +1,54 @@ +/* ExchangeKeys.java -- key exchange values. + 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; +import java.nio.ByteOrder; + +public abstract class ExchangeKeys implements Constructed +{ + + protected ByteBuffer buffer; + + public ExchangeKeys (final ByteBuffer buffer) + { + if (buffer != null) + this.buffer = buffer.duplicate().order(ByteOrder.BIG_ENDIAN); + } +} diff --git a/libjava/classpath/gnu/javax/net/ssl/provider/Extension.java b/libjava/classpath/gnu/javax/net/ssl/provider/Extension.java new file mode 100644 index 000000000..5cbcd5790 --- /dev/null +++ b/libjava/classpath/gnu/javax/net/ssl/provider/Extension.java @@ -0,0 +1,246 @@ +/* Extension.java -- A TLS hello extension. + 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.PrintWriter; +import java.io.StringWriter; + +import java.nio.ByteBuffer; +import java.nio.ByteOrder; + +/** + * An SSL hello extension. + * + *
    + * struct {
    + *   ExtensionType extension_type;
    + *   opaque extension_data<0..2^16-1>;
    + * } Extension;
    + * + * @author csm@gnu.org + */ +public final class Extension implements Builder, Constructed +{ + + // Fields. + // ------------------------------------------------------------------------- + + private ByteBuffer buffer; + + // Constructor. + // ------------------------------------------------------------------------- + + public Extension(final ByteBuffer buffer) + { + this.buffer = buffer.duplicate().order(ByteOrder.BIG_ENDIAN); + } + + public Extension(final Type type, final Value value) + { + ByteBuffer valueBuffer = value.buffer(); + int length = 2 + 2 + valueBuffer.remaining(); + buffer = ByteBuffer.allocate(length); + buffer.putShort((short) type.getValue()); + buffer.putShort((short) valueBuffer.remaining()); + buffer.put(valueBuffer); + buffer.rewind(); + } + + // Instance methods. + // ------------------------------------------------------------------------- + + public int length () + { + return (buffer.getShort (2) & 0xFFFF) + 4; + } + + public ByteBuffer buffer() + { + return (ByteBuffer) buffer.duplicate().limit(length()); + } + + public Type type() + { + return Type.forValue (buffer.getShort (0) & 0xFFFF); + } + + public byte[] valueBytes() + { + int len = buffer.getShort (2) & 0xFFFF; + byte[] value = new byte[len]; + ((ByteBuffer) buffer.duplicate ().position (4)).get (value); + return value; + } + + public ByteBuffer valueBuffer() + { + int len = buffer.getShort(2) & 0xFFFF; + return ((ByteBuffer) buffer.duplicate().position(4).limit(len+4)).slice(); + } + + public Value value() + { + switch (type ()) + { + case SERVER_NAME: + return new ServerNameList(valueBuffer()); + + case MAX_FRAGMENT_LENGTH: + switch (valueBuffer().get() & 0xFF) + { + case 1: return MaxFragmentLength.LEN_2_9; + case 2: return MaxFragmentLength.LEN_2_10; + case 3: return MaxFragmentLength.LEN_2_11; + case 4: return MaxFragmentLength.LEN_2_12; + default: + throw new IllegalArgumentException("invalid max_fragment_len"); + } + + case TRUNCATED_HMAC: + return new TruncatedHMAC(); + + case CLIENT_CERTIFICATE_URL: + return new CertificateURL(valueBuffer()); + + case TRUSTED_CA_KEYS: + return new TrustedAuthorities(valueBuffer()); + + case STATUS_REQUEST: + return new CertificateStatusRequest(valueBuffer()); + + case SRP: + case CERT_TYPE: + } + return new UnresolvedExtensionValue(valueBuffer()); + } + + public void setLength (final int newLength) + { + if (newLength < 0 || newLength > 65535) + throw new IllegalArgumentException ("length is out of bounds"); + buffer.putShort (2, (short) newLength); + } + + public void setType (final Type type) + { + buffer.putShort(0, (short) type.getValue()); + } + + public void setValue (byte[] value) + { + setValue (value, 0, value.length); + } + + public void setValue (final byte[] value, final int offset, final int length) + { + if (length != length ()) + throw new IllegalArgumentException ("length is different than claimed length"); + ((ByteBuffer) buffer.duplicate().position(4)).put(value, offset, length); + } + + public String toString() + { + return toString(null); + } + + public String toString(String prefix) + { + StringWriter str = new StringWriter(); + PrintWriter out = new PrintWriter(str); + if (prefix != null) out.print (prefix); + out.println("struct {"); + if (prefix != null) out.print (prefix); + out.println(" type = " + type () + ";"); + if (prefix != null) out.print (prefix); + String subprefix = " "; + if (prefix != null) subprefix = prefix + subprefix; + out.println(" value ="); + out.println(value().toString(subprefix)); + if (prefix != null) out.print (prefix); + out.print("} Extension;"); + return str.toString(); + } + + // Inner classes. + // ------------------------------------------------------------------------- + + public static enum Type + { + SERVER_NAME (0), + MAX_FRAGMENT_LENGTH (1), + CLIENT_CERTIFICATE_URL (2), + TRUSTED_CA_KEYS (3), + TRUNCATED_HMAC (4), + STATUS_REQUEST (5), + SRP (6), + CERT_TYPE (7); + + private final int value; + + private Type(int value) + { + this.value = value; + } + + public static Type forValue (final int value) + { + switch (value & 0xFFFF) + { + case 0: return SERVER_NAME; + case 1: return MAX_FRAGMENT_LENGTH; + case 2: return CLIENT_CERTIFICATE_URL; + case 3: return TRUSTED_CA_KEYS; + case 4: return TRUNCATED_HMAC; + case 5: return STATUS_REQUEST; + case 6: return SRP; + case 7: return CERT_TYPE; + default: return null; + } + } + + public int getValue() + { + return value; + } + } + + public static abstract class Value implements Builder, Constructed + { + } +} diff --git a/libjava/classpath/gnu/javax/net/ssl/provider/ExtensionList.java b/libjava/classpath/gnu/javax/net/ssl/provider/ExtensionList.java new file mode 100644 index 000000000..fb7b12d9e --- /dev/null +++ b/libjava/classpath/gnu/javax/net/ssl/provider/ExtensionList.java @@ -0,0 +1,290 @@ +package gnu.javax.net.ssl.provider; + +import java.io.PrintWriter; +import java.io.StringWriter; +import java.nio.ByteBuffer; +import java.nio.ByteOrder; +import java.util.ConcurrentModificationException; +import java.util.Iterator; +import java.util.List; +import java.util.ListIterator; +import java.util.NoSuchElementException; + +/** + * A list of extensions, that may appear in either the {@link ClientHello} or + * {@link ServerHello}. The form of the extensions list is: + * + * Extension extensions_list<1..2^16-1> + * + * @author csm + */ +public class ExtensionList implements Builder, Iterable +{ + private final ByteBuffer buffer; + private int modCount; + + public ExtensionList (ByteBuffer buffer) + { + this.buffer = buffer.duplicate().order(ByteOrder.BIG_ENDIAN); + modCount = 0; + } + + public ExtensionList(List extensions) + { + int length = 2; + for (Extension extension : extensions) + length += extension.length(); + buffer = ByteBuffer.allocate(length); + buffer.putShort((short) (length - 2)); + for (Extension extension : extensions) + buffer.put(extension.buffer()); + buffer.rewind(); + } + + public ByteBuffer buffer() + { + return (ByteBuffer) buffer.duplicate().limit(length()); + } + + public Extension get (final int index) + { + int length = length (); + int i; + int n = 0; + for (i = 2; i < length && n < index; ) + { + int l = buffer.getShort (i+2) & 0xFFFF; + i += l + 4; + n++; + } + if (n < index) + throw new IndexOutOfBoundsException ("no elemenet at " + index); + int el = buffer.getShort (i+2) & 0xFFFF; + ByteBuffer b = (ByteBuffer) buffer.duplicate().position(i).limit(i+el+4); + return new Extension(b.slice()); + } + + /** + * Returns the number of extensions this list contains. + * + * @return The number of extensions. + */ + public int size () + { + int length = length (); + if (length == 0) + return 0; + int n = 0; + for (int i = 2; i < length; ) + { + int len = buffer.getShort (i+2) & 0xFFFF; + i += len + 4; + n++; + } + return n; + } + + /** + * Returns the length of this extension list, in bytes. + * + * @return The length of this extension list, in bytes. + */ + public int length () + { + return (buffer.getShort (0) & 0xFFFF) + 2; + } + + /** + * Sets the extension at index i to e. Note that setting an + * element at an index may invalidate any other elements that come + * after element at index i. In other words, no attempt is made to + * move existing elements in this list, and since extensions are variable + * length, you can not guarantee that extensions later in the list + * will still be valid. + * + *

    Thus, elements of this list must be set in order of increasing + * index. + * + * @param index The index to set the extension at. + * @param e The extension. + * @throws java.nio.BufferOverflowException If setting the extension overflows + * the buffer. + * @throws IllegalArgumentException If it isn't possible to find the given index + * in the current list (say, if no element index - 1 is set), or if setting + * the extension will overflow the current list length (given by {@link + * #length()}). + */ + public void set (final int index, Extension e) + { + int length = length(); + int n = 0; + int i; + for (i = 2; i < length && n < index; ) + { + int len = buffer.getShort(i+2) & 0xFFFF; + i += len + 4; + n++; + } + if (n < index) + throw new IllegalArgumentException("nothing set at index " + (index-1) + + " or insufficient space"); + if (i + e.length() + 2 > length) + throw new IllegalArgumentException("adding this element will exceed the " + + "list length"); + buffer.putShort(i, (short) e.type().getValue()); + buffer.putShort(i+2, (short) e.length()); + ((ByteBuffer) buffer.duplicate().position(i+4)).put (e.valueBuffer()); + modCount++; + } + + /** + * Reserve space for an extension at index i in the list. In other + * words, this does the job of {@link #set(int, Extension)}, but does not + * copy the extension value to the underlying buffer. + * + * @param index The index of the extension to reserve space for. + * @param t The type of the extension. + * @param eLength The number of bytes to reserve for this extension. The total + * number of bytes used by this method is this length, plus four. + */ + public void set (final int index, Extension.Type t, final int eLength) + { + int length = length (); + int n = 0; + int i; + for (i = 2; i < length && n < index; ) + { + int len = buffer.getShort (i+2) & 0xFFFF; + i += len + 4; + n++; + } + if (n < index) + throw new IllegalArgumentException ("nothing set at index " + (index-1) + + " or insufficient space"); + if (i + eLength + 2 > length) + throw new IllegalArgumentException ("adding this element will exceed the " + + "list length"); + buffer.putShort(i, (short) t.getValue()); + buffer.putShort(i+2, (short) eLength); + modCount++; + } + + /** + * Set the total length of this list, in bytes. + * + * @param newLength The new list length. + */ + public void setLength (final int newLength) + { + if (newLength < 0 || newLength > 65535) + throw new IllegalArgumentException ("invalid length"); + buffer.putShort (0, (short) newLength); + modCount++; + } + + public Iterator iterator() + { + return new ExtensionsIterator(); + } + + public String toString() + { + return toString (null); + } + + public String toString(final String prefix) + { + StringWriter str = new StringWriter(); + PrintWriter out = new PrintWriter(str); + if (prefix != null) out.print(prefix); + out.println("ExtensionList {"); + if (prefix != null) out.print(prefix); + out.print(" length = "); + out.print(length()); + out.println(";"); + String subprefix = " "; + if (prefix != null) + subprefix = prefix + subprefix; + for (Extension e : this) + out.println(e.toString(subprefix)); + if (prefix != null) out.print(prefix); + out.print("};"); + return str.toString(); + } + + /** + * List iterator interface to an extensions list. + * + * @author csm@gnu.org + */ + public final class ExtensionsIterator implements ListIterator + { + private final int modCount; + private int index; + private final int size; + + public ExtensionsIterator () + { + this.modCount = ExtensionList.this.modCount; + index = 0; + size = size (); + } + + public boolean hasNext() + { + return index < size; + } + + public boolean hasPrevious() + { + return index > 0; + } + + public Extension next() throws NoSuchElementException + { + if (modCount != ExtensionList.this.modCount) + throw new ConcurrentModificationException (); + if (!hasNext ()) + throw new NoSuchElementException (); + return get (index++); + } + + public Extension previous() throws NoSuchElementException + { + if (modCount != ExtensionList.this.modCount) + throw new ConcurrentModificationException (); + if (!hasPrevious ()) + throw new NoSuchElementException (); + return get (--index); + } + + public int nextIndex() + { + if (hasNext ()) + return index + 1; + return index; + } + + public int previousIndex() + { + if (hasPrevious ()) + return index - 1; + return -1; + } + + public void add(Extension e) + { + throw new UnsupportedOperationException ("cannot add items to this iterator"); + } + + public void remove() + { + throw new UnsupportedOperationException ("cannot remove items from this iterator"); + } + + public void set(Extension e) + { + ExtensionList.this.set (index, e); + } + } +} diff --git a/libjava/classpath/gnu/javax/net/ssl/provider/Finished.java b/libjava/classpath/gnu/javax/net/ssl/provider/Finished.java new file mode 100644 index 000000000..9a2a4707a --- /dev/null +++ b/libjava/classpath/gnu/javax/net/ssl/provider/Finished.java @@ -0,0 +1,173 @@ +/* Finished.java -- SSL Finished message. + 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.PrintWriter; +import java.io.StringWriter; + +import java.nio.ByteBuffer; + +final class Finished implements Handshake.Body +{ + + // Fields. + // ------------------------------------------------------------------------- + + private final ByteBuffer buffer; + private final ProtocolVersion version; + + // Constructor. + // ------------------------------------------------------------------------- + + Finished (final ByteBuffer buffer, final ProtocolVersion version) + { + buffer.getClass (); + version.getClass (); + this.buffer = buffer; + this.version = version; + } + + // Instance methods. + // ------------------------------------------------------------------------- + + public int length () + { + if (version.compareTo(ProtocolVersion.TLS_1) >= 0) + return 12; + if (version == ProtocolVersion.SSL_3) + return 36; + throw new IllegalArgumentException ("length for this version unknown"); + } + + byte[] verifyData() + { + if (version.compareTo(ProtocolVersion.TLS_1) >= 0) + { + byte[] verify = new byte[12]; + buffer.position (0); + buffer.get (verify); + return verify; + } + throw new IllegalArgumentException ("not TLSv1.0 or later"); + } + + byte[] md5Hash() + { + if (version == ProtocolVersion.SSL_3) + { + byte[] md5 = new byte[16]; + buffer.position (0); + buffer.get (md5); + return md5; + } + throw new IllegalArgumentException ("not SSLv3"); + } + + byte[] shaHash() + { + if (version == ProtocolVersion.SSL_3) + { + byte[] sha = new byte[20]; + buffer.position (16); + buffer.get (sha); + return sha; + } + throw new IllegalArgumentException ("not SSLv3"); + } + + void setVerifyData (final byte[] verifyData, final int offset) + { + if (version == ProtocolVersion.SSL_3) + throw new IllegalArgumentException ("not TLSv1"); + buffer.position (0); + buffer.put (verifyData, offset, 12); + } + + void setMD5Hash (final byte[] md5, final int offset) + { + if (version != ProtocolVersion.SSL_3) + throw new IllegalArgumentException ("not SSLv3"); + buffer.position (0); + buffer.put (md5, offset, 16); + } + + void setShaHash (final byte[] sha, final int offset) + { + if (version != ProtocolVersion.SSL_3) + throw new IllegalArgumentException ("not SSLv3"); + buffer.position (16); + buffer.put (sha, offset, 20); + } + + public String toString () + { + return toString (null); + } + + public String toString (final String prefix) + { + StringWriter str = new StringWriter (); + PrintWriter out = new PrintWriter (str); + if (prefix != null) + out.print (prefix); + out.println ("struct {"); + if (prefix != null) + out.print (prefix); + if (version.compareTo(ProtocolVersion.TLS_1) >= 0) + { + out.print (" verifyData = "); + out.print (Util.toHexString (verifyData (), ':')); + } + else if (version == ProtocolVersion.SSL_3) + { + out.print (" md5 = "); + out.print (Util.toHexString (md5Hash (), ':')); + out.println (';'); + if (prefix != null) + out.print (prefix); + out.print (" sha = "); + out.print (Util.toHexString (shaHash (), ':')); + } + out.println (';'); + if (prefix != null) + out.print (prefix); + out.print ("} Finished;"); + return str.toString (); + } +} diff --git a/libjava/classpath/gnu/javax/net/ssl/provider/Handshake.java b/libjava/classpath/gnu/javax/net/ssl/provider/Handshake.java new file mode 100644 index 000000000..31f142d3e --- /dev/null +++ b/libjava/classpath/gnu/javax/net/ssl/provider/Handshake.java @@ -0,0 +1,299 @@ +/* Handshake.java -- SSL Handshake message. + 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.PrintWriter; +import java.io.StringWriter; + +import java.nio.ByteBuffer; + +/** + * An SSL handshake message. SSL handshake messages have the following + * form: + * + *

    +struct
    +{
    +  HandshakeType msg_type;
    +  uint24        length;
    +  select (msg_type)
    +  {
    +    case hello_request:       HelloRequest;
    +    case client_hello:        ClientHello;
    +    case server_hello:        ServerHello;
    +    case certificate:         Certificate;
    +    case server_key_exchange: ServerKeyExchange;
    +    case certificate_request: CertificateRequest;
    +    case server_hello_done:   ServerHelloDone;
    +    case certificate_verify:  CertificateVerify;
    +    case client_key_exchange: ClientKeyExchange;
    +    case finished:            Finished;
    +  } body;
    +};
    + */ +public final class Handshake implements Constructed +{ + + // Fields. + // ------------------------------------------------------------------------- + + private final ByteBuffer buffer; + private final CipherSuite suite; + private final ProtocolVersion version; + + // Constructors. + // ------------------------------------------------------------------------- + + public Handshake (final ByteBuffer buffer) + { + this (buffer, null, ProtocolVersion.TLS_1_1); + } + + public Handshake (final ByteBuffer buffer, final CipherSuite suite, + final ProtocolVersion version) + { + this.buffer = buffer; + this.suite = suite; + this.version = version; + } + + // Instance methods. + // ------------------------------------------------------------------------- + + /** + * Returns the handshake type. + * + * @return The handshake type. + */ + public Type type() + { + return Type.forInteger (buffer.get (0) & 0xFF); + } + + /** + * Returns the message length. + * + * @return The message length. + */ + public int length () + { + // Length is a uint24. + return buffer.getInt (0) & 0xFFFFFF; + } + + /** + * Returns the handshake message body. Depending on the handshake + * type, some implementation of the Body interface is returned. + * + * @return The handshake body. + */ + public Body body() + { + Type type = type (); + ByteBuffer bodyBuffer = bodyBuffer (); + switch (type) + { + case HELLO_REQUEST: + return new HelloRequest (); + + case CLIENT_HELLO: + return new ClientHello (bodyBuffer); + + case SERVER_HELLO: + return new ServerHello (bodyBuffer); + + case CERTIFICATE: + return new Certificate (bodyBuffer, CertificateType.X509); + + case SERVER_KEY_EXCHANGE: + return new ServerKeyExchange (bodyBuffer, suite); + + case CERTIFICATE_REQUEST: + return new CertificateRequest (bodyBuffer); + + case SERVER_HELLO_DONE: + return new ServerHelloDone (); + + case CERTIFICATE_VERIFY: + return new CertificateVerify (bodyBuffer, suite.signatureAlgorithm ()); + + case CLIENT_KEY_EXCHANGE: + return new ClientKeyExchange (bodyBuffer, suite, version); + + case FINISHED: + return new Finished (bodyBuffer, version); + + case CERTIFICATE_URL: + case CERTIFICATE_STATUS: + throw new UnsupportedOperationException ("FIXME"); + } + throw new IllegalArgumentException ("unknown handshake type " + type); + } + + /** + * Returns a subsequence of the underlying buffer, containing only + * the bytes that compose the handshake body. + * + * @return The body's byte buffer. + */ + public ByteBuffer bodyBuffer () + { + int length = length (); + return ((ByteBuffer) buffer.position (4).limit (4 + length)).slice (); + } + + /** + * Sets the handshake body type. + * + * @param type The handshake type. + */ + public void setType (final Type type) + { + buffer.put (0, (byte) type.getValue ()); + } + + /** + * Sets the length of the handshake body. + * + * @param length The handshake body length. + * @throws java.nio.ReadOnlyBufferException If the underlying buffer + * is not writable. + * @throws IllegalArgumentException of length is not + * between 0 and 16777215, inclusive. + */ + public void setLength (final int length) + { + if (length < 0 || length > 0xFFFFFF) + throw new IllegalArgumentException ("length " + length + " out of range;" + + " must be between 0 and 16777215"); + buffer.put (1, (byte) (length >>> 16)); + buffer.put (2, (byte) (length >>> 8)); + buffer.put (3, (byte) length); + } + + public String toString() + { + return toString (null); + } + + public String toString (final String prefix) + { + StringWriter str = new StringWriter(); + PrintWriter out = new PrintWriter(str); + if (prefix != null) out.print (prefix); + out.println("struct {"); + if (prefix != null) out.print (prefix); + out.print (" type: "); + out.print (type ()); + out.println (";"); + Body body = body (); + out.println (body.toString (prefix != null ? (prefix + " ") : " ")); + if (prefix != null) out.print (prefix); + out.print ("} Handshake;"); + return str.toString(); + } + + // Inner class. + // ------------------------------------------------------------------------- + + public static interface Body extends Constructed + { + int length (); + + String toString (String prefix); + } + + public static enum Type + { + HELLO_REQUEST ( 0), + CLIENT_HELLO ( 1), + SERVER_HELLO ( 2), + CERTIFICATE (11), + SERVER_KEY_EXCHANGE (12), + CERTIFICATE_REQUEST (13), + SERVER_HELLO_DONE (14), + CERTIFICATE_VERIFY (15), + CLIENT_KEY_EXCHANGE (16), + FINISHED (20), + CERTIFICATE_URL (21), + CERTIFICATE_STATUS (22); + + private final int value; + + private Type(int value) + { + this.value = value; + } + + // Class methods. + // ----------------------------------------------------------------------- + + /** + * Convert a raw handshake type value to a type enum value. + * + * @return The corresponding enum value for the raw integer value. + * @throws IllegalArgumentException If the value is not a known handshake + * type. + */ + public static Type forInteger (final int value) + { + switch (value & 0xFF) + { + case 0: return HELLO_REQUEST; + case 1: return CLIENT_HELLO; + case 2: return SERVER_HELLO; + case 11: return CERTIFICATE; + case 12: return SERVER_KEY_EXCHANGE; + case 13: return CERTIFICATE_REQUEST; + case 14: return SERVER_HELLO_DONE; + case 15: return CERTIFICATE_VERIFY; + case 16: return CLIENT_KEY_EXCHANGE; + case 20: return FINISHED; + case 21: return CERTIFICATE_URL; + case 22: return CERTIFICATE_STATUS; + default: throw new IllegalArgumentException ("unsupported value type " + value); + } + } + + public int getValue() + { + return value; + } + } +} diff --git a/libjava/classpath/gnu/javax/net/ssl/provider/HelloRequest.java b/libjava/classpath/gnu/javax/net/ssl/provider/HelloRequest.java new file mode 100644 index 000000000..81dfce591 --- /dev/null +++ b/libjava/classpath/gnu/javax/net/ssl/provider/HelloRequest.java @@ -0,0 +1,72 @@ +/* HelloRequest.java -- SSL HelloRequest handshake message. + 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.java.lang.CPStringBuilder; + +/** + * The handshake body for a HelloRequest handshake message. + * + *
    struct { } HelloRequest;
    + */ +public final class HelloRequest implements Handshake.Body +{ + public HelloRequest () + { + } + + public String toString (final String prefix) + { + CPStringBuilder str = new CPStringBuilder (); + if (prefix != null) + str.append (prefix); + str.append ("HelloRequest { };"); + return str.toString (); + } + + public int length () + { + return 0; + } + + public String toString () + { + return toString (null); + } +} diff --git a/libjava/classpath/gnu/javax/net/ssl/provider/InputSecurityParameters.java b/libjava/classpath/gnu/javax/net/ssl/provider/InputSecurityParameters.java new file mode 100644 index 000000000..1d3da833a --- /dev/null +++ b/libjava/classpath/gnu/javax/net/ssl/provider/InputSecurityParameters.java @@ -0,0 +1,334 @@ +/* SecurityParameters.java -- SSL security parameters. + 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 gnu.java.security.util.ByteArray; +import gnu.java.security.util.ByteBufferOutputStream; + +import java.nio.BufferOverflowException; +import java.nio.ByteBuffer; + +import java.util.Arrays; +import java.util.zip.DataFormatException; +import java.util.zip.Inflater; + +import javax.crypto.Cipher; +import javax.crypto.IllegalBlockSizeException; +import javax.crypto.Mac; +import javax.crypto.ShortBufferException; + +import javax.net.ssl.SSLException; + +public class InputSecurityParameters +{ + private static final SystemLogger logger = SystemLogger.SYSTEM; + private final Cipher cipher; + private final Mac mac; + private final Inflater inflater; + private SessionImpl session; + private final CipherSuite suite; + private long sequence; + + public InputSecurityParameters (final Cipher cipher, final Mac mac, + final Inflater inflater, + final SessionImpl session, + final CipherSuite suite) + { + this.cipher = cipher; + this.mac = mac; + this.inflater = inflater; + this.session = session; + this.suite = suite; + sequence = 0; + } + + /** + * Decrypt a record, storing the decrypted fragment into the given array + * of byte buffers. + * + * @param record The input record. + * @param output The output buffers. + * @param offset The offset of the first buffer to use. + * @param length The number of buffers to use. + * @return The number of bytes put in the output buffers. + * @throws DataFormatException If decompression fails. + * @throws IllegalBlockSizeException If the current cipher is a block cipher, + * and the input fragment is not a multiple of the block size. + * @throws MacException If verifying the MAC fails. + * @throws SSLException ??? + * @throws ShortBufferException + */ + public int decrypt(Record record, ByteBuffer[] output, int offset, int length) + throws DataFormatException, IllegalBlockSizeException, + MacException, SSLException, ShortBufferException + { + return decrypt(record, output, offset, length, null); + } + + /** + * Decrypt a record, storing the decrypted fragment into the given growable + * buffer. + * + * @param record The input record. + * @param outputStream The output buffer. + * @return The number of bytes put into the output buffer. + * @throws DataFormatException + * @throws IllegalBlockSizeException + * @throws MacException + * @throws SSLException + * @throws ShortBufferException + */ + public int decrypt(Record record, ByteBufferOutputStream outputStream) + throws DataFormatException, IllegalBlockSizeException, + MacException, SSLException, ShortBufferException + { + return decrypt(record, null, 0, 0, outputStream); + } + + private int decrypt(Record record, ByteBuffer[] output, int offset, int length, + ByteBufferOutputStream outputStream) + throws DataFormatException, IllegalBlockSizeException, + MacException, SSLException, ShortBufferException + { + boolean badPadding = false; + ByteBuffer fragment; + if (cipher != null) + { + ByteBuffer input = record.fragment(); + fragment = ByteBuffer.allocate(input.remaining()); + cipher.update(input, fragment); + } + else + fragment = record.fragment(); + + if (Debug.DEBUG_DECRYPTION) + logger.logv(Component.SSL_RECORD_LAYER, "decrypted fragment:\n{0}", + Util.hexDump((ByteBuffer) fragment.duplicate().position(0), " >> ")); + + int fragmentLength = record.length(); + int maclen = 0; + if (mac != null) + maclen = mac.getMacLength(); + fragmentLength -= maclen; + + int padlen = 0; + int padRemoveLen = 0; + if (!suite.isStreamCipher ()) + { + padlen = fragment.get(record.length() - 1) & 0xFF; + padRemoveLen = padlen + 1; + if (Debug.DEBUG) + logger.logv(Component.SSL_RECORD_LAYER, "padlen:{0}", padlen); + + if (record.version() == ProtocolVersion.SSL_3) + { + // In SSLv3, the padding length must not be larger than + // the cipher's block size. + if (padlen > cipher.getBlockSize ()) + badPadding = true; + } + else if (record.version().compareTo(ProtocolVersion.TLS_1) >= 0) + { + // In TLSv1 and later, the padding must be `padlen' copies of the + // value `padlen'. + byte[] pad = new byte[padlen]; + ((ByteBuffer) fragment.duplicate().position(record.length() - padlen - 1)).get(pad); + for (int i = 0; i < pad.length; i++) + if ((pad[i] & 0xFF) != padlen) + badPadding = true; + if (Debug.DEBUG) + logger.logv(Component.SSL_RECORD_LAYER, "TLSv1.x padding\n{0}", + new ByteArray(pad)); + } + + if (Debug.DEBUG) + logger.logv(Component.SSL_RECORD_LAYER, "padding bad? {0}", + badPadding); + if (!badPadding) + fragmentLength = fragmentLength - padRemoveLen; + } + + int ivlen = 0; + if (session.version.compareTo(ProtocolVersion.TLS_1_1) >= 0 + && !suite.isStreamCipher()) + ivlen = cipher.getBlockSize(); + + // Compute and check the MAC. + if (mac != null) + { + mac.update((byte) (sequence >>> 56)); + mac.update((byte) (sequence >>> 48)); + mac.update((byte) (sequence >>> 40)); + mac.update((byte) (sequence >>> 32)); + mac.update((byte) (sequence >>> 24)); + mac.update((byte) (sequence >>> 16)); + mac.update((byte) (sequence >>> 8)); + mac.update((byte) sequence); + mac.update((byte) record.getContentType().getValue()); + ProtocolVersion version = record.version(); + if (version != ProtocolVersion.SSL_3) + { + mac.update((byte) version.major()); + mac.update((byte) version.minor()); + } + mac.update((byte) ((fragmentLength - ivlen) >>> 8)); + mac.update((byte) (fragmentLength - ivlen)); + ByteBuffer content = + (ByteBuffer) fragment.duplicate().position(ivlen).limit(fragmentLength); + mac.update(content); + byte[] mac1 = mac.doFinal (); + byte[] mac2 = new byte[maclen]; + mac.reset(); + ((ByteBuffer) fragment.duplicate().position(fragmentLength)).get(mac2); + if (Debug.DEBUG) + logger.logv(Component.SSL_RECORD_LAYER, "mac1:{0} mac2:{1}", + Util.toHexString(mac1, ':'), Util.toHexString(mac2, ':')); + if (!Arrays.equals (mac1, mac2)) + badPadding = true; + } + + // We always say "bad MAC" and not "bad padding," because saying + // the latter will leak information to an attacker. + if (badPadding) + throw new MacException (); + + // Inflate the compressed bytes. + int produced = 0; + if (inflater != null) + { + ByteBufferOutputStream out = new ByteBufferOutputStream(fragmentLength); + byte[] inbuffer = new byte[1024]; + byte[] outbuffer = new byte[1024]; + boolean done = false; + if (record.version().compareTo(ProtocolVersion.TLS_1_1) >= 0 + && !suite.isStreamCipher()) + fragment.position (cipher.getBlockSize()); + else + fragment.position(0); + fragment.limit(fragmentLength); + + while (!done) + { + int l; + if (inflater.needsInput()) + { + l = Math.min(inbuffer.length, fragment.remaining()); + fragment.get(inbuffer, 0, l); + inflater.setInput(inbuffer); + } + + l = inflater.inflate(outbuffer); + out.write(outbuffer, 0, l); + done = !fragment.hasRemaining() && inflater.finished(); + } + + ByteBuffer outbuf = out.buffer(); + if (outputStream != null) + { + byte[] buf = new byte[1024]; + while (outbuf.hasRemaining()) + { + int l = Math.min(outbuf.remaining(), buf.length); + outbuf.get(buf, 0, l); + outputStream.write(buf, 0, l); + produced += l; + } + } + else + { + int i = offset; + while (outbuf.hasRemaining() && i < offset + length) + { + int l = Math.min(output[i].remaining(), outbuf.remaining()); + ByteBuffer b = (ByteBuffer) + outbuf.duplicate().limit(outbuf.position() + l); + output[i++].put(b); + outbuf.position(outbuf.position() + l); + produced += l; + } + if (outbuf.hasRemaining()) + throw new BufferOverflowException(); + } + } + else + { + ByteBuffer outbuf = (ByteBuffer) + fragment.duplicate().position(0).limit(record.length() - maclen - padRemoveLen); + if (record.version().compareTo(ProtocolVersion.TLS_1_1) >= 0 + && !suite.isStreamCipher()) + outbuf.position(cipher.getBlockSize()); + if (outputStream != null) + { + byte[] buf = new byte[1024]; + while (outbuf.hasRemaining()) + { + int l = Math.min(outbuf.remaining(), buf.length); + outbuf.get(buf, 0, l); + outputStream.write(buf, 0, l); + produced += l; + } + } + else + { + int i = offset; + while (outbuf.hasRemaining() && i < offset + length) + { + int l = Math.min(output[i].remaining(), outbuf.remaining()); + ByteBuffer b = (ByteBuffer) outbuf.duplicate().limit(outbuf.position() + l); + output[i++].put(b); + outbuf.position(outbuf.position() + l); + produced += l; + } + if (outbuf.hasRemaining()) + throw new BufferOverflowException(); + } + } + + sequence++; + + return produced; + } + + CipherSuite cipherSuite () + { + return suite; + } +} diff --git a/libjava/classpath/gnu/javax/net/ssl/provider/Jessie.java b/libjava/classpath/gnu/javax/net/ssl/provider/Jessie.java new file mode 100644 index 000000000..d3fb3a658 --- /dev/null +++ b/libjava/classpath/gnu/javax/net/ssl/provider/Jessie.java @@ -0,0 +1,102 @@ +/* Jessie.java -- JESSIE's JSSE provider. + 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.security.AccessController; +import java.security.PrivilegedAction; +import java.security.Provider; + +/** + * This is the security provider for Jessie. It implements the following + * algorithms: + * + *
    + * {@link javax.net.ssl.SSLContext}.SSLv3
    + * {@link javax.net.ssl.SSLContext}.SSL
    + * {@link javax.net.ssl.SSLContext}.TLSv1
    + * {@link javax.net.ssl.SSLContext}.TLS
    + * {@link javax.net.ssl.KeyManagerFactory}.JessieX509
    + * {@link javax.net.ssl.TrustManagerFactory}.JessieX509
    + * {@link javax.net.ssl.TrustManagerFactory}.SRP
    + * 
    + * + */ +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; + + public Jessie() + { + super("Jessie", VERSION_DOUBLE, + "Implementing TLSv1.1, with SSLv3, TLSv1.0 compatibility modes; " + + "X.509 Key Manager Factory; " + + "X.509 Trust Manager Factory; " + + "SSLv3 MD5 and SHA Mac."); + + AccessController.doPrivileged(new PrivilegedAction() + { + public Object run() + { + put("SSLContext.TLSv1.1", SSLContextImpl.class.getName()); + put("Alg.Alias.SSLContext.SSLv3", "TLSv1.1"); + 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()); + put("KeyManagerFactory.JessiePSK", PreSharedKeyManagerFactoryImpl.class.getName()); + //put("TrustManagerFactory.SRP", SRPTrustManagerFactory.class.getName()); + + put("Mac.SSLv3HMac-MD5", SSLv3HMacMD5Impl.class.getName()); + put("Mac.SSLv3HMac-SHA", SSLv3HMacSHAImpl.class.getName()); + + put("Signature.TLSv1.1-RSA", SSLRSASignatureImpl.class.getName()); + put("Alg.Alias.Signature.TLSv1-RSA", "TLSv1.1-RSA"); + put("Alg.Alias.Signature.SSLv3-RSA", "TLSv1.1-RSA"); + + return null; + } + }); + } +} diff --git a/libjava/classpath/gnu/javax/net/ssl/provider/KeyExchangeAlgorithm.java b/libjava/classpath/gnu/javax/net/ssl/provider/KeyExchangeAlgorithm.java new file mode 100644 index 000000000..04416c5a5 --- /dev/null +++ b/libjava/classpath/gnu/javax/net/ssl/provider/KeyExchangeAlgorithm.java @@ -0,0 +1,57 @@ +/* KeyExchangeAlgorithm.java -- Key exchange algorithm enumeration. + 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; + +/** + * The enumeration of supported key exchange algorithms. + */ +public enum KeyExchangeAlgorithm +{ + NONE, + RSA, + DH_DSS, + DH_RSA, + DH_anon, + DHE_DSS, + DHE_RSA, +// SRP, + PSK, + DHE_PSK, + RSA_PSK; +} diff --git a/libjava/classpath/gnu/javax/net/ssl/provider/MacAlgorithm.java b/libjava/classpath/gnu/javax/net/ssl/provider/MacAlgorithm.java new file mode 100644 index 000000000..cae0efbfa --- /dev/null +++ b/libjava/classpath/gnu/javax/net/ssl/provider/MacAlgorithm.java @@ -0,0 +1,47 @@ +/* MacAlgorithm.java -- MAC algorithm enumeration. + 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; + +/** + * An enumeration of MAC algorithms we support. + */ +public enum MacAlgorithm +{ + NULL, MD5, SHA; +} diff --git a/libjava/classpath/gnu/javax/net/ssl/provider/MacException.java b/libjava/classpath/gnu/javax/net/ssl/provider/MacException.java new file mode 100644 index 000000000..b8c479fdb --- /dev/null +++ b/libjava/classpath/gnu/javax/net/ssl/provider/MacException.java @@ -0,0 +1,53 @@ +/* MacException.java -- signals a bad record MAC. + 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; + +class MacException extends IOException +{ + + // Constructor. + // ------------------------------------------------------------------------- + + MacException() + { + super(); + } +} diff --git a/libjava/classpath/gnu/javax/net/ssl/provider/MaxFragmentLength.java b/libjava/classpath/gnu/javax/net/ssl/provider/MaxFragmentLength.java new file mode 100644 index 000000000..acbfedff1 --- /dev/null +++ b/libjava/classpath/gnu/javax/net/ssl/provider/MaxFragmentLength.java @@ -0,0 +1,59 @@ +package gnu.javax.net.ssl.provider; + +import gnu.javax.net.ssl.provider.Extension.Value; + +import java.nio.ByteBuffer; + +/** + * Extension value + * @author csm + */ +public class MaxFragmentLength extends Value +{ + public static final MaxFragmentLength LEN_2_9 = new MaxFragmentLength(1, 1 << 9); + public static final MaxFragmentLength LEN_2_10 = new MaxFragmentLength(2, 1 << 10); + public static final MaxFragmentLength LEN_2_11 = new MaxFragmentLength(3, 1 << 11); + public static final MaxFragmentLength LEN_2_12 = new MaxFragmentLength(4, 1 << 12); + + private final int value; + private final int length; + + private MaxFragmentLength(int value, int length) + { + this.value = value; + this.length = length; + } + + public ByteBuffer buffer() + { + return ByteBuffer.allocate(1).put(0, (byte) value); + } + + public int length() + { + return 1; + } + + public int getValue() + { + return value; + } + + public int maxLength() + { + return length; + } + + public String toString() + { + return toString(null); + } + + public String toString(String prefix) + { + String s = "max_fragment_length = "; + if (prefix != null) + s = prefix + s; + return s + maxLength() + ";"; + } +} diff --git a/libjava/classpath/gnu/javax/net/ssl/provider/OutputSecurityParameters.java b/libjava/classpath/gnu/javax/net/ssl/provider/OutputSecurityParameters.java new file mode 100644 index 000000000..c6ed7d587 --- /dev/null +++ b/libjava/classpath/gnu/javax/net/ssl/provider/OutputSecurityParameters.java @@ -0,0 +1,294 @@ +/* OutputSecurityParameters.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; +import gnu.java.security.util.ByteBufferOutputStream; + +import java.nio.ByteBuffer; + +import java.util.zip.DataFormatException; +import java.util.zip.Deflater; + +import javax.crypto.Cipher; +import javax.crypto.IllegalBlockSizeException; +import javax.crypto.Mac; +import javax.crypto.ShortBufferException; + +public class OutputSecurityParameters +{ + private static final SystemLogger logger = SystemLogger.SYSTEM; + private final Cipher cipher; + private final Mac mac; + private final Deflater deflater; + private final SessionImpl session; + private final CipherSuite suite; + private long sequence; + + public OutputSecurityParameters (final Cipher cipher, final Mac mac, + final Deflater deflater, SessionImpl session, + CipherSuite suite) + { + this.cipher = cipher; + this.mac = mac; + this.deflater = deflater; + this.session = session; + this.suite = suite; + sequence = 0; + } + + /** + * Encrypt a record, storing the result in the given output buffer. + * + * @return The number of bytes taken from the input, and the number stored + * into `output;' that is, the size of the encrypted fragment, plus the + * encoding for the record. + */ + public int[] encrypt (final ByteBuffer[] input, int offset, int length, + final ContentType contentType, final ByteBuffer output) + throws DataFormatException, IllegalBlockSizeException, ShortBufferException + { + if (offset < 0 || offset >= input.length + || length <= 0 || offset + length > input.length) + throw new IndexOutOfBoundsException(); + + if (Debug.DEBUG) + for (int i = offset; i < offset+length; i++) + logger.logv(Component.SSL_RECORD_LAYER, "encrypting record [{0}]: {1}", + i-offset, input[i]); + + int maclen = 0; + if (mac != null) + maclen = session.isTruncatedMac() ? 10 : mac.getMacLength (); + + int ivlen = 0; + byte[] iv = null; + if (session.version.compareTo(ProtocolVersion.TLS_1_1) >= 0 + && !suite.isStreamCipher()) + { + ivlen = cipher.getBlockSize(); + iv = new byte[ivlen]; + session.random().nextBytes(iv); + } + + int padaddlen = 0; + if (!suite.isStreamCipher() + && session.version.compareTo(ProtocolVersion.TLS_1) >= 0) + { + padaddlen = (session.random().nextInt(255 / cipher.getBlockSize()) + * cipher.getBlockSize()); + } + + int fragmentLength = 0; + ByteBuffer[] fragments = null; + // Compress the content, if needed. + if (deflater != null) + { + ByteBufferOutputStream deflated = new ByteBufferOutputStream(); + + byte[] inbuf = new byte[1024]; + byte[] outbuf = new byte[1024]; + int written = 0; + + // Here we use the guarantee that the deflater won't increase the + // output size by more than 1K -- we resign ourselves to only deflate + // as much data as we have space for *uncompressed*, + int limit = output.remaining() - (maclen + ivlen + padaddlen) - 1024; + + for (int i = offset; i < length && written < limit; i++) + { + ByteBuffer in = input[i]; + while (in.hasRemaining() && written < limit) + { + int l = Math.min(in.remaining(), inbuf.length); + l = Math.min(limit - written, l); + in.get(inbuf, 0, l); + deflater.setInput(inbuf, 0, l); + l = deflater.deflate(outbuf); + deflated.write(outbuf, 0, l); + written += l; + } + } + deflater.finish(); + while (!deflater.finished()) + { + int l = deflater.deflate(outbuf); + deflated.write(outbuf, 0, l); + written += l; + } + fragments = new ByteBuffer[] { deflated.buffer() }; + fragmentLength = ((int) deflater.getBytesWritten()) + maclen + ivlen; + deflater.reset(); + offset = 0; + length = 1; + } + else + { + int limit = output.remaining() - (maclen + ivlen + padaddlen); + fragments = input; + for (int i = offset; i < length && fragmentLength < limit; i++) + { + int l = Math.min(limit - fragmentLength, fragments[i].remaining()); + fragmentLength += l; + } + fragmentLength += maclen + ivlen; + } + + // Compute padding... + int padlen = 0; + byte[] pad = null; + if (!suite.isStreamCipher()) + { + int bs = cipher.getBlockSize(); + padlen = bs - (fragmentLength % bs); + if (Debug.DEBUG) + logger.logv(Component.SSL_RECORD_LAYER, + "framentLen:{0} padlen:{1} blocksize:{2}", + fragmentLength, padlen, bs); + if (session.version.compareTo(ProtocolVersion.TLS_1) >= 0) + { + // TLS 1.0 and later uses a random amount of padding, up to + // 255 bytes. Each byte of the pad is equal to the padding + // length, minus one. + padlen += padaddlen; + while (padlen > 255) + padlen -= bs; + pad = new byte[padlen]; + for (int i = 0; i < padlen; i++) + pad[i] = (byte) (padlen - 1); + } + else + { + // SSL 3 uses a pad only as large as the block size, but the + // pad may contain any values. + pad = new byte[padlen]; + session.random().nextBytes(pad); + pad[padlen - 1] = (byte) (padlen - 1); + } + fragmentLength += pad.length; + } + + // If there is a MAC, compute it. + byte[] macValue = null; + if (mac != null) + { + mac.update((byte) (sequence >>> 56)); + mac.update((byte) (sequence >>> 48)); + mac.update((byte) (sequence >>> 40)); + mac.update((byte) (sequence >>> 32)); + mac.update((byte) (sequence >>> 24)); + mac.update((byte) (sequence >>> 16)); + mac.update((byte) (sequence >>> 8)); + mac.update((byte) sequence); + mac.update((byte) contentType.getValue()); + if (session.version != ProtocolVersion.SSL_3) + { + mac.update((byte) session.version.major ()); + mac.update((byte) session.version.minor ()); + } + int toWrite = fragmentLength - maclen - ivlen - padlen; + mac.update((byte) (toWrite >>> 8)); + mac.update((byte) toWrite); + int written = 0; + for (int i = offset; i < length && written < toWrite; i++) + { + ByteBuffer fragment = fragments[i].duplicate(); + int l = Math.min(fragment.remaining(), toWrite - written); + fragment.limit(fragment.position() + l); + mac.update(fragment); + } + macValue = mac.doFinal(); + } + + Record outrecord = new Record(output); + outrecord.setContentType(contentType); + outrecord.setVersion(session.version); + outrecord.setLength(fragmentLength); + + int consumed = 0; + ByteBuffer outfragment = outrecord.fragment(); + + if (cipher != null) + { + if (iv != null) + cipher.update(ByteBuffer.wrap(iv), outfragment); + int toWrite = fragmentLength - maclen - ivlen - padlen; + for (int i = offset; i < offset + length && consumed < toWrite; i++) + { + ByteBuffer fragment = fragments[i].slice(); + int l = Math.min(fragment.remaining(), toWrite - consumed); + fragment.limit(fragment.position() + l); + cipher.update(fragment, outfragment); + fragments[i].position(fragments[i].position() + l); + consumed += l; + } + if (macValue != null) + cipher.update(ByteBuffer.wrap(macValue), outfragment); + if (pad != null) + cipher.update(ByteBuffer.wrap(pad), outfragment); + } + else + { + // iv and pad are only used if we have a block cipher. + int toWrite = fragmentLength - maclen; + for (int i = offset; i < offset + length && consumed < toWrite; i++) + { + ByteBuffer fragment = fragments[i]; + int l = Math.min(fragment.remaining(), toWrite - consumed); + fragment.limit(fragment.position() + l); + outfragment.put(fragment); + consumed += l; + } + if (macValue != null) + outfragment.put(macValue); + } + + // Advance the output buffer's position. + output.position(output.position() + outrecord.length() + 5); + sequence++; + + return new int[] { consumed, fragmentLength + 5 }; + } + + CipherSuite suite() + { + return suite; + } +} diff --git a/libjava/classpath/gnu/javax/net/ssl/provider/PreSharedKeyManagerFactoryImpl.java b/libjava/classpath/gnu/javax/net/ssl/provider/PreSharedKeyManagerFactoryImpl.java new file mode 100644 index 000000000..16263fb37 --- /dev/null +++ b/libjava/classpath/gnu/javax/net/ssl/provider/PreSharedKeyManagerFactoryImpl.java @@ -0,0 +1,118 @@ +/* PreSharedKeyManagerFactory.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.javax.net.ssl.PreSharedKeyManager; +import gnu.javax.net.ssl.PreSharedKeyManagerParameters; + +import java.security.InvalidAlgorithmParameterException; +import java.security.KeyManagementException; +import java.security.KeyStore; +import java.security.KeyStoreException; +import java.security.NoSuchAlgorithmException; +import java.security.UnrecoverableKeyException; +import java.util.Iterator; + +import javax.crypto.SecretKey; +import javax.net.ssl.KeyManager; +import javax.net.ssl.KeyManagerFactorySpi; +import javax.net.ssl.ManagerFactoryParameters; + +/** + * @author Casey Marshall (csm@gnu.org) + */ +public class PreSharedKeyManagerFactoryImpl + extends KeyManagerFactorySpi +{ + PreSharedKeyManagerParameters params; + + /* (non-Javadoc) + * @see javax.net.ssl.KeyManagerFactorySpi#engineGetKeyManagers() + */ + @Override protected KeyManager[] engineGetKeyManagers() + { + if (params == null) + throw new IllegalStateException("not initialized"); + return new KeyManager[] { new Manager() }; + } + + /* (non-Javadoc) + * @see javax.net.ssl.KeyManagerFactorySpi#engineInit(javax.net.ssl.ManagerFactoryParameters) + */ + @Override protected void engineInit(ManagerFactoryParameters params) + throws InvalidAlgorithmParameterException + { + if (!(params instanceof PreSharedKeyManagerParameters)) + throw new InvalidAlgorithmParameterException("only supports gnu.javax.net.ssl.PreSharedKeyManagerParameters"); + params = (PreSharedKeyManagerParameters) params; + } + + /* (non-Javadoc) + * @see javax.net.ssl.KeyManagerFactorySpi#engineInit(java.security.KeyStore, char[]) + */ + @Override protected void engineInit(KeyStore store, char[] passwd) + throws KeyStoreException, NoSuchAlgorithmException, + UnrecoverableKeyException + { + // XXX Could implement this. + } + + class Manager implements PreSharedKeyManager + { + Manager() + { + } + + /* (non-Javadoc) + * @see gnu.javax.net.ssl.PreSharedKeyManager#getKey(java.lang.String) + */ + public SecretKey getKey(String name) throws KeyManagementException + { + return params.getKey(name); + } + + public String chooseIdentityHint() + { + Iterator it = params.identities(); + if (it.hasNext()) + return it.next(); + return null; + } + } +} diff --git a/libjava/classpath/gnu/javax/net/ssl/provider/ProtocolVersion.java b/libjava/classpath/gnu/javax/net/ssl/provider/ProtocolVersion.java new file mode 100644 index 000000000..3c3f29a21 --- /dev/null +++ b/libjava/classpath/gnu/javax/net/ssl/provider/ProtocolVersion.java @@ -0,0 +1,200 @@ +/* ProtocolVersion.java -- An SSL version number. + 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.InputStream; +import java.io.IOException; + +public final class ProtocolVersion + implements Comparable, Constructed +{ + + // Constants and fields. + // ------------------------------------------------------------------------- + + public static final ProtocolVersion SSL_3 = new ProtocolVersion(3, 0); + public static final ProtocolVersion TLS_1 = new ProtocolVersion(3, 1); + public static final ProtocolVersion TLS_1_1 = new ProtocolVersion(3, 2); + + private final int major; + private final int minor; + + // Constructor. + // ------------------------------------------------------------------------- + + private ProtocolVersion(int major, int minor) + { + this.major = major; + this.minor = minor; + } + + // Class methods. + // ------------------------------------------------------------------------- + + public static ProtocolVersion read(InputStream in) throws IOException + { + int major = in.read() & 0xFF; + int minor = in.read() & 0xFF; + return getInstance(major, minor); + } + + public static ProtocolVersion forName (final String name) + { + if (name.equalsIgnoreCase ("SSLv3")) + return SSL_3; + if (name.equalsIgnoreCase ("TLSv1")) + return TLS_1; + if (name.equalsIgnoreCase("TLSv1.1")) + return TLS_1_1; + throw new IllegalArgumentException ("unknown protocol name: " + name); + } + + public static ProtocolVersion getInstance(final int major, final int minor) + { + if (major == 3) + { + switch (minor) + { + case 0: return SSL_3; + case 1: return TLS_1; + case 2: return TLS_1_1; + } + } + return new ProtocolVersion(major, minor); + } + + public static ProtocolVersion getInstance (final short raw_value) + { + int major = raw_value >>> 8 & 0xFF; + int minor = raw_value & 0xFF; + return getInstance (major, minor); + } + + // Instance methods. + // ------------------------------------------------------------------------- + + public int length () + { + return 2; + } + + public byte[] getEncoded() + { + return new byte[] { + (byte) major, (byte) minor + }; + } + + public int major() + { + return major; + } + + public int minor() + { + return minor; + } + + public int rawValue () + { + return (major << 8) | minor; + } + + public boolean equals(Object o) + { + if (!(o instanceof ProtocolVersion)) + { + return false; + } + return ((ProtocolVersion) o).major == this.major + && ((ProtocolVersion) o).minor == this.minor; + } + + public int hashCode() + { + return major << 8 | minor; + } + + public int compareTo(ProtocolVersion that) + { + if (major > that.major) + { + return 1; + } + else if (major < that.major) + { + return -1; + } + + if (minor > that.minor) + { + return 1; + } + else if (minor < that.minor) + { + return -1; + } + return 0; + } + + public String toString (String prefix) + { + return toString (); + } + + public String toString() + { + if (this == SSL_3) + { + return "SSLv3"; + } + else if (this == TLS_1) + { + return "TLSv1"; + } + else if (this == TLS_1_1) + { + return "TLSv1.1"; + } + else + { + return "Unsupported; major=" + major + " minor=" + minor; + } + } +} diff --git a/libjava/classpath/gnu/javax/net/ssl/provider/Random.java b/libjava/classpath/gnu/javax/net/ssl/provider/Random.java new file mode 100644 index 000000000..bd5c037f5 --- /dev/null +++ b/libjava/classpath/gnu/javax/net/ssl/provider/Random.java @@ -0,0 +1,150 @@ +/* Random.java -- SSL Random structure. + 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.PrintWriter; +import java.io.StringWriter; + +import java.nio.ByteBuffer; +import java.nio.ByteOrder; + +/** + * An SSL nonce. + * + *
    +struct
    +{
    +  uint32 gmt_unix_time;
    +  opaque random_bytes[28];
    +} Random;
    + */
    +public class Random implements Builder, Constructed
    +{
    +
    +  // Fields.
    +  // -------------------------------------------------------------------------
    +
    +  static final int RANDOM_LENGTH = 28;
    +
    +  private final ByteBuffer buffer;
    +
    +  // Constructors.
    +  // -------------------------------------------------------------------------
    +
    +  public Random (final ByteBuffer buffer)
    +  {
    +    this.buffer = buffer.duplicate().order(ByteOrder.BIG_ENDIAN);
    +  }
    +
    +  public Random copy()
    +  {
    +    ByteBuffer buffer = ByteBuffer.allocate(32);
    +    buffer.put((ByteBuffer) this.buffer.duplicate().position(0));
    +    return new Random(buffer);
    +  }
    +
    +  public int length()
    +  {
    +    return RANDOM_LENGTH + 4;
    +  }
    +
    +  public ByteBuffer buffer()
    +  {
    +    return ((ByteBuffer) buffer.duplicate().position(0).limit(length())).slice();
    +  }
    +
    +  public int gmtUnixTime ()
    +  {
    +    return buffer.getInt(0);
    +  }
    +
    +  public byte[] randomBytes()
    +  {
    +    byte[] buf = new byte[28];
    +    buffer.position (4);
    +    buffer.get (buf);
    +    return buf;
    +  }
    +
    +  public void setGmtUnixTime (final int gmtUnixTime)
    +  {
    +    buffer.putInt (0, gmtUnixTime);
    +  }
    +
    +  public void setRandomBytes (final byte[] randomBytes)
    +  {
    +    setRandomBytes (randomBytes, 0);
    +  }
    +
    +  public void setRandomBytes (final byte[] randomBytes, final int offset)
    +  {
    +    if (randomBytes.length - offset < RANDOM_LENGTH)
    +      throw new IllegalArgumentException ("random value too short");
    +    buffer.position (4);
    +    buffer.put (randomBytes, offset, RANDOM_LENGTH);
    +  }
    +
    +  public String toString (final String prefix)
    +  {
    +    StringWriter str = new StringWriter();
    +    PrintWriter out = new PrintWriter(str);
    +    if (prefix != null)
    +      out.print (prefix);
    +    out.println("struct {");
    +    if (prefix != null)
    +      out.print (prefix);
    +    out.print ("  gmt_unix_time: ");
    +    out.print (gmtUnixTime ());
    +    out.println (";");
    +    if (prefix != null)
    +      out.print (prefix);
    +    out.print ("  random_bytes:  ");
    +    out.print (Util.toHexString (randomBytes (), ':'));
    +    out.println (";");
    +    if (prefix != null)
    +      out.print (prefix);
    +    out.print ("} Random;");
    +    return str.toString();
    +  }
    +
    +  public String toString ()
    +  {
    +    return toString (null);
    +  }
    +}
    diff --git a/libjava/classpath/gnu/javax/net/ssl/provider/Record.java b/libjava/classpath/gnu/javax/net/ssl/provider/Record.java
    new file mode 100644
    index 000000000..6f5a23ef4
    --- /dev/null
    +++ b/libjava/classpath/gnu/javax/net/ssl/provider/Record.java
    @@ -0,0 +1,198 @@
    +/* Record.java -- A single SSL Record.
    +   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.PrintWriter;
    +import java.io.StringWriter;
    +import java.nio.ByteBuffer;
    +import java.nio.ByteOrder;
    +
    +/**
    + * A SSL/TLS record structure. An SSL record is defined to be:
    + *
    + * 
    +struct
    +{
    +  {@link ContentType}     type;
    +  {@link ProtocolVersion} version;
    +  uint16          length;
    +  opaque          fragment[TLSPlaintext.length];
    +} TLSPlaintext;
    +
    + */ +public class Record +{ + private final ByteBuffer buffer; + + public Record (final ByteBuffer buffer) + { + this.buffer = buffer.duplicate().order(ByteOrder.BIG_ENDIAN); + } + + // XXX remove + public ContentType getContentType () + { + return contentType (); + } + + /** + * Gets the content type field. + * + * @return The content type field. + */ + public ContentType contentType () + { + return ContentType.forInteger (buffer.get (0) & 0xFF); + } + + /** + * Get the fragment content, storing it into sink. + * + * @param sink The sink for the fragment bytes. + * @return The number of bytes put into sink + */ + public int fragment (final ByteBuffer sink) + { + int length = length (); + sink.put (((ByteBuffer) buffer.limit (5 + length).position (5)).slice ()); + return length; + } + + /** + * Returns the fragment field as a ByteBuffer. The returned buffer + * is shared with this object's underlying buffer, so it will share + * its attributes. For example, if the underlying buffer is + * read-only, the returned buffer will be read-only. + * + * @return The fragment buffer. + */ + public ByteBuffer fragment () + { + int length = length (); + return ((ByteBuffer) buffer.limit (5 + length).position (5)).slice (); + } + + /** + * Gets the fragment length. + * + * @return The fragment length. + */ + public int length () + { + // XXX this is different behavior than we usually want: we return the + // length field, not the total length. We should consider changing this. + return buffer.getShort (3) & 0xFFFF; + } + + /** + * Gets the protocol version field. + * + * @return The protocol version field. + */ + public ProtocolVersion version () + { + int major = buffer.get (1) & 0xFF; + int minor = buffer.get (2) & 0xFF; + return ProtocolVersion.getInstance (major, minor); + } + + /** + * Sets the content type field. + * + * @param type The content type. + * @throws java.nio.ReadOnlyBufferException If the underlying buffer + * is not writeable. + * @throws NullPointerException If type is null. + */ + public void setContentType (final ContentType type) + { + buffer.put (0, (byte) type.getValue ()); + } + + /** + * Sets the fragment length. + * + * @param length The fragment length. + * @throws java.nio.ReadOnlyBufferException If the underlying buffer + * is not writeable. + * @throws IllegalArgumentException If the length is not between 0 + * and 16384, inclusive. + */ + public void setLength (final int length) + { + if (length < 0 || length > 16384) + throw new IllegalArgumentException ("length " + length + " out of range; " + + "must be between 0 and 16384"); + buffer.putShort (3, (short) length); + } + + /** + * Sets the protocol version field. + * + * @param version The protocol version. + * @throws java.nio.ReadOnlyBufferException If the underlying buffer + * is not writeable. + * @throws NullPointerException If version is null. + */ + public void setVersion (final ProtocolVersion version) + { + buffer.put (1, (byte) version.major ()).put (2, (byte) version.minor ()); + } + + public String toString () + { + StringWriter str = new StringWriter (); + PrintWriter out = new PrintWriter (str); + out.println ("struct {"); + out.print (" type: "); + out.print (contentType ()); + out.println (";"); + out.print (" version: "); + out.print (version ()); + out.println (";"); + out.print(" length: "); + out.print(length()); + out.println(";"); + out.println (" fragment {"); + out.print (Util.hexDump (fragment (), " ")); + out.println (" };"); + out.print ("} Record;"); + return str.toString (); + } +} diff --git a/libjava/classpath/gnu/javax/net/ssl/provider/SRPTrustManagerFactory.java b/libjava/classpath/gnu/javax/net/ssl/provider/SRPTrustManagerFactory.java new file mode 100644 index 000000000..c5422871d --- /dev/null +++ b/libjava/classpath/gnu/javax/net/ssl/provider/SRPTrustManagerFactory.java @@ -0,0 +1,223 @@ +/* SRPTrustManagerFactory.java -- trust manager for SRP. + 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.math.BigInteger; + +import java.security.InvalidAlgorithmParameterException; +import java.security.KeyPair; +import java.security.KeyStore; +import java.util.HashMap; + +import javax.net.ssl.ManagerFactoryParameters; +import javax.net.ssl.TrustManager; +import javax.net.ssl.TrustManagerFactorySpi; + +import gnu.java.security.key.IKeyPairGenerator; +import gnu.javax.crypto.key.srp6.SRPKeyPairGenerator; +import gnu.javax.crypto.sasl.srp.PasswordFile; +import gnu.javax.crypto.sasl.srp.SRP; + +import gnu.javax.net.ssl.SRPManagerParameters; +import gnu.javax.net.ssl.SRPTrustManager; + +/** + * This is an implementation of a {@link javax.net.ssl.TrustManagerFactory} + * engine for the ``SRP'' algorithm. You must initialize instances of this + * algorithm with {@link SRPManagerParameters}. + */ +public class SRPTrustManagerFactory extends TrustManagerFactorySpi +{ + + // Field. + // ------------------------------------------------------------------------- + + private Manager current; + + // Constructor. + // ------------------------------------------------------------------------- + + public SRPTrustManagerFactory() + { + super(); + } + + // Instance methods. + // ------------------------------------------------------------------------- + + protected TrustManager[] engineGetTrustManagers() + { + if (current == null) + throw new IllegalStateException("not initialized"); + return new TrustManager[] { current }; + } + + protected void engineInit(KeyStore ks) + { + throw new IllegalArgumentException("only accepts SRPManagerParameters"); + } + + protected void engineInit(ManagerFactoryParameters params) + throws InvalidAlgorithmParameterException + { + if (params == null) + { + try + { + String srpPasswd = Util.getSecurityProperty("jessie.srp.password.file"); + if (srpPasswd == null) + { + current = new Manager(new PasswordFile()); + return; + } + String srpPasswd2 = Util.getSecurityProperty("jessie.srp.password.file2"); + if (srpPasswd2 == null) + srpPasswd2 = srpPasswd + "2"; + String srpConfig = Util.getSecurityProperty("jessie.srp.config"); + if (srpConfig == null) + srpConfig = srpPasswd + ".conf"; + current = new Manager(new PasswordFile(srpPasswd, srpPasswd2, srpConfig)); + return; + } + catch (IOException ioe) + { + throw new InvalidAlgorithmParameterException("default initialization failed: " + + ioe.toString()); + } + } + if (params instanceof SRPManagerParameters) + { + current = new Manager(((SRPManagerParameters) params).getPasswordFile()); + return; + } + throw new InvalidAlgorithmParameterException(); + } + + // Inner class. + // ------------------------------------------------------------------------- + + private class Manager implements SRPTrustManager + { + + // Field. + // ----------------------------------------------------------------------- + + private final PasswordFile file; + + // Constructor. + // ----------------------------------------------------------------------- + + Manager(PasswordFile file) + { + this.file = file; + } + + // Instance methods. + // ----------------------------------------------------------------------- + + public boolean contains(String user) + { + try + { + return file.contains(user); + } + catch (IOException ioe) { } + return false; + } + + public KeyPair getKeyPair(String user) + { + try + { + if (file.contains(user)) + { + SRP srp = SRP.instance("SHA"); + String[] ent = file.lookup(user, "SHA"); + String[] cnf = file.lookupConfig(ent[2]); + BigInteger v, N, g; + v = new BigInteger(1, gnu.java.security.util.Util.fromBase64(ent[0])); + N = new BigInteger(1, gnu.java.security.util.Util.fromBase64(cnf[0])); + g = new BigInteger(1, gnu.java.security.util.Util.fromBase64(cnf[1])); + IKeyPairGenerator kpg = new SRPKeyPairGenerator(); + HashMap attr = new HashMap(); + attr.put(SRPKeyPairGenerator.SHARED_MODULUS, N); + attr.put(SRPKeyPairGenerator.GENERATOR, g); + attr.put(SRPKeyPairGenerator.USER_VERIFIER, v); + kpg.setup(attr); + return kpg.generate(); + } + } + catch (IOException ioe) { } + return null; + } + + public byte[] getSalt(String user) + { + try + { + if (file.contains(user)) + { + return gnu.java.security.util.Util.fromBase64(file.lookup(user, "SHA")[1]); + } + } + catch (IOException ioe) { } + return null; + } + + public BigInteger getVerifier(String user) + { + try + { + if (file.contains(user)) + { + return new BigInteger(1, + gnu.java.security.util.Util.fromBase64(file.lookup(user, "SHA")[0])); + } + } + catch (IOException ioe) { } + return null; + } + + public PasswordFile getPasswordFile() + { + return file; + } + } +} diff --git a/libjava/classpath/gnu/javax/net/ssl/provider/SSLContextImpl.java b/libjava/classpath/gnu/javax/net/ssl/provider/SSLContextImpl.java new file mode 100644 index 000000000..50bbdb61b --- /dev/null +++ b/libjava/classpath/gnu/javax/net/ssl/provider/SSLContextImpl.java @@ -0,0 +1,315 @@ +/* SSLContextImpl.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.java.security.action.GetSecurityPropertyAction; +import gnu.javax.net.ssl.AbstractSessionContext; +import gnu.javax.net.ssl.NullManagerParameters; +import gnu.javax.net.ssl.PreSharedKeyManager; +import gnu.javax.net.ssl.SRPTrustManager; + +import java.security.AccessController; +import java.security.KeyManagementException; +import java.security.KeyStore; +import java.security.KeyStoreException; +import java.security.NoSuchAlgorithmException; +import java.security.NoSuchProviderException; +import java.security.SecureRandom; +import java.security.UnrecoverableKeyException; + +import javax.net.ssl.KeyManager; +import javax.net.ssl.KeyManagerFactory; +import javax.net.ssl.SSLContextSpi; +import javax.net.ssl.SSLEngine; +import javax.net.ssl.SSLException; +import javax.net.ssl.SSLServerSocketFactory; +import javax.net.ssl.SSLSessionContext; +import javax.net.ssl.SSLSocketFactory; +import javax.net.ssl.TrustManager; +import javax.net.ssl.TrustManagerFactory; +import javax.net.ssl.X509ExtendedKeyManager; +import javax.net.ssl.X509TrustManager; + +/** + * Our implementation of {@link SSLContextSpi}. + * + * @author Casey Marshall (csm@gnu.org) + */ +public final class SSLContextImpl extends SSLContextSpi +{ + AbstractSessionContext serverContext; + AbstractSessionContext clientContext; + + PreSharedKeyManager pskManager; + X509ExtendedKeyManager keyManager; + X509TrustManager trustManager; + SRPTrustManager srpTrustManager; + SecureRandom random; + + public SSLContextImpl() + { + } + + /* (non-Javadoc) + * @see javax.net.ssl.SSLContextSpi#engineCreateSSLEngine() + */ + protected @Override SSLEngine engineCreateSSLEngine() + { + return engineCreateSSLEngine(null, -1); + } + + /* (non-Javadoc) + * @see javax.net.ssl.SSLContextSpi#engineCreateSSLEngine(java.lang.String, int) + */ + protected @Override SSLEngine engineCreateSSLEngine(String host, int port) + { + return new SSLEngineImpl(this, host, port); + } + + /* (non-Javadoc) + * @see javax.net.ssl.SSLContextSpi#engineGetClientSessionContext() + */ + protected @Override synchronized SSLSessionContext engineGetClientSessionContext() + { + if (clientContext == null) + { + try + { + clientContext = AbstractSessionContext.newInstance(); + } + catch (SSLException ssle) + { + // XXX Ignore? + } + } + return clientContext; + } + + /* (non-Javadoc) + * @see javax.net.ssl.SSLContextSpi#engineGetServerSessionContext() + */ + protected @Override synchronized SSLSessionContext engineGetServerSessionContext() + { + if (serverContext == null) + { + try + { + serverContext = AbstractSessionContext.newInstance(); + } + catch (SSLException ssle) + { + // XXX Ignore? + } + } + return serverContext; + } + + /* (non-Javadoc) + * @see javax.net.ssl.SSLContextSpi#engineGetServerSocketFactory() + */ + protected @Override SSLServerSocketFactory engineGetServerSocketFactory() + { + return new SSLServerSocketFactoryImpl(this); + } + + /* (non-Javadoc) + * @see javax.net.ssl.SSLContextSpi#engineGetSocketFactory() + */ + protected @Override SSLSocketFactory engineGetSocketFactory() + { + return new SSLSocketFactoryImpl(this); + } + + /* (non-Javadoc) + * @see javax.net.ssl.SSLContextSpi#engineInit(javax.net.ssl.KeyManager[], javax.net.ssl.TrustManager[], java.security.SecureRandom) + */ + protected @Override void engineInit(KeyManager[] keyManagers, + TrustManager[] trustManagers, + SecureRandom random) + throws KeyManagementException + { + keyManager = null; + trustManager = null; + srpTrustManager = null; + if (keyManagers != null) + { + for (int i = 0; i < keyManagers.length; i++) + { + if ((keyManagers[i] instanceof X509ExtendedKeyManager) + && keyManager == null) + keyManager = (X509ExtendedKeyManager) keyManagers[i]; + if (keyManagers[i] instanceof PreSharedKeyManager + && pskManager == null) + pskManager = (PreSharedKeyManager) keyManagers[i]; + } + } + if (keyManager == null) + keyManager = defaultKeyManager(); + if (trustManagers != null) + { + for (int i = 0; i < trustManagers.length; i++) + { + if (trustManagers[i] instanceof X509TrustManager) + { + if (trustManager == null) + trustManager = (X509TrustManager) trustManagers[i]; + } + else if (trustManagers[i] instanceof SRPTrustManager) + { + if (srpTrustManager == null) + srpTrustManager = (SRPTrustManager) trustManagers[i]; + } + } + } + if (trustManager == null && srpTrustManager == null) + { + trustManager = defaultTrustManager(); + } + if (random != null) + { + this.random = random; + } + else + { + this.random = defaultRandom(); + } + } + + /** + * Create and return a default key manager. The default is the JessieX509 + * algorithm, loaded from either the jssecerts file, or the cacerts file. + * + * @return The default key manager instance. + * @throws KeyManagementException If the instance cannot be created. + */ + private X509ExtendedKeyManager defaultKeyManager() throws KeyManagementException + { + KeyManagerFactory fact = null; + try + { + fact = KeyManagerFactory.getInstance("JessieX509", "Jessie"); + } + catch (NoSuchAlgorithmException nsae) + { + throw new KeyManagementException(nsae); + } + catch (NoSuchProviderException nspe) + { + throw new KeyManagementException(nspe); + } + try + { + fact.init(null, null); + return (X509ExtendedKeyManager) fact.getKeyManagers()[0]; + } + catch (NoSuchAlgorithmException nsae) { } + catch (KeyStoreException kse) { } + catch (UnrecoverableKeyException uke) { } + catch (IllegalStateException ise) { } + + try + { + fact.init(new NullManagerParameters()); + return (X509ExtendedKeyManager) fact.getKeyManagers()[0]; + } + catch (Exception shouldNotHappen) + { + throw new Error(shouldNotHappen.toString()); + } + } + + /** + * Create and return a default trust manager. The default is the JessieX509 + * algorithm, loaded from either the jssecerts file, or the cacerts file. + * + * @return The default trust manager instance. + * @throws KeyManagementException If the instance cannot be created. + */ + private X509TrustManager defaultTrustManager() throws KeyManagementException + { + try + { + TrustManagerFactory fact = + TrustManagerFactory.getInstance("JessieX509", "Jessie"); + fact.init((KeyStore) null); + return (X509TrustManager) fact.getTrustManagers()[0]; + } + catch (NoSuchAlgorithmException nsae) + { + throw new KeyManagementException(nsae); + } + catch (NoSuchProviderException nspe) + { + throw new KeyManagementException(nspe); + } + catch (KeyStoreException kse) + { + throw new KeyManagementException(kse); + } + } + + /** + * Create a default secure PRNG. This is defined as either the algorithm + * given in the gnu.javax.net.ssl.secureRandom security + * property, or Fortuna if that property is not set. If none of these + * algorithms can be found, and instance created with the SecureRandom + * constructor is returned. + * + * @return The default secure PRNG instance. + */ + private SecureRandom defaultRandom() + { + GetSecurityPropertyAction gspa + = new GetSecurityPropertyAction("gnu.javax.net.ssl.secureRandom"); + String alg = AccessController.doPrivileged(gspa); + if (alg == null) + alg = "Fortuna"; + SecureRandom rand = null; + try + { + rand = SecureRandom.getInstance(alg); + } + catch (NoSuchAlgorithmException nsae) + { + rand = new SecureRandom(); + } + + return rand; + } +} diff --git a/libjava/classpath/gnu/javax/net/ssl/provider/SSLEngineImpl.java b/libjava/classpath/gnu/javax/net/ssl/provider/SSLEngineImpl.java new file mode 100644 index 000000000..b63fb2f20 --- /dev/null +++ b/libjava/classpath/gnu/javax/net/ssl/provider/SSLEngineImpl.java @@ -0,0 +1,842 @@ +/* SSLEngineImpl.java -- implementation of SSLEngine. + 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 gnu.java.security.util.ByteBufferOutputStream; +import gnu.javax.net.ssl.Session; +import gnu.javax.net.ssl.SSLRecordHandler; + +import java.nio.BufferOverflowException; +import java.nio.ByteBuffer; +import java.nio.ByteOrder; + +import java.security.NoSuchAlgorithmException; +import java.util.ArrayList; +import java.util.List; +import java.util.zip.DataFormatException; + +import javax.crypto.IllegalBlockSizeException; +import javax.crypto.ShortBufferException; +import javax.net.ssl.SSLEngine; +import javax.net.ssl.SSLEngineResult; +import javax.net.ssl.SSLException; +import javax.net.ssl.SSLSession; +import javax.net.ssl.SSLEngineResult.HandshakeStatus; +import javax.net.ssl.SSLEngineResult.Status; + +public final class SSLEngineImpl extends SSLEngine +{ + final SSLContextImpl contextImpl; + private SSLRecordHandler[] handlers; + private static final SystemLogger logger = SystemLogger.SYSTEM; + private SessionImpl session; + private InputSecurityParameters insec; + private OutputSecurityParameters outsec; + private boolean inClosed; + private boolean outClosed; + private boolean createSessions; + private boolean needClientAuth; + private boolean wantClientAuth; + private boolean initialHandshakeDone; + private AbstractHandshake handshake; + private Alert lastAlert; + private SSLEngineResult.HandshakeStatus handshakeStatus; + private boolean changeCipherSpec; + + private String[] enabledSuites; + private String[] enabledProtocols; + + /** + * We can receive any message chunked across multiple records, + * including alerts, even though all alert messages are only two + * bytes long. Handshake messages are de-chunked in the handshake + * handler, change-cipher-spec messages are always empty, and we + * don't care about chunking of application messages. + * + * This buffer will hold the incomplete alert that we receive, if + * any. + */ + private final ByteBuffer alertBuffer; + + private Mode mode; + + private enum Mode { SERVER, CLIENT } + + SSLEngineImpl (SSLContextImpl contextImpl, String host, int port) + { + super(host, port); + this.contextImpl = contextImpl; + handlers = new SSLRecordHandler[256]; + session = new SessionImpl(); + session.suite = CipherSuite.TLS_NULL_WITH_NULL_NULL; + session.version = ProtocolVersion.TLS_1_1; + byte[] sid = new byte[32]; + contextImpl.random.nextBytes(sid); + session.setId(new Session.ID(sid)); + session.setRandom(contextImpl.random); + + if (Debug.DEBUG) + logger.logv(Component.SSL_RECORD_LAYER, "generated session ID {0} with random {1}", + session.id(), contextImpl.random); + + // Begin with no encryption. + insec = new InputSecurityParameters (null, null, null, session, + CipherSuite.TLS_NULL_WITH_NULL_NULL); + outsec = new OutputSecurityParameters (null, null, null, session, + CipherSuite.TLS_NULL_WITH_NULL_NULL); + inClosed = false; + outClosed = false; + needClientAuth = false; + wantClientAuth = false; + createSessions = true; + initialHandshakeDone = false; + alertBuffer = ByteBuffer.wrap (new byte[2]); + mode = null; + lastAlert = null; + handshakeStatus = SSLEngineResult.HandshakeStatus.NOT_HANDSHAKING; + changeCipherSpec = false; + + // Set up default protocols and suites. + enabledProtocols = new String[] { + ProtocolVersion.TLS_1_1.toString(), + ProtocolVersion.TLS_1.toString(), + ProtocolVersion.SSL_3.toString() + }; + 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(), + CipherSuite.TLS_DH_RSA_WITH_AES_256_CBC_SHA.toString(), + CipherSuite.TLS_RSA_WITH_AES_256_CBC_SHA.toString(), + CipherSuite.TLS_DHE_DSS_WITH_AES_128_CBC_SHA.toString(), + CipherSuite.TLS_DHE_RSA_WITH_AES_128_CBC_SHA.toString(), + CipherSuite.TLS_DH_DSS_WITH_AES_128_CBC_SHA.toString(), + CipherSuite.TLS_DH_RSA_WITH_AES_128_CBC_SHA.toString(), + CipherSuite.TLS_RSA_WITH_AES_128_CBC_SHA.toString(), + CipherSuite.TLS_DHE_DSS_WITH_3DES_EDE_CBC_SHA.toString(), + CipherSuite.TLS_DHE_RSA_WITH_3DES_EDE_CBC_SHA.toString(), + CipherSuite.TLS_DH_DSS_WITH_3DES_EDE_CBC_SHA.toString(), + CipherSuite.TLS_DH_RSA_WITH_3DES_EDE_CBC_SHA.toString(), + CipherSuite.TLS_RSA_WITH_3DES_EDE_CBC_SHA.toString(), + CipherSuite.TLS_RSA_WITH_RC4_128_MD5.toString(), + CipherSuite.TLS_RSA_WITH_RC4_128_SHA.toString(), + CipherSuite.TLS_DHE_DSS_WITH_DES_CBC_SHA.toString(), + CipherSuite.TLS_DHE_RSA_WITH_DES_CBC_SHA.toString(), + CipherSuite.TLS_DH_DSS_WITH_DES_CBC_SHA.toString(), + CipherSuite.TLS_DH_RSA_WITH_DES_CBC_SHA.toString(), + CipherSuite.TLS_RSA_WITH_DES_CBC_SHA.toString(), + CipherSuite.TLS_DH_DSS_EXPORT_WITH_DES40_CBC_SHA.toString(), + CipherSuite.TLS_DH_RSA_EXPORT_WITH_DES40_CBC_SHA.toString(), + CipherSuite.TLS_RSA_EXPORT_WITH_DES40_CBC_SHA.toString(), + CipherSuite.TLS_RSA_EXPORT_WITH_RC4_40_MD5.toString(), + CipherSuite.TLS_DHE_DSS_EXPORT_WITH_DES40_CBC_SHA.toString(), + CipherSuite.TLS_DHE_RSA_EXPORT_WITH_DES40_CBC_SHA.toString(), + CipherSuite.TLS_RSA_WITH_NULL_MD5.toString(), + CipherSuite.TLS_RSA_WITH_NULL_SHA.toString() + }; + } + + // XXX implement? + /*public void registerHandler (final int contentType, + SSLRecordHandler handler) + throws SSLException + { + if (type.equals (ContentType.CHANGE_CIPHER_SPEC) + || type.equals (ContentType.ALERT) + || type.equals (ContentType.HANDSHAKE) + || type.equals (ContentType.APPLICATION_DATA)) + throw new SSLException ("can't override handler for content type " + type); + int i = type.getValue (); + if (i < 0 || i > 255) + throw new SSLException ("illegal content type: " + type); + handlers[i] = handler; + }*/ + + @Override + public void beginHandshake () throws SSLException + { + if (Debug.DEBUG) + logger.log(Component.SSL_HANDSHAKE, "{0} handshake begins", mode); + + if (mode == null) + throw new IllegalStateException("setUseClientMode was never used"); + + switch (mode) + { + case SERVER: + if (getHandshakeStatus() != SSLEngineResult.HandshakeStatus.NOT_HANDSHAKING) + throw new SSLException("handshake already in progress"); + try + { + handshake = new ServerHandshake(initialHandshakeDone, this); + } + catch (NoSuchAlgorithmException nsae) + { + throw new SSLException(nsae); + } + break; + + case CLIENT: + try + { + handshake = new ClientHandshake(this); + } + catch (NoSuchAlgorithmException nsae) + { + throw new SSLException(nsae); + } + break; + } + } + + @Override + public void closeInbound() + { + inClosed = true; + } + + @Override + public void closeOutbound() + { + lastAlert = new Alert(Alert.Level.WARNING, Alert.Description.CLOSE_NOTIFY); + } + + @Override + public Runnable getDelegatedTask() + { + if (handshake == null) + return null; + return handshake.getTask(); + } + + @Override + public String[] getEnabledCipherSuites() + { + return (String[]) enabledSuites.clone(); + } + + @Override + public String[] getEnabledProtocols() + { + return (String[]) enabledProtocols.clone(); + } + + @Override + public boolean getEnableSessionCreation() + { + return createSessions; + } + + @Override + public HandshakeStatus getHandshakeStatus() + { + if (handshake == null) + return HandshakeStatus.NOT_HANDSHAKING; + return handshake.status(); + } + + @Override + public boolean getNeedClientAuth() + { + return needClientAuth; + } + + @Override + public SSLSession getSession() + { + return session; + } + + @Override + public boolean getUseClientMode () + { + return (mode == Mode.CLIENT); + } + + @Override + public boolean getWantClientAuth() + { + return wantClientAuth; + } + + @Override + public boolean isInboundDone() + { + return inClosed; + } + + @Override + public boolean isOutboundDone() + { + return outClosed; + } + + @Override + public void setEnableSessionCreation(final boolean createSessions) + { + this.createSessions = createSessions; + } + + @Override + public void setEnabledCipherSuites(final String[] suites) + { + if (suites.length == 0) + throw new IllegalArgumentException("need at least one suite"); + enabledSuites = (String[]) suites.clone(); + } + + @Override + public void setEnabledProtocols(final String[] protocols) + { + if (protocols.length == 0) + throw new IllegalArgumentException("need at least one protocol"); + enabledProtocols = (String[]) protocols.clone(); + } + + @Override + public String[] getSupportedCipherSuites() + { + // XXX if we ever want to support "pluggable" cipher suites, we'll need + // to figure this out. + + return CipherSuite.availableSuiteNames().toArray(new String[0]); + } + + @Override + public String[] getSupportedProtocols() + { + return new String[] { ProtocolVersion.SSL_3.toString(), + ProtocolVersion.TLS_1.toString(), + ProtocolVersion.TLS_1_1.toString() }; + } + + @Override + public void setNeedClientAuth(final boolean needClientAuth) + { + this.needClientAuth = needClientAuth; + } + + @Override + public void setUseClientMode (final boolean clientMode) + { + if (clientMode) + mode = Mode.CLIENT; + else + mode = Mode.SERVER; + } + + public @Override void setWantClientAuth(final boolean wantClientAuth) + { + this.wantClientAuth = wantClientAuth; + } + + public @Override SSLEngineResult unwrap (final ByteBuffer source, + final ByteBuffer[] sinks, + final int offset, final int length) + throws SSLException + { + if (mode == null) + throw new IllegalStateException ("setUseClientMode was never called"); + + if (inClosed) + return new SSLEngineResult(SSLEngineResult.Status.CLOSED, + handshakeStatus, 0, 0); + + if (source.remaining() < 5) + { + return new SSLEngineResult(SSLEngineResult.Status.BUFFER_UNDERFLOW, + handshakeStatus, 0, 0); + } + + Record record = null; + boolean helloV2 = false; + + // XXX: messages may be chunked across multiple records; does this + // include the SSLv2 message? I don't think it does, but we should + // make sure. + if (!getUseClientMode() && (source.get(source.position()) & 0x80) == 0x80) + { + if (handshake == null) + beginHandshake(); + int hellolen = source.getShort(source.position()) & 0x7FFF; + this.handshake.handleV2Hello(source.slice()); + if (!insec.cipherSuite().equals (CipherSuite.TLS_NULL_WITH_NULL_NULL)) + throw new SSLException ("received SSLv2 client hello in encrypted " + + "session; this is invalid."); + if (Debug.DEBUG) + logger.log (Component.SSL_RECORD_LAYER, + "converting SSLv2 client hello to version 3 hello"); + + source.getShort(); // skip length + ClientHelloV2 v2 = new ClientHelloV2(source.slice()); + + if (Debug.DEBUG) + logger.log(Component.SSL_RECORD_LAYER, "v2 hello: {0}", v2); + + List suites = v2.cipherSpecs(); + + ClientHelloBuilder hello = new ClientHelloBuilder(); + hello.setVersion(v2.version ()); + + Random random = hello.random(); + byte[] challenge = v2.challenge(); + if (challenge.length < 32) + { + byte[] b = new byte[32]; + System.arraycopy(challenge, 0, b, b.length - challenge.length, + challenge.length); + challenge = b; + } + random.setGmtUnixTime((challenge[0] & 0xFF) << 24 + | (challenge[1] & 0xFF) << 16 + | (challenge[2] & 0xFF) << 8 + | (challenge[3] & 0xFF)); + random.setRandomBytes(challenge, 4); + + byte[] sessionId = v2.sessionId(); + hello.setSessionId(sessionId, 0, sessionId.length); + hello.setCipherSuites(suites); + ArrayList comps = new ArrayList(1); + comps.add(CompressionMethod.NULL); + hello.setCompressionMethods(comps); + + record = new Record(ByteBuffer.allocate(hello.length() + 9)); + record.setContentType(ContentType.HANDSHAKE); + record.setVersion(v2.version()); + record.setLength(hello.length() + 4); + + Handshake handshake = new Handshake(record.fragment()); + handshake.setLength(hello.length()); + handshake.setType(Handshake.Type.CLIENT_HELLO); + + handshake.bodyBuffer().put(hello.buffer()); + source.position(source.position() + hellolen); + helloV2 = true; + } + else + record = new Record(source); + + ContentType type = record.contentType (); + + if (Debug.DEBUG) + logger.log(Component.SSL_RECORD_LAYER, "input record:\n{0}", record); + + if (record.length() > session.getPacketBufferSize() - 5) + { + lastAlert = new Alert(Alert.Level.FATAL, + Alert.Description.RECORD_OVERFLOW); + throw new AlertException(lastAlert); + } + + ByteBufferOutputStream sysMsg = null; + ByteBuffer msg = null; + + int produced = 0; + try + { + // Application data will get decrypted directly into the user's + // output buffers. + if (record.contentType() == ContentType.APPLICATION_DATA) + produced = insec.decrypt(record, sinks, offset, length); + else + { + if (insec.cipherSuite() == CipherSuite.TLS_NULL_WITH_NULL_NULL) + msg = record.fragment(); + else + { + sysMsg = new ByteBufferOutputStream(); + insec.decrypt(record, sysMsg); + } + } + + // Advance the input buffer past the record we just read. + if (!helloV2) + source.position(source.position() + record.length() + 5); + } + catch (BufferOverflowException boe) + { + // We throw this if the output buffers are not large enough; signal + // the caller about this. + logger.log(Component.SSL_RECORD_LAYER, "buffer overflow when decrypting", boe); + return new SSLEngineResult(SSLEngineResult.Status.BUFFER_OVERFLOW, + handshakeStatus, 0, 0); + } + catch (IllegalBlockSizeException ibse) + { + lastAlert = new Alert(Alert.Level.FATAL, + Alert.Description.BAD_RECORD_MAC); + throw new AlertException(lastAlert, ibse); + } + catch (DataFormatException dfe) + { + lastAlert = new Alert(Alert.Level.FATAL, + Alert.Description.DECOMPRESSION_FAILURE); + throw new AlertException(lastAlert, dfe); + } + catch (MacException me) + { + lastAlert = new Alert(Alert.Level.FATAL, + Alert.Description.BAD_RECORD_MAC); + throw new AlertException(lastAlert, me); + } + catch (ShortBufferException sbe) + { + // We've messed up if this happens. + lastAlert = new Alert(Alert.Level.FATAL, + Alert.Description.INTERNAL_ERROR); + throw new AlertException(lastAlert, sbe); + } + + SSLEngineResult result = null; + + // If we need to handle the output here, do it. Otherwise, the output + // has been stored in the supplied output buffers. + if (sysMsg != null) + { + if (Debug.DEBUG) + logger.logv(Component.SSL_RECORD_LAYER, "sysmessage {0}", sysMsg); + msg = sysMsg.buffer(); + } + + if (type == ContentType.CHANGE_CIPHER_SPEC) + { + // We *may* get a partial message, even though the message is only + // one byte long. + if (msg.remaining() == 0) + { + result = new SSLEngineResult (SSLEngineResult.Status.OK, + handshakeStatus, + record.length() + 5, 0); + } + else + { + byte b = msg.get(); + if (b != 1) + throw new SSLException ("unknown ChangeCipherSpec value: " + (b & 0xFF)); + InputSecurityParameters params = handshake.getInputParams(); + logger.log (Component.SSL_RECORD_LAYER, + "switching to input security parameters {0}", + params.cipherSuite()); + insec = params; + result = new SSLEngineResult (SSLEngineResult.Status.OK, + handshakeStatus, + record.length() + 5, 0); + } + } + else if (type == ContentType.ALERT) + { + int len = 0; + if (alertBuffer.position() > 0) + { + alertBuffer.put(msg.get()); + len = 1; + } + if (Debug.DEBUG) + logger.logv(Component.SSL_RECORD_LAYER, "processing alerts {0}", + Util.wrapBuffer(msg)); + len += msg.remaining() / 2; + Alert[] alerts = new Alert[len]; + int i = 0; + if (alertBuffer.position() > 0) + { + alertBuffer.flip(); + alerts[0] = new Alert(alertBuffer); + i++; + } + while (i < alerts.length) + { + alerts[i++] = new Alert(msg.duplicate()); + msg.position(msg.position() + 2); + } + if (Debug.DEBUG) + logger.logv(Component.SSL_RECORD_LAYER, "alerts: {0}", alerts.length); + + for (i = 0; i < alerts.length; i++) + { + if (alerts[i].level() == Alert.Level.FATAL) + throw new AlertException(alerts[i], false); + if (alerts[i].description() != Alert.Description.CLOSE_NOTIFY) + logger.log(java.util.logging.Level.WARNING, + "received alert: {0}", alerts[i]); + if (alerts[i].description() == Alert.Description.CLOSE_NOTIFY) + inClosed = true; + } + + if (msg.hasRemaining()) + alertBuffer.position(0).limit(2); + + result = new SSLEngineResult (SSLEngineResult.Status.OK, + handshakeStatus, + record.length() + 5, 0); + } + else if (type == ContentType.HANDSHAKE) + { + if (handshake == null) + beginHandshake(); + 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, + handshakeStatus, + record.length() + 5, + 0); + if (handshakeStatus == HandshakeStatus.FINISHED) + { + handshake = null; + handshakeStatus = HandshakeStatus.NOT_HANDSHAKING; + } + } + else if (type == ContentType.APPLICATION_DATA) + { + // Do nothing more; the application data has been put into + // the output buffers. + result = new SSLEngineResult(SSLEngineResult.Status.OK, + handshakeStatus, + record.length() + 5, + produced); + } + else + { + SSLRecordHandler handler = handlers[type.getValue()]; + if (handler != null) + { + result = new SSLEngineResult(SSLEngineResult.Status.OK, + handshakeStatus, + record.length() + 5, + 0); + } + else + throw new SSLException ("unknown content type: " + type); + } + + if (Debug.DEBUG) + logger.logv(Component.SSL_RECORD_LAYER, "return result: {0}", result); + + return result; + } + + public @Override SSLEngineResult wrap (ByteBuffer[] sources, int offset, int length, + ByteBuffer sink) + throws SSLException + { + if (mode == null) + throw new IllegalStateException ("setUseClientMode was never called"); + + if (outClosed) + return new SSLEngineResult(SSLEngineResult.Status.CLOSED, + handshakeStatus, 0, 0); + + 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; + sysMessage = ByteBuffer.allocate(2); + Alert alert = new Alert(sysMessage); + alert.setDescription(lastAlert.description()); + alert.setLevel(lastAlert.level()); + if (lastAlert.description() == Alert.Description.CLOSE_NOTIFY) + outClosed = true; + } + else if (changeCipherSpec) + { + type = ContentType.CHANGE_CIPHER_SPEC; + sysMessage = ByteBuffer.allocate(1); + sysMessage.put(0, (byte) 1); + } + else if (getHandshakeStatus() == SSLEngineResult.HandshakeStatus.NEED_WRAP) + { + // If we are not encrypting, optimize the handshake to fill + // the buffer directly. + if (outsec.suite() == CipherSuite.TLS_NULL_WITH_NULL_NULL) + { + int orig = sink.position(); + sink.order(ByteOrder.BIG_ENDIAN); + sink.put((byte) ContentType.HANDSHAKE.getValue()); + sink.putShort((short) session.version.rawValue()); + sink.putShort((short) 0); + handshakeStatus = handshake.handleOutput(sink); + int produced = sink.position() - orig; + sink.putShort(orig + 3, (short) (produced - 5)); + 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); + + // Note, this will only happen if we transition from + // TLS_NULL_WITH_NULL_NULL *to* TLS_NULL_WITH_NULL_NULL, which + // doesn't make a lot of sense, but we support it anyway. + if (handshakeStatus == HandshakeStatus.FINISHED) + { + handshake = null; // finished with it. + handshakeStatus = HandshakeStatus.NOT_HANDSHAKING; + } + return result; + } + + // Rough guideline; XXX. + sysMessage = ByteBuffer.allocate(sink.remaining() - 2048); + type = ContentType.HANDSHAKE; + 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}", + handshakeStatus); + } + + int produced = 0; + int consumed = 0; + + try + { + int orig = sink.position(); + int[] inout = null; + if (sysMessage != null) + { + if (Debug.DEBUG) + logger.logv(Component.SSL_RECORD_LAYER, "encrypt system message {0} to {1}", sysMessage, sink); + inout = outsec.encrypt(new ByteBuffer[] { sysMessage }, 0, 1, + type, sink); + produced = inout[1]; + } + else + { + inout = outsec.encrypt(sources, offset, length, + ContentType.APPLICATION_DATA, sink); + consumed = inout[0]; + produced = inout[1]; + } + + if (Debug.DEBUG) + logger.logv(Component.SSL_RECORD_LAYER, "emitting record:\n{0}", + new Record((ByteBuffer) sink.duplicate().position(orig).limit(produced))); + } + catch (ShortBufferException sbe) + { + // We don't expect this to happen, except for bugs; signal an + // internal error. + lastAlert = new Alert(Alert.Level.FATAL, Alert.Description.INTERNAL_ERROR); + return new SSLEngineResult(SSLEngineResult.Status.OK, handshakeStatus, 0, 0); + } + catch (IllegalBlockSizeException ibse) + { + // We don't expect this to happen, except for bugs; signal an + // internal error. + lastAlert = new Alert(Alert.Level.FATAL, Alert.Description.INTERNAL_ERROR); + return new SSLEngineResult(SSLEngineResult.Status.OK, handshakeStatus, 0, 0); + } + catch (DataFormatException dfe) + { + // We don't expect this to happen; signal an internal error. + lastAlert = new Alert(Alert.Level.FATAL, Alert.Description.INTERNAL_ERROR); + return new SSLEngineResult(SSLEngineResult.Status.OK, handshakeStatus, 0, 0); + } + + if (lastAlert != null && lastAlert.level() == Alert.Level.FATAL) + { + AlertException ae = new AlertException(lastAlert); + lastAlert = null; + throw ae; + } + + if (changeCipherSpec) + { + outsec = handshake.getOutputParams(); + changeCipherSpec = false; + } + SSLEngineResult result + = new SSLEngineResult(outClosed ? SSLEngineResult.Status.CLOSED + : SSLEngineResult.Status.OK, + handshakeStatus, consumed, produced); + if (handshakeStatus == HandshakeStatus.FINISHED) + { + handshake = null; // done with it. + handshakeStatus = HandshakeStatus.NOT_HANDSHAKING; + } + return result; + } + + // Package-private methods. + + SessionImpl session () + { + return session; + } + + void setSession(SessionImpl session) + { + this.session = session; + } + + void changeCipherSpec() + { + changeCipherSpec = true; + } +} diff --git a/libjava/classpath/gnu/javax/net/ssl/provider/SSLHMac.java b/libjava/classpath/gnu/javax/net/ssl/provider/SSLHMac.java new file mode 100644 index 000000000..002b3077f --- /dev/null +++ b/libjava/classpath/gnu/javax/net/ssl/provider/SSLHMac.java @@ -0,0 +1,158 @@ +/* SSLHMac.java -- SSLv3's MAC algorithm. + 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.util.Arrays; +import java.util.Map; + +import gnu.java.security.hash.HashFactory; +import gnu.java.security.hash.IMessageDigest; +import gnu.javax.crypto.mac.IMac; + +/** + * The MAC function in SSLv3. This mac is defined as: + * + *
    + * hash(MAC_write_secret, pad_2 +
    + *      hash(MAC_write_secret + pad_1 + data));
    + * + *

    hash is e.g. MD5 or SHA-1, pad_1 is the value + * 0x36 48 times for MD5 and 40 times for SHA-1, and pad_2 is + * the value 0x5c repeated similarly. + */ +class SSLHMac implements IMac, Cloneable +{ + + // Fields. + // ------------------------------------------------------------------------- + + static final byte PAD1 = 0x36; + static final byte PAD2 = 0x5c; + + protected IMessageDigest md; + protected byte[] key; + protected final byte[] pad1, pad2; + + // Constructors. + // ------------------------------------------------------------------------- + + SSLHMac(String mdName) + { + super(); + this.md = HashFactory.getInstance(mdName); + if (mdName.equalsIgnoreCase("MD5")) + { + pad1 = new byte[48]; + pad2 = new byte[48]; + } + else + { + pad1 = new byte[40]; + pad2 = new byte[40]; + } + Arrays.fill(pad1, PAD1); + Arrays.fill(pad2, PAD2); + } + + // Instance methods. + // ------------------------------------------------------------------------- + + public Object clone() + { + try + { + return super.clone(); + } + catch (CloneNotSupportedException cnse) + { + throw new Error(); + } + } + + public String name() + { + return "SSLHMac-" + md.name(); + } + + public int macSize() + { + return md.hashSize(); + } + + public void init(Map attributes) + { + key = (byte[]) attributes.get(MAC_KEY_MATERIAL); + if (key == null) + throw new NullPointerException(); + reset(); + } + + public void reset() + { + md.reset(); + md.update(key, 0, key.length); + md.update(pad1, 0, pad1.length); + } + + public byte[] digest() + { + byte[] h1 = md.digest(); + md.update(key, 0, key.length); + md.update(pad2, 0, pad2.length); + md.update(h1, 0, h1.length); + byte[] result = md.digest(); + reset(); + return result; + } + + public void update(byte b) + { + md.update(b); + } + + public void update(byte[] buf, int off, int len) + { + md.update(buf, off, len); + } + + public boolean selfTest() + { + return true; // XXX + } +} diff --git a/libjava/classpath/gnu/javax/net/ssl/provider/SSLRSASignatureImpl.java b/libjava/classpath/gnu/javax/net/ssl/provider/SSLRSASignatureImpl.java new file mode 100644 index 000000000..105b4d5d7 --- /dev/null +++ b/libjava/classpath/gnu/javax/net/ssl/provider/SSLRSASignatureImpl.java @@ -0,0 +1,234 @@ +/* SSLRSASignatureImpl.java -- SSL/TLS RSA implementation. + 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 gnu.java.security.sig.rsa.RSA; + +import java.math.BigInteger; +import java.security.InvalidKeyException; +import java.security.InvalidParameterException; +import java.security.MessageDigest; +import java.security.NoSuchAlgorithmException; +import java.security.PrivateKey; +import java.security.PublicKey; +import java.security.SignatureException; +import java.security.SignatureSpi; +import java.security.interfaces.RSAPrivateKey; +import java.security.interfaces.RSAPublicKey; +import java.util.Arrays; + +/** + * An implementation of of the RSA signature algorithm; this is an RSA + * encrypted MD5 hash followed by a SHA-1 hash. + * + * @author Casey Marshall (csm@gnu.org) + */ +public class SSLRSASignatureImpl extends SignatureSpi +{ + private static final SystemLogger logger = SystemLogger.SYSTEM; + private RSAPublicKey pubkey; + private RSAPrivateKey privkey; + private final MessageDigest md5, sha; + private boolean initSign = false; + private boolean initVerify = false; + + public SSLRSASignatureImpl() throws NoSuchAlgorithmException + { + md5 = MessageDigest.getInstance("MD5"); + sha = MessageDigest.getInstance("SHA-1"); + } + + /* (non-Javadoc) + * @see java.security.SignatureSpi#engineInitVerify(java.security.PublicKey) + */ + @Override protected void engineInitVerify(PublicKey publicKey) + throws InvalidKeyException + { + try + { + pubkey = (RSAPublicKey) publicKey; + initVerify = true; + initSign = false; + privkey = null; + } + catch (ClassCastException cce) + { + throw new InvalidKeyException(cce); + } + } + + /* (non-Javadoc) + * @see java.security.SignatureSpi#engineInitSign(java.security.PrivateKey) + */ + @Override protected void engineInitSign(PrivateKey privateKey) + throws InvalidKeyException + { + try + { + privkey = (RSAPrivateKey) privateKey; + initSign = true; + initVerify = false; + pubkey = null; + } + catch (ClassCastException cce) + { + throw new InvalidKeyException(cce); + } + } + + /* (non-Javadoc) + * @see java.security.SignatureSpi#engineUpdate(byte) + */ + @Override protected void engineUpdate(byte b) throws SignatureException + { + if (!initSign && !initVerify) + throw new IllegalStateException("not initialized"); + if (Debug.DEBUG) + logger.log(Component.SSL_HANDSHAKE, "SSL/RSA update 0x{0}", + Util.formatInt(b & 0xFF, 16, 2)); + md5.update(b); + sha.update(b); + } + + /* (non-Javadoc) + * @see java.security.SignatureSpi#engineUpdate(byte[], int, int) + */ + @Override protected void engineUpdate(byte[] b, int off, int len) + throws SignatureException + { + if (!initSign && !initVerify) + throw new IllegalStateException("not initialized"); + if (Debug.DEBUG) + logger.log(Component.SSL_HANDSHAKE, "SSL/RSA update\n{0}", + Util.hexDump(b, off, len, ">> ")); + md5.update(b, off, len); + sha.update(b, off, len); + } + + /* (non-Javadoc) + * @see java.security.SignatureSpi#engineSign() + */ + @Override protected byte[] engineSign() throws SignatureException + { + // FIXME we need to add RSA blinding to this, somehow. + + if (!initSign) + throw new SignatureException("not initialized for signing"); + // Pad the hash results with RSA block type 1. + final int k = (privkey.getModulus().bitLength() + 7) >>> 3; + final byte[] d = Util.concat(md5.digest(), sha.digest()); + if (k - 11 < d.length) + throw new SignatureException("message too long"); + final byte[] eb = new byte[k]; + eb[0] = 0x00; + eb[1] = 0x01; + for (int i = 2; i < k - d.length - 1; i++) + eb[i] = (byte) 0xFF; + System.arraycopy(d, 0, eb, k - d.length, d.length); + BigInteger EB = new BigInteger(eb); + + // Private-key encrypt the padded hashes. + BigInteger EM = RSA.sign(privkey, EB); + return Util.trim(EM); + } + + /* (non-Javadoc) + * @see java.security.SignatureSpi#engineVerify(byte[]) + */ + @Override protected boolean engineVerify(byte[] sigBytes) + throws SignatureException + { + if (!initVerify) + throw new SignatureException("not initialized for verifying"); + + // Public-key decrypt the signature representative. + BigInteger EM = new BigInteger(1, (byte[]) sigBytes); + BigInteger EB = RSA.verify(pubkey, EM); + + // Unpad the decrypted message. + int i = 0; + final byte[] eb = EB.toByteArray(); + if (eb[0] == 0x00) + { + for (i = 0; i < eb.length && eb[i] == 0x00; i++) + ; + } + else if (eb[0] == 0x01) + { + for (i = 1; i < eb.length && eb[i] != 0x00; i++) + { + if (eb[i] != (byte) 0xFF) + { + throw new SignatureException("bad padding"); + } + } + i++; + } + else + { + throw new SignatureException("decryption failed"); + } + byte[] d1 = Util.trim(eb, i, eb.length - i); + byte[] d2 = Util.concat(md5.digest(), sha.digest()); + if (Debug.DEBUG) + logger.logv(Component.SSL_HANDSHAKE, "SSL/RSA d1:{0} d2:{1}", + Util.toHexString(d1, ':'), Util.toHexString(d2, ':')); + return Arrays.equals(d1, d2); + } + + /* (non-Javadoc) + * @see java.security.SignatureSpi#engineSetParameter(java.lang.String, java.lang.Object) + */ + @Override protected void engineSetParameter(String param, Object value) + throws InvalidParameterException + { + throw new InvalidParameterException("parameters not supported"); + } + + /* (non-Javadoc) + * @see java.security.SignatureSpi#engineGetParameter(java.lang.String) + */ + @Override protected Object engineGetParameter(String param) + throws InvalidParameterException + { + throw new InvalidParameterException("parameters not supported"); + } +} diff --git a/libjava/classpath/gnu/javax/net/ssl/provider/SSLRandom.java b/libjava/classpath/gnu/javax/net/ssl/provider/SSLRandom.java new file mode 100644 index 000000000..0b28f1044 --- /dev/null +++ b/libjava/classpath/gnu/javax/net/ssl/provider/SSLRandom.java @@ -0,0 +1,165 @@ +/* SSLRandom.java -- SSLv3 pseudo-random function. + 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.util.Map; +import gnu.java.security.hash.HashFactory; +import gnu.java.security.hash.IMessageDigest; +import gnu.java.security.prng.IRandom; +import gnu.java.security.prng.LimitReachedException; + +class SSLRandom implements IRandom +{ + + // Fields. + // ------------------------------------------------------------------------- + + static final String SECRET = "jessie.sslprng.secret"; + static final String SEED = "jessie.sslprng.seed"; + + private final IMessageDigest md5, sha; + private byte[] secret; + private byte[] buffer; + private byte pad; + private byte[] seed; + private int idx; + + // Constructor. + // ------------------------------------------------------------------------- + + SSLRandom() + { + md5 = HashFactory.getInstance("MD5"); + sha = HashFactory.getInstance("SHA-1"); + } + + // Instance methods. + // ------------------------------------------------------------------------- + + public void init(Map attrib) + { + secret = (byte[]) attrib.get(SECRET); + seed = (byte[]) attrib.get(SEED); + + if (secret == null || seed == null) + throw new NullPointerException(); + + pad = (byte) 'A'; + try { buffer = nextBlock(); } + catch (LimitReachedException cantHappen) { } + } + + public String name() + { + return "SSLRandom"; + } + + public Object clone() + { + throw new UnsupportedOperationException(); + } + + public byte nextByte() throws LimitReachedException + { + if (buffer == null) + throw new IllegalStateException(); + if (idx >= buffer.length) + buffer = nextBlock(); + return buffer[idx++]; + } + + public void nextBytes(byte[] buf, int off, int len) + throws LimitReachedException + { + if (buffer == null) + throw new IllegalStateException(); + if (buf == null) + throw new NullPointerException(); + if (off < 0 || len < 0 || off+len > buf.length) + throw new IndexOutOfBoundsException(); + int count = 0; + while (count < len) + { + if (idx >= buffer.length) + buffer = nextBlock(); + int l = Math.min(buffer.length-idx, len-count); + System.arraycopy(buffer, idx, buf, off+count, l); + count += l; + idx += l; + } + } + + public boolean selfTest() + { + return true; // XXX + } + + // For future versions of GNU Crypto. No-ops. + public void addRandomByte (byte b) + { + } + + public void addRandomBytes(byte[] buffer) { + addRandomBytes(buffer, 0, buffer.length); + } + + public void addRandomBytes (byte[] b, int i, int j) + { + } + + // Own methods. + // ------------------------------------------------------------------------- + + private byte[] nextBlock() throws LimitReachedException + { + int count = pad - 'A' + 1; + if (count > 26) + throw new LimitReachedException(); + for (int i = 0; i < count; i++) + sha.update(pad); + sha.update(secret, 0, secret.length); + sha.update(seed, 0, seed.length); + byte[] b = sha.digest(); + md5.update(secret, 0, secret.length); + md5.update(b, 0, b.length); + idx = 0; + pad++; + return md5.digest(); + } +} diff --git a/libjava/classpath/gnu/javax/net/ssl/provider/SSLServerSocketFactoryImpl.java b/libjava/classpath/gnu/javax/net/ssl/provider/SSLServerSocketFactoryImpl.java new file mode 100644 index 000000000..67620d173 --- /dev/null +++ b/libjava/classpath/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/libjava/classpath/gnu/javax/net/ssl/provider/SSLServerSocketImpl.java b/libjava/classpath/gnu/javax/net/ssl/provider/SSLServerSocketImpl.java new file mode 100644 index 000000000..5b07017f0 --- /dev/null +++ b/libjava/classpath/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/libjava/classpath/gnu/javax/net/ssl/provider/SSLSocketFactoryImpl.java b/libjava/classpath/gnu/javax/net/ssl/provider/SSLSocketFactoryImpl.java new file mode 100644 index 000000000..d5dd54bce --- /dev/null +++ b/libjava/classpath/gnu/javax/net/ssl/provider/SSLSocketFactoryImpl.java @@ -0,0 +1,143 @@ +/* SSLSocketFactoryImpl.java -- + Copyright (C) 2006, 2007 Free Software Foundation, Inc. + +This file is a part of GNU Classpath. + +GNU Classpath is free software; you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation; either version 2 of the License, or (at +your option) any later version. + +GNU Classpath is distributed in the hope that it will be useful, but +WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +General Public License for more details. + +You should have received a copy of the GNU General Public License +along with GNU Classpath; if not, write to the Free Software +Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 +USA + +Linking this library statically or dynamically with other modules is +making a combined work based on this library. Thus, the terms and +conditions of the GNU General Public License cover the whole +combination. + +As a special exception, the copyright holders of this library give you +permission to link this library with independent modules to produce an +executable, regardless of the license terms of these independent +modules, and to copy and distribute the resulting executable under +terms of your choice, provided that you also meet, for each linked +independent module, the terms and conditions of the license of that +module. An independent module is a module which is not derived from +or based on this library. If you modify this library, you may extend +this exception to your version of the library, but you are not +obligated to do so. If you do not wish to do so, delete this +exception statement from your version. */ + + +package gnu.javax.net.ssl.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 + { + return createSocket(host, port, null, 0); + } + + /* (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 = new SSLSocketImpl(contextImpl, host, port); + InetSocketAddress endpoint = new InetSocketAddress(host, port); + socket.bind(new InetSocketAddress(localHost, localPort)); + socket.connect(endpoint); + return socket; + } + + /* (non-Javadoc) + * @see javax.net.SocketFactory#createSocket(java.net.InetAddress, int) + */ + @Override public SSLSocketImpl createSocket(InetAddress host, int port) + throws IOException + { + return createSocket(host, port, null, 0); + } + + /* (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 = new SSLSocketImpl(contextImpl, + host.getCanonicalHostName(), port); + socket.bind(new InetSocketAddress(localHost, localPort)); + socket.connect(new InetSocketAddress(host, port)); + return socket; + } + + /* (non-Javadoc) + * @see javax.net.SocketFactory#createSocket() + */ + @Override public Socket createSocket() throws IOException + { + return new SSLSocketImpl(contextImpl, null, -1, new Socket(), true); + } +} diff --git a/libjava/classpath/gnu/javax/net/ssl/provider/SSLSocketImpl.java b/libjava/classpath/gnu/javax/net/ssl/provider/SSLSocketImpl.java new file mode 100644 index 000000000..9072c2886 --- /dev/null +++ b/libjava/classpath/gnu/javax/net/ssl/provider/SSLSocketImpl.java @@ -0,0 +1,740 @@ +/* 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(); + if (handshakeException != null) + throw handshakeException; + } + + 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(); + buffer.clear(); + } + } + } + + @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 (handshakeException != null) + throw handshakeException; + } + + 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 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, new Socket(), true); + } + + 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(); + 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 = new DataInputStream(underlyingSocket.getInputStream()); + OutputStream sockOut = underlyingSocket.getOutputStream(); + + try + { + 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()); + } + catch (SSLException ssle) + { + handshakeException = ssle; + throw ssle; + } + finally + { + synchronized (engine) + { + isHandshaking = false; + engine.notifyAll(); + } + } + } + + // Methods overriding Socket. + + @Override public void bind(SocketAddress bindpoint) throws IOException + { + underlyingSocket.bind(bindpoint); + } + + @Override public void connect(SocketAddress endpoint) throws IOException + { + underlyingSocket.connect(endpoint); + } + + @Override public void connect(SocketAddress endpoint, int timeout) + throws IOException + { + underlyingSocket.connect(endpoint, timeout); + } + + @Override public InetAddress getInetAddress() + { + return underlyingSocket.getInetAddress(); + } + + @Override public InetAddress getLocalAddress() + { + return underlyingSocket.getLocalAddress(); + } + + @Override public int getPort() + { + return underlyingSocket.getPort(); + } + + @Override public int getLocalPort() + { + return underlyingSocket.getLocalPort(); + } + + @Override public SocketAddress getRemoteSocketAddress() + { + return underlyingSocket.getRemoteSocketAddress(); + } + + public SocketAddress getLocalSocketAddress() + { + return underlyingSocket.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 + { + underlyingSocket.setTcpNoDelay(on); + } + + @Override public boolean getTcpNoDelay() throws SocketException + { + return underlyingSocket.getTcpNoDelay(); + } + + @Override public void setSoLinger(boolean on, int linger) throws SocketException + { + underlyingSocket.setSoLinger(on, linger); + } + + public int getSoLinger() throws SocketException + { + return underlyingSocket.getSoLinger(); + } + + @Override public void sendUrgentData(int x) throws IOException + { + throw new UnsupportedOperationException("not supported"); + } + + @Override public void setOOBInline(boolean on) throws SocketException + { + underlyingSocket.setOOBInline(on); + } + + @Override public boolean getOOBInline() throws SocketException + { + return underlyingSocket.getOOBInline(); + } + + @Override public void setSoTimeout(int timeout) throws SocketException + { + underlyingSocket.setSoTimeout(timeout); + } + + @Override public int getSoTimeout() throws SocketException + { + return underlyingSocket.getSoTimeout(); + } + + @Override public void setSendBufferSize(int size) throws SocketException + { + underlyingSocket.setSendBufferSize(size); + } + + @Override public int getSendBufferSize() throws SocketException + { + return underlyingSocket.getSendBufferSize(); + } + + @Override public void setReceiveBufferSize(int size) throws SocketException + { + underlyingSocket.setReceiveBufferSize(size); + } + + @Override public int getReceiveBufferSize() throws SocketException + { + return underlyingSocket.getReceiveBufferSize(); + } + + @Override public void setKeepAlive(boolean on) throws SocketException + { + underlyingSocket.setKeepAlive(on); + } + + @Override public boolean getKeepAlive() throws SocketException + { + return underlyingSocket.getKeepAlive(); + } + + @Override public void setTrafficClass(int tc) throws SocketException + { + underlyingSocket.setTrafficClass(tc); + } + + @Override public int getTrafficClass() throws SocketException + { + return underlyingSocket.getTrafficClass(); + } + + @Override public void setReuseAddress(boolean reuseAddress) + throws SocketException + { + underlyingSocket.setReuseAddress(reuseAddress); + } + + @Override public boolean getReuseAddress() throws SocketException + { + return underlyingSocket.getReuseAddress(); + } + + @Override public void close() throws IOException + { + // XXX closure alerts. + if (autoClose) + underlyingSocket.close(); + } + + @Override public void shutdownInput() throws IOException + { + underlyingSocket.shutdownInput(); + } + + @Override public void shutdownOutput() throws IOException + { + underlyingSocket.shutdownOutput(); + } + + @Override public boolean isConnected() + { + return underlyingSocket.isConnected(); + } + + @Override public boolean isBound() + { + return underlyingSocket.isBound(); + } + + @Override public boolean isClosed() + { + return underlyingSocket.isClosed(); + } + + @Override public boolean isInputShutdown() + { + return underlyingSocket.isInputShutdown(); + } + + @Override public boolean isOutputShutdown() + { + return underlyingSocket.isOutputShutdown(); + } +} diff --git a/libjava/classpath/gnu/javax/net/ssl/provider/SSLv3HMacMD5Impl.java b/libjava/classpath/gnu/javax/net/ssl/provider/SSLv3HMacMD5Impl.java new file mode 100644 index 000000000..5ef84ca1c --- /dev/null +++ b/libjava/classpath/gnu/javax/net/ssl/provider/SSLv3HMacMD5Impl.java @@ -0,0 +1,116 @@ +/* SSLv3HMacMD5.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.security.InvalidAlgorithmParameterException; +import java.security.InvalidKeyException; +import java.security.Key; +import java.security.spec.AlgorithmParameterSpec; +import java.util.Collections; +import java.util.Map; + +import javax.crypto.MacSpi; +import javax.crypto.SecretKey; + +/** + * @author csm + */ +public class SSLv3HMacMD5Impl extends MacSpi +{ + private final SSLHMac adaptee; + + public SSLv3HMacMD5Impl() + { + adaptee = new SSLHMac("MD5"); + } + + /* (non-Javadoc) + * @see javax.crypto.MacSpi#engineDoFinal() + */ + @Override protected byte[] engineDoFinal() + { + return adaptee.digest(); + } + + /* (non-Javadoc) + * @see javax.crypto.MacSpi#engineGetMacLength() + */ + @Override protected int engineGetMacLength() + { + return adaptee.macSize(); + } + + /* (non-Javadoc) + * @see javax.crypto.MacSpi#engineInit(java.security.Key, java.security.spec.AlgorithmParameterSpec) + */ + @Override protected void engineInit(Key key, AlgorithmParameterSpec params) + throws InvalidAlgorithmParameterException, InvalidKeyException + { + if (!(key instanceof SecretKey) + || !key.getAlgorithm().equalsIgnoreCase("SSLv3HMac-MD5")) + throw new InvalidKeyException("expecting secret key with algorithm \"SSLv3HMac-MD5\""); + Map attr = + Collections.singletonMap(SSLHMac.MAC_KEY_MATERIAL, key.getEncoded()); + adaptee.init(attr); + } + + /* (non-Javadoc) + * @see javax.crypto.MacSpi#engineReset() + */ + @Override protected void engineReset() + { + adaptee.reset(); + } + + /* (non-Javadoc) + * @see javax.crypto.MacSpi#engineUpdate(byte) + */ + @Override protected void engineUpdate(byte input) + { + adaptee.update(input); + } + + /* (non-Javadoc) + * @see javax.crypto.MacSpi#engineUpdate(byte[], int, int) + */ + @Override protected void engineUpdate(byte[] input, int offset, int length) + { + adaptee.update(input, offset, length); + } +} diff --git a/libjava/classpath/gnu/javax/net/ssl/provider/SSLv3HMacSHAImpl.java b/libjava/classpath/gnu/javax/net/ssl/provider/SSLv3HMacSHAImpl.java new file mode 100644 index 000000000..6b9c9e9cc --- /dev/null +++ b/libjava/classpath/gnu/javax/net/ssl/provider/SSLv3HMacSHAImpl.java @@ -0,0 +1,116 @@ +/* SSLv3HMacSHA.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.security.InvalidAlgorithmParameterException; +import java.security.InvalidKeyException; +import java.security.Key; +import java.security.spec.AlgorithmParameterSpec; +import java.util.Collections; +import java.util.Map; + +import javax.crypto.MacSpi; +import javax.crypto.SecretKey; + +/** + * @author csm + */ +public class SSLv3HMacSHAImpl extends MacSpi +{ + private final SSLHMac adaptee; + + public SSLv3HMacSHAImpl() + { + adaptee = new SSLHMac("SHA-160"); + } + + /* (non-Javadoc) + * @see javax.crypto.MacSpi#engineDoFinal() + */ + @Override protected byte[] engineDoFinal() + { + return adaptee.digest(); + } + + /* (non-Javadoc) + * @see javax.crypto.MacSpi#engineGetMacLength() + */ + @Override protected int engineGetMacLength() + { + return adaptee.macSize(); + } + + /* (non-Javadoc) + * @see javax.crypto.MacSpi#engineInit(java.security.Key, java.security.spec.AlgorithmParameterSpec) + */ + @Override protected void engineInit(Key key, AlgorithmParameterSpec params) + throws InvalidAlgorithmParameterException, InvalidKeyException + { + if (!(key instanceof SecretKey) + || !key.getAlgorithm().equalsIgnoreCase("SSLv3HMac-SHA")) + throw new InvalidKeyException("expecting secret key with algorithm \"SSLv3HMac-SHA\""); + Map attr = + Collections.singletonMap(SSLHMac.MAC_KEY_MATERIAL, key.getEncoded()); + adaptee.init(attr); + } + + /* (non-Javadoc) + * @see javax.crypto.MacSpi#engineReset() + */ + @Override protected void engineReset() + { + adaptee.reset(); + } + + /* (non-Javadoc) + * @see javax.crypto.MacSpi#engineUpdate(byte) + */ + @Override protected void engineUpdate(byte input) + { + adaptee.update(input); + } + + /* (non-Javadoc) + * @see javax.crypto.MacSpi#engineUpdate(byte[], int, int) + */ + @Override protected void engineUpdate(byte[] input, int offset, int length) + { + adaptee.update(input, offset, length); + } +} diff --git a/libjava/classpath/gnu/javax/net/ssl/provider/ServerDHE_PSKParameters.java b/libjava/classpath/gnu/javax/net/ssl/provider/ServerDHE_PSKParameters.java new file mode 100644 index 000000000..1de3f8124 --- /dev/null +++ b/libjava/classpath/gnu/javax/net/ssl/provider/ServerDHE_PSKParameters.java @@ -0,0 +1,148 @@ +/* ServerDHE_PSKParameters.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.PrintWriter; +import java.io.StringWriter; +import java.nio.ByteBuffer; +import java.nio.ByteOrder; +import java.nio.charset.Charset; + +/** + *

    +      struct {
    +          select (KeyExchangeAlgorithm) {
    +              /* other cases for rsa, diffie_hellman, etc. */
    +              case diffie_hellman_psk:  /* NEW */
    +                  opaque psk_identity_hint<0..2^16-1>;
    +                  ServerDHParams params;
    +          };
    +      } ServerKeyExchange;
    + * + * @author Casey Marshall (csm@gnu.org) + */ +public class ServerDHE_PSKParameters implements Constructed, Builder, ServerKeyExchangeParams +{ + private ByteBuffer buffer; + + public ServerDHE_PSKParameters(ByteBuffer buffer) + { + this.buffer = buffer.duplicate().order(ByteOrder.BIG_ENDIAN); + } + + public ServerDHE_PSKParameters(String identityHint, ServerDHParams dhParams) + { + this(identityHint, dhParams.buffer()); + } + + public ServerDHE_PSKParameters(String identityHint, ByteBuffer dhParams) + { + Charset utf8 = Charset.forName("UTF-8"); + ByteBuffer hintBuf = utf8.encode(identityHint); + buffer = ByteBuffer.allocate(2 + hintBuf.remaining() + dhParams.remaining()); + buffer.putShort((short) hintBuf.remaining()); + buffer.put(hintBuf); + buffer.put(dhParams); + } + + public KeyExchangeAlgorithm algorithm() + { + return KeyExchangeAlgorithm.DHE_PSK; + } + + /* (non-Javadoc) + * @see gnu.javax.net.ssl.provider.Constructed#length() + */ + public int length() + { + return (buffer.getShort(0) & 0xFFFF) + 2 + params().length(); + } + + private int hintLength() + { + return (buffer.getShort(0) & 0xFFFF) + 2; + } + + public String identityHint() + { + Charset utf8 = Charset.forName("UTF-8"); + return utf8.decode((ByteBuffer) buffer.duplicate().position(2).limit + (hintLength())).toString(); + } + + public ServerDHParams params() + { + return new ServerDHParams(((ByteBuffer) buffer.duplicate().position + (hintLength()).limit(buffer.capacity())).slice()); + } + + /* (non-Javadoc) + * @see gnu.javax.net.ssl.provider.Builder#buffer() + */ + public ByteBuffer buffer() + { + return (ByteBuffer) buffer.duplicate().rewind().limit(length()); + } + + public @Override String toString() + { + return toString(null); + } + + /* (non-Javadoc) + * @see gnu.javax.net.ssl.provider.Constructed#toString(java.lang.String) + */ + public String toString(String prefix) + { + StringWriter str = new StringWriter(); + PrintWriter out = new PrintWriter(str); + if (prefix != null) out.print(prefix); + out.println("struct {"); + if (prefix != null) out.print(prefix); + out.print(" identity_hint = "); + out.print(identityHint()); + out.println(";"); + if (prefix != null) out.print(prefix); + out.println(" params ="); + out.println(params().toString(prefix != null ? prefix + " " : " ")); + if (prefix != null) out.print(prefix); + out.print("} ServerDHE_PSKParameters;"); + return str.toString(); + } +} diff --git a/libjava/classpath/gnu/javax/net/ssl/provider/ServerDHParams.java b/libjava/classpath/gnu/javax/net/ssl/provider/ServerDHParams.java new file mode 100644 index 000000000..0e2c34881 --- /dev/null +++ b/libjava/classpath/gnu/javax/net/ssl/provider/ServerDHParams.java @@ -0,0 +1,248 @@ +/* ServerDHParams.java -- The server's Diffie-Hellman parameters. + 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.PrintWriter; +import java.io.StringWriter; +import java.math.BigInteger; +import java.nio.ByteBuffer; +import java.nio.ByteOrder; + +/** + * The server's Diffie-Hellman parameters message. + * + *
    +struct
    +{
    +  opaque dh_p<1..2^16-1>;
    +  opaque dh_g<1..2^16-1>;
    +  opaque dh_Ys<1..2^16-1>;
    +} ServerDHParams;
    +
    + */ +public class ServerDHParams implements Builder, ServerKeyExchangeParams +{ + private final ByteBuffer buffer; + + public ServerDHParams (final ByteBuffer buffer) + { + this.buffer = buffer.duplicate().order(ByteOrder.BIG_ENDIAN); + } + + public ServerDHParams (final BigInteger p, final BigInteger g, + final BigInteger y) + { + byte[] p_bytes = p.toByteArray(); + byte[] g_bytes = g.toByteArray(); + byte[] y_bytes = y.toByteArray(); + int len = p_bytes.length + g_bytes.length + y_bytes.length + 6; + + int p_off = 0; + if (p_bytes[0] == 0x00) + { + p_off = 1; + len--; + } + int g_off = 0; + if (g_bytes[0] == 0x00) + { + g_off = 1; + len--; + } + int y_off = 0; + if (y_bytes[0] == 0x00) + { + y_off = 1; + len--; + } + int p_len = p_bytes.length - p_off; + int g_len = g_bytes.length - g_off; + int y_len = y_bytes.length - y_off; + + buffer = ByteBuffer.allocate(len); + buffer.putShort((short) p_len); + buffer.put(p_bytes, p_off, p_len); + buffer.putShort((short) g_len); + buffer.put(g_bytes, g_off, g_len); + buffer.putShort((short) y_len); + buffer.put(y_bytes, y_off, y_len); + } + + @Deprecated public KeyExchangeAlgorithm algorithm () + { + return null; // XXX can't support this. + } + + public int length () + { + int offset1 = buffer.getShort (0) & 0xFFFF; + int offset2 = buffer.getShort (offset1 + 2) & 0xFFFF; + return ((buffer.getShort (offset1 + offset2 + 4) & 0xFFFF) + + offset1 + offset2 + 6); + } + + public ByteBuffer buffer() + { + return (ByteBuffer) buffer.duplicate().position(0).limit(length()); + } + + /** + * Returns the server's prime modulus. + * + * @return p. + */ + public BigInteger p () + { + int len = buffer.getShort (0) & 0xFFFF; + byte[] buf = new byte[len]; + buffer.position (2); + buffer.get (buf); + return new BigInteger (1, buf); + } + + /** + * Returns the server's generator value. + * + * @return g. + */ + public BigInteger g () + { + int off = (buffer.getShort (0) & 0xFFFF) + 2; + int len = buffer.getShort (off) & 0xFFFF; + byte[] buf = new byte[len]; + buffer.position (off + 2); + buffer.get (buf); + return new BigInteger (1, buf); + } + + /** + * Returns the server's public value. + * + * @return Y. + */ + public BigInteger y () + { + int offset1 = (buffer.getShort (0) & 0xFFFF) + 2; + int offset2 = (buffer.getShort (offset1) & 0xFFFF) + offset1 + 2; + int len = buffer.getShort (offset2) & 0xFFFF; + byte[] buf = new byte[len]; + buffer.position (offset2 + 2); + buffer.get (buf); + return new BigInteger (1, buf); + } + + /** + * Sets the server's prime modulus, p. + * + * @param p The p parameter. + * @throws java.nio.ReadOnlyBufferException If the underlying buffer + * is not writeable. + */ + public void setP (final BigInteger p) + { + byte[] buf = p.toByteArray (); + int length = (buf[0] == 0x00 ? buf.length - 1 : buf.length); + int offset = (buf[0] == 0x00 ? 1 : 0); + buffer.putShort (0, (short) length); + buffer.position (2); + buffer.put (buf, offset, length); + } + + /** + * Sets the server's generator value, g. + * + * @param g The g parameter. + * @throws java.nio.ReadOnlyBufferException If the underlying buffer + * is not writeable. + */ + public void setG (final BigInteger g) + { + byte[] buf = g.toByteArray (); + int length = (buf[0] == 0x00 ? buf.length -1 : buf.length); + int offset = (buf[0] == 0x00 ? 1 : 0); + int where = (buffer.getShort (0) & 0xFFFF) + 2; + buffer.putShort (where, (short) length); + buffer.position (where + 2); + buffer.put (buf, offset, length); + } + + /** + * Sets the server's public value, Y. + * + * @param y The Y parameter. + * @throws java.nio.ReadOnlyBufferException If the underlying buffer + * is not writeable. + */ + public void setY (final BigInteger y) + { + int offset1 = (buffer.getShort (0) & 0xFFFF) + 2; + int offset2 = (buffer.getShort (offset1) & 0xFFFF) + offset1 + 2; + byte[] buf = y.toByteArray (); + int length = (buf[0] == 0x00 ? buf.length -1 : buf.length); + int offset = (buf[0] == 0x00 ? 1 : 0); + buffer.putShort (offset2, (short) length); + buffer.position (offset2 + 2); + buffer.put (buf, offset, length); + } + + public String toString () + { + return toString (null); + } + + public String toString (final String prefix) + { + StringWriter str = new StringWriter (); + PrintWriter out = new PrintWriter (str); + if (prefix != null) out.print (prefix); + out.println ("struct {"); + if (prefix != null) out.print (prefix); + out.print (" dh_p: "); + out.println (p ().toString (16)); + if (prefix != null) out.print (prefix); + out.print (" dh_g: "); + out.println (g ().toString (16)); + if (prefix != null) out.print (prefix); + out.print (" dh_Ys: "); + out.println (y ().toString (16)); + if (prefix != null) out.print (prefix); + out.print ("} ServerDHParams;"); + return str.toString (); + } +} diff --git a/libjava/classpath/gnu/javax/net/ssl/provider/ServerHandshake.java b/libjava/classpath/gnu/javax/net/ssl/provider/ServerHandshake.java new file mode 100644 index 000000000..d69fa120d --- /dev/null +++ b/libjava/classpath/gnu/javax/net/ssl/provider/ServerHandshake.java @@ -0,0 +1,1377 @@ +/* ServerHandshake.java -- the server-side handshake. + 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.Handshake.Type.*; +import static gnu.javax.net.ssl.provider.KeyExchangeAlgorithm.*; +import static gnu.javax.net.ssl.provider.ServerHandshake.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.CertificateRequest.ClientCertificateType; + +import java.nio.ByteBuffer; + +import java.security.AccessController; +import java.security.InvalidAlgorithmParameterException; +import java.security.InvalidKeyException; +import java.security.KeyManagementException; +import java.security.KeyPair; +import java.security.KeyPairGenerator; +import java.security.MessageDigest; +import java.security.NoSuchAlgorithmException; +import java.security.Principal; +import java.security.PrivateKey; +import java.security.SignatureException; +import java.security.cert.CertificateException; +import java.security.cert.X509Certificate; +import java.util.ArrayList; +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.NoSuchPaddingException; +import javax.crypto.SecretKey; +import javax.crypto.interfaces.DHPrivateKey; +import javax.crypto.interfaces.DHPublicKey; +import javax.crypto.spec.DHParameterSpec; +import javax.crypto.spec.SecretKeySpec; +import javax.net.ssl.SSLException; +import javax.net.ssl.SSLPeerUnverifiedException; +import javax.net.ssl.SSLSession; +import javax.net.ssl.X509ExtendedKeyManager; +import javax.net.ssl.SSLEngineResult.HandshakeStatus; +import javax.security.auth.x500.X500Principal; + +class ServerHandshake extends AbstractHandshake +{ + /** + * Handshake state enumeration. + */ + static enum State + { + WRITE_HELLO_REQUEST (true, false), + WRITE_SERVER_HELLO (true, false), + WRITE_CERTIFICATE (true, false), + WRITE_SERVER_KEY_EXCHANGE (true, false), + WRITE_CERTIFICATE_REQUEST (true, false), + WRITE_SERVER_HELLO_DONE (true, false), + WRITE_FINISHED (true, false), + READ_CLIENT_HELLO (false, true), + READ_CERTIFICATE (false, true), + READ_CLIENT_KEY_EXCHANGE (false, true), + READ_CERTIFICATE_VERIFY (false, true), + READ_FINISHED (false, true), + DONE (false, false); + + private final boolean isWriteState; + private final boolean isReadState; + + private State(final boolean isWriteState, final boolean isReadState) + { + this.isWriteState = isWriteState; + this.isReadState = isReadState; + } + + boolean isReadState() + { + return isReadState; + } + + boolean isWriteState() + { + return isWriteState; + } + } + + private State state; + + /* Handshake result fields. */ + private ByteBuffer outBuffer; + private boolean clientHadExtensions = false; + private boolean continuedSession = false; + private ServerNameList requestedNames = null; + private String keyAlias = null; + private X509Certificate clientCert = null; + private X509Certificate localCert = null; + private boolean helloV2 = false; + private KeyPair dhPair; + private PrivateKey serverKey; + + // 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; + handshakeOffset = 0; + } + + /** + * Choose the protocol version. Here we choose the largest protocol + * version we support that is not greater than the client's + * requested version. + */ + private static ProtocolVersion chooseProtocol (final ProtocolVersion clientVersion, + final String[] enabledVersions) + throws SSLException + { + ProtocolVersion version = null; + for (int i = 0; i < enabledVersions.length; i++) + { + ProtocolVersion v = ProtocolVersion.forName (enabledVersions[i]); + if (v.compareTo (clientVersion) <= 0) + { + if (version == null + || v.compareTo (version) > 0) + version = v; + } + } + + // The client requested a protocol version too old, or no protocol + // versions are enabled. + if (version == null) + throw new SSLException ("no acceptable protocol version available"); + return version; + } + + /** + * Choose the first cipher suite in the client's requested list that + * we have enabled. + */ + private CipherSuite chooseSuite (final CipherSuiteList clientSuites, + final String[] enabledSuites, + final ProtocolVersion version) + throws SSLException + { + // Figure out which SignatureAlgorithms we can support. + HashSet kexes = new HashSet(8); + + kexes.add(NONE); + X509ExtendedKeyManager km = engine.contextImpl.keyManager; + if (km != null) + { + if (km.getServerAliases(DH_DSS.name(), null).length > 0) + kexes.add(DH_DSS); + if (km.getServerAliases(DH_RSA.name(), null).length > 0) + kexes.add(DH_RSA); + if (km.getServerAliases(DHE_DSS.name(), null).length > 0) + kexes.add(DHE_DSS); + if (km.getServerAliases(DHE_RSA.name(), null).length > 0) + kexes.add(DHE_RSA); + if (km.getServerAliases(RSA.name(), null).length > 0) + kexes.add(RSA); + if (km.getServerAliases(RSA_PSK.name(), null).length > 0 + && engine.contextImpl.pskManager != null) + kexes.add(RSA_PSK); + } + if (engine.contextImpl.pskManager != null) + { + kexes.add(DHE_PSK); + kexes.add(PSK); + } + + if (Debug.DEBUG) + logger.logv(Component.SSL_HANDSHAKE, + "we have certs for key exchange algorithms {0}", kexes); + + HashSet suites = new HashSet(); + for (String s : enabledSuites) + { + CipherSuite suite = CipherSuite.forName(s); + if (suite == null) + continue; + if (!kexes.contains(suite.keyExchangeAlgorithm())) + continue; + suites.add(suite); + } + for (CipherSuite suite : clientSuites) + { + CipherSuite resolved = suite.resolve(); + if (!resolved.isResolved()) + continue; + if (suites.contains(resolved)) + return resolved; + } + + // We didn't find a match? + throw new AlertException(new Alert(Alert.Level.FATAL, + Alert.Description.INSUFFICIENT_SECURITY)); + } + + /** + * Choose a compression method that we support, among the client's + * requested compression methods. We prefer ZLIB over NONE in this + * implementation. + * + * XXX Maybe consider implementing lzo (GNUTLS supports that). + */ + private static CompressionMethod chooseCompression (final CompressionMethodList comps) + throws SSLException + { + GetSecurityPropertyAction gspa + = new GetSecurityPropertyAction("jessie.enable.compression"); + String enable = AccessController.doPrivileged(gspa); + // Scan for ZLIB first. + if (Boolean.valueOf(enable)) + { + for (CompressionMethod cm : comps) + { + if (cm.equals (CompressionMethod.ZLIB)) + return CompressionMethod.ZLIB; + } + } + for (CompressionMethod cm : comps) + { + if (cm.equals (CompressionMethod.NULL)) + return CompressionMethod.NULL; + } + + throw new SSLException ("no supported compression method"); + } + + protected @Override boolean doHash() + { + boolean b = helloV2; + helloV2 = false; + return (state != WRITE_HELLO_REQUEST) && !b; + } + + public @Override 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) + { + // Client Hello. + // + // This message is sent by the client to initiate a new handshake. + // On a new connection, it is the first handshake message sent. + // + // The state of the handshake, after this message is processed, + // will have a protocol version, cipher suite, compression method, + // session ID, and various extensions (that the server also + // supports). + case READ_CLIENT_HELLO: + if (handshake.type () != CLIENT_HELLO) + throw new AlertException(new Alert(Alert.Level.FATAL, + Alert.Description.UNEXPECTED_MESSAGE)); + + { + ClientHello hello = (ClientHello) handshake.body (); + engine.session().version + = chooseProtocol (hello.version (), + engine.getEnabledProtocols ()); + engine.session().suite = + chooseSuite (hello.cipherSuites (), + engine.getEnabledCipherSuites (), + engine.session().version); + compression = chooseCompression (hello.compressionMethods ()); + if (Debug.DEBUG) + logger.logv(Component.SSL_HANDSHAKE, + "chose version:{0} suite:{1} compression:{2}", + engine.session().version, engine.session().suite, + compression); + clientRandom = hello.random().copy(); + byte[] sessionId = hello.sessionId(); + if (hello.hasExtensions()) + { + ExtensionList exts = hello.extensions(); + clientHadExtensions = exts.size() > 0; + for (Extension e : hello.extensions()) + { + Extension.Type type = e.type(); + if (type == null) + continue; + switch (type) + { + case TRUNCATED_HMAC: + engine.session().setTruncatedMac(true); + break; + + case MAX_FRAGMENT_LENGTH: + MaxFragmentLength len = (MaxFragmentLength) e.value(); + engine.session().maxLength = len; + engine.session().setApplicationBufferSize(len.maxLength()); + break; + + case SERVER_NAME: + requestedNames = (ServerNameList) e.value(); + List names + = new ArrayList(requestedNames.size()); + for (ServerNameList.ServerName name : requestedNames) + names.add(name.name()); + engine.session().putValue("gnu.javax.net.ssl.RequestedServerNames", names); + break; + + default: + logger.log(Level.INFO, "skipping unsupported extension {0}", e); + } + } + } + AbstractSessionContext sessions = (AbstractSessionContext) + engine.contextImpl.engineGetServerSessionContext(); + SSLSession s = sessions.getSession(sessionId); + if (Debug.DEBUG) + logger.logv(Component.SSL_HANDSHAKE, "looked up saved session {0}", s); + if (s != null && s.isValid() && (s instanceof SessionImpl)) + { + engine.setSession((SessionImpl) s); + continuedSession = true; + } + else + { + // We *may* wind up with a badly seeded PRNG, and emit the + // same session ID over and over (this did happen to me, + // so we add this sanity check just in case). + if (engine.session().id().equals(new Session.ID(sessionId))) + { + byte[] newId = new byte[32]; + engine.session().random().nextBytes(newId); + engine.session().setId(new Session.ID(newId)); + } + sessions.put(engine.session()); + } + state = WRITE_SERVER_HELLO; + } + break; + + // Certificate. + // + // This message is sent by the client if the server had previously + // requested that the client authenticate itself with a certificate, + // and if the client has an appropriate certificate available. + // + // Processing this message will save the client's certificate, + // rejecting it if the certificate is not trusted, in preparation + // for the certificate verify message that will follow. + case READ_CERTIFICATE: + { + if (handshake.type() != CERTIFICATE) + { + if (engine.getNeedClientAuth()) // XXX throw better exception. + throw new SSLException("client auth required"); + state = READ_CLIENT_KEY_EXCHANGE; + return HandshakeStatus.NEED_UNWRAP; + } + + Certificate cert = (Certificate) handshake.body(); + try + { + engine.session().setPeerVerified(false); + X509Certificate[] chain + = cert.certificates().toArray(new X509Certificate[0]); + if (chain.length == 0) + throw new CertificateException("no certificates in chain"); + certVerifier = new CertVerifier(false, chain); + tasks.add(certVerifier); + engine.session().setPeerCertificates(chain); + clientCert = chain[0]; + // Delay setting 'peerVerified' until CertificateVerify. + } + catch (CertificateException ce) + { + if (engine.getNeedClientAuth()) + { + SSLPeerUnverifiedException x + = new SSLPeerUnverifiedException("client certificates could not be verified"); + x.initCause(ce); + throw x; + } + } + catch (NoSuchAlgorithmException nsae) + { + throw new SSLException(nsae); + } + state = READ_CLIENT_KEY_EXCHANGE; + } + break; + + // Client Key Exchange. + // + // The client's key exchange. This message is sent either following + // the certificate message, or if no certificate is available or + // requested, following the server's hello done message. + // + // After receipt of this message, the session keys for this + // session will have been created. + case READ_CLIENT_KEY_EXCHANGE: + { + if (handshake.type() != CLIENT_KEY_EXCHANGE) + throw new SSLException("expecting client key exchange"); + ClientKeyExchange kex = (ClientKeyExchange) handshake.body(); + + KeyExchangeAlgorithm alg = engine.session().suite.keyExchangeAlgorithm(); + switch (alg) + { + case DHE_DSS: + case DHE_RSA: + case DH_anon: + { + ClientDiffieHellmanPublic pub = (ClientDiffieHellmanPublic) + kex.exchangeKeys(); + DHPublicKey myKey = (DHPublicKey) dhPair.getPublic(); + DHPublicKey clientKey = + new GnuDHPublicKey(null, myKey.getParams().getP(), + myKey.getParams().getG(), + pub.publicValue()); + keyExchangeTask = new DHPhase(clientKey); + tasks.add(keyExchangeTask); + } + break; + + case RSA: + { + EncryptedPreMasterSecret secret = (EncryptedPreMasterSecret) + kex.exchangeKeys(); + keyExchangeTask = new RSAKeyExchange(secret.encryptedSecret()); + tasks.add(keyExchangeTask); + } + break; + + case PSK: + { + ClientPSKParameters params = (ClientPSKParameters) + kex.exchangeKeys(); + generatePSKSecret(params.identity(), null, false); + } + break; + + case DHE_PSK: + { + ClientDHE_PSKParameters params = (ClientDHE_PSKParameters) + kex.exchangeKeys(); + DHPublicKey serverKey = (DHPublicKey) dhPair.getPublic(); + DHPublicKey clientKey = + new GnuDHPublicKey(null, serverKey.getParams().getP(), + serverKey.getParams().getG(), + params.params().publicValue()); + SecretKey psk = null; + try + { + psk = engine.contextImpl.pskManager.getKey(params.identity()); + } + catch (KeyManagementException kme) + { + } + keyExchangeTask = new DHE_PSKGen(clientKey, psk, false); + tasks.add(keyExchangeTask); + } + break; + + case RSA_PSK: + { + ClientRSA_PSKParameters params = (ClientRSA_PSKParameters) + kex.exchangeKeys(); + SecretKey psk = null; + try + { + psk = engine.contextImpl.pskManager.getKey(params.identity()); + } + catch (KeyManagementException kme) + { + } + if (psk == null) + { + byte[] fakeKey = new byte[16]; + engine.session().random().nextBytes(fakeKey); + psk = new SecretKeySpec(fakeKey, "DHE_PSK"); + } + keyExchangeTask = + new RSA_PSKExchange(params.secret().encryptedSecret(), psk); + tasks.add(keyExchangeTask); + } + break; + + case NONE: + { + Inflater inflater = null; + Deflater deflater = null; + if (compression == CompressionMethod.ZLIB) + { + inflater = new Inflater(); + deflater = new Deflater(); + } + inParams = new InputSecurityParameters(null, null, inflater, + engine.session(), + engine.session().suite); + outParams = new OutputSecurityParameters(null, null, deflater, + engine.session(), + engine.session().suite); + engine.session().privateData.masterSecret = new byte[0]; + } + break; + } + // XXX SRP + + if (clientCert != null) + state = READ_CERTIFICATE_VERIFY; + else + state = READ_FINISHED; + } + break; + + // Certificate Verify. + // + // This message is sent following the client key exchange message, + // but only when the client included its certificate in a previous + // message. + // + // After receipt of this message, the client's certificate (and, + // to a degree, the client's identity) will have been verified. + case READ_CERTIFICATE_VERIFY: + { + if (handshake.type() != CERTIFICATE_VERIFY) + throw new SSLException("expecting certificate verify message"); + + CertificateVerify verify = (CertificateVerify) handshake.body(); + try + { + verifyClient(verify.signature()); + if (certVerifier != null && certVerifier.verified()) + engine.session().setPeerVerified(true); + } + catch (SignatureException se) + { + if (engine.getNeedClientAuth()) + throw new SSLException("client auth failed", se); + } + if (continuedSession) + { + engine.changeCipherSpec(); + state = WRITE_FINISHED; + } + else + state = READ_FINISHED; + } + break; + + // Finished. + // + // This message is sent immediately following the change cipher + // spec message (which is sent outside of the handshake layer). + // After receipt of this message, the session keys for the client + // side will have been verified (this is the first message the + // client sends encrypted and authenticated with the newly + // negotiated keys). + // + // In the case of a continued session, the client sends its + // finished message first. Otherwise, the server will send its + // finished message first. + case READ_FINISHED: + { + if (handshake.type() != FINISHED) + throw new AlertException(new Alert(Alert.Level.FATAL, + Description.UNEXPECTED_MESSAGE)); + + Finished clientFinished = (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 serverFinished = + new Finished(generateFinished(md5copy, shacopy, + true, engine.session()), + engine.session().version); + + if (Debug.DEBUG) + logger.log(Component.SSL_HANDSHAKE, "server finished: {0}", + serverFinished); + + 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) + state = DONE; + else + { + engine.changeCipherSpec(); + state = WRITE_FINISHED; + } + } + break; + } + + 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; + } + + public @Override HandshakeStatus implHandleOutput (ByteBuffer fragment) + throws SSLException + { + if (Debug.DEBUG) + logger.logv(Component.SSL_HANDSHAKE, + "handle output state: {0}; output fragment: {1}", + state, fragment); + + // 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; + } + + // XXX what we need to do here is generate a "stream" of handshake + // messages, and insert them into fragment amounts that we have available. + // A handshake message can span multiple records, and we can put + // multiple records into a single record. + // + // So, we can have one of two states: + // + // 1) We have enough space in the record we are creating to push out + // everything we need to on this round. This is easy; we just + // repeatedly fill in these messages in the buffer, so we get something + // that looks like this: + // ________________________________ + // records: |________________________________| + // handshakes: |______|__|__________| + // + // 2) We can put part of one handshake message in the current record, + // but we must put the rest of it in the following record, or possibly + // more than one following record. So here, we'd see this: + // + // ________________________ + // records: |_______|_______|________| + // handshakes: |____|_______|_________| + // + // We *could* make this a lot easier by just only ever emitting one + // record per call, but then we would waste potentially a lot of space + // and waste a lot of TCP packets by doing it the simple way. What + // we desire here is that we *maximize* our usage of the resources + // given to us, and to use as much space in the present fragment as + // we can. + // + // Note that we pretty much have to support this, anyway, because SSL + // provides no guarantees that the record size is large enough to + // admit *even one* handshake message. Also, callers could call on us + // with a short buffer, even though they aren't supposed to. + // + // This is somewhat complicated by the fact that we don't know, a priori, + // how large a handshake message will be until we've built it, and our + // design builds the message around the byte buffer. + // + // Some ways to handle this: + // + // 1. Write our outgoing handshake messages to a private buffer, + // big enough per message (and, if we run out of space, resize that + // buffer) and push (possibly part of) this buffer out to the + // outgoing buffer. This isn't that great because we'd need to + // store and copy things unnecessarily. + // + // 2. Build outgoing handshake objects 'virtually', that is, store them + // as collections of objects, then compute the length, and then write + // them to a buffer, instead of making the objects views on + // ByteBuffers for both input and output. This would complicate the + // protocol objects a bit (although, it would amount to doing + // separation between client objects and server objects, which is + // pretty OK), and we still need to figure out how exactly to chunk + // those objects across record boundaries. + // + // 3. Try to build these objects on the buffer we're given, but detect + // when we run out of space in the output buffer, and split the + // overflow message. This sounds like the best, but also probably + // the hardest to code. +output_loop: + while (fragment.remaining() >= 4 && state.isWriteState()) + { + switch (state) + { + // Hello Request. + // + // This message is sent by the server to initiate a new + // handshake, to establish new session keys. + case WRITE_HELLO_REQUEST: + { + Handshake handshake = new Handshake(fragment); + handshake.setType(Handshake.Type.HELLO_REQUEST); + handshake.setLength(0); + fragment.position(fragment.position() + 4); + if (Debug.DEBUG) + logger.log(Component.SSL_HANDSHAKE, "{0}", handshake); + state = READ_CLIENT_HELLO; + } + break output_loop; // XXX temporary + + // Server Hello. + // + // This message is sent immediately following the client hello. + // It informs the client of the cipher suite, compression method, + // session ID (which may have been a continued session), and any + // supported extensions. + case WRITE_SERVER_HELLO: + { + ServerHelloBuilder hello = new ServerHelloBuilder(); + hello.setVersion(engine.session().version); + Random r = hello.random(); + r.setGmtUnixTime(Util.unixTime()); + byte[] nonce = new byte[28]; + engine.session().random().nextBytes(nonce); + r.setRandomBytes(nonce); + serverRandom = r.copy(); + hello.setSessionId(engine.session().getId()); + hello.setCipherSuite(engine.session().suite); + hello.setCompressionMethod(compression); + if (clientHadExtensions) + { + // XXX figure this out. + } + else // Don't send any extensions. + hello.setDisableExtensions(true); + + if (Debug.DEBUG) + logger.log(Component.SSL_HANDSHAKE, "{0}", hello); + + int typeLen = ((Handshake.Type.SERVER_HELLO.getValue() << 24) + | (hello.length() & 0xFFFFFF)); + fragment.putInt(typeLen); + + 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); + + CipherSuite cs = engine.session().suite; + KeyExchangeAlgorithm kex = cs.keyExchangeAlgorithm(); + if (continuedSession) + { + byte[][] keys = generateKeys(clientRandom, serverRandom, + engine.session()); + setupSecurityParameters(keys, false, engine, compression); + engine.changeCipherSpec(); + state = WRITE_FINISHED; + } + else if (kex == DHE_DSS || kex == DHE_RSA || kex == RSA + || kex == RSA_PSK) + { + certLoader = new CertLoader(); + tasks.add(certLoader); + state = WRITE_CERTIFICATE; + if (kex == DHE_DSS || kex == DHE_RSA) + { + genDH = new GenDH(); + tasks.add(genDH); + } + break output_loop; + } + else if (kex == PSK) + { + state = WRITE_SERVER_KEY_EXCHANGE; + } + else if (kex == DHE_PSK || kex == DH_anon) + { + 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; + + // Certificate. + // + // This message is sent immediately following the server hello, + // IF the cipher suite chosen requires that the server identify + // itself (usually, servers must authenticate). + case WRITE_CERTIFICATE: + { + // 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 + { + cert.setCertificates(Arrays.asList(chain)); + } + catch (CertificateException ce) + { + throw new SSLException(ce); + } + + if (Debug.DEBUG) + { + logger.logv(Component.SSL_HANDSHAKE, "my cert:\n{0}", localCert); + logger.logv(Component.SSL_HANDSHAKE, "{0}", cert); + } + + int typeLen = ((CERTIFICATE.getValue() << 24) + | (cert.length() & 0xFFFFFF)); + fragment.putInt(typeLen); + + outBuffer = cert.buffer(); + final int l = Math.min(fragment.remaining(), outBuffer.remaining()); + fragment.put((ByteBuffer) outBuffer.duplicate().limit(outBuffer.position() + l)); + outBuffer.position(outBuffer.position() + l); + + CipherSuite s = engine.session().suite; + KeyExchangeAlgorithm kexalg = s.keyExchangeAlgorithm(); + if (kexalg == DHE_DSS || kexalg == DHE_RSA) + { + genDH = new GenDH(); + tasks.add(genDH); + state = WRITE_SERVER_KEY_EXCHANGE; + break output_loop; + } + else if (kexalg == RSA_PSK) + state = WRITE_SERVER_KEY_EXCHANGE; + else if (engine.getWantClientAuth() || engine.getNeedClientAuth()) + { + state = WRITE_CERTIFICATE_REQUEST; + } + else + state = WRITE_SERVER_HELLO_DONE; + } + break output_loop; // XXX temporary + + // Server key exchange. + // + // This message is sent, following the certificate if sent, + // otherwise following the server hello, IF the chosen cipher + // suite requires that the server send explicit key exchange + // parameters (that is, if the key exchange parameters are not + // implicit in the server's certificate). + case WRITE_SERVER_KEY_EXCHANGE: + { + KeyExchangeAlgorithm kex = engine.session().suite.keyExchangeAlgorithm(); + + ByteBuffer paramBuffer = null; + ByteBuffer sigBuffer = null; + if (kex == DHE_DSS || kex == DHE_RSA || kex == DH_anon + || kex == DHE_PSK) + { + 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; + + if (kex == DHE_PSK) + { + String identityHint + = engine.contextImpl.pskManager.chooseIdentityHint(); + ServerDHE_PSKParameters psk = + new ServerDHE_PSKParameters(identityHint, paramBuffer); + paramBuffer = psk.buffer(); + } + } + if (kex == RSA_PSK) + { + String idHint = engine.contextImpl.pskManager.chooseIdentityHint(); + if (idHint != null) + { + ServerRSA_PSKParameters params + = new ServerRSA_PSKParameters(idHint); + paramBuffer = params.buffer(); + } + } + if (kex == PSK) + { + String idHint = engine.contextImpl.pskManager.chooseIdentityHint(); + if (idHint != null) + { + ServerPSKParameters params + = new ServerPSKParameters(idHint); + paramBuffer = params.buffer(); + } + } + // XXX handle SRP + + if (paramBuffer != null) + { + ServerKeyExchangeBuilder ske + = new ServerKeyExchangeBuilder(engine.session().suite); + ske.setParams(paramBuffer); + if (sigBuffer != null) + ske.setSignature(sigBuffer); + + 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); + } + + if (engine.getWantClientAuth() || engine.getNeedClientAuth()) + state = WRITE_CERTIFICATE_REQUEST; + else + state = WRITE_SERVER_HELLO_DONE; + } + break; + + // Certificate Request. + // + // This message is sent when the server desires or requires + // client authentication with a certificate; if it is sent, it + // will be sent just after the Certificate or Server Key + // Exchange messages, whichever is sent. If neither of the + // above are sent, it will be the message that follows the + // server hello. + case WRITE_CERTIFICATE_REQUEST: + { + CertificateRequestBuilder req = new CertificateRequestBuilder(); + + List types + = new ArrayList(4); + types.add(ClientCertificateType.RSA_SIGN); + types.add(ClientCertificateType.RSA_FIXED_DH); + types.add(ClientCertificateType.DSS_SIGN); + types.add(ClientCertificateType.DSS_FIXED_DH); + req.setTypes(types); + + X509Certificate[] anchors + = engine.contextImpl.trustManager.getAcceptedIssuers(); + List issuers + = new ArrayList(anchors.length); + for (X509Certificate cert : anchors) + issuers.add(cert.getIssuerX500Principal()); + req.setAuthorities(issuers); + + if (Debug.DEBUG) + logger.log(Component.SSL_HANDSHAKE, "{0}", req); + + fragment.putInt((CERTIFICATE_REQUEST.getValue() << 24) + | (req.length() & 0xFFFFFF)); + + outBuffer = req.buffer(); + int l = Math.min(outBuffer.remaining(), fragment.remaining()); + fragment.put((ByteBuffer) outBuffer.duplicate().limit(outBuffer.position() + l)); + outBuffer.position(outBuffer.position() + l); + + state = WRITE_SERVER_HELLO_DONE; + } + break; + + // Server Hello Done. + // + // This message is always sent by the server, to terminate its + // side of the handshake. Since the server's handshake message + // may comprise multiple, optional messages, this sentinel + // message lets the client know when the server's message stream + // is complete. + case WRITE_SERVER_HELLO_DONE: + { + // ServerHelloDone is zero-length; just put in the type + // field. + fragment.putInt(SERVER_HELLO_DONE.getValue() << 24); + if (Debug.DEBUG) + logger.logv(Component.SSL_HANDSHAKE, "writing ServerHelloDone"); + state = READ_CERTIFICATE; + } + break output_loop; // XXX temporary + + // Finished. + // + // This is always sent by the server to verify the keys that the + // server will use to encrypt and authenticate. In a full + // handshake, this message will be sent after the client's + // finished message; in an abbreviated handshake (with a continued + // session) the server sends its finished message first. + // + // This message follows the change cipher spec message, which is + // sent out-of-band in a different SSL content-type. + // + // This is the first message that the server will send encrypted + // and authenticated with the newly negotiated session keys. + 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, false, + engine.session()); + + fragment.putInt((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 = READ_FINISHED; + else + state = DONE; + } + break; + } + } + if (!tasks.isEmpty()) + return HandshakeStatus.NEED_TASK; + if (state.isWriteState() || outBuffer.hasRemaining()) + return HandshakeStatus.NEED_WRAP; + if (state.isReadState()) + return HandshakeStatus.NEED_UNWRAP; + + return HandshakeStatus.FINISHED; + } + + @Override HandshakeStatus status() + { + if (!tasks.isEmpty()) + return HandshakeStatus.NEED_TASK; + if (state.isReadState()) + return HandshakeStatus.NEED_UNWRAP; + if (state.isWriteState()) + return HandshakeStatus.NEED_WRAP; + + return HandshakeStatus.FINISHED; + } + + @Override void checkKeyExchange() throws SSLException + { + if (continuedSession) // No key exchange needed. + return; + KeyExchangeAlgorithm kex = engine.session().suite.keyExchangeAlgorithm(); + if (kex == NONE || kex == PSK || kex == RSA_PSK) // Don't need one. + 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) + { + int len = hello.getShort(0) & 0x7FFF; + md5.update((ByteBuffer) hello.duplicate().position(2).limit(len+2)); + sha.update((ByteBuffer) hello.duplicate().position(2).limit(len+2)); + helloV2 = true; + } + + private ByteBuffer signParams(ByteBuffer serverParams) + throws NoSuchAlgorithmException, InvalidKeyException, SignatureException + { + 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 + { + MessageDigest md5copy = null; + MessageDigest shacopy = null; + try + { + md5copy = (MessageDigest) md5.clone(); + shacopy = (MessageDigest) sha.clone(); + } + catch (CloneNotSupportedException cnse) + { + // Mis-configured with non-cloneable digests. + throw new SSLException(cnse); + } + byte[] toSign = null; + if (engine.session().version == ProtocolVersion.SSL_3) + toSign = genV3CertificateVerify(md5copy, shacopy, engine.session()); + else + { + if (engine.session().suite.signatureAlgorithm() == SignatureAlgorithm.RSA) + toSign = Util.concat(md5copy.digest(), shacopy.digest()); + else + toSign = shacopy.digest(); + } + + try + { + java.security.Signature sig = java.security.Signature.getInstance(engine.session().suite.signatureAlgorithm().toString()); + sig.initVerify(clientCert); + sig.update(toSign); + sig.verify(sigValue); + } + catch (InvalidKeyException ike) + { + throw new SSLException(ike); + } + catch (NoSuchAlgorithmException nsae) + { + throw new SSLException(nsae); + } + } + + // Delegated tasks. + + class CertLoader extends DelegatedTask + { + CertLoader() + { + } + + public void implRun() throws SSLException + { + KeyExchangeAlgorithm kexalg = engine.session().suite.keyExchangeAlgorithm(); + X509ExtendedKeyManager km = engine.contextImpl.keyManager; + Principal[] issuers = null; // XXX use TrustedAuthorities extension. + keyAlias = km.chooseEngineServerAlias(kexalg.name(), issuers, engine); + if (keyAlias == null) + throw new SSLException("no certificates available"); + X509Certificate[] chain = km.getCertificateChain(keyAlias); + engine.session().setLocalCertificates(chain); + localCert = chain[0]; + serverKey = km.getPrivateKey(keyAlias); + if (kexalg == DH_DSS || kexalg == DH_RSA) + 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, serverKey); + 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); + } + } + + class RSA_PSKExchange extends DelegatedTask + { + private final byte[] encryptedPreMasterSecret; + private final SecretKey psKey; + + RSA_PSKExchange(byte[] encryptedPreMasterSecret, SecretKey psKey) + { + this.encryptedPreMasterSecret = encryptedPreMasterSecret; + this.psKey = psKey; + } + + public @Override void implRun() + throws BadPaddingException, IllegalBlockSizeException, InvalidKeyException, + NoSuchAlgorithmException, NoSuchPaddingException, SSLException + { + Cipher rsa = Cipher.getInstance("RSA"); + rsa.init(Cipher.DECRYPT_MODE, serverKey); + rsa.init(Cipher.DECRYPT_MODE, localCert); + byte[] rsaSecret = rsa.doFinal(encryptedPreMasterSecret); + byte[] psSecret = psKey.getEncoded(); + preMasterSecret = new byte[rsaSecret.length + psSecret.length + 4]; + preMasterSecret[0] = (byte) (rsaSecret.length >>> 8); + preMasterSecret[1] = (byte) rsaSecret.length; + System.arraycopy(rsaSecret, 0, preMasterSecret, 2, rsaSecret.length); + preMasterSecret[rsaSecret.length + 2] = (byte) (psSecret.length >>> 8); + preMasterSecret[rsaSecret.length + 3] = (byte) psSecret.length; + System.arraycopy(psSecret, 0, preMasterSecret, rsaSecret.length+4, + psSecret.length); + + generateMasterSecret(clientRandom, serverRandom, engine.session()); + byte[][] keys = generateKeys(clientRandom, serverRandom, engine.session()); + setupSecurityParameters(keys, false, engine, compression); + } + } +} diff --git a/libjava/classpath/gnu/javax/net/ssl/provider/ServerHello.java b/libjava/classpath/gnu/javax/net/ssl/provider/ServerHello.java new file mode 100644 index 000000000..944194b3e --- /dev/null +++ b/libjava/classpath/gnu/javax/net/ssl/provider/ServerHello.java @@ -0,0 +1,231 @@ +/* ServerHello.java -- SSL ServerHello message. + 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.PrintWriter; +import java.io.StringWriter; + +import java.nio.ByteBuffer; + +/** + * The server hello message. + * + *
    +struct
    +{
    +  ProtocolVersion server_version;
    +  Random random;
    +  SessionID session_id;
    +  CipherSuite cipher_suite;
    +  CompressionMethod compression_method;
    +  Extensions server_hello_extension_list<0..2^16-1>
    +} ServerHello;
    +
    + * + *

    Server hello messages may contain extra data after the + * compression_method field, which are interpreted as + * extensions to the basic handshake. + */ +public class ServerHello implements Handshake.Body +{ + + // Fields. + // ------------------------------------------------------------------------- + + protected static final int RANDOM_OFFSET = 2; + protected static final int SESSID_OFFSET = 32 + RANDOM_OFFSET; + protected static final int SESSID_OFFSET2 = SESSID_OFFSET + 1; + + protected ByteBuffer buffer; + protected boolean disableExtensions; + + // Constructor. + // ------------------------------------------------------------------------- + + public ServerHello (final ByteBuffer buffer) + { + this.buffer = buffer; + disableExtensions = false; + } + + public int length () + { + int sessionLen = buffer.get(SESSID_OFFSET) & 0xFF; + int len = SESSID_OFFSET2 + sessionLen + 3; + int elen = 0; + if (!disableExtensions && len + 1 < buffer.limit() + && (elen = buffer.getShort(len)) != 0) + len += 2 + elen; + return len; + } + + /** + * Returns the server's protocol version. This will read two bytes + * from the beginning of the underlying buffer, and return an + * instance of the appropriate {@link ProtocolVersion}; if the + * version read is a supported version, this method returns a static + * constant instance. + * + * @return The server's protocol version. + */ + public ProtocolVersion version() + { + return ProtocolVersion.getInstance (buffer.getShort (0)); + } + + /** + * Returns the server's random value. This method returns a + * lightwieght wrapper around the existing bytes; modifications to + * the underlying buffer will modify the returned object, and + * vice-versa. + * + * @return The server's random value. + */ + public Random random() + { + ByteBuffer randomBuf = + ((ByteBuffer) buffer.duplicate ().position (RANDOM_OFFSET) + .limit (SESSID_OFFSET)).slice (); + return new Random (randomBuf); + } + + /** + * Returns the session ID. This method returns a new byte array with + * the session ID bytes. + * + * @return The session ID. + */ + public byte[] sessionId() + { + int idlen = buffer.get (SESSID_OFFSET) & 0xFF; + byte[] sessionId = new byte[idlen]; + buffer.position (SESSID_OFFSET2); + buffer.get (sessionId); + return sessionId; + } + + /** + * Returns the server's chosen cipher suite. The returned cipher + * suite will be "resolved" to this structure's version. + * + * @return The server's chosen cipher suite. + */ + public CipherSuite cipherSuite() + { + int offset = SESSID_OFFSET2 + (buffer.get(SESSID_OFFSET) & 0xFF); + return CipherSuite.forValue(buffer.getShort(offset)).resolve(); + } + + /** + * Returns the server's chosen compression method. + * + * @return The chosen compression method. + */ + public CompressionMethod compressionMethod() + { + int offset = SESSID_OFFSET2 + (buffer.get(SESSID_OFFSET) & 0xFF) + 2; + return CompressionMethod.getInstance(buffer.get(offset) & 0xFF); + } + + public int extensionsLength() + { + int offset = SESSID_OFFSET2 + (buffer.get (SESSID_OFFSET) & 0xFF) + 3; + if (offset + 1 >= buffer.limit()) + return 0; + return buffer.getShort(offset) & 0xFFFF; + } + + public ExtensionList extensions () + { + int offset = SESSID_OFFSET2 + (buffer.get (SESSID_OFFSET) & 0xFF) + 3; + if (offset + 1 >= buffer.limit()) + return null; + int len = buffer.getShort(offset) & 0xFFFF; + if (len == 0) + len = buffer.limit() - offset - 2; + ByteBuffer ebuf = ((ByteBuffer) buffer.duplicate().position(offset) + .limit(offset + len + 2)).slice(); + return new ExtensionList(ebuf); + } + + public String toString() + { + return toString(null); + } + + public String toString (final String prefix) + { + StringWriter str = new StringWriter(); + PrintWriter out = new PrintWriter(str); + if (prefix != null) + out.print (prefix); + out.println ("struct {"); + String subprefix = " "; + if (prefix != null) + subprefix += prefix; + out.print (subprefix); + out.print ("version: "); + out.print (version ()); + out.println (";"); + out.print (subprefix); + out.println ("random:"); + out.println (random ().toString (subprefix)); + out.print (subprefix); + out.print ("sessionId: "); + out.print (Util.toHexString(sessionId (), ':')); + out.println (";"); + out.print (subprefix); + out.print ("cipherSuite: "); + out.print (cipherSuite ()); + out.println (";"); + out.print (subprefix); + out.print ("compressionMethod: "); + out.print (compressionMethod ()); + out.println (";"); + ExtensionList exts = extensions (); + out.print (subprefix); + out.println ("extensions:"); + out.println (exts != null ? exts.toString (subprefix+" ") + : subprefix + " (nil)"); + if (prefix != null) + out.print (prefix); + out.print ("} ServerHello;"); + return str.toString(); + } +} diff --git a/libjava/classpath/gnu/javax/net/ssl/provider/ServerHelloBuilder.java b/libjava/classpath/gnu/javax/net/ssl/provider/ServerHelloBuilder.java new file mode 100644 index 000000000..47bce29ee --- /dev/null +++ b/libjava/classpath/gnu/javax/net/ssl/provider/ServerHelloBuilder.java @@ -0,0 +1,131 @@ +/* ServerHelloBuilder.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; + +/** + * @author csm + * + */ +public class ServerHelloBuilder extends ServerHello implements Builder +{ + public ServerHelloBuilder() + { + // Allocate a large enough buffer to hold a hello with the maximum + // size session ID, and no extensions. + super(ByteBuffer.allocate(SESSID_OFFSET2 + 35)); + } + + public ByteBuffer buffer() + { + return ((ByteBuffer) buffer.duplicate().position(0).limit(length())).slice(); + } + + // We don't reallocate the buffer in any of the following methods, + // because we always allocate a large enough buffer for the base + // object in the constructor. + + public void setVersion (final ProtocolVersion version) + { + buffer.putShort (0, (short) version.rawValue ()); + } + + public void setSessionId (final byte[] sessionId) + { + setSessionId (sessionId, 0, sessionId.length); + } + + public void setSessionId (final byte[] sessionId, final int offset, + final int length) + { + if (length < 0 || length > 32) + throw new IllegalArgumentException("length must be between 0 and 32"); + buffer.put(SESSID_OFFSET, (byte) length); + ((ByteBuffer) buffer.duplicate().position(SESSID_OFFSET2)) + .put(sessionId, offset, length); + } + + public void setCipherSuite (final CipherSuite suite) + { + int offset = SESSID_OFFSET + (buffer.get(SESSID_OFFSET) & 0xFF) + 1; + ((ByteBuffer) buffer.duplicate().position(offset)).put(suite.id()); + } + + public void setCompressionMethod (final CompressionMethod comp) + { + int offset = SESSID_OFFSET + (buffer.get(SESSID_OFFSET) & 0xFF) + 3; + buffer.put (offset, (byte) comp.getValue ()); + } + + // For extensions, we do reallocate the buffer. + + public void setDisableExtensions(boolean disable) + { + disableExtensions = disable; + } + + public void setExtensionsLength (final int length) + { + if (length < 0 || length > 16384) + throw new IllegalArgumentException("length must be nonnegative and not exceed 16384"); + int needed = SESSID_OFFSET2 + (buffer.get(SESSID_OFFSET) & 0xFF) + 5 + length; + if (buffer.capacity() < needed) + ensureCapacity(needed); + buffer.putShort (SESSID_OFFSET2 + (buffer.get (SESSID_OFFSET) & 0xFF) + 3, + (short) length); + } + + public void setExtensions(ByteBuffer extensions) + { + extensions = (ByteBuffer) + extensions.duplicate().limit(extensions.position() + extensionsLength()); + ((ByteBuffer) buffer.duplicate().position(SESSID_OFFSET2 + + (buffer.get(SESSID_OFFSET) & 0xFF) + )).put(extensions); + } + + public void ensureCapacity(int newCapacity) + { + ByteBuffer newBuffer = ByteBuffer.allocate(newCapacity); + newBuffer.put(buffer); + newBuffer.position(0); + buffer = newBuffer; + } +} diff --git a/libjava/classpath/gnu/javax/net/ssl/provider/ServerHelloDone.java b/libjava/classpath/gnu/javax/net/ssl/provider/ServerHelloDone.java new file mode 100644 index 000000000..987b51c56 --- /dev/null +++ b/libjava/classpath/gnu/javax/net/ssl/provider/ServerHelloDone.java @@ -0,0 +1,66 @@ +/* ServerHelloDone.java -- SSL ServerHelloDone message. + 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; + +/** + * An empty message that signals that the server is finished sending + * its handshake data. + * + *

    struct { } ServerHelloDone;
    + */ +public class ServerHelloDone implements Handshake.Body +{ + public ServerHelloDone () { } + + public int length () + { + return 0; + } + + public String toString () + { + return toString (null); + } + + public String toString (final String prefix) + { + return ((prefix != null ? prefix : "") + + "struct { } ServerHelloDone;"); + } +} diff --git a/libjava/classpath/gnu/javax/net/ssl/provider/ServerKeyExchange.java b/libjava/classpath/gnu/javax/net/ssl/provider/ServerKeyExchange.java new file mode 100644 index 000000000..1206ae6b2 --- /dev/null +++ b/libjava/classpath/gnu/javax/net/ssl/provider/ServerKeyExchange.java @@ -0,0 +1,173 @@ +/* ServerKeyExchange.java -- SSL ServerKeyExchange message. + 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.PrintWriter; +import java.io.StringWriter; + +import java.nio.ByteBuffer; +import java.nio.ByteOrder; + +/** + * The server key exchange message. + * + *
    +struct
    +{
    +  select (KeyExchangeAlgorithm)
    +  {
    +    case diffie_hellman:
    +      ServerDHParams params;
    +      Signature signed_params;
    +    case rsa:
    +      ServerRSAParams params;
    +      Signature signed_params;
    +    case srp:
    +      ServerSRPParams params;
    +      Signature signed_params;
    +  };
    +} ServerKeyExchange;
    +
    + */ +public class ServerKeyExchange implements Handshake.Body +{ + + protected ByteBuffer buffer; + protected final CipherSuite suite; + + public ServerKeyExchange(final ByteBuffer buffer, final CipherSuite suite) + { + suite.getClass(); + this.buffer = buffer.duplicate().order(ByteOrder.BIG_ENDIAN); + this.suite = suite; + } + + public int length () + { + if (suite.keyExchangeAlgorithm ().equals (KeyExchangeAlgorithm.NONE)) + return 0; + int len = 0; + ServerKeyExchangeParams params = params(); + Signature sig = signature(); + if (params != null) + len += params.length(); + if (sig != null) + len += sig.length(); + return len; + } + + /** + * Returns the server's key exchange parameters. The value returned will + * depend on the key exchange algorithm this object was created with. + * + * @return The server's key exchange parameters. + */ + public ServerKeyExchangeParams params () + { + KeyExchangeAlgorithm kex = suite.keyExchangeAlgorithm (); + if (kex == KeyExchangeAlgorithm.RSA) + return new ServerRSAParams(buffer.duplicate ()); + else if (kex == KeyExchangeAlgorithm.DHE_DSS + || kex == KeyExchangeAlgorithm.DHE_RSA + || kex == KeyExchangeAlgorithm.DH_anon) + return new ServerDHParams(buffer.duplicate()); +// else if (kex.equals (KeyExchangeAlgorithm.SRP)) +// return new ServerSRPParams (buffer.duplicate ()); + else if (kex == KeyExchangeAlgorithm.NONE) + return null; + else if (kex == KeyExchangeAlgorithm.DHE_PSK) + return new ServerDHE_PSKParameters(buffer.duplicate()); + else if (kex == KeyExchangeAlgorithm.PSK) + return new ServerPSKParameters(buffer.duplicate()); + else if (kex == KeyExchangeAlgorithm.RSA_PSK) + return new ServerPSKParameters(buffer.duplicate()); + throw new IllegalArgumentException ("unsupported key exchange: " + kex); + } + + /** + * Returns the digital signature made over the key exchange parameters. + * + * @return The signature. + */ + public Signature signature () + { + KeyExchangeAlgorithm kex = suite.keyExchangeAlgorithm(); + if (kex == KeyExchangeAlgorithm.NONE + || kex == KeyExchangeAlgorithm.DH_anon + || kex == KeyExchangeAlgorithm.DHE_PSK + || kex == KeyExchangeAlgorithm.PSK + || kex == KeyExchangeAlgorithm.RSA_PSK) + return null; + ServerKeyExchangeParams params = params(); + ByteBuffer sigbuf = ((ByteBuffer) buffer.position(params.length ())).slice (); + return new Signature (sigbuf, suite.signatureAlgorithm ()); + } + + public String toString() + { + return toString (null); + } + + public String toString (final String prefix) + { + StringWriter str = new StringWriter(); + PrintWriter out = new PrintWriter(str); + if (prefix != null) out.print (prefix); + out.println("struct {"); + if (prefix != null) out.print (prefix); + out.print (" algorithm: "); + out.print (suite.keyExchangeAlgorithm ()); + out.println (";"); + if (!suite.keyExchangeAlgorithm ().equals (KeyExchangeAlgorithm.NONE)) + { + if (prefix != null) out.print (prefix); + out.println (" parameters:"); + out.println (params ().toString (prefix != null ? prefix+" " : " ")); + } + if (!suite.signatureAlgorithm ().equals (SignatureAlgorithm.ANONYMOUS)) + { + if (prefix != null) out.print (prefix); + out.println (" signature:"); + out.println (signature ().toString (prefix != null ? prefix+" " : " ")); + } + if (prefix != null) out.print (prefix); + out.print ("} ServerKeyExchange;"); + return str.toString(); + } +} diff --git a/libjava/classpath/gnu/javax/net/ssl/provider/ServerKeyExchangeBuilder.java b/libjava/classpath/gnu/javax/net/ssl/provider/ServerKeyExchangeBuilder.java new file mode 100644 index 000000000..658ae228a --- /dev/null +++ b/libjava/classpath/gnu/javax/net/ssl/provider/ServerKeyExchangeBuilder.java @@ -0,0 +1,89 @@ +/* ServerKeyExchangeBuilder.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 ServerKeyExchange} objects. + * + * @author Casey Marshall (csm@gnu.org) + */ +public class ServerKeyExchangeBuilder extends ServerKeyExchange + implements Builder +{ + public ServerKeyExchangeBuilder(final CipherSuite suite) + { + super(ByteBuffer.allocate(1024), suite); + } + + /* (non-Javadoc) + * @see gnu.javax.net.ssl.provider.Builder#buffer() + */ + public ByteBuffer buffer() + { + return ((ByteBuffer) buffer.duplicate().position(0).limit(length())).slice(); + } + + public void setParams(ByteBuffer params) + { + if (suite.keyExchangeAlgorithm() == KeyExchangeAlgorithm.NONE) + throw new IllegalArgumentException("key exchange algorithm is none"); + ensureCapacity(params.remaining()); + buffer.duplicate().put(params); + } + + public void setSignature(ByteBuffer signature) + { + if (suite.keyExchangeAlgorithm() == KeyExchangeAlgorithm.NONE) + throw new IllegalArgumentException("key exchange algorithm is none"); + int paramsLen = params().length(); + ensureCapacity(paramsLen + signature.remaining()); + ((ByteBuffer) buffer.duplicate().position(paramsLen)).put(signature); + } + + public void ensureCapacity(int capacity) + { + if (buffer.capacity() >= capacity) + return; + ByteBuffer newBuffer = ByteBuffer.allocate(capacity); + newBuffer.duplicate().put(buffer); + buffer = newBuffer; + } +} diff --git a/libjava/classpath/gnu/javax/net/ssl/provider/ServerKeyExchangeParams.java b/libjava/classpath/gnu/javax/net/ssl/provider/ServerKeyExchangeParams.java new file mode 100644 index 000000000..cb523650f --- /dev/null +++ b/libjava/classpath/gnu/javax/net/ssl/provider/ServerKeyExchangeParams.java @@ -0,0 +1,50 @@ +/* ServerKeyExchangeParams.java -- Server key exchange parameters interface. + 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; + +/** + * A parameter structure sent by the server in an SSL key exchange. + * + * @see ServerRSAParams + * @see ServerDHParams + */ +interface ServerKeyExchangeParams extends Constructed +{ + KeyExchangeAlgorithm algorithm (); +} diff --git a/libjava/classpath/gnu/javax/net/ssl/provider/ServerNameList.java b/libjava/classpath/gnu/javax/net/ssl/provider/ServerNameList.java new file mode 100644 index 000000000..38f092476 --- /dev/null +++ b/libjava/classpath/gnu/javax/net/ssl/provider/ServerNameList.java @@ -0,0 +1,311 @@ +/* ServerNameList.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.javax.net.ssl.provider.Extension.Value; + +import java.io.PrintWriter; +import java.io.StringWriter; + +import java.nio.ByteBuffer; +import java.nio.ByteOrder; +import java.nio.CharBuffer; +import java.nio.charset.CharacterCodingException; +import java.nio.charset.Charset; +import java.nio.charset.CharsetEncoder; +import java.util.List; +import java.util.NoSuchElementException; + +/** + * The ServerName extension. + * + *
    + struct {
    +   NameType name_type;
    +   select (name_type) {
    +     case host_name: HostName;
    +   } name;
    +} ServerName;
    +
    +enum {
    +  host_name(0), (255)
    +} NameType;
    +
    +opaque HostName<1..2^16-1>;
    +
    +struct {
    +  ServerName server_name_list<1..2^16-1>
    +} ServerNameList;
    + * + *

    Implementation note: this class does not currently contain a + * set method. If you are modifying this list, then use the + * {@link #get(int)} method, and modify the returned {@link ServerName}. + * + * @author csm + */ +public class ServerNameList extends Value implements Iterable +{ + private ByteBuffer buffer; + + public ServerNameList (final ByteBuffer buffer) + { + this.buffer = buffer.duplicate().order(ByteOrder.BIG_ENDIAN); + } + + public ServerNameList(List names) + { + int length = 2; + for (ServerName name : names) + length += name.length(); + buffer = ByteBuffer.allocate(length); + buffer.putShort((short) (length - 2)); + for (ServerName name : names) + buffer.put(name.buffer()); + buffer.rewind(); + } + + public int length() + { + return (buffer.getShort(0) & 0xFFFF) + 2; + } + + public ByteBuffer buffer() + { + return (ByteBuffer) buffer.duplicate().limit(length()); + } + + public int size() + { + int n = 0; + final int len = length(); + for (int i = 2; i < len; ) + { + int l = buffer.getShort(i+1); + i += l + 3; + n++; + } + return n; + } + + public ServerName get (int index) + { + final int len = length(); + if (len == 0) + throw new IndexOutOfBoundsException("0; " + index); + int n = 0; + int i; + int l = buffer.getShort(3); + for (i = 2; i < len && n < index; ) + { + l = buffer.getShort(i+1); + i += l + 3; + n++; + } + if (n < index) + throw new IndexOutOfBoundsException(n + "; " + index); + ByteBuffer buf = ((ByteBuffer) buffer.duplicate().position(i).limit(i+l+3)).slice(); + return new ServerName (buf); + } + + public void setLength(final int newLength) + { + if (newLength < 0 || newLength > 65535) + throw new IllegalArgumentException("length must be between 0 and 65535"); + buffer.putShort(0, (short) newLength); + } + + public String toString() + { + return toString(null); + } + + public String toString(String prefix) + { + StringWriter str = new StringWriter(); + PrintWriter out = new PrintWriter(str); + if (prefix != null) out.print(prefix); + out.println ("ServerNameList {"); + String subprefix = " "; + if (prefix != null) + subprefix = prefix + subprefix; + for (ServerName name : this) + { + out.println (name.toString(subprefix)); + } + if (prefix != null) out.print(prefix); + out.print ("};"); + return str.toString(); + } + + public java.util.Iterator iterator() + { + return new Iterator(); + } + + public class Iterator implements java.util.Iterator + { + private int index; + + public Iterator() + { + index = 0; + } + + public boolean hasNext() + { + return index < size(); + } + + public ServerName next() throws NoSuchElementException + { + try + { + return get (index++); + } + catch (IndexOutOfBoundsException ioobe) + { + throw new NoSuchElementException(); + } + } + + public void remove() + { + throw new UnsupportedOperationException(); + } + } + + public static class ServerName implements Constructed + { + private ByteBuffer buffer; + + public ServerName(final ByteBuffer buffer) + { + this.buffer = buffer.duplicate().order(ByteOrder.BIG_ENDIAN); + } + + public ServerName(NameType type, String name) + { + CharsetEncoder utf8 = Charset.forName("UTF-8").newEncoder(); + ByteBuffer nameBuf = null; + try + { + nameBuf = utf8.encode(CharBuffer.wrap(name)); + } + catch (CharacterCodingException cce) + { + // We don't expect this to happen; it's UTF-8. + throw new IllegalArgumentException(cce); + } + int length = 3 + nameBuf.remaining(); + buffer = ByteBuffer.allocate(length); + buffer.put((byte) type.getValue()); + buffer.putShort((short) (length - 3)); + buffer.put(nameBuf); + buffer.rewind(); + } + + public int length() + { + return (buffer.getShort(1) & 0xFFFF) + 3; + } + + public ByteBuffer buffer() + { + return (ByteBuffer) buffer.duplicate().limit(length()); + } + + public NameType type() + { + int v = (buffer.get(0) & 0xFF); + if (v == 0) + { + return NameType.HOST_NAME; + } + throw new IllegalArgumentException ("illegal name type: " + v); + } + + public String name() + { + int len = length(); + Charset cs = Charset.forName ("UTF-8"); + return cs.decode(((ByteBuffer) buffer.duplicate().position(3).limit(len))).toString(); + } + + public String toString() + { + return toString (null); + } + + public String toString(String prefix) + { + StringWriter str = new StringWriter(); + PrintWriter out = new PrintWriter(str); + if (prefix != null) out.print (prefix); + out.println ("struct {"); + if (prefix != null) out.print (prefix); + out.print (" name_type = "); + out.print (type()); + out.println (";"); + if (prefix != null) out.print (prefix); + out.print (" server_name = "); + out.print (name()); + out.println (";"); + if (prefix != null) out.print (prefix); + out.print ("} ServerName;"); + return str.toString(); + } + } + + public static enum NameType + { + HOST_NAME (0); + + private final int value; + + private NameType (int value) + { + this.value = value; + } + + public int getValue() + { + return value; + } + } +} diff --git a/libjava/classpath/gnu/javax/net/ssl/provider/ServerPSKParameters.java b/libjava/classpath/gnu/javax/net/ssl/provider/ServerPSKParameters.java new file mode 100644 index 000000000..9ecedb513 --- /dev/null +++ b/libjava/classpath/gnu/javax/net/ssl/provider/ServerPSKParameters.java @@ -0,0 +1,127 @@ +/* ServerPSKParameters.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.PrintWriter; +import java.io.StringWriter; +import java.nio.ByteBuffer; +import java.nio.ByteOrder; +import java.nio.charset.Charset; + +/** + *

    +      struct {
    +          select (KeyExchangeAlgorithm) {
    +              /* other cases for rsa, diffie_hellman, etc. */
    +              case psk:  /* NEW */
    +                  opaque psk_identity_hint<0..2^16-1>;
    +          };
    +      } ServerKeyExchange;
    + * + * @author Casey Marshall (csm@gnu.org) + */ +public class ServerPSKParameters implements Builder, Constructed, ServerKeyExchangeParams +{ + private ByteBuffer buffer; + + public ServerPSKParameters(ByteBuffer buffer) + { + this.buffer = buffer.duplicate().order(ByteOrder.BIG_ENDIAN); + } + + public ServerPSKParameters(String identityHint) + { + Charset utf8 = Charset.forName("UTF-8"); + ByteBuffer identityHintBuffer = utf8.encode(identityHint); + buffer = ByteBuffer.allocate(2 + identityHintBuffer.remaining()); + buffer.putShort((short) identityHintBuffer.remaining()); + buffer.put(identityHintBuffer); + buffer.rewind(); + } + + public KeyExchangeAlgorithm algorithm() + { + return KeyExchangeAlgorithm.PSK; + } + + /* (non-Javadoc) + * @see gnu.javax.net.ssl.provider.Builder#buffer() + */ + public ByteBuffer buffer() + { + return (ByteBuffer) buffer.duplicate().rewind().limit(length()); + } + + /* (non-Javadoc) + * @see gnu.javax.net.ssl.provider.Constructed#length() + */ + public int length() + { + return (buffer.getShort(0) & 0xFFFF) + 2; + } + + public String identityHint() + { + Charset utf8 = Charset.forName("UTF-8"); + return utf8.decode((ByteBuffer) buffer.duplicate().position(2).limit(length())).toString(); + } + + public @Override String toString() + { + return toString(null); + } + + /* (non-Javadoc) + * @see gnu.javax.net.ssl.provider.Constructed#toString(java.lang.String) + */ + public String toString(String prefix) + { + StringWriter str = new StringWriter(); + PrintWriter out = new PrintWriter(str); + if (prefix != null) out.print(prefix); + out.println("struct {"); + if (prefix != null) out.print(prefix); + out.print(" identity_hint = "); + out.print(identityHint()); + out.println(";"); + if (prefix != null) out.print(prefix); + out.print("} ServerPSKParamaters;"); + return str.toString(); + } +} diff --git a/libjava/classpath/gnu/javax/net/ssl/provider/ServerRSAParams.java b/libjava/classpath/gnu/javax/net/ssl/provider/ServerRSAParams.java new file mode 100644 index 000000000..ff265ce8a --- /dev/null +++ b/libjava/classpath/gnu/javax/net/ssl/provider/ServerRSAParams.java @@ -0,0 +1,163 @@ +/* ServerRSAParams.java -- The server's RSA parameters. + 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.PrintWriter; +import java.io.StringWriter; +import java.math.BigInteger; +import java.nio.ByteBuffer; + +/** + * The ServerRSAParams structure. + * + *
    +struct
    +{
    +  opaque rsa_modulus<1..2^16-1>;
    +  opaque rsa_exponent<1..2^16-1>;
    +} ServerRSAParams;
    +
    + */ +public class ServerRSAParams implements ServerKeyExchangeParams +{ + + private final ByteBuffer buffer; + + public ServerRSAParams (final ByteBuffer buffer) + { + this.buffer = buffer; + } + + public KeyExchangeAlgorithm algorithm () + { + return KeyExchangeAlgorithm.RSA; + } + + public int length () + { + int offset = buffer.getShort (0) & 0xFFFF; + return (buffer.getShort (offset + 2) & 0xFFFF) + offset + 4; + } + + /** + * Gets the modulus field. + * + * @return The modulus. + */ + public BigInteger modulus () + { + int len = buffer.getShort (0) & 0xFFFF; + byte[] buf = new byte[len]; + buffer.position (2); + buffer.get (buf); + return new BigInteger (1, buf); + } + + /** + * Returns the exponent field. + * + * @return The exponent. + */ + public BigInteger exponent () + { + int off = (buffer.getShort (0) & 0xFFFF) + 2; + int len = buffer.getShort (off) & 0xFFFF; + byte[] buf = new byte[len]; + buffer.position (off + 2); + buffer.get (buf); + return new BigInteger (1, buf); + } + + /** + * Sets the modulus. + * + * @param modulus The modulus. + * @throws java.nio.ReadOnlyBufferException If the underlying buffer + * is not writable. + */ + public void setModulus (final BigInteger modulus) + { + byte[] buf = modulus.toByteArray (); + int length = (buf[0] == 0x00 ? buf.length - 1 : buf.length); + int offset = (buf[0] == 0x00 ? 1 : 0); + buffer.putShort (0, (short) length); + buffer.position (2); + buffer.put (buf, offset, length); + } + + /** + * Sets the exponent. + * + * @param exponent The exponent. + * @throws java.nio.ReadOnlyBufferException If the underlying buffer + * is not writeable. + */ + public void setExponent (final BigInteger exponent) + { + byte[] buf = exponent.toByteArray (); + int length = (buf[0] == 0x00 ? buf.length -1 : buf.length); + int offset = (buf[0] == 0x00 ? 1 : 0); + int where = (buffer.getShort (0) & 0xFFFF) + 2; + buffer.putShort (where, (short) length); + buffer.position (where + 2); + buffer.put (buf, offset, length); + } + + public String toString () + { + return toString (null); + } + + public String toString (final String prefix) + { + StringWriter str = new StringWriter (); + PrintWriter out = new PrintWriter (str); + if (prefix != null) out.print (prefix); + out.println ("struct {"); + if (prefix != null) out.print (prefix); + out.print (" rsa_modulus: "); + out.println (modulus ().toString (16)); + if (prefix != null) out.print (prefix); + out.print (" rsa_exponent: "); + out.println (exponent ()); + if (prefix != null) out.print (prefix); + out.print ("} ServerRSAParams;"); + return str.toString (); + } +} diff --git a/libjava/classpath/gnu/javax/net/ssl/provider/ServerRSA_PSKParameters.java b/libjava/classpath/gnu/javax/net/ssl/provider/ServerRSA_PSKParameters.java new file mode 100644 index 000000000..0895afe96 --- /dev/null +++ b/libjava/classpath/gnu/javax/net/ssl/provider/ServerRSA_PSKParameters.java @@ -0,0 +1,62 @@ +/* ServerRSA_PSKParameters.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; + +/** + * @author Casey Marshall (csm@gnu.org) + */ +public class ServerRSA_PSKParameters extends ServerPSKParameters +{ + public ServerRSA_PSKParameters(ByteBuffer buffer) + { + super(buffer); + } + + public ServerRSA_PSKParameters(String identityHint) + { + super(identityHint); + } + + public @Override KeyExchangeAlgorithm algorithm() + { + return KeyExchangeAlgorithm.RSA_PSK; + } +} diff --git a/libjava/classpath/gnu/javax/net/ssl/provider/SessionImpl.java b/libjava/classpath/gnu/javax/net/ssl/provider/SessionImpl.java new file mode 100644 index 000000000..6eb070efc --- /dev/null +++ b/libjava/classpath/gnu/javax/net/ssl/provider/SessionImpl.java @@ -0,0 +1,192 @@ +/* SessionImpl.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.javax.crypto.key.GnuPBEKey; +import gnu.javax.net.ssl.Session; +import java.io.IOException; +import java.io.Serializable; + +import java.security.InvalidKeyException; +import java.security.NoSuchAlgorithmException; +import java.security.SecureRandom; +import javax.crypto.Cipher; +import javax.crypto.IllegalBlockSizeException; +import javax.crypto.NoSuchPaddingException; +import javax.crypto.SealedObject; +import javax.net.ssl.SSLException; + +public class SessionImpl extends Session +{ + static final long serialVersionUID = 8932976607588442485L; + CipherSuite suite; + ProtocolVersion version; + byte[] privateDataSalt; + SealedObject sealedPrivateData; + MaxFragmentLength maxLength; + + transient PrivateData privateData; + + public SessionImpl() + { + super(); + privateData = new PrivateData(); + } + + SecureRandom random () + { + return random; + } + + public String getProtocol() + { + return version.toString(); + } + + public void prepare(char[] passwd) throws SSLException + { + try + { + privateDataSalt = new byte[32]; + random.nextBytes(privateDataSalt); + GnuPBEKey key = new GnuPBEKey(passwd, privateDataSalt, 1000); + Cipher cipher = Cipher.getInstance("PBEWithHMacSHA256AndAES/OFB/PKCS7Padding"); + cipher.init(Cipher.ENCRYPT_MODE, key); + sealedPrivateData = new SealedObject(privateData, cipher); + } + catch (IllegalBlockSizeException ibse) + { + throw new SSLException(ibse); + } + catch (InvalidKeyException ike) + { + throw new SSLException(ike); + } + catch (IOException ioe) + { + throw new SSLException(ioe); + } + catch (NoSuchAlgorithmException nsae) + { + throw new SSLException(nsae); + } + catch (NoSuchPaddingException nspe) + { + throw new SSLException(nspe); + } + } + + public void repair(char[] passwd) throws SSLException + { + try + { + GnuPBEKey key = new GnuPBEKey(passwd, privateDataSalt, 1000); + privateData = (PrivateData) sealedPrivateData.getObject(key); + } + catch (ClassNotFoundException cnfe) + { + throw new SSLException(cnfe); + } + catch (InvalidKeyException ike) + { + throw new SSLException(ike); + } + catch (IOException ioe) + { + throw new SSLException(ioe); + } + catch (NoSuchAlgorithmException nsae) + { + throw new SSLException(nsae); + } + } + + public SealedObject privateData() throws SSLException + { + if (privateData == null) + throw new SSLException("this session has not been prepared"); + return sealedPrivateData; + } + + public void setPrivateData(SealedObject so) throws SSLException + { + this.sealedPrivateData = so; + } + + void setApplicationBufferSize(int size) + { + applicationBufferSize = size; + } + + void setRandom(SecureRandom random) + { + this.random = random; + } + + void setTruncatedMac(boolean truncatedMac) + { + this.truncatedMac = truncatedMac; + } + + void setId(Session.ID id) + { + this.sessionId = id; + } + + void setLocalCertificates(java.security.cert.Certificate[] chain) + { + this.localCerts = chain; + } + + void setPeerCertificates(java.security.cert.Certificate[] chain) + { + this.peerCerts = chain; + } + + void setPeerVerified(boolean peerVerified) + { + this.peerVerified = peerVerified; + } + + static class PrivateData implements Serializable + { + static final long serialVersionUID = -8040597659545984581L; + byte[] masterSecret; + } +} diff --git a/libjava/classpath/gnu/javax/net/ssl/provider/Signature.java b/libjava/classpath/gnu/javax/net/ssl/provider/Signature.java new file mode 100644 index 000000000..160dd805f --- /dev/null +++ b/libjava/classpath/gnu/javax/net/ssl/provider/Signature.java @@ -0,0 +1,157 @@ +/* Signature.java -- SSL Signature structure. + 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.PrintWriter; +import java.io.StringWriter; + +import java.nio.ByteBuffer; + +/** + * The signature structure. + * + *
    +select (SignatureAlgorithm)
    +{
    +case anonymous:
    +  struct { };
    +case rsa:
    +  digitally-signed struct
    +  {
    +    opaque md5_hash[16];
    +    opaque sha_hash[20];
    +  };
    +case dsa:
    +  digitally-signed struct
    +  {
    +    opaque sha_hash[20];
    +  };
    +} Signature;
    + */ +public class Signature implements Builder, Constructed +{ + + // Fields. + // ------------------------------------------------------------------------- + + private final ByteBuffer buffer; + private final SignatureAlgorithm alg; + + // Constructor. + // ------------------------------------------------------------------------- + + public Signature (final ByteBuffer buffer, final SignatureAlgorithm alg) + { + this.buffer = buffer; + this.alg = alg; + } + + public Signature (final byte[] sigValue, final SignatureAlgorithm alg) + { + buffer = ByteBuffer.allocate(sigValue.length + 2); + buffer.putShort((short) sigValue.length); + buffer.put(sigValue); + buffer.position(0); + this.alg = alg; + } + + // Instance methods. + // ------------------------------------------------------------------------- + + public int length () + { + if (alg.equals (SignatureAlgorithm.ANONYMOUS)) + return 0; + return (buffer.getShort (0) & 0xFFFF) + 2; + } + + public ByteBuffer buffer() + { + return (ByteBuffer) buffer.duplicate().limit(length()); + } + + public byte[] signature () + { + if (alg.equals (SignatureAlgorithm.ANONYMOUS)) + return new byte[0]; + int length = buffer.getShort (0) & 0xFFFF; + byte[] buf = new byte[length]; + ((ByteBuffer) buffer.duplicate().position(2)).get(buf); + return buf; + } + + public void setSignature (final byte[] signature) + { + setSignature (signature, 0, signature.length); + } + + public void setSignature (final byte[] signature, final int offset, final int length) + { + if (alg.equals (SignatureAlgorithm.ANONYMOUS)) + return; + buffer.putShort (0, (short) length); + buffer.position (2); + buffer.put (signature, offset, length); + } + + public String toString () + { + return toString (null); + } + + public String toString (final String prefix) + { + StringWriter str = new StringWriter(); + PrintWriter out = new PrintWriter(str); + if (prefix != null) + out.print (prefix); + out.println("struct {"); + if (!alg.equals (SignatureAlgorithm.ANONYMOUS)) + { + String subprefix = " "; + if (prefix != null) + subprefix = prefix + subprefix; + out.print (Util.hexDump (signature (), subprefix)); + } + if (prefix != null) + out.print (prefix); + out.print ("} Signature;"); + return str.toString(); + } +} diff --git a/libjava/classpath/gnu/javax/net/ssl/provider/SignatureAlgorithm.java b/libjava/classpath/gnu/javax/net/ssl/provider/SignatureAlgorithm.java new file mode 100644 index 000000000..79cff5626 --- /dev/null +++ b/libjava/classpath/gnu/javax/net/ssl/provider/SignatureAlgorithm.java @@ -0,0 +1,62 @@ +/* SignatureAlgorithm.java -- Signature algorithm enumeration. + 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; + +public enum SignatureAlgorithm +{ + ANONYMOUS, RSA, DSA; + + /** + * Returns the algorithm name for this signature algorithm, which can + * be used with the JCA API to get a {@link java.security.Signature} for + * that algorithm. + * + * @return The algorithm name. + */ + public String algorithm() + { + switch (this) + { + case ANONYMOUS: return null; + case RSA: return "TLSv1.1-RSA"; + case DSA: return "DSS"; + } + return null; + } +} diff --git a/libjava/classpath/gnu/javax/net/ssl/provider/SimpleSessionContext.java b/libjava/classpath/gnu/javax/net/ssl/provider/SimpleSessionContext.java new file mode 100644 index 000000000..8d5745061 --- /dev/null +++ b/libjava/classpath/gnu/javax/net/ssl/provider/SimpleSessionContext.java @@ -0,0 +1,144 @@ +/* SimpleSessionContext.java -- memory-only session store. + 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.javax.net.ssl.AbstractSessionContext; +import gnu.javax.net.ssl.Session; +import gnu.javax.net.ssl.SessionStoreException; +import java.util.Enumeration; +import java.util.HashMap; +import java.util.Iterator; +import java.util.Map; + +/** + * A simple, non-persistent SessionContext. + * + * @author csm + */ +public final class SimpleSessionContext + extends AbstractSessionContext +{ + /** + * By default, sessions last for 5 minutes. + */ + public static final int DEFAULT_TIMEOUT = 300; + + private final HashMap store; + private int storeLimit; + + public SimpleSessionContext() + { + super(DEFAULT_TIMEOUT); + storeLimit = 0; + store = new HashMap(); + } + + @Override + protected Session implGet(byte[] sessionId) + { + return store.get(new Session.ID(sessionId)); + } + + @Override + public void load(char[] password) throws SessionStoreException + { + // Not supported. Memory-only. + } + + @Override + public void put(Session session) + { + if (storeLimit > 0 && store.size() >= storeLimit) + { + Session oldest = null; + for (Map.Entry e : store.entrySet()) + { + Session s = e.getValue(); + long stamp = s.getLastAccessedTime(); + if (oldest == null || oldest.getLastAccessedTime() > stamp) + oldest = s; + } + store.remove(oldest.id()); + } + store.put(session.id(), session); + } + + @Override + public void remove(byte[] sessionId) + { + store.remove(new Session.ID(sessionId)); + } + + @Override + public void store(char[] password) throws SessionStoreException + { + // Not supported. Memory-only. + } + + public Enumeration getIds() + { + return new Enumeration() + { + Iterator it = store.keySet().iterator(); + + public boolean hasMoreElements() + { + return it.hasNext(); + } + + public Object nextElement() + { + return it.next().id(); + } + }; + } + + public int getSessionCacheSize() + { + return storeLimit; + } + + public void setSessionCacheSize(int size) + { + if (size < 0) + throw new IllegalArgumentException("cache size must be nonnegative"); + this.storeLimit = size; + } + +} diff --git a/libjava/classpath/gnu/javax/net/ssl/provider/TLSHMac.java b/libjava/classpath/gnu/javax/net/ssl/provider/TLSHMac.java new file mode 100644 index 000000000..8bdda930b --- /dev/null +++ b/libjava/classpath/gnu/javax/net/ssl/provider/TLSHMac.java @@ -0,0 +1,137 @@ +/* TLSHMac.java -- HMAC used in TLS. + Copyright (C) 2001, 2002, 2003, 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.security.InvalidKeyException; +import java.util.Map; + +import gnu.java.security.hash.IMessageDigest; +import gnu.javax.crypto.mac.HMac; + +/** + * The operation of this HMac is identical to normal HMacs, but this one + * allows keys with short lengths (including zero). + */ +class TLSHMac extends HMac +{ + + // Constants. + // ------------------------------------------------------------------------- + + private static final byte IPAD_BYTE = 0x36; + private static final byte OPAD_BYTE = 0x5C; + + // Constructor. + // ------------------------------------------------------------------------- + + TLSHMac(IMessageDigest hash) + { + super(hash); + } + + // Instance methods. + // ------------------------------------------------------------------------- + + public void init(Map attributes) + throws InvalidKeyException, IllegalStateException + { + Integer ts = (Integer) attributes.get(TRUNCATED_SIZE); + truncatedSize = (ts == null ? macSize : ts.intValue()); + if (truncatedSize < (macSize / 2)) { + throw new IllegalArgumentException("Truncated size too small"); + } else if (truncatedSize < 10) { + throw new IllegalArgumentException("Truncated size less than 80 bits"); + } + + // we dont use/save the key outside this method + byte[] K = (byte[]) attributes.get(MAC_KEY_MATERIAL); + if (K == null) { // take it as an indication to re-use previous key if set + if (ipadHash == null) + { + throw new InvalidKeyException("Null key"); + } + // we already went through the motions; ie. up to step #4. re-use + underlyingHash = (IMessageDigest) ipadHash.clone(); + return; + } + + if (K.length > blockSize) + { + // (0) replace K with HASH(K) if K is larger than the hash's + // block size. Then pad with zeros until it is the correct + // size (the next `if'). + underlyingHash.update(K, 0, K.length); + K = underlyingHash.digest(); + } + if (K.length < blockSize) + { + // (1) append zeros to the end of K to create a B byte string + // (e.g., if K is of length 20 bytes and B=64, then K will be + // appended with 44 zero bytes 0x00) + int limit = (K.length > blockSize) ? blockSize : K.length; + byte[] newK = new byte[blockSize]; + System.arraycopy(K, 0, newK, 0, limit); + K = newK; + } + + underlyingHash.reset(); + opadHash = (IMessageDigest) underlyingHash.clone(); + if (ipad == null) + { + ipad = new byte[blockSize]; + } + // (2) XOR (bitwise exclusive-OR) the B byte string computed in step + // (1) with ipad + // (3) append the stream of data 'text' to the B byte string resulting + // from step (2) + // (4) apply H to the stream generated in step (3) + for (int i = 0; i < blockSize; i++) + { + ipad[i] = (byte)(K[i] ^ IPAD_BYTE); + } + for (int i = 0; i < blockSize; i++) + { + opadHash.update((byte)(K[i] ^ OPAD_BYTE)); + } + + underlyingHash.update(ipad, 0, blockSize); + ipadHash = (IMessageDigest) underlyingHash.clone(); + K = null; + } +} diff --git a/libjava/classpath/gnu/javax/net/ssl/provider/TLSRandom.java b/libjava/classpath/gnu/javax/net/ssl/provider/TLSRandom.java new file mode 100644 index 000000000..ded632928 --- /dev/null +++ b/libjava/classpath/gnu/javax/net/ssl/provider/TLSRandom.java @@ -0,0 +1,252 @@ +/* TLSRandom.java -- The TLS pseudo-random function. + 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.security.InvalidKeyException; +import java.util.HashMap; +import java.util.Map; + +import gnu.java.security.hash.HashFactory; +import gnu.javax.crypto.mac.IMac; +import gnu.java.security.prng.IRandom; + +class TLSRandom implements IRandom +{ + + // Fields. + // ------------------------------------------------------------------------- + + /** + * Property name for the secret that will be used to initialize the HMACs. + */ + static final String SECRET = "jessie.tls.prng.secret"; + + /** + * Property name for the seed. + */ + static final String SEED = "jessie.tls.prng.seed"; + + private final IMac hmac_sha, hmac_md5; + private byte[] sha_a, md5_a; + private byte[] seed; + private final byte[] buffer; + private int idx; + private boolean init; + + // Constructors. + // ------------------------------------------------------------------------- + + TLSRandom() + { + hmac_sha = new TLSHMac(HashFactory.getInstance("SHA1")); + hmac_md5 = new TLSHMac(HashFactory.getInstance("MD5")); + buffer = new byte[80]; // 80 == LCM of 16 and 20. + idx = 0; + init = false; + } + + // Instance methods. + // ------------------------------------------------------------------------- + + public Object clone() + { + try + { + return super.clone(); + } + catch (CloneNotSupportedException shouldNotHappen) + { + throw new Error(); + } + } + + public void init(Map attributes) + { + HashMap sha_attr = new HashMap(); + HashMap md5_attr = new HashMap(); + byte[] secret = (byte[]) attributes.get(SECRET); + if (secret != null) + { + int l = (secret.length >>> 1) + (secret.length & 1); + byte[] s1 = Util.trim(secret, 0, l); + byte[] s2 = Util.trim(secret, secret.length - l, l); + md5_attr.put(IMac.MAC_KEY_MATERIAL, s1); + sha_attr.put(IMac.MAC_KEY_MATERIAL, s2); + try + { + hmac_md5.init(md5_attr); + hmac_sha.init(sha_attr); + } + catch (InvalidKeyException ike) + { + throw new Error(ike.toString()); + } + } + else if (!init) + { + throw new IllegalArgumentException("no secret supplied"); + } + // else re-use + + byte[] seeed = (byte[]) attributes.get(SEED); + if (seeed != null) + { + seed = (byte[]) seeed.clone(); + } + else if (!init) + { + throw new IllegalArgumentException("no seed supplied"); + } + // else re-use + + // A(0) is the seed, A(1) = HMAC_hash(secret, A(0)). + hmac_md5.update(seed, 0, seed.length); + md5_a = hmac_md5.digest(); + hmac_md5.reset(); + hmac_sha.update(seed, 0, seed.length); + sha_a = hmac_sha.digest(); + hmac_sha.reset(); + fillBuffer(); + init = true; + } + + public String name() + { + return "TLSRandom"; + } + + public byte nextByte() + { + if (!init) + throw new IllegalStateException(); + if (idx >= buffer.length) + fillBuffer(); + return buffer[idx++]; + } + + public void nextBytes(byte[] buf, int off, int len) + { + if (!init) + throw new IllegalStateException(); + if (buf == null) + throw new NullPointerException(); + if (off < 0 || off > buf.length || off + len > buf.length) + throw new ArrayIndexOutOfBoundsException(); + int count = 0; + if (idx >= buffer.length) + fillBuffer(); + while (count < len) + { + int l = Math.min(buffer.length-idx, len-count); + System.arraycopy(buffer, idx, buf, off+count, l); + idx += l; + count += l; + if (count < len && idx >= buffer.length) + fillBuffer(); + } + } + + // For future versions of GNU Crypto. No-ops. + public void addRandomByte (byte b) + { + } + + public void addRandomBytes(byte[] buffer) { + addRandomBytes(buffer, 0, buffer.length); + } + + public void addRandomBytes (byte[] b, int i, int j) + { + } + + // Own methods. + // ------------------------------------------------------------------------- + + /* + * The PRF is defined as: + * + * PRF(secret, label, seed) = P_MD5(S1, label + seed) XOR + * P_SHA-1(S2, label + seed); + * + * P_hash is defined as: + * + * P_hash(secret, seed) = HMAC_hash(secret, A(1) + seed) + + * HMAC_hash(secret, A(2) + seed) + + * HMAC_hash(secret, A(3) + seed) + ... + * + * And A() is defined as: + * + * A(0) = seed + * A(i) = HMAC_hash(secret, A(i-1)) + * + * For simplicity, we compute an 80-byte block on each call, which + * corresponds to five iterations of MD5, and four of SHA-1. + */ + private synchronized void fillBuffer() + { + int len = hmac_md5.macSize(); + for (int i = 0; i < buffer.length; i += len) + { + hmac_md5.update(md5_a, 0, md5_a.length); + hmac_md5.update(seed, 0, seed.length); + byte[] b = hmac_md5.digest(); + hmac_md5.reset(); + System.arraycopy(b, 0, buffer, i, len); + hmac_md5.update(md5_a, 0, md5_a.length); + md5_a = hmac_md5.digest(); + hmac_md5.reset(); + } + len = hmac_sha.macSize(); + for (int i = 0; i < buffer.length; i += len) + { + hmac_sha.update(sha_a, 0, sha_a.length); + hmac_sha.update(seed, 0, seed.length); + byte[] b = hmac_sha.digest(); + hmac_sha.reset(); + for (int j = 0; j < len; j++) + { + buffer[j + i] ^= b[j]; + } + hmac_sha.update(sha_a, 0, sha_a.length); + sha_a = hmac_sha.digest(); + hmac_sha.reset(); + } + idx = 0; + } +} diff --git a/libjava/classpath/gnu/javax/net/ssl/provider/TruncatedHMAC.java b/libjava/classpath/gnu/javax/net/ssl/provider/TruncatedHMAC.java new file mode 100644 index 000000000..97fff98dc --- /dev/null +++ b/libjava/classpath/gnu/javax/net/ssl/provider/TruncatedHMAC.java @@ -0,0 +1,76 @@ +/* TruncatedHMAC.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.javax.net.ssl.provider.Extension.Value; + +import java.nio.ByteBuffer; + +/** + * The value type for the {@link Extension.Type#TRUNCATED_HMAC} extension. + * This extension has an empty value; this class is thusly empty. + * + * @author csm + */ +public class TruncatedHMAC extends Value +{ + + public int length() + { + return 0; + } + + public ByteBuffer buffer() + { + return ByteBuffer.wrap(new byte[0]); + } + + public String toString() + { + return toString(null); + } + + public String toString(String prefix) + { + String s = "TruncatedHMAC;"; + if (prefix != null) + s = prefix + s; + return s; + } +} diff --git a/libjava/classpath/gnu/javax/net/ssl/provider/TrustedAuthorities.java b/libjava/classpath/gnu/javax/net/ssl/provider/TrustedAuthorities.java new file mode 100644 index 000000000..72d072739 --- /dev/null +++ b/libjava/classpath/gnu/javax/net/ssl/provider/TrustedAuthorities.java @@ -0,0 +1,297 @@ +/* TrustedAuthorities.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.javax.net.ssl.provider.Extension.Value; + +import java.io.PrintWriter; +import java.io.StringWriter; +import java.nio.ByteBuffer; +import java.nio.ByteOrder; +import java.util.Iterator; +import java.util.NoSuchElementException; + +import javax.security.auth.x500.X500Principal; + +/** + * The trusted authorities hello extension. + * + *
    +struct {
    +  TrustedAuthority trusted_authorities_list<0..2^16-1>;
    +} TrustedAuthorities;
    +
    +struct {
    +  IdentifierType identifier_type;
    +  select (identifier_type) {
    +    case pre_agreed: struct {};
    +    case key_sha1_hash: SHA1Hash;
    +    case x509_name: DistinguishedName;
    +    case cert_sha1_hash: SHA1Hash;
    +  } identifier;
    +} TrustedAuthority;
    +
    +enum {
    +  pre_agreed(0), key_sha1_hash(1), x509_name(2),
    +  cert_sha1_hash(3), (255)
    +} IdentifierType;
    +
    +opaque DistinguishedName<1..2^16-1>;
    + * + * @author csm + */ +public class TrustedAuthorities extends Value + implements Iterable +{ + private final ByteBuffer buffer; + + public TrustedAuthorities(final ByteBuffer buffer) + { + this.buffer = buffer.duplicate().order(ByteOrder.BIG_ENDIAN); + } + + // XXX really implement Builder. + + public int length() + { + return 2 + (buffer.getShort(0) & 0xFFFF); + } + + public ByteBuffer buffer() + { + return (ByteBuffer) buffer.duplicate().limit(length()); + } + + public int size() + { + int len = buffer.getShort(0) & 0xFFFF; + int n = 0; + for (int i = 2; i < len; i++) + { + TrustedAuthority auth = + new TrustedAuthority((ByteBuffer) buffer.duplicate().position(i)); + i += auth.length(); + n++; + } + return n; + } + + public TrustedAuthority get(final int index) + { + int len = buffer.getShort(0) & 0xFFFF; + int n = 0; + int i = 2; + while (i < len && n <= index) + { + TrustedAuthority auth = + new TrustedAuthority((ByteBuffer) buffer.duplicate().position(i)); + if (n == index) + return auth; + i += auth.length(); + n++; + } + throw new IndexOutOfBoundsException(); + } + + public String toString() + { + return toString(null); + } + + public String toString(String prefix) + { + StringWriter str = new StringWriter(); + PrintWriter out = new PrintWriter(str); + if (prefix != null) out.print(prefix); + out.println("struct {"); + String subprefix = " "; + if (prefix != null) + subprefix = prefix + subprefix; + for(TrustedAuthority ta : this) + out.println(ta); + if (prefix != null) out.print(prefix); + out.print("} TrustedAuthorities;"); + return str.toString(); + } + + public Iterator iterator() + { + return new AuthoritiesIterator(); + } + + public class AuthoritiesIterator implements Iterator + { + private int index; + + public AuthoritiesIterator() + { + index = 0; + } + + public TrustedAuthority next() throws NoSuchElementException + { + try + { + return get(index++); + } + catch (IndexOutOfBoundsException ioobe) + { + throw new NoSuchElementException(); + } + } + + public boolean hasNext() + { + return index < size(); + } + + public void remove() + { + throw new UnsupportedOperationException(); + } + } + + public static class TrustedAuthority implements Constructed + { + private final ByteBuffer buffer; + + public TrustedAuthority(final ByteBuffer buffer) + { + this.buffer = buffer; + } + + public int length() + { + switch (type().getValue()) + { + case 0: return 1; + case 1: + case 3: return 21; + case 2: return 3 + (buffer.getShort(1) & 0xFFFF); + } + throw new IllegalArgumentException("unknown authority type"); + } + + public byte[] sha1Hash() + { + IdentifierType t = type(); + if (t != IdentifierType.CERT_SHA1_HASH + && t != IdentifierType.KEY_SHA1_HASH) + throw new IllegalArgumentException(t + " does not have a hash value"); + byte[] b = new byte[20]; + ((ByteBuffer) buffer.duplicate().position(1)).get(b); + return b; + } + + public X500Principal name() + { + int len = buffer.getShort(1) & 0xFFFF; + byte[] b = new byte[len]; + ((ByteBuffer) buffer.duplicate().position(3)).get(b); + return new X500Principal(b); + } + + public IdentifierType type() + { + switch (buffer.get(0)) + { + case 0: return IdentifierType.PRE_AGREED; + case 1: return IdentifierType.KEY_SHA1_HASH; + case 2: return IdentifierType.X509_NAME; + case 3: return IdentifierType.CERT_SHA1_HASH; + } + + throw new IllegalArgumentException("invalid IdentifierType"); + } + + public String toString() + { + return toString(null); + } + + public String toString(String prefix) + { + StringWriter str = new StringWriter(); + PrintWriter out = new PrintWriter(str); + if (prefix != null) out.print(prefix); + out.println("struct {"); + if (prefix != null) out.print(prefix); + out.print(" identifier_type = "); + out.print(type()); + out.println(";"); + switch (type().getValue()) + { + case 0: break; + case 1: + case 3: + if (prefix != null) out.print(prefix); + out.print(" sha1_hash = "); + out.print(Util.toHexString(sha1Hash(), ':')); + out.println(";"); + break; + + case 2: + if (prefix != null) out.print(prefix); + out.print(" name = "); + out.print(name()); + out.println(";"); + } + if (prefix != null) out.print(prefix); + out.print("} TrustedAuthority;"); + return str.toString(); + } + } + + public static enum IdentifierType + { + PRE_AGREED (0), KEY_SHA1_HASH (1), X509_NAME (2), CERT_SHA1_HASH (3); + + private final int value; + + private IdentifierType(final int value) + { + this.value = value; + } + + public int getValue() + { + return value; + } + } +} diff --git a/libjava/classpath/gnu/javax/net/ssl/provider/UnresolvedExtensionValue.java b/libjava/classpath/gnu/javax/net/ssl/provider/UnresolvedExtensionValue.java new file mode 100644 index 000000000..94cd091c5 --- /dev/null +++ b/libjava/classpath/gnu/javax/net/ssl/provider/UnresolvedExtensionValue.java @@ -0,0 +1,81 @@ +/* UnresolvedExtensionValue.jav -- + 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.javax.net.ssl.provider.Extension.Value; + +import java.nio.ByteBuffer; + +public class UnresolvedExtensionValue extends Value +{ + private final ByteBuffer buffer; + + public UnresolvedExtensionValue (final ByteBuffer buffer) + { + this.buffer = buffer; + } + + public int length() + { + return buffer.limit(); + } + + public ByteBuffer buffer() + { + return value(); + } + + public ByteBuffer value() + { + return buffer.slice(); + } + + public String toString() + { + return toString(null); + } + + public String toString(final String prefix) + { + String s = Util.hexDump(buffer); + if (prefix != null) + s = prefix + s; + return s; + } +} diff --git a/libjava/classpath/gnu/javax/net/ssl/provider/Util.java b/libjava/classpath/gnu/javax/net/ssl/provider/Util.java new file mode 100644 index 000000000..a2004b7aa --- /dev/null +++ b/libjava/classpath/gnu/javax/net/ssl/provider/Util.java @@ -0,0 +1,495 @@ +/* Util.java -- Miscellaneous utility methods. + 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.java.lang.CPStringBuilder; + +import java.io.PrintWriter; +import java.io.StringWriter; + +import java.lang.reflect.Array; +import java.lang.reflect.InvocationTargetException; +import java.lang.reflect.Method; +import java.math.BigInteger; + +import java.nio.ByteBuffer; + +import java.security.AccessController; +import java.security.PrivilegedAction; +import java.security.Security; + +/** + * A collection of useful class methods. + * + * @author Casey Marshall (rsdio@metastatic.org) + */ +public final class Util +{ + + // Constants. + // ------------------------------------------------------------------------- + + static final String HEX = "0123456789abcdef"; + + // Static methods only. + private Util() { } + + // Class methods. + // ------------------------------------------------------------------------- + + public static Object wrapBuffer(ByteBuffer buffer) + { + return wrapBuffer(buffer, ""); + } + + public static Object wrapBuffer(ByteBuffer buffer, String prefix) + { + return new WrappedBuffer(buffer, prefix); + } + + private static class WrappedBuffer + { + private final ByteBuffer buffer; + private final String prefix; + + WrappedBuffer(ByteBuffer buffer, String prefix) + { + this.buffer = buffer; + this.prefix = prefix; + } + + public String toString() + { + return hexDump(buffer, prefix); + } + } + + /** + * Convert a hexadecimal string into its byte representation. + * + * @param hex The hexadecimal string. + * @return The converted bytes. + */ + public static byte[] toByteArray(String hex) + { + hex = hex.toLowerCase(); + byte[] buf = new byte[hex.length() / 2]; + int j = 0; + for (int i = 0; i < buf.length; i++) + { + buf[i] = (byte) ((Character.digit(hex.charAt(j++), 16) << 4) | + Character.digit(hex.charAt(j++), 16)); + } + return buf; + } + + /** + * Convert a byte array to a hexadecimal string, as though it were a + * big-endian arbitrarily-sized integer. + * + * @param buf The bytes to format. + * @param off The offset to start at. + * @param len The number of bytes to format. + * @return A hexadecimal representation of the specified bytes. + */ + public static String toHexString(byte[] buf, int off, int len) + { + CPStringBuilder str = new CPStringBuilder(); + for (int i = 0; i < len; i++) + { + str.append(HEX.charAt(buf[i+off] >>> 4 & 0x0F)); + str.append(HEX.charAt(buf[i+off] & 0x0F)); + } + return str.toString(); + } + + /** + * See {@link #toHexString(byte[],int,int)}. + */ + public static String toHexString(byte[] buf) + { + return Util.toHexString(buf, 0, buf.length); + } + + /** + * Convert a byte array to a hexadecimal string, separating octets + * with the given character. + * + * @param buf The bytes to format. + * @param off The offset to start at. + * @param len The number of bytes to format. + * @param sep The character to insert between octets. + * @return A hexadecimal representation of the specified bytes. + */ + public static String toHexString(byte[] buf, int off, int len, char sep) + { + CPStringBuilder str = new CPStringBuilder(); + for (int i = 0; i < len; i++) + { + str.append(HEX.charAt(buf[i+off] >>> 4 & 0x0F)); + str.append(HEX.charAt(buf[i+off] & 0x0F)); + if (i < len - 1) + str.append(sep); + } + return str.toString(); + } + + /** + * See {@link #toHexString(byte[],int,int,char)}. + */ + public static String toHexString(byte[] buf, char sep) + { + return Util.toHexString(buf, 0, buf.length, sep); + } + + /** + * Create a representation of the given byte array similar to the + * output of `hexdump -C', which is + * + *

    OFFSET  SIXTEEN-BYTES-IN-HEX  PRINTABLE-BYTES
    + * + *

    The printable bytes show up as-is if they are printable and + * not a newline character, otherwise showing as '.'. + * + * @param buf The bytes to format. + * @param off The offset to start at. + * @param len The number of bytes to encode. + * @param prefix A string to prepend to every line. + * @return The formatted string. + */ + public static String hexDump(byte[] buf, int off, int len, String prefix) + { + String nl = getProperty("line.separator"); + CPStringBuilder str = new CPStringBuilder(); + int i = 0; + while (i < len) + { + if (prefix != null) + str.append(prefix); + str.append(Util.formatInt(i+off, 16, 8)); + str.append(" "); + String s = Util.toHexString(buf, i+off, Math.min(16, len-i), ' '); + str.append(s); + for (int j = s.length(); j < 49; j++) + str.append(" "); + for (int j = 0; j < Math.min(16, len - i); j++) + { + if ((buf[i+off+j] & 0xFF) < 0x20 || (buf[i+off+j] & 0xFF) > 0x7E) + str.append('.'); + else + str.append((char) (buf[i+off+j] & 0xFF)); + } + str.append(nl); + i += 16; + } + return str.toString(); + } + + public static String hexDump (ByteBuffer buf) + { + return hexDump (buf, null); + } + + public static String hexDump (ByteBuffer buf, String prefix) + { + buf = buf.duplicate(); + StringWriter str = new StringWriter (); + PrintWriter out = new PrintWriter (str); + int i = 0; + int len = buf.remaining(); + byte[] line = new byte[16]; + while (i < len) + { + if (prefix != null) + out.print(prefix); + out.print(Util.formatInt (i, 16, 8)); + out.print(" "); + int l = Math.min(16, len - i); + buf.get(line, 0, l); + String s = Util.toHexString(line, 0, l, ' '); + out.print(s); + for (int j = s.length(); j < 49; j++) + out.print(' '); + for (int j = 0; j < l; j++) + { + int c = line[j] & 0xFF; + if (c < 0x20 || c > 0x7E) + out.print('.'); + else + out.print((char) c); + } + out.println(); + i += 16; + } + return str.toString(); + } + + /** + * See {@link #hexDump(byte[],int,int,String)}. + */ + public static String hexDump(byte[] buf, int off, int len) + { + return hexDump(buf, off, len, ""); + } + + /** + * See {@link #hexDump(byte[],int,int,String)}. + */ + public static String hexDump(byte[] buf, String prefix) + { + return hexDump(buf, 0, buf.length, prefix); + } + + /** + * See {@link #hexDump(byte[],int,int,String)}. + */ + public static String hexDump(byte[] buf) + { + return hexDump(buf, 0, buf.length); + } + + /** + * Format an integer into the specified radix, zero-filled. + * + * @param i The integer to format. + * @param radix The radix to encode to. + * @param len The target length of the string. The string is + * zero-padded to this length, but may be longer. + * @return The formatted integer. + */ + public static String formatInt(int i, int radix, int len) + { + String s = Integer.toString(i, radix); + CPStringBuilder buf = new CPStringBuilder(); + for (int j = 0; j < len - s.length(); j++) + buf.append("0"); + buf.append(s); + return buf.toString(); + } + + /** + * Concatenate two byte arrays into one. + * + * @param b1 The first byte array. + * @param b2 The second byte array. + * @return The concatenation of b1 and b2. + */ + public static byte[] concat(byte[] b1, byte[] b2) + { + byte[] b3 = new byte[b1.length+b2.length]; + System.arraycopy(b1, 0, b3, 0, b1.length); + System.arraycopy(b2, 0, b3, b1.length, b2.length); + return b3; + } + + /** + * See {@link #trim(byte[],int,int)}. + */ + public static byte[] trim(byte[] buffer, int len) + { + return trim(buffer, 0, len); + } + + /** + * Returns a portion of a byte array, possibly zero-filled. + * + * @param buffer The byte array to trim. + * @param off The offset to begin reading at. + * @param len The number of bytes to return. This value can be larger + * than buffer.length - off, in which case the rest of the + * returned byte array will be filled with zeros. + * @throws IndexOutOfBoundsException If off or len is + * negative, or if off is larger than the byte array's + * length. + * @return The trimmed byte array. + */ + public static byte[] trim(byte[] buffer, int off, int len) + { + if (off < 0 || len < 0 || off > buffer.length) + throw new IndexOutOfBoundsException("max=" + buffer.length + + " off=" + off + " len=" + len); + if (off == 0 && len == buffer.length) + return buffer; + byte[] b = new byte[len]; + System.arraycopy(buffer, off, b, 0, Math.min(len, buffer.length - off)); + return b; + } + + /** + * Returns the byte array representation of the given big integer with + * the leading zero byte (if any) trimmed off. + * + * @param bi The integer to trim. + * @return The byte representation of the big integer, with any leading + * zero removed. + */ + public static byte[] trim(BigInteger bi) + { + byte[] buf = bi.toByteArray(); + if (buf[0] == 0x00 && !bi.equals(BigInteger.ZERO)) + { + return trim(buf, 1, buf.length - 1); + } + else + { + return buf; + } + } + + /** + * Returns the integer value of {@link + * java.lang.System#currentTimeMillis()} / 1000. + * + * @return The current time, in seconds. + */ + public static int unixTime() + { + return (int) (System.currentTimeMillis() / 1000L); + } + + /** + * Transform an Object array into another by calling the given method + * on each object. The returned object array will have the runtime + * type of returnType. For example, the following will transform + * array of objects into their String representations, returning a String + * array. For example: + * + *

    + * String[] strings = (String[]) Util.transform(array, String.class, + * "toString", null); + *

    + * + *

    If any element of the given array is null, then that + * entry in the returned array will also be null. + * + * @param array The array to transform. It does not need to be of + * uniform type. + * @param returnType The desired return type of the returned array. + * This must by the component type, not the array type. + * @param method The name of the method to invoke from each object. + * @param args The arguments to pass to the method, or null + * if the method takes no arguments. + * @throws InvocationTargetException If an exception occurs while + * calling method of any object. + * @throws NoSuchMethodException If method is not the name of + * a valid method of any component of the array. + * @throws ClassCastException If the returned object from the method + * is not assignable to the return type. + * @throws IllegalArgumentException If args is not appropriate + * for method + * @throws IllegalAccessException If method is not accessible. + * @throws SecurityException If method is not accessible. + * @return An array containing the output of method called on + * each element of array with args. The return type + * of the array will be an array of returnType. + */ + static Object[] transform(Object[] array, Class returnType, + String method, Object[] args) + throws InvocationTargetException, NoSuchMethodException, + IllegalAccessException + { + if (args == null) + args = new Object[0]; + Object[] result = (Object[]) Array.newInstance(returnType, array.length); + Class[] argsClasses = new Class[args.length]; + for (int i = 0; i < args.length; i++) + { + argsClasses[i] = args[i].getClass(); + } + for (int i = 0; i < array.length; i++) + { + if (array[i] == null) + { + result[i] = null; + continue; + } + Class objClass = array[i].getClass(); + Method objMethod = objClass.getMethod(method, argsClasses); + Object o = objMethod.invoke(array[i], args); + if (!returnType.isAssignableFrom(o.getClass())) + throw new ClassCastException(); + result[i] = o; + } + return result; + } + + /** + * Get a system property as a privileged action. + * + * @param name The name of the property to get. + * @return The property named name, or null if the property is + * not set. + * @throws SecurityException If the Jessie code still does not have + * permission to read the property. + */ + @Deprecated static String getProperty(final String name) + { + return (String) AccessController.doPrivileged( + new PrivilegedAction() + { + public Object run() + { + return System.getProperty(name); + } + } + ); + } + + /** + * Get a security property as a privileged action. + * + * @param name The name of the property to get. + * @return The property named name, or null if the property is + * not set. + * @throws SecurityException If the Jessie code still does not have + * permission to read the property. + */ + @Deprecated static String getSecurityProperty(final String name) + { + return (String) AccessController.doPrivileged( + new PrivilegedAction() + { + public Object run() + { + return Security.getProperty(name); + } + } + ); + } +} diff --git a/libjava/classpath/gnu/javax/net/ssl/provider/X500PrincipalList.java b/libjava/classpath/gnu/javax/net/ssl/provider/X500PrincipalList.java new file mode 100644 index 000000000..ffdcbbad2 --- /dev/null +++ b/libjava/classpath/gnu/javax/net/ssl/provider/X500PrincipalList.java @@ -0,0 +1,272 @@ +/* X500PrincipalList.java -- A list of X.500 names. + 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.PrintWriter; +import java.io.StringWriter; + +import java.nio.ByteBuffer; + +import java.util.ConcurrentModificationException; +import java.util.ListIterator; +import java.util.NoSuchElementException; + +import javax.security.auth.x500.X500Principal; + +public final class X500PrincipalList implements Iterable +{ + private final ByteBuffer buffer; + private int modCount; + + public X500PrincipalList (final ByteBuffer buffer) + { + this.buffer = buffer; + modCount = 0; + } + + public int size () + { + return (buffer.getShort (0) & 0xFFFF); + } + + public int count () + { + int size = size (); + int i = 0; + for (int offset = 2; offset < size; i++) + { + int _size = (buffer.getShort (offset) & 0xFFFF); + // We don't want this going into an infinite loop if + // you mistakenly put a zero-length name. + if (_size == 0) + break; + offset += _size + 2; + } + return i; + } + + public X500Principal get (final int index) + { + if (index < 0) + throw new IndexOutOfBoundsException ("negative index"); + int size = size (); + int i = 0; + for (int offset = 2; offset < size; i++) + { + int _size = (buffer.getShort (offset) & 0xFFFF); + if (_size == 0) + throw new IndexOutOfBoundsException ("zero-length name encountered"); + if (i == index) + { + byte[] buf = new byte[_size]; + buffer.position (offset + 2); + buffer.get (buf); + return new X500Principal (buf); + } + offset += 2 + _size; + } + throw new IndexOutOfBoundsException ("limit: " + i + "; requested: " + index); + } + + public void put (final int index, final X500Principal principal) + { + put (index, principal.getEncoded ()); + } + + public void put (final int index, final byte[] encoded) + { + if (index < 0) + throw new IndexOutOfBoundsException ("negative index"); + int size = size (); + int i = 0; + for (int offset = 2; offset < size; i++) + { + int off = (buffer.getShort (offset) & 0xFFFF); + if (i == index) + { + buffer.putShort (offset, (short) encoded.length); + buffer.position (offset + 2); + buffer.put (encoded); + modCount++; + return; + } + offset += 2 + off; + } + throw new IndexOutOfBoundsException ("limit: " + (i-1) + "; requested: " + index); + } + + public void setSize (final int numNames, final int namesSize) + { + if (numNames < 1) + throw new IllegalArgumentException ("must have at least one name"); + int size = (numNames * 2) + namesSize; + if (size < 3 || size > buffer.capacity () || size > 0xFFFF) + throw new IllegalArgumentException ("size out of range; maximum: " + + Math.min (buffer.capacity (), 0xFFFF)); + buffer.putShort (0, (short) size); + } + + public String toString () + { + return toString (null); + } + + public String toString (final String prefix) + { + StringWriter str = new StringWriter (); + PrintWriter out = new PrintWriter (str); + if (prefix != null) out.print (prefix); + out.print ("["); + out.print (count ()); + out.println ("] {"); + for (Iterator it = new Iterator (); it.hasNext (); ) + { + if (prefix != null) out.print (prefix); + out.print (" "); + out.println (it.next ()); + } + if (prefix != null) out.print (prefix); + out.print ("};"); + return str.toString (); + } + + public boolean equals (Object o) + { + if (!(o instanceof X500PrincipalList)) + return false; + X500PrincipalList that = (X500PrincipalList) o; + + if (size () != that.size ()) + return false; + + for (Iterator it1 = new Iterator (), it2 = that.new Iterator (); + it1.hasNext () && it2.hasNext (); ) + { + if (!it1.next ().equals (it2.next ())) + return false; + } + return true; + } + + public java.util.Iterator iterator () + { + return new Iterator(); + } + + public class Iterator implements ListIterator + { + private final int modCount; + private int index; + private final int count; + + public Iterator () + { + this.modCount = X500PrincipalList.this.modCount; + index = 0; + count = count (); + } + + public void add (X500Principal o) + { + throw new UnsupportedOperationException (); + } + + public boolean hasNext () + { + return (index < count); + } + + public boolean hasPrevious () + { + return (index > 0); + } + + public X500Principal next () throws NoSuchElementException + { + if (modCount != X500PrincipalList.this.modCount) + throw new ConcurrentModificationException (); + try + { + return get (index++); + } + catch (IndexOutOfBoundsException ioobe) + { + throw new NoSuchElementException (); + } + } + + public int nextIndex () + { + if (hasNext ()) + return (index + 1); + return -1; + } + + public X500Principal previous () throws NoSuchElementException + { + if (index == 0) + throw new NoSuchElementException (); + if (modCount != X500PrincipalList.this.modCount) + throw new ConcurrentModificationException (); + try + { + return get (--index); + } + catch (IndexOutOfBoundsException ioobe) + { + throw new NoSuchElementException (); + } + } + + public int previousIndex () + { + return (index - 1); + } + + public void remove () + { + throw new UnsupportedOperationException (); + } + + public void set (final X500Principal o) + { + throw new UnsupportedOperationException (); + } + } +} diff --git a/libjava/classpath/gnu/javax/net/ssl/provider/X509KeyManagerFactory.java b/libjava/classpath/gnu/javax/net/ssl/provider/X509KeyManagerFactory.java new file mode 100644 index 000000000..a63cb2cbe --- /dev/null +++ b/libjava/classpath/gnu/javax/net/ssl/provider/X509KeyManagerFactory.java @@ -0,0 +1,396 @@ +/* X509KeyManagerFactory.java -- X.509 key manager factory. + 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.FileInputStream; +import java.io.IOException; +import java.net.Socket; + +import java.util.HashMap; +import java.util.Iterator; +import java.util.LinkedList; +import java.util.Enumeration; + +import java.security.InvalidAlgorithmParameterException; +import java.security.KeyStore; +import java.security.KeyStoreException; +import java.security.NoSuchAlgorithmException; +import java.security.Principal; +import java.security.PrivateKey; +import java.security.PublicKey; +import java.security.UnrecoverableKeyException; +import java.security.cert.Certificate; +import java.security.cert.CertificateException; +import java.security.cert.X509Certificate; +import java.security.interfaces.DSAPrivateKey; +import java.security.interfaces.DSAPublicKey; +import java.security.interfaces.RSAPrivateKey; +import java.security.interfaces.RSAPublicKey; + +import java.util.Collections; +import java.util.Map; +import java.util.List; + +import javax.crypto.interfaces.DHPrivateKey; +import javax.crypto.interfaces.DHPublicKey; + +import javax.net.ssl.KeyManager; +import javax.net.ssl.KeyManagerFactorySpi; +import javax.net.ssl.ManagerFactoryParameters; +import javax.net.ssl.SSLEngine; +import javax.net.ssl.X509ExtendedKeyManager; +import gnu.javax.net.ssl.NullManagerParameters; +import gnu.javax.net.ssl.PrivateCredentials; + +/** + * This class implements a {@link javax.net.ssl.KeyManagerFactory} engine + * for the ``JessieX509'' algorithm. + */ +public class X509KeyManagerFactory extends KeyManagerFactorySpi +{ + + // Fields. + // ------------------------------------------------------------------------- + + private Manager current; + + // Constructor. + // ------------------------------------------------------------------------- + + public X509KeyManagerFactory() + { + super(); + } + + // Instance methods. + // ------------------------------------------------------------------------- + + protected KeyManager[] engineGetKeyManagers() + { + if (current == null) + { + throw new IllegalStateException(); + } + return new KeyManager[] { current }; + } + + protected void engineInit(ManagerFactoryParameters params) + throws InvalidAlgorithmParameterException + { + if (params instanceof NullManagerParameters) + { + current = new Manager(Collections.EMPTY_MAP, Collections.EMPTY_MAP); + } + else if (params instanceof PrivateCredentials) + { + List chains + = ((PrivateCredentials) params).getCertChains(); + List keys + = ((PrivateCredentials) params).getPrivateKeys(); + int i = 0; + HashMap certMap + = new HashMap(); + HashMap keyMap + = new HashMap(); + Iterator c = chains.iterator(); + Iterator k = keys.iterator(); + while (c.hasNext() && k.hasNext()) + { + certMap.put(String.valueOf(i), c.next()); + keyMap.put(String.valueOf(i), k.next()); + i++; + } + current = new Manager(keyMap, certMap); + } + else + { + throw new InvalidAlgorithmParameterException(); + } + } + + protected void engineInit(KeyStore store, char[] passwd) + throws KeyStoreException, NoSuchAlgorithmException, + UnrecoverableKeyException + { + if (store == null) + { + String s = Util.getProperty("javax.net.ssl.keyStoreType"); + if (s == null) + s = KeyStore.getDefaultType(); + store = KeyStore.getInstance(s); + s = Util.getProperty("javax.net.ssl.keyStore"); + if (s == null) + return; + String p = Util.getProperty("javax.net.ssl.keyStorePassword"); + try + { + store.load(new FileInputStream(s), p != null ? p.toCharArray() : null); + } + catch (IOException ioe) + { + throw new KeyStoreException(ioe.toString()); + } + catch (CertificateException ce) + { + throw new KeyStoreException(ce.toString()); + } + } + + HashMap p = new HashMap(); + HashMap c + = new HashMap(); + Enumeration aliases = store.aliases(); + UnrecoverableKeyException exception = null; + while (aliases.hasMoreElements()) + { + String alias = (String) aliases.nextElement(); + if (!store.isKeyEntry(alias)) + { + continue; + } + X509Certificate[] chain = null; + Certificate[] chain2 = store.getCertificateChain (alias); + if (chain2 != null && chain2.length > 0 && + (chain2[0] instanceof X509Certificate)) + { + chain = toX509Chain(chain2); + } + else + { + continue; + } + PrivateKey key = null; + try + { + key = (PrivateKey) store.getKey(alias, passwd); + } + catch (UnrecoverableKeyException uke) + { + exception = uke; + continue; + } + if (key == null) + { + continue; + } + p.put(alias, key); + c.put(alias, chain); + } + if (p.isEmpty () && c.isEmpty ()) + { + if (exception != null) + { + throw exception; + } + throw new KeyStoreException ("no private credentials found"); + } + current = this.new Manager(p, c); + } + + private static X509Certificate[] toX509Chain(Certificate[] chain) + { + if (chain instanceof X509Certificate[]) + { + return (X509Certificate[]) chain; + } + X509Certificate[] _chain = new X509Certificate[chain.length]; + for (int i = 0; i < chain.length; i++) + _chain[i] = (X509Certificate) chain[i]; + return _chain; + } + + // Inner class. + // ------------------------------------------------------------------------- + + private class Manager extends X509ExtendedKeyManager + { + // Fields. + // ----------------------------------------------------------------------- + + private final Map privateKeys; + private final Map certChains; + + // Constructor. + // ----------------------------------------------------------------------- + + Manager(Map privateKeys, + Map certChains) + { + this.privateKeys = privateKeys; + this.certChains = certChains; + } + + // Instance methods. + // ----------------------------------------------------------------------- + + public String chooseClientAlias(String[] keyTypes, Principal[] issuers, + Socket socket) + { + for (int i = 0; i < keyTypes.length; i++) + { + String[] s = getClientAliases(keyTypes[i], issuers); + if (s.length > 0) + return s[0]; + } + return null; + } + + public @Override String chooseEngineClientAlias(String[] keyTypes, + Principal[] issuers, + SSLEngine engine) + { + for (String type : keyTypes) + { + String[] s = getClientAliases(type, issuers); + if (s.length > 0) + return s[0]; + } + return null; + } + + public String[] getClientAliases(String keyType, Principal[] issuers) + { + return getAliases(keyType, issuers); + } + + public String chooseServerAlias(String keyType, Principal[] issuers, + Socket socket) + { + String[] s = getServerAliases(keyType, issuers); + if (s.length > 0) + return s[0]; + return null; + } + + public @Override String chooseEngineServerAlias(String keyType, + Principal[] issuers, + SSLEngine engine) + { + String[] s = getServerAliases(keyType, issuers); + if (s.length > 0) + return s[0]; + return null; + } + + public String[] getServerAliases(String keyType, Principal[] issuers) + { + return getAliases(keyType, issuers); + } + + private String[] getAliases(String keyType, Principal[] issuers) + { + LinkedList l = new LinkedList(); + for (Iterator i = privateKeys.keySet().iterator(); i.hasNext(); ) + { + String alias = (String) i.next(); + X509Certificate[] chain = getCertificateChain(alias); + if (chain.length == 0) + continue; + PrivateKey privKey = getPrivateKey(alias); + if (privKey == null) + continue; + PublicKey pubKey = chain[0].getPublicKey(); + if (keyType.equalsIgnoreCase("RSA") + || keyType.equalsIgnoreCase("DHE_RSA") + || keyType.equalsIgnoreCase("SRP_RSA") + || keyType.equalsIgnoreCase("rsa_sign") + || keyType.equalsIgnoreCase("RSA_PSK")) + { + if (!(privKey instanceof RSAPrivateKey) || + !(pubKey instanceof RSAPublicKey)) + continue; + } + else if (keyType.equalsIgnoreCase("DHE_DSS") + || keyType.equalsIgnoreCase("dss_sign") + || keyType.equalsIgnoreCase("SRP_DSS") + || keyType.equalsIgnoreCase("DSA")) + { + if (!(privKey instanceof DSAPrivateKey) || + !(pubKey instanceof DSAPublicKey)) + continue; + } + else if (keyType.equalsIgnoreCase("DH_RSA") + || keyType.equalsIgnoreCase("rsa_fixed_dh")) + { + if (!(privKey instanceof DHPrivateKey) || + !(pubKey instanceof DHPublicKey)) + continue; + if (!chain[0].getSigAlgName().equalsIgnoreCase("RSA")) + continue; + } + else if (keyType.equalsIgnoreCase("DH_DSS") + || keyType.equalsIgnoreCase("dss_fixed_dh")) + { + if (!(privKey instanceof DHPrivateKey) || + !(pubKey instanceof DHPublicKey)) + continue; + if (!chain[0].getSigAlgName().equalsIgnoreCase("DSA")) + continue; + } + else // Unknown key type; ignore it. + continue; + if (issuers == null || issuers.length == 0) + { + l.add(alias); + continue; + } + for (Principal issuer : issuers) + { + if (chain[0].getIssuerDN().equals(issuer)) + { + l.add(alias); + break; + } + } + } + return l.toArray(new String[l.size()]); + } + + public X509Certificate[] getCertificateChain(String alias) + { + X509Certificate[] c = (X509Certificate[]) certChains.get(alias); + return c != null ? (X509Certificate[]) c.clone() : null; + } + + public PrivateKey getPrivateKey(String alias) + { + return (PrivateKey) privateKeys.get(alias); + } + } +} diff --git a/libjava/classpath/gnu/javax/net/ssl/provider/X509TrustManagerFactory.java b/libjava/classpath/gnu/javax/net/ssl/provider/X509TrustManagerFactory.java new file mode 100644 index 000000000..ddd2f9c8b --- /dev/null +++ b/libjava/classpath/gnu/javax/net/ssl/provider/X509TrustManagerFactory.java @@ -0,0 +1,295 @@ +/* X509TrustManagerFactory.java -- X.509 trust manager factory. + 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.FileInputStream; +import java.io.IOException; + +import java.util.Arrays; +import java.util.Enumeration; +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; +import java.security.NoSuchAlgorithmException; +import java.security.cert.CertPath; +import java.security.cert.CertPathValidator; +import java.security.cert.CertPathValidatorException; +import java.security.cert.Certificate; +import java.security.cert.CertificateException; +import java.security.cert.PKIXParameters; +import java.security.cert.TrustAnchor; +import java.security.cert.X509Certificate; + +import javax.net.ssl.ManagerFactoryParameters; +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; + +/** + * This class implements a {@link javax.net.ssl.TrustManagerFactory} engine + * for the ``JessieX509'' algorithm. + */ +public class X509TrustManagerFactory extends TrustManagerFactorySpi +{ + + // Constants and fields. + // ------------------------------------------------------------------------- + + private static final String sep + = AccessController.doPrivileged(new GetPropertyAction("file.separator")); + + /** + * The location of the JSSE key store. + */ + 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 + = AccessController.doPrivileged(new GetPropertyAction("java.home")) + + sep + "lib" + sep + "security" + sep + "cacerts"; + + private Manager current; + + // Construtors. + // ------------------------------------------------------------------------- + + public X509TrustManagerFactory() + { + super(); + } + + // Instance methods. + // ------------------------------------------------------------------------- + + protected TrustManager[] engineGetTrustManagers() + { + if (current == null) + { + throw new IllegalStateException("not initialized"); + } + return new TrustManager[] { current }; + } + + protected void engineInit(ManagerFactoryParameters params) + throws InvalidAlgorithmParameterException + { + if (params instanceof StaticTrustAnchors) + { + current = new Manager(((StaticTrustAnchors) params).getCertificates()); + } + else if (params instanceof NullManagerParameters) + { + current = new Manager(new X509Certificate[0]); + } + else + { + throw new InvalidAlgorithmParameterException(); + } + } + + protected void engineInit(KeyStore store) throws KeyStoreException + { + if (store == null) + { + 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 = AccessController.doPrivileged(gpa.setParameters("javax.net.ssl.trustStore")); + FileInputStream in = null; + if (s == null) + { + try + { + in = new FileInputStream(JSSE_CERTS); + } + catch (IOException e) + { + in = new FileInputStream(CA_CERTS); + } + } + else + { + in = new FileInputStream(s); + } + 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); + } + catch (CertificateException ce) + { + throw new KeyStoreException(ce); + } + catch (NoSuchAlgorithmException nsae) + { + throw new KeyStoreException(nsae); + } + } + + LinkedList l = new LinkedList(); + Enumeration aliases = store.aliases(); + while (aliases.hasMoreElements()) + { + String alias = (String) aliases.nextElement(); + if (!store.isCertificateEntry(alias)) + continue; + Certificate c = store.getCertificate(alias); + if (!(c instanceof X509Certificate)) + continue; + l.add((X509Certificate) c); + } + current = this.new Manager(l.toArray(new X509Certificate[l.size()])); + } + + // Inner class. + // ------------------------------------------------------------------------- + + /** + * The actual manager implementation returned. + */ + private class Manager implements X509TrustManager + { + + // Fields. + // ----------------------------------------------------------------------- + + private final Set anchors; + + // Constructor. + // ----------------------------------------------------------------------- + + Manager(X509Certificate[] trusted) + { + anchors = new HashSet(); + if (trusted != null) + { + for (X509Certificate cert : trusted) + { + anchors.add(new TrustAnchor(cert, null)); + } + } + } + + // Instance methodns. + // ----------------------------------------------------------------------- + + public void checkClientTrusted(X509Certificate[] chain, String authType) + throws CertificateException + { + checkTrusted(chain, authType); + } + + public void checkServerTrusted(X509Certificate[] chain, String authType) + throws CertificateException + { + checkTrusted(chain, authType); + } + + public X509Certificate[] getAcceptedIssuers() + { + return anchors.toArray(new X509Certificate[anchors.size()]); + } + + // Own methods. + // ----------------------------------------------------------------------- + + private void checkTrusted(X509Certificate[] chain, String authType) + throws CertificateException + { + CertPathValidator validator = null; + + try + { + validator = CertPathValidator.getInstance("PKIX"); + } + catch (NoSuchAlgorithmException nsae) + { + throw new CertificateException(nsae); + } + + CertPath path = new X509CertPath(Arrays.asList(chain)); + + PKIXParameters params = null; + 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) + { + throw new CertificateException(iape); + } + + try + { + validator.validate(path, params); + } + catch (CertPathValidatorException cpve) + { + throw new CertificateException(cpve); + } + catch (InvalidAlgorithmParameterException iape) + { + throw new CertificateException(iape); + } + } + } +} -- cgit v1.2.3