void print(PrintStream s) throws IOException { s.println("*** ServerHelloDone"); }} /* * CertificateVerify ... CLIENT --> SERVER * * Sent after client sends signature-capable certificates (e.g. not * Diffie-Hellman) to verify. */static final class CertificateVerify extends HandshakeMessage { // the signature bytes private byte[] signature; // protocol version being established using this ServerKeyExchange message ProtocolVersion protocolVersion; // the preferable signature algorithm used by this CertificateVerify message private SignatureAndHashAlgorithm preferableSignatureAlgorithm = null; /* * Create an RSA or DSA signed certificate verify message. */ CertificateVerify(ProtocolVersion protocolVersion, HandshakeHash handshakeHash, PrivateKey privateKey, SecretKey masterSecret, SecureRandom sr, SignatureAndHashAlgorithm signAlgorithm) throws GeneralSecurityException { this.protocolVersion = protocolVersion; String algorithm = privateKey.getAlgorithm(); Signature sig = null; if (protocolVersion.v >= ProtocolVersion.TLS12.v) { this.preferableSignatureAlgorithm = signAlgorithm; sig = JsseJce.getSignature(signAlgorithm.getAlgorithmName()); } else { sig = getSignature(protocolVersion, algorithm); } sig.initSign(privateKey, sr); updateSignature(sig, protocolVersion, handshakeHash, algorithm, masterSecret); signature = sig.sign(); } // // Unmarshal the signed data from the input stream. // CertificateVerify(HandshakeInStream input, Collection<SignatureAndHashAlgorithm> localSupportedSignAlgs, ProtocolVersion protocolVersion) throws IOException { this.protocolVersion = protocolVersion; // read the signature and hash algorithm if (protocolVersion.v >= ProtocolVersion.TLS12.v) { int hashAlg = input.getInt8(); // hash algorithm int signAlg = input.getInt8(); // signature algorithm preferableSignatureAlgorithm = SignatureAndHashAlgorithm.valueOf(hashAlg, signAlg, 0); // Is it a local supported signature algorithm? if (!localSupportedSignAlgs.contains( preferableSignatureAlgorithm)) { throw new SSLHandshakeException( "Unsupported SignatureAndHashAlgorithm in " + "ServerKeyExchange message"); } } // read the signature signature = input.getBytes16(); } /* * Get the preferable signature algorithm used by this message */ SignatureAndHashAlgorithm getPreferableSignatureAlgorithm() { return preferableSignatureAlgorithm; } /* * Verify a certificate verify message. Return the result of verification, * if there is a problem throw a GeneralSecurityException. */ boolean verify(ProtocolVersion protocolVersion, HandshakeHash handshakeHash, PublicKey publicKey, SecretKey masterSecret) throws GeneralSecurityException { String algorithm = publicKey.getAlgorithm(); Signature sig = null; if (protocolVersion.v >= ProtocolVersion.TLS12.v) { sig = JsseJce.getSignature( preferableSignatureAlgorithm.getAlgorithmName()); } else { sig = getSignature(protocolVersion, algorithm); } sig.initVerify(publicKey); updateSignature(sig, protocolVersion, handshakeHash, algorithm, masterSecret); return sig.verify(signature); } /* * Get the Signature object appropriate for verification using the * given signature algorithm and protocol version. */ private static Signature getSignature(ProtocolVersion protocolVersion, String algorithm) throws GeneralSecurityException { if (algorithm.equals("RSA")) { return RSASignature.getInternalInstance(); } else if (algorithm.equals("DSA")) { return JsseJce.getSignature(JsseJce.SIGNATURE_RAWDSA); } else if (algorithm.equals("EC")) { return JsseJce.getSignature(JsseJce.SIGNATURE_RAWECDSA); } else { throw new SignatureException("Unrecognized algorithm: " + algorithm); } } /* * Update the Signature with the data appropriate for the given * signature algorithm and protocol version so that the object is * ready for signing or verifying. */ private static void updateSignature(Signature sig, ProtocolVersion protocolVersion, HandshakeHash handshakeHash, String algorithm, SecretKey masterKey) throws SignatureException { if (algorithm.equals("RSA")) { if (protocolVersion.v < ProtocolVersion.TLS12.v) { // TLS1.1- MessageDigest md5Clone = handshakeHash.getMD5Clone(); MessageDigest shaClone = handshakeHash.getSHAClone(); if (protocolVersion.v < ProtocolVersion.TLS10.v) { // SSLv3 updateDigest(md5Clone, MD5_pad1, MD5_pad2, masterKey); updateDigest(shaClone, SHA_pad1, SHA_pad2, masterKey); } // The signature must be an instance of RSASignature, need // to use these hashes directly. RSASignature.setHashes(sig, md5Clone, shaClone); } else { // TLS1.2+ sig.update(handshakeHash.getAllHandshakeMessages()); } } else { // DSA, ECDSA if (protocolVersion.v < ProtocolVersion.TLS12.v) { // TLS1.1- MessageDigest shaClone = handshakeHash.getSHAClone(); if (protocolVersion.v < ProtocolVersion.TLS10.v) { // SSLv3 updateDigest(shaClone, SHA_pad1, SHA_pad2, masterKey); } sig.update(shaClone.digest()); } else { // TLS1.2+ sig.update(handshakeHash.getAllHandshakeMessages()); } } } /* * Update the MessageDigest for SSLv3 certificate verify or finished * message calculation. The digest must already have been updated with * all preceding handshake messages. * Used by the Finished class as well. */ private static void updateDigest(MessageDigest md, byte[] pad1, byte[] pad2, SecretKey masterSecret) { // Digest the key bytes if available. // Otherwise (sensitive key), try digesting the key directly. // That is currently only implemented in SunPKCS11 using a private // reflection API, so we avoid that if possible. byte[] keyBytes = "RAW".equals(masterSecret.getFormat()) ? masterSecret.getEncoded() : null; if (keyBytes != null) { md.update(keyBytes); } else { digestKey(md, masterSecret); } md.update(pad1); byte[] temp = md.digest(); if (keyBytes != null) { md.update(keyBytes); } else { digestKey(md, masterSecret); } md.update(pad2); md.update(temp); } private final static Class delegate; private final static Field spiField; static { try { delegate = Class.forName("java.security.MessageDigest$Delegate"); spiField = delegate.getDeclaredField("digestSpi"); } catch (Exception e) { throw new RuntimeException("Reflection failed", e); } makeAccessible(spiField); } private static void makeAccessible(final AccessibleObject o) { AccessController.doPrivileged(new PrivilegedAction<Object>() { public Object run() { o.setAccessible(true); return null; } }); } // ConcurrentHashMap does not allow null values, use this marker object private final static Object NULL_OBJECT = new Object(); // cache Method objects per Spi class // Note that this will prevent the Spi classes from being GC'd. We assume // that is not a problem. private final static Map<Class,Object> methodCache = new ConcurrentHashMap<>(); private static void digestKey(MessageDigest md, SecretKey key) { try { // Verify that md is implemented via MessageDigestSpi, not // via JDK 1.1 style MessageDigest subclassing. if (md.getClass() != delegate) { throw new Exception("Digest is not a MessageDigestSpi"); } MessageDigestSpi spi = (MessageDigestSpi)spiField.get(md); Class<?> clazz = spi.getClass(); Object r = methodCache.get(clazz); if (r == null) { try { r = clazz.getDeclaredMethod("implUpdate", SecretKey.class); makeAccessible((Method)r); } catch (NoSuchMethodException e) { r = NULL_OBJECT; } methodCache.put(clazz, r); } if (r == NULL_OBJECT) { throw new Exception( "Digest does not support implUpdate(SecretKey)"); } Method update = (Method)r; update.invoke(spi, key); } catch (Exception e) { throw new RuntimeException( "Could not obtain encoded key and " + "MessageDigest cannot digest key", e); } } @Override int messageType() { return ht_certificate_verify; } @Override int messageLength() { int temp = 2; if (protocolVersion.v >= ProtocolVersion.TLS12.v) { temp += SignatureAndHashAlgorithm.sizeInRecord(); } return temp + signature.length; } @Override void send(HandshakeOutStream s) throws IOException { if (protocolVersion.v >= ProtocolVersion.TLS12.v) { s.putInt8(preferableSignatureAlgorithm.getHashValue()); s.putInt8(preferableSignatureAlgorithm.getSignatureValue()); } s.putBytes16(signature); } @Override void print(PrintStream s) throws IOException { s.println("*** CertificateVerify"); if (debug != null && Debug.isOn("verbose")) { if (protocolVersion.v >= ProtocolVersion.TLS12.v) { s.println("Signature Algorithm " + preferableSignatureAlgorithm.getAlgorithmName()); } } }} /* * FINISHED ... sent by both CLIENT and SERVER * * This is the FINISHED message as defined in the SSL and TLS protocols. * Both protocols define this handshake message slightly differently. * This class supports both formats. * * When handshaking is finished, each side sends a "change_cipher_spec" * record, then immediately sends a "finished" handshake message prepared * according to the newly adopted cipher spec. * * NOTE that until this is sent, no application data may be passed, unless * some non-default cipher suite has already been set up on this connection * connection (e.g. a previous handshake arranged one). */static final class Finished extends HandshakeMessage { // constant for a Finished message sent by the client final static int CLIENT = 1; // constant for a Finished message sent by the server final static int SERVER = 2; // enum Sender: "CLNT" and "SRVR" private static final byte[] SSL_CLIENT = { 0x43, 0x4C, 0x4E, 0x54 }; private static final byte[] SSL_SERVER = { 0x53, 0x52, 0x56, 0x52 }; /* * Contents of the finished message ("checksum"). For TLS, it * is 12 bytes long, for SSLv3 36 bytes. */ private byte[] verifyData; /* * Current cipher suite we are negotiating. TLS 1.2 has * ciphersuite-defined PRF algorithms. */ private ProtocolVersion protocolVersion; private CipherSuite cipherSuite; /* * Create a finished message to send to the remote peer. */ Finished(ProtocolVersion protocolVersion, HandshakeHash handshakeHash, int sender, SecretKey master, CipherSuite cipherSuite) { this.protocolVersion = protocolVersion; this.cipherSuite = cipherSuite; verifyData = getFinished(handshakeHash, sender, master); } /* * Constructor that reads FINISHED message from stream. */ Finished(ProtocolVersion protocolVersion, HandshakeInStream input, CipherSuite cipherSuite) throws IOException { this.protocolVersion = protocolVersion; this.cipherSuite = cipherSuite; int msgLen = (protocolVersion.v >= ProtocolVersion.TLS10.v) ? 12 : 36; verifyData = new byte[msgLen]; input.read(verifyData); } /* * Verify that the hashes here are what would have been produced * according to a given set of inputs. This is used to ensure that * both client and server are fully in sync, and that the handshake * computations have been successful. */ boolean verify(HandshakeHash handshakeHash, int sender, SecretKey master) { byte[] myFinished = getFinished(handshakeHash, sender, master); return Arrays.equals(myFinished, verifyData); } /* * Perform the actual finished message calculation. */ private byte[] getFinished(HandshakeHash handshakeHash, int sender, SecretKey masterKey) { byte[] sslLabel; String tlsLabel; if (sender == CLIENT) { sslLabel = SSL_CLIENT; tlsLabel = "client finished"; } else if (sender == SERVER) { sslLabel = SSL_SERVER; tlsLabel = "server finished"; } else { throw new RuntimeException("Invalid sender: " + sender); } if (protocolVersion.v >= ProtocolVersion.TLS10.v) { // TLS 1.0+ try { byte [] seed; String prfAlg; PRF prf; // Get the KeyGenerator alg and calculate the seed. if (protocolVersion.v >= ProtocolVersion.TLS12.v) { // TLS 1.2 seed = handshakeHash.getFinishedHash(); prfAlg = "SunTls12Prf"; prf = cipherSuite.prfAlg; } else { // TLS 1.0/1.1 MessageDigest md5Clone = handshakeHash.getMD5Clone(); MessageDigest shaClone = handshakeHash.getSHAClone(); seed = new byte[36]; md5Clone.digest(seed, 0, 16); shaClone.digest(seed, 16, 20); prfAlg = "SunTlsPrf"; prf = P_NONE; } String prfHashAlg = prf.getPRFHashAlg(); int prfHashLength = prf.getPRFHashLength(); int prfBlockSize = prf.getPRFBlockSize(); /* * RFC 5246/7.4.9 says that finished messages can * be ciphersuite-specific in both length/PRF hash * algorithm. If we ever run across a different * length, this call will need to be updated. */ TlsPrfParameterSpec spec = new TlsPrfParameterSpec( masterKey, tlsLabel, seed, 12, prfHashAlg, prfHashLength, prfBlockSize); KeyGenerator kg = JsseJce.getKeyGenerator(prfAlg); kg.init(spec); SecretKey prfKey = kg.generateKey(); if ("RAW".equals(prfKey.getFormat()) == false) { throw new ProviderException( "Invalid PRF output, format must be RAW"); } byte[] finished = prfKey.getEncoded(); return finished; } catch (GeneralSecurityException e) { throw new RuntimeException("PRF failed", e); } } else { // SSLv3 MessageDigest md5Clone = handshakeHash.getMD5Clone(); MessageDigest shaClone = handshakeHash.getSHAClone(); updateDigest(md5Clone, sslLabel, MD5_pad1, MD5_pad2, masterKey); updateDigest(shaClone, sslLabel, SHA_pad1, SHA_pad2, masterKey); byte[] finished = new byte[36]; try { md5Clone.digest(finished, 0, 16); shaClone.digest(finished, 16, 20); } catch (DigestException e) { // cannot occur throw new RuntimeException("Digest failed", e); } return finished; } } /* * Update the MessageDigest for SSLv3 finished message calculation. * The digest must already have been updated with all preceding handshake * messages. This operation is almost identical to the certificate verify * hash, reuse that code. */ private static void updateDigest(MessageDigest md, byte[] sender, byte[] pad1, byte[] pad2, SecretKey masterSecret) { md.update(sender); CertificateVerify.updateDigest(md, pad1, pad2, masterSecret); } // get the verify_data of the finished message byte[] getVerifyData() { return verifyData; } @Override int messageType() { return ht_finished; } @Override int messageLength() { return verifyData.length; } @Override void send(HandshakeOutStream out) throws IOException { out.write(verifyData); } @Override void print(PrintStream s) throws IOException { s.println("*** Finished"); if (debug != null && Debug.isOn("verbose")) { Debug.println(s, "verify_data", verifyData); s.println("***"); } }}Lines 1501–2000 of 2006. Previous lines Load more
void print(PrintStream s) throws IOException { s.println("*** ServerHelloDone"); } } /* * CertificateVerify ... CLIENT --> SERVER * * Sent after client sends signature-capable certificates (e.g. not * Diffie-Hellman) to verify. */ static final class CertificateVerify extends HandshakeMessage { // the signature bytes private byte[] signature; // protocol version being established using this ServerKeyExchange message ProtocolVersion protocolVersion; // the preferable signature algorithm used by this CertificateVerify message private SignatureAndHashAlgorithm preferableSignatureAlgorithm = null; /* * Create an RSA or DSA signed certificate verify message. */ CertificateVerify(ProtocolVersion protocolVersion, HandshakeHash handshakeHash, PrivateKey privateKey, SecretKey masterSecret, SecureRandom sr, SignatureAndHashAlgorithm signAlgorithm) throws GeneralSecurityException { this.protocolVersion = protocolVersion; String algorithm = privateKey.getAlgorithm(); Signature sig = null; if (protocolVersion.v >= ProtocolVersion.TLS12.v) { this.preferableSignatureAlgorithm = signAlgorithm; sig = JsseJce.getSignature(signAlgorithm.getAlgorithmName()); } else { sig = getSignature(protocolVersion, algorithm); } sig.initSign(privateKey, sr); updateSignature(sig, protocolVersion, handshakeHash, algorithm, masterSecret); signature = sig.sign(); } // // Unmarshal the signed data from the input stream. // CertificateVerify(HandshakeInStream input, Collection<SignatureAndHashAlgorithm> localSupportedSignAlgs, ProtocolVersion protocolVersion) throws IOException { this.protocolVersion = protocolVersion; // read the signature and hash algorithm if (protocolVersion.v >= ProtocolVersion.TLS12.v) { int hashAlg = input.getInt8(); // hash algorithm int signAlg = input.getInt8(); // signature algorithm preferableSignatureAlgorithm = SignatureAndHashAlgorithm.valueOf(hashAlg, signAlg, 0); // Is it a local supported signature algorithm? if (!localSupportedSignAlgs.contains( preferableSignatureAlgorithm)) { throw new SSLHandshakeException( "Unsupported SignatureAndHashAlgorithm in " + "ServerKeyExchange message"); } } // read the signature signature = input.getBytes16(); } /* * Get the preferable signature algorithm used by this message */ SignatureAndHashAlgorithm getPreferableSignatureAlgorithm() { return preferableSignatureAlgorithm; } /* * Verify a certificate verify message. Return the result of verification, * if there is a problem throw a GeneralSecurityException. */ boolean verify(ProtocolVersion protocolVersion, HandshakeHash handshakeHash, PublicKey publicKey, SecretKey masterSecret) throws GeneralSecurityException { String algorithm = publicKey.getAlgorithm(); Signature sig = null; if (protocolVersion.v >= ProtocolVersion.TLS12.v) { sig = JsseJce.getSignature( preferableSignatureAlgorithm.getAlgorithmName()); } else { sig = getSignature(protocolVersion, algorithm); } sig.initVerify(publicKey); updateSignature(sig, protocolVersion, handshakeHash, algorithm, masterSecret); return sig.verify(signature); } /* * Get the Signature object appropriate for verification using the * given signature algorithm and protocol version. */ private static Signature getSignature(ProtocolVersion protocolVersion, String algorithm) throws GeneralSecurityException { if (algorithm.equals("RSA")) { return RSASignature.getInternalInstance(); } else if (algorithm.equals("DSA")) { return JsseJce.getSignature(JsseJce.SIGNATURE_RAWDSA); } else if (algorithm.equals("EC")) { return JsseJce.getSignature(JsseJce.SIGNATURE_RAWECDSA); } else { throw new SignatureException("Unrecognized algorithm: " + algorithm); } } /* * Update the Signature with the data appropriate for the given * signature algorithm and protocol version so that the object is * ready for signing or verifying. */ private static void updateSignature(Signature sig, ProtocolVersion protocolVersion, HandshakeHash handshakeHash, String algorithm, SecretKey masterKey) throws SignatureException { if (algorithm.equals("RSA")) { if (protocolVersion.v < ProtocolVersion.TLS12.v) { // TLS1.1- MessageDigest md5Clone = handshakeHash.getMD5Clone(); MessageDigest shaClone = handshakeHash.getSHAClone(); if (protocolVersion.v < ProtocolVersion.TLS10.v) { // SSLv3 updateDigest(md5Clone, MD5_pad1, MD5_pad2, masterKey); updateDigest(shaClone, SHA_pad1, SHA_pad2, masterKey); } // The signature must be an instance of RSASignature, need // to use these hashes directly. RSASignature.setHashes(sig, md5Clone, shaClone); } else { // TLS1.2+ sig.update(handshakeHash.getAllHandshakeMessages()); } } else { // DSA, ECDSA if (protocolVersion.v < ProtocolVersion.TLS12.v) { // TLS1.1- MessageDigest shaClone = handshakeHash.getSHAClone(); if (protocolVersion.v < ProtocolVersion.TLS10.v) { // SSLv3 updateDigest(shaClone, SHA_pad1, SHA_pad2, masterKey); } sig.update(shaClone.digest()); } else { // TLS1.2+ sig.update(handshakeHash.getAllHandshakeMessages()); } } } /* * Update the MessageDigest for SSLv3 certificate verify or finished * message calculation. The digest must already have been updated with * all preceding handshake messages. * Used by the Finished class as well. */ private static void updateDigest(MessageDigest md, byte[] pad1, byte[] pad2, SecretKey masterSecret) { // Digest the key bytes if available. // Otherwise (sensitive key), try digesting the key directly. // That is currently only implemented in SunPKCS11 using a private // reflection API, so we avoid that if possible. byte[] keyBytes = "RAW".equals(masterSecret.getFormat()) ? masterSecret.getEncoded() : null; if (keyBytes != null) { md.update(keyBytes); } else { digestKey(md, masterSecret); } md.update(pad1); byte[] temp = md.digest(); if (keyBytes != null) { md.update(keyBytes); } else { digestKey(md, masterSecret); } md.update(pad2); md.update(temp); } private final static Class delegate; private final static Field spiField; static { try { delegate = Class.forName("java.security.MessageDigest$Delegate"); spiField = delegate.getDeclaredField("digestSpi"); } catch (Exception e) { throw new RuntimeException("Reflection failed", e); } makeAccessible(spiField); } private static void makeAccessible(final AccessibleObject o) { AccessController.doPrivileged(new PrivilegedAction<Object>() { public Object run() { o.setAccessible(true); return null; } }); } // ConcurrentHashMap does not allow null values, use this marker object private final static Object NULL_OBJECT = new Object(); // cache Method objects per Spi class // Note that this will prevent the Spi classes from being GC'd. We assume // that is not a problem. private final static Map<Class,Object> methodCache = new ConcurrentHashMap<>(); private static void digestKey(MessageDigest md, SecretKey key) { try { // Verify that md is implemented via MessageDigestSpi, not // via JDK 1.1 style MessageDigest subclassing. if (md.getClass() != delegate) { throw new Exception("Digest is not a MessageDigestSpi"); } MessageDigestSpi spi = (MessageDigestSpi)spiField.get(md); Class<?> clazz = spi.getClass(); Object r = methodCache.get(clazz); if (r == null) { try { r = clazz.getDeclaredMethod("implUpdate", SecretKey.class); makeAccessible((Method)r); } catch (NoSuchMethodException e) { r = NULL_OBJECT; } methodCache.put(clazz, r); } if (r == NULL_OBJECT) { throw new Exception( "Digest does not support implUpdate(SecretKey)"); } Method update = (Method)r; update.invoke(spi, key); } catch (Exception e) { throw new RuntimeException( "Could not obtain encoded key and " + "MessageDigest cannot digest key", e); } } @Override int messageType() { return ht_certificate_verify; } @Override int messageLength() { int temp = 2; if (protocolVersion.v >= ProtocolVersion.TLS12.v) { temp += SignatureAndHashAlgorithm.sizeInRecord(); } return temp + signature.length; } @Override void send(HandshakeOutStream s) throws IOException { if (protocolVersion.v >= ProtocolVersion.TLS12.v) { s.putInt8(preferableSignatureAlgorithm.getHashValue()); s.putInt8(preferableSignatureAlgorithm.getSignatureValue()); } s.putBytes16(signature); } @Override void print(PrintStream s) throws IOException { s.println("*** CertificateVerify"); if (debug != null && Debug.isOn("verbose")) { if (protocolVersion.v >= ProtocolVersion.TLS12.v) { s.println("Signature Algorithm " + preferableSignatureAlgorithm.getAlgorithmName()); } } } } /* * FINISHED ... sent by both CLIENT and SERVER * * This is the FINISHED message as defined in the SSL and TLS protocols. * Both protocols define this handshake message slightly differently. * This class supports both formats. * * When handshaking is finished, each side sends a "change_cipher_spec" * record, then immediately sends a "finished" handshake message prepared * according to the newly adopted cipher spec. * * NOTE that until this is sent, no application data may be passed, unless * some non-default cipher suite has already been set up on this connection * connection (e.g. a previous handshake arranged one). */ static final class Finished extends HandshakeMessage { // constant for a Finished message sent by the client final static int CLIENT = 1; // constant for a Finished message sent by the server final static int SERVER = 2; // enum Sender: "CLNT" and "SRVR" private static final byte[] SSL_CLIENT = { 0x43, 0x4C, 0x4E, 0x54 }; private static final byte[] SSL_SERVER = { 0x53, 0x52, 0x56, 0x52 }; /* * Contents of the finished message ("checksum"). For TLS, it * is 12 bytes long, for SSLv3 36 bytes. */ private byte[] verifyData; /* * Current cipher suite we are negotiating. TLS 1.2 has * ciphersuite-defined PRF algorithms. */ private ProtocolVersion protocolVersion; private CipherSuite cipherSuite; /* * Create a finished message to send to the remote peer. */ Finished(ProtocolVersion protocolVersion, HandshakeHash handshakeHash, int sender, SecretKey master, CipherSuite cipherSuite) { this.protocolVersion = protocolVersion; this.cipherSuite = cipherSuite; verifyData = getFinished(handshakeHash, sender, master); } /* * Constructor that reads FINISHED message from stream. */ Finished(ProtocolVersion protocolVersion, HandshakeInStream input, CipherSuite cipherSuite) throws IOException { this.protocolVersion = protocolVersion; this.cipherSuite = cipherSuite; int msgLen = (protocolVersion.v >= ProtocolVersion.TLS10.v) ? 12 : 36; verifyData = new byte[msgLen]; input.read(verifyData); } /* * Verify that the hashes here are what would have been produced * according to a given set of inputs. This is used to ensure that * both client and server are fully in sync, and that the handshake * computations have been successful. */ boolean verify(HandshakeHash handshakeHash, int sender, SecretKey master) { byte[] myFinished = getFinished(handshakeHash, sender, master); return Arrays.equals(myFinished, verifyData); } /* * Perform the actual finished message calculation. */ private byte[] getFinished(HandshakeHash handshakeHash, int sender, SecretKey masterKey) { byte[] sslLabel; String tlsLabel; if (sender == CLIENT) { sslLabel = SSL_CLIENT; tlsLabel = "client finished"; } else if (sender == SERVER) { sslLabel = SSL_SERVER; tlsLabel = "server finished"; } else { throw new RuntimeException("Invalid sender: " + sender); } if (protocolVersion.v >= ProtocolVersion.TLS10.v) { // TLS 1.0+ try { byte [] seed; String prfAlg; PRF prf; // Get the KeyGenerator alg and calculate the seed. if (protocolVersion.v >= ProtocolVersion.TLS12.v) { // TLS 1.2 seed = handshakeHash.getFinishedHash(); prfAlg = "SunTls12Prf"; prf = cipherSuite.prfAlg; } else { // TLS 1.0/1.1 MessageDigest md5Clone = handshakeHash.getMD5Clone(); MessageDigest shaClone = handshakeHash.getSHAClone(); seed = new byte[36]; md5Clone.digest(seed, 0, 16); shaClone.digest(seed, 16, 20); prfAlg = "SunTlsPrf"; prf = P_NONE; } String prfHashAlg = prf.getPRFHashAlg(); int prfHashLength = prf.getPRFHashLength(); int prfBlockSize = prf.getPRFBlockSize(); /* * RFC 5246/7.4.9 says that finished messages can * be ciphersuite-specific in both length/PRF hash * algorithm. If we ever run across a different * length, this call will need to be updated. */ TlsPrfParameterSpec spec = new TlsPrfParameterSpec( masterKey, tlsLabel, seed, 12, prfHashAlg, prfHashLength, prfBlockSize); KeyGenerator kg = JsseJce.getKeyGenerator(prfAlg); kg.init(spec); SecretKey prfKey = kg.generateKey(); if ("RAW".equals(prfKey.getFormat()) == false) { throw new ProviderException( "Invalid PRF output, format must be RAW"); } byte[] finished = prfKey.getEncoded(); return finished; } catch (GeneralSecurityException e) { throw new RuntimeException("PRF failed", e); } } else { // SSLv3 MessageDigest md5Clone = handshakeHash.getMD5Clone(); MessageDigest shaClone = handshakeHash.getSHAClone(); updateDigest(md5Clone, sslLabel, MD5_pad1, MD5_pad2, masterKey); updateDigest(shaClone, sslLabel, SHA_pad1, SHA_pad2, masterKey); byte[] finished = new byte[36]; try { md5Clone.digest(finished, 0, 16); shaClone.digest(finished, 16, 20); } catch (DigestException e) { // cannot occur throw new RuntimeException("Digest failed", e); } return finished; } } /* * Update the MessageDigest for SSLv3 finished message calculation. * The digest must already have been updated with all preceding handshake * messages. This operation is almost identical to the certificate verify * hash, reuse that code. */ private static void updateDigest(MessageDigest md, byte[] sender, byte[] pad1, byte[] pad2, SecretKey masterSecret) { md.update(sender); CertificateVerify.updateDigest(md, pad1, pad2, masterSecret); } // get the verify_data of the finished message byte[] getVerifyData() { return verifyData; } @Override int messageType() { return ht_finished; } @Override int messageLength() { return verifyData.length; } @Override void send(HandshakeOutStream out) throws IOException { out.write(verifyData); } @Override void print(PrintStream s) throws IOException { s.println("*** Finished"); if (debug != null && Debug.isOn("verbose")) { Debug.println(s, "verify_data", verifyData); s.println("***"); } } }