2006 lines · 67.7 KB · Java
RawDownload
    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