/* * Copyright (c) 1996, 2011, Oracle and/or its affiliates. All rights reserved. * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. * * This code is free software; you can redistribute it and/or modify it * under the terms of the GNU General Public License version 2 only, as * published by the Free Software Foundation. Oracle designates this * particular file as subject to the "Classpath" exception as provided * by Oracle in the LICENSE file that accompanied this code. * * This code 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 * version 2 for more details (a copy is included in the LICENSE file that * accompanied this code). * * You should have received a copy of the GNU General Public License version * 2 along with this work; if not, write to the Free Software Foundation, * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. * * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA * or visit www.oracle.com if you need additional information or have any * questions. */ package sun.security.ssl; import java.io.*;import java.math.BigInteger;import java.security.*;import java.security.interfaces.*;import java.security.spec.*;import java.security.cert.*;import java.security.cert.Certificate;import java.util.*;import java.util.concurrent.ConcurrentHashMap; import java.lang.reflect.*; import javax.security.auth.x500.X500Principal; import javax.crypto.KeyGenerator;import javax.crypto.SecretKey; import javax.net.ssl.*; import sun.security.internal.spec.TlsPrfParameterSpec;import sun.security.ssl.CipherSuite.*;import static sun.security.ssl.CipherSuite.PRF.*; /** * Many data structures are involved in the handshake messages. These * classes are used as structures, with public data members. They are * not visible outside the SSL package. * * Handshake messages all have a common header format, and they are all * encoded in a "handshake data" SSL record substream. The base class * here (HandshakeMessage) provides a common framework and records the * SSL record type of the particular handshake message. * * This file contains subclasses for all the basic handshake messages. * All handshake messages know how to encode and decode themselves on * SSL streams; this facilitates using the same code on SSL client and * server sides, although they don't send and receive the same messages. * * Messages also know how to print themselves, which is quite handy * for debugging. They always identify their type, and can optionally * dump all of their content. * * @author David Brownell */public abstract class HandshakeMessage { HandshakeMessage() { } // enum HandshakeType: static final byte ht_hello_request = 0; static final byte ht_client_hello = 1; static final byte ht_server_hello = 2; static final byte ht_certificate = 11; static final byte ht_server_key_exchange = 12; static final byte ht_certificate_request = 13; static final byte ht_server_hello_done = 14; static final byte ht_certificate_verify = 15; static final byte ht_client_key_exchange = 16; static final byte ht_finished = 20; /* Class and subclass dynamic debugging support */ public static final Debug debug = Debug.getInstance("ssl"); /** * Utility method to convert a BigInteger to a byte array in unsigned * format as needed in the handshake messages. BigInteger uses * 2's complement format, i.e. it prepends an extra zero if the MSB * is set. We remove that. */ static byte[] toByteArray(BigInteger bi) { byte[] b = bi.toByteArray(); if ((b.length > 1) && (b[0] == 0)) { int n = b.length - 1; byte[] newarray = new byte[n]; System.arraycopy(b, 1, newarray, 0, n); b = newarray; } return b; } /* * SSL 3.0 MAC padding constants. * Also used by CertificateVerify and Finished during the handshake. */ static final byte[] MD5_pad1 = genPad(0x36, 48); static final byte[] MD5_pad2 = genPad(0x5c, 48); static final byte[] SHA_pad1 = genPad(0x36, 40); static final byte[] SHA_pad2 = genPad(0x5c, 40); private static byte[] genPad(int b, int count) { byte[] padding = new byte[count]; Arrays.fill(padding, (byte)b); return padding; } /* * Write a handshake message on the (handshake) output stream. * This is just a four byte header followed by the data. * * NOTE that huge messages -- notably, ones with huge cert * chains -- are handled correctly. */ final void write(HandshakeOutStream s) throws IOException { int len = messageLength(); if (len > (1 << 24)) { throw new SSLException("Handshake message too big" + ", type = " + messageType() + ", len = " + len); } s.write(messageType()); s.putInt24(len); send(s); } /* * Subclasses implement these methods so those kinds of * messages can be emitted. Base class delegates to subclass. */ abstract int messageType(); abstract int messageLength(); abstract void send(HandshakeOutStream s) throws IOException; /* * Write a descriptive message on the output stream; for debugging. */ abstract void print(PrintStream p) throws IOException; //// NOTE: the rest of these classes are nested within this one, and are// imported by other classes in this package. There are a few other// handshake message classes, not neatly nested here because of current// licensing requirement for native (RSA) methods. They belong here,// but those native methods complicate things a lot!// /* * HelloRequest ... SERVER --> CLIENT * * Server can ask the client to initiate a new handshake, e.g. to change * session parameters after a connection has been (re)established. */static final class HelloRequest extends HandshakeMessage { int messageType() { return ht_hello_request; } HelloRequest() { } HelloRequest(HandshakeInStream in) throws IOException { // nothing in this message } int messageLength() { return 0; } void send(HandshakeOutStream out) throws IOException { // nothing in this messaage } void print(PrintStream out) throws IOException { out.println("*** HelloRequest (empty)"); } } /* * ClientHello ... CLIENT --> SERVER * * Client initiates handshake by telling server what it wants, and what it * can support (prioritized by what's first in the ciphe suite list). * * By RFC2246:7.4.1.2 it's explicitly anticipated that this message * will have more data added at the end ... e.g. what CAs the client trusts. * Until we know how to parse it, we will just read what we know * about, and let our caller handle the jumps over unknown data. */static final class ClientHello extends HandshakeMessage { ProtocolVersion protocolVersion; RandomCookie clnt_random; SessionId sessionId; private CipherSuiteList cipherSuites; byte[] compression_methods; HelloExtensions extensions = new HelloExtensions(); private final static byte[] NULL_COMPRESSION = new byte[] {0}; ClientHello(SecureRandom generator, ProtocolVersion protocolVersion, SessionId sessionId, CipherSuiteList cipherSuites) { this.protocolVersion = protocolVersion; this.sessionId = sessionId; this.cipherSuites = cipherSuites; if (cipherSuites.containsEC()) { extensions.add(SupportedEllipticCurvesExtension.DEFAULT); extensions.add(SupportedEllipticPointFormatsExtension.DEFAULT); } clnt_random = new RandomCookie(generator); compression_methods = NULL_COMPRESSION; } ClientHello(HandshakeInStream s, int messageLength) throws IOException { protocolVersion = ProtocolVersion.valueOf(s.getInt8(), s.getInt8()); clnt_random = new RandomCookie(s); sessionId = new SessionId(s.getBytes8()); cipherSuites = new CipherSuiteList(s); compression_methods = s.getBytes8(); if (messageLength() != messageLength) { extensions = new HelloExtensions(s); } } CipherSuiteList getCipherSuites() { return cipherSuites; } // add renegotiation_info extension void addRenegotiationInfoExtension(byte[] clientVerifyData) { HelloExtension renegotiationInfo = new RenegotiationInfoExtension( clientVerifyData, new byte[0]); extensions.add(renegotiationInfo); } // add server_name extension void addServerNameIndicationExtension(String hostname) { // We would have checked that the hostname ia a FQDN. ArrayList<String> hostnames = new ArrayList<>(1); hostnames.add(hostname); try { extensions.add(new ServerNameExtension(hostnames)); } catch (IOException ioe) { // ignore the exception and return } } // add signature_algorithm extension void addSignatureAlgorithmsExtension( Collection<SignatureAndHashAlgorithm> algorithms) { HelloExtension signatureAlgorithm = new SignatureAlgorithmsExtension(algorithms); extensions.add(signatureAlgorithm); } @Override int messageType() { return ht_client_hello; } @Override int messageLength() { /* * Add fixed size parts of each field... * version + random + session + cipher + compress */ return (2 + 32 + 1 + 2 + 1 + sessionId.length() /* ... + variable parts */ + (cipherSuites.size() * 2) + compression_methods.length) + extensions.length(); } @Override void send(HandshakeOutStream s) throws IOException { s.putInt8(protocolVersion.major); s.putInt8(protocolVersion.minor); clnt_random.send(s); s.putBytes8(sessionId.getId()); cipherSuites.send(s); s.putBytes8(compression_methods); extensions.send(s); } @Override void print(PrintStream s) throws IOException { s.println("*** ClientHello, " + protocolVersion); if (debug != null && Debug.isOn("verbose")) { s.print("RandomCookie: "); clnt_random.print(s); s.print("Session ID: "); s.println(sessionId); s.println("Cipher Suites: " + cipherSuites); Debug.println(s, "Compression Methods", compression_methods); extensions.print(s); s.println("***"); } }} /* * ServerHello ... SERVER --> CLIENT * * Server chooses protocol options from among those it supports and the * client supports. Then it sends the basic session descriptive parameters * back to the client. */static finalclass ServerHello extends HandshakeMessage{ int messageType() { return ht_server_hello; } ProtocolVersion protocolVersion; RandomCookie svr_random; SessionId sessionId; CipherSuite cipherSuite; byte compression_method; HelloExtensions extensions = new HelloExtensions(); ServerHello() { // empty } ServerHello(HandshakeInStream input, int messageLength) throws IOException { protocolVersion = ProtocolVersion.valueOf(input.getInt8(), input.getInt8()); svr_random = new RandomCookie(input); sessionId = new SessionId(input.getBytes8()); cipherSuite = CipherSuite.valueOf(input.getInt8(), input.getInt8()); compression_method = (byte)input.getInt8(); if (messageLength() != messageLength) { extensions = new HelloExtensions(input); } } int messageLength() { // almost fixed size, except session ID and extensions: // major + minor = 2 // random = 32 // session ID len field = 1 // cipher suite + compression = 3 // extensions: if present, 2 + length of extensions return 38 + sessionId.length() + extensions.length(); } void send(HandshakeOutStream s) throws IOException { s.putInt8(protocolVersion.major); s.putInt8(protocolVersion.minor); svr_random.send(s); s.putBytes8(sessionId.getId()); s.putInt8(cipherSuite.id >> 8); s.putInt8(cipherSuite.id & 0xff); s.putInt8(compression_method); extensions.send(s); } void print(PrintStream s) throws IOException { s.println("*** ServerHello, " + protocolVersion); if (debug != null && Debug.isOn("verbose")) { s.print("RandomCookie: "); svr_random.print(s); int i; s.print("Session ID: "); s.println(sessionId); s.println("Cipher Suite: " + cipherSuite); s.println("Compression Method: " + compression_method); extensions.print(s); s.println("***"); } }} /* * CertificateMsg ... send by both CLIENT and SERVER * * Each end of a connection may need to pass its certificate chain to * the other end. Such chains are intended to validate an identity with * reference to some certifying authority. Examples include companies * like Verisign, or financial institutions. There's some control over * the certifying authorities which are sent. * * NOTE: that these messages might be huge, taking many handshake records. * Up to 2^48 bytes of certificate may be sent, in records of at most 2^14 * bytes each ... up to 2^32 records sent on the output stream. */static finalclass CertificateMsg extends HandshakeMessage{ int messageType() { return ht_certificate; } private X509Certificate[] chain; private List<byte[]> encodedChain; private int messageLength; CertificateMsg(X509Certificate[] certs) { chain = certs; } CertificateMsg(HandshakeInStream input) throws IOException { int chainLen = input.getInt24(); List<Certificate> v = new ArrayList<>(4); CertificateFactory cf = null; while (chainLen > 0) { byte[] cert = input.getBytes24(); chainLen -= (3 + cert.length); try { if (cf == null) { cf = CertificateFactory.getInstance("X.509"); } v.add(cf.generateCertificate(new ByteArrayInputStream(cert))); } catch (CertificateException e) { throw (SSLProtocolException)new SSLProtocolException( e.getMessage()).initCause(e); } } chain = v.toArray(new X509Certificate[v.size()]); } int messageLength() { if (encodedChain == null) { messageLength = 3; encodedChain = new ArrayList<byte[]>(chain.length); try { for (X509Certificate cert : chain) { byte[] b = cert.getEncoded(); encodedChain.add(b); messageLength += b.length + 3; } } catch (CertificateEncodingException e) { encodedChain = null; throw new RuntimeException("Could not encode certificates", e); } } return messageLength; } void send(HandshakeOutStream s) throws IOException { s.putInt24(messageLength() - 3); for (byte[] b : encodedChain) { s.putBytes24(b); } } void print(PrintStream s) throws IOException { s.println("*** Certificate chain"); if (debug != null && Debug.isOn("verbose")) { for (int i = 0; i < chain.length; i++) s.println("chain [" + i + "] = " + chain[i]); s.println("***"); } } X509Certificate[] getCertificateChain() { return chain.clone(); }} /* * ServerKeyExchange ... SERVER --> CLIENT * * The cipher suite selected, when combined with the certificate exchanged,Lines 1–500 of 2006. Load more
/* * Copyright (c) 1996, 2011, Oracle and/or its affiliates. All rights reserved. * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. * * This code is free software; you can redistribute it and/or modify it * under the terms of the GNU General Public License version 2 only, as * published by the Free Software Foundation. Oracle designates this * particular file as subject to the "Classpath" exception as provided * by Oracle in the LICENSE file that accompanied this code. * * This code 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 * version 2 for more details (a copy is included in the LICENSE file that * accompanied this code). * * You should have received a copy of the GNU General Public License version * 2 along with this work; if not, write to the Free Software Foundation, * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. * * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA * or visit www.oracle.com if you need additional information or have any * questions. */ package sun.security.ssl; import java.io.*; import java.math.BigInteger; import java.security.*; import java.security.interfaces.*; import java.security.spec.*; import java.security.cert.*; import java.security.cert.Certificate; import java.util.*; import java.util.concurrent.ConcurrentHashMap; import java.lang.reflect.*; import javax.security.auth.x500.X500Principal; import javax.crypto.KeyGenerator; import javax.crypto.SecretKey; import javax.net.ssl.*; import sun.security.internal.spec.TlsPrfParameterSpec; import sun.security.ssl.CipherSuite.*; import static sun.security.ssl.CipherSuite.PRF.*; /** * Many data structures are involved in the handshake messages. These * classes are used as structures, with public data members. They are * not visible outside the SSL package. * * Handshake messages all have a common header format, and they are all * encoded in a "handshake data" SSL record substream. The base class * here (HandshakeMessage) provides a common framework and records the * SSL record type of the particular handshake message. * * This file contains subclasses for all the basic handshake messages. * All handshake messages know how to encode and decode themselves on * SSL streams; this facilitates using the same code on SSL client and * server sides, although they don't send and receive the same messages. * * Messages also know how to print themselves, which is quite handy * for debugging. They always identify their type, and can optionally * dump all of their content. * * @author David Brownell */ public abstract class HandshakeMessage { HandshakeMessage() { } // enum HandshakeType: static final byte ht_hello_request = 0; static final byte ht_client_hello = 1; static final byte ht_server_hello = 2; static final byte ht_certificate = 11; static final byte ht_server_key_exchange = 12; static final byte ht_certificate_request = 13; static final byte ht_server_hello_done = 14; static final byte ht_certificate_verify = 15; static final byte ht_client_key_exchange = 16; static final byte ht_finished = 20; /* Class and subclass dynamic debugging support */ public static final Debug debug = Debug.getInstance("ssl"); /** * Utility method to convert a BigInteger to a byte array in unsigned * format as needed in the handshake messages. BigInteger uses * 2's complement format, i.e. it prepends an extra zero if the MSB * is set. We remove that. */ static byte[] toByteArray(BigInteger bi) { byte[] b = bi.toByteArray(); if ((b.length > 1) && (b[0] == 0)) { int n = b.length - 1; byte[] newarray = new byte[n]; System.arraycopy(b, 1, newarray, 0, n); b = newarray; } return b; } /* * SSL 3.0 MAC padding constants. * Also used by CertificateVerify and Finished during the handshake. */ static final byte[] MD5_pad1 = genPad(0x36, 48); static final byte[] MD5_pad2 = genPad(0x5c, 48); static final byte[] SHA_pad1 = genPad(0x36, 40); static final byte[] SHA_pad2 = genPad(0x5c, 40); private static byte[] genPad(int b, int count) { byte[] padding = new byte[count]; Arrays.fill(padding, (byte)b); return padding; } /* * Write a handshake message on the (handshake) output stream. * This is just a four byte header followed by the data. * * NOTE that huge messages -- notably, ones with huge cert * chains -- are handled correctly. */ final void write(HandshakeOutStream s) throws IOException { int len = messageLength(); if (len > (1 << 24)) { throw new SSLException("Handshake message too big" + ", type = " + messageType() + ", len = " + len); } s.write(messageType()); s.putInt24(len); send(s); } /* * Subclasses implement these methods so those kinds of * messages can be emitted. Base class delegates to subclass. */ abstract int messageType(); abstract int messageLength(); abstract void send(HandshakeOutStream s) throws IOException; /* * Write a descriptive message on the output stream; for debugging. */ abstract void print(PrintStream p) throws IOException; // // NOTE: the rest of these classes are nested within this one, and are // imported by other classes in this package. There are a few other // handshake message classes, not neatly nested here because of current // licensing requirement for native (RSA) methods. They belong here, // but those native methods complicate things a lot! // /* * HelloRequest ... SERVER --> CLIENT * * Server can ask the client to initiate a new handshake, e.g. to change * session parameters after a connection has been (re)established. */ static final class HelloRequest extends HandshakeMessage { int messageType() { return ht_hello_request; } HelloRequest() { } HelloRequest(HandshakeInStream in) throws IOException { // nothing in this message } int messageLength() { return 0; } void send(HandshakeOutStream out) throws IOException { // nothing in this messaage } void print(PrintStream out) throws IOException { out.println("*** HelloRequest (empty)"); } } /* * ClientHello ... CLIENT --> SERVER * * Client initiates handshake by telling server what it wants, and what it * can support (prioritized by what's first in the ciphe suite list). * * By RFC2246:7.4.1.2 it's explicitly anticipated that this message * will have more data added at the end ... e.g. what CAs the client trusts. * Until we know how to parse it, we will just read what we know * about, and let our caller handle the jumps over unknown data. */ static final class ClientHello extends HandshakeMessage { ProtocolVersion protocolVersion; RandomCookie clnt_random; SessionId sessionId; private CipherSuiteList cipherSuites; byte[] compression_methods; HelloExtensions extensions = new HelloExtensions(); private final static byte[] NULL_COMPRESSION = new byte[] {0}; ClientHello(SecureRandom generator, ProtocolVersion protocolVersion, SessionId sessionId, CipherSuiteList cipherSuites) { this.protocolVersion = protocolVersion; this.sessionId = sessionId; this.cipherSuites = cipherSuites; if (cipherSuites.containsEC()) { extensions.add(SupportedEllipticCurvesExtension.DEFAULT); extensions.add(SupportedEllipticPointFormatsExtension.DEFAULT); } clnt_random = new RandomCookie(generator); compression_methods = NULL_COMPRESSION; } ClientHello(HandshakeInStream s, int messageLength) throws IOException { protocolVersion = ProtocolVersion.valueOf(s.getInt8(), s.getInt8()); clnt_random = new RandomCookie(s); sessionId = new SessionId(s.getBytes8()); cipherSuites = new CipherSuiteList(s); compression_methods = s.getBytes8(); if (messageLength() != messageLength) { extensions = new HelloExtensions(s); } } CipherSuiteList getCipherSuites() { return cipherSuites; } // add renegotiation_info extension void addRenegotiationInfoExtension(byte[] clientVerifyData) { HelloExtension renegotiationInfo = new RenegotiationInfoExtension( clientVerifyData, new byte[0]); extensions.add(renegotiationInfo); } // add server_name extension void addServerNameIndicationExtension(String hostname) { // We would have checked that the hostname ia a FQDN. ArrayList<String> hostnames = new ArrayList<>(1); hostnames.add(hostname); try { extensions.add(new ServerNameExtension(hostnames)); } catch (IOException ioe) { // ignore the exception and return } } // add signature_algorithm extension void addSignatureAlgorithmsExtension( Collection<SignatureAndHashAlgorithm> algorithms) { HelloExtension signatureAlgorithm = new SignatureAlgorithmsExtension(algorithms); extensions.add(signatureAlgorithm); } @Override int messageType() { return ht_client_hello; } @Override int messageLength() { /* * Add fixed size parts of each field... * version + random + session + cipher + compress */ return (2 + 32 + 1 + 2 + 1 + sessionId.length() /* ... + variable parts */ + (cipherSuites.size() * 2) + compression_methods.length) + extensions.length(); } @Override void send(HandshakeOutStream s) throws IOException { s.putInt8(protocolVersion.major); s.putInt8(protocolVersion.minor); clnt_random.send(s); s.putBytes8(sessionId.getId()); cipherSuites.send(s); s.putBytes8(compression_methods); extensions.send(s); } @Override void print(PrintStream s) throws IOException { s.println("*** ClientHello, " + protocolVersion); if (debug != null && Debug.isOn("verbose")) { s.print("RandomCookie: "); clnt_random.print(s); s.print("Session ID: "); s.println(sessionId); s.println("Cipher Suites: " + cipherSuites); Debug.println(s, "Compression Methods", compression_methods); extensions.print(s); s.println("***"); } } } /* * ServerHello ... SERVER --> CLIENT * * Server chooses protocol options from among those it supports and the * client supports. Then it sends the basic session descriptive parameters * back to the client. */ static final class ServerHello extends HandshakeMessage { int messageType() { return ht_server_hello; } ProtocolVersion protocolVersion; RandomCookie svr_random; SessionId sessionId; CipherSuite cipherSuite; byte compression_method; HelloExtensions extensions = new HelloExtensions(); ServerHello() { // empty } ServerHello(HandshakeInStream input, int messageLength) throws IOException { protocolVersion = ProtocolVersion.valueOf(input.getInt8(), input.getInt8()); svr_random = new RandomCookie(input); sessionId = new SessionId(input.getBytes8()); cipherSuite = CipherSuite.valueOf(input.getInt8(), input.getInt8()); compression_method = (byte)input.getInt8(); if (messageLength() != messageLength) { extensions = new HelloExtensions(input); } } int messageLength() { // almost fixed size, except session ID and extensions: // major + minor = 2 // random = 32 // session ID len field = 1 // cipher suite + compression = 3 // extensions: if present, 2 + length of extensions return 38 + sessionId.length() + extensions.length(); } void send(HandshakeOutStream s) throws IOException { s.putInt8(protocolVersion.major); s.putInt8(protocolVersion.minor); svr_random.send(s); s.putBytes8(sessionId.getId()); s.putInt8(cipherSuite.id >> 8); s.putInt8(cipherSuite.id & 0xff); s.putInt8(compression_method); extensions.send(s); } void print(PrintStream s) throws IOException { s.println("*** ServerHello, " + protocolVersion); if (debug != null && Debug.isOn("verbose")) { s.print("RandomCookie: "); svr_random.print(s); int i; s.print("Session ID: "); s.println(sessionId); s.println("Cipher Suite: " + cipherSuite); s.println("Compression Method: " + compression_method); extensions.print(s); s.println("***"); } } } /* * CertificateMsg ... send by both CLIENT and SERVER * * Each end of a connection may need to pass its certificate chain to * the other end. Such chains are intended to validate an identity with * reference to some certifying authority. Examples include companies * like Verisign, or financial institutions. There's some control over * the certifying authorities which are sent. * * NOTE: that these messages might be huge, taking many handshake records. * Up to 2^48 bytes of certificate may be sent, in records of at most 2^14 * bytes each ... up to 2^32 records sent on the output stream. */ static final class CertificateMsg extends HandshakeMessage { int messageType() { return ht_certificate; } private X509Certificate[] chain; private List<byte[]> encodedChain; private int messageLength; CertificateMsg(X509Certificate[] certs) { chain = certs; } CertificateMsg(HandshakeInStream input) throws IOException { int chainLen = input.getInt24(); List<Certificate> v = new ArrayList<>(4); CertificateFactory cf = null; while (chainLen > 0) { byte[] cert = input.getBytes24(); chainLen -= (3 + cert.length); try { if (cf == null) { cf = CertificateFactory.getInstance("X.509"); } v.add(cf.generateCertificate(new ByteArrayInputStream(cert))); } catch (CertificateException e) { throw (SSLProtocolException)new SSLProtocolException( e.getMessage()).initCause(e); } } chain = v.toArray(new X509Certificate[v.size()]); } int messageLength() { if (encodedChain == null) { messageLength = 3; encodedChain = new ArrayList<byte[]>(chain.length); try { for (X509Certificate cert : chain) { byte[] b = cert.getEncoded(); encodedChain.add(b); messageLength += b.length + 3; } } catch (CertificateEncodingException e) { encodedChain = null; throw new RuntimeException("Could not encode certificates", e); } } return messageLength; } void send(HandshakeOutStream s) throws IOException { s.putInt24(messageLength() - 3); for (byte[] b : encodedChain) { s.putBytes24(b); } } void print(PrintStream s) throws IOException { s.println("*** Certificate chain"); if (debug != null && Debug.isOn("verbose")) { for (int i = 0; i < chain.length; i++) s.println("chain [" + i + "] = " + chain[i]); s.println("***"); } } X509Certificate[] getCertificateChain() { return chain.clone(); } } /* * ServerKeyExchange ... SERVER --> CLIENT * * The cipher suite selected, when combined with the certificate exchanged,