| 
 | 
 | 
 | 
 | 
 | 
 | 
 | 
 | 
 | 
 | 
 | 
 | 
 | 
 | 
 | 
 | 
 | 
 | 
 | 
 | 
 | 
 | 
 | 
 | 
 | 
 | 
 | 
 | 
 | 
 | 
 | 
 | 
 | 
 | 
 | 
 | 
 | 
 | 
 | 
 | 
 | 
 | 
 | 
 | 
 | 
 | 
 */  | 
 | 
 | 
 | 
package sun.security.ssl;  | 
 | 
 | 
 | 
import java.io.IOException;  | 
 | 
import java.math.BigInteger;  | 
 | 
import java.nio.ByteBuffer;  | 
 | 
import java.security.CryptoPrimitive;  | 
 | 
import java.security.GeneralSecurityException;  | 
 | 
import java.security.KeyFactory;  | 
 | 
import java.text.MessageFormat;  | 
 | 
import java.util.EnumSet;  | 
 | 
import java.util.Locale;  | 
 | 
import javax.crypto.SecretKey;  | 
 | 
import javax.crypto.interfaces.DHPublicKey;  | 
 | 
import javax.crypto.spec.DHParameterSpec;  | 
 | 
import javax.crypto.spec.DHPublicKeySpec;  | 
 | 
import javax.net.ssl.SSLHandshakeException;  | 
 | 
import sun.security.ssl.DHKeyExchange.DHECredentials;  | 
 | 
import sun.security.ssl.DHKeyExchange.DHEPossession;  | 
 | 
import sun.security.ssl.SSLHandshake.HandshakeMessage;  | 
 | 
import sun.security.ssl.SupportedGroupsExtension.NamedGroup;  | 
 | 
import sun.misc.HexDumpEncoder;  | 
 | 
 | 
 | 
 | 
 | 
 | 
 | 
 */  | 
 | 
final class DHClientKeyExchange { | 
 | 
    static final DHClientKeyExchangeConsumer dhHandshakeConsumer =  | 
 | 
            new DHClientKeyExchangeConsumer();  | 
 | 
    static final DHClientKeyExchangeProducer dhHandshakeProducer =  | 
 | 
            new DHClientKeyExchangeProducer();  | 
 | 
 | 
 | 
      | 
 | 
 | 
 | 
 | 
 | 
 | 
 | 
 | 
 | 
 | 
 | 
 | 
 | 
 | 
 | 
 | 
 | 
 | 
 | 
 | 
 | 
     */  | 
 | 
    private static final  | 
 | 
            class DHClientKeyExchangeMessage extends HandshakeMessage { | 
 | 
        private byte[] y;          | 
 | 
 | 
 | 
        DHClientKeyExchangeMessage(  | 
 | 
                HandshakeContext handshakeContext) throws IOException { | 
 | 
            super(handshakeContext);  | 
 | 
              | 
 | 
            ClientHandshakeContext chc =  | 
 | 
                    (ClientHandshakeContext)handshakeContext;  | 
 | 
 | 
 | 
            DHEPossession dhePossession = null;  | 
 | 
            for (SSLPossession possession : chc.handshakePossessions) { | 
 | 
                if (possession instanceof DHEPossession) { | 
 | 
                    dhePossession = (DHEPossession)possession;  | 
 | 
                    break;  | 
 | 
                }  | 
 | 
            }  | 
 | 
 | 
 | 
            if (dhePossession == null) { | 
 | 
                  | 
 | 
                throw chc.conContext.fatal(Alert.HANDSHAKE_FAILURE,  | 
 | 
                    "No DHE credentials negotiated for client key exchange");  | 
 | 
            }  | 
 | 
 | 
 | 
            DHPublicKey publicKey = dhePossession.publicKey;  | 
 | 
            DHParameterSpec params = publicKey.getParams();  | 
 | 
            this.y = Utilities.toByteArray(publicKey.getY());  | 
 | 
        }  | 
 | 
 | 
 | 
        DHClientKeyExchangeMessage(HandshakeContext handshakeContext,  | 
 | 
                ByteBuffer m) throws IOException { | 
 | 
            super(handshakeContext);  | 
 | 
              | 
 | 
            ServerHandshakeContext shc =  | 
 | 
                    (ServerHandshakeContext)handshakeContext;  | 
 | 
 | 
 | 
            if (m.remaining() < 3) { | 
 | 
                throw shc.conContext.fatal(Alert.HANDSHAKE_FAILURE,  | 
 | 
                    "Invalid DH ClientKeyExchange message: insufficient data");  | 
 | 
            }  | 
 | 
 | 
 | 
            this.y = Record.getBytes16(m);  | 
 | 
 | 
 | 
            if (m.hasRemaining()) { | 
 | 
                throw shc.conContext.fatal(Alert.HANDSHAKE_FAILURE,  | 
 | 
                    "Invalid DH ClientKeyExchange message: unknown extra data");  | 
 | 
            }  | 
 | 
        }  | 
 | 
 | 
 | 
        @Override  | 
 | 
        public SSLHandshake handshakeType() { | 
 | 
            return SSLHandshake.CLIENT_KEY_EXCHANGE;  | 
 | 
        }  | 
 | 
 | 
 | 
        @Override  | 
 | 
        public int messageLength() { | 
 | 
            return y.length + 2;      | 
 | 
        }  | 
 | 
 | 
 | 
        @Override  | 
 | 
        public void send(HandshakeOutStream hos) throws IOException { | 
 | 
            hos.putBytes16(y);  | 
 | 
        }  | 
 | 
 | 
 | 
        @Override  | 
 | 
        public String toString() { | 
 | 
            MessageFormat messageFormat = new MessageFormat(  | 
 | 
                "\"DH ClientKeyExchange\": '{'\n" + | 
 | 
                "  \"parameters\": '{'\n" + | 
 | 
                "    \"dh_Yc\": '{'\n" + | 
 | 
                "{0}\n" + | 
 | 
                "    '}',\n" +  | 
 | 
                "  '}'\n" +  | 
 | 
                "'}'",  | 
 | 
                Locale.ENGLISH);  | 
 | 
 | 
 | 
            HexDumpEncoder hexEncoder = new HexDumpEncoder();  | 
 | 
            Object[] messageFields = { | 
 | 
                Utilities.indent(  | 
 | 
                        hexEncoder.encodeBuffer(y), "      "),  | 
 | 
            };  | 
 | 
            return messageFormat.format(messageFields);  | 
 | 
        }  | 
 | 
    }  | 
 | 
 | 
 | 
      | 
 | 
 | 
 | 
     */  | 
 | 
    private static final  | 
 | 
            class DHClientKeyExchangeProducer implements HandshakeProducer { | 
 | 
          | 
 | 
        private DHClientKeyExchangeProducer() { | 
 | 
            // blank  | 
 | 
        }  | 
 | 
 | 
 | 
        @Override  | 
 | 
        public byte[] produce(ConnectionContext context,  | 
 | 
                HandshakeMessage message) throws IOException { | 
 | 
              | 
 | 
            ClientHandshakeContext chc = (ClientHandshakeContext)context;  | 
 | 
 | 
 | 
            DHECredentials dheCredentials = null;  | 
 | 
            for (SSLCredentials cd : chc.handshakeCredentials) { | 
 | 
                if (cd instanceof DHECredentials) { | 
 | 
                    dheCredentials = (DHECredentials)cd;  | 
 | 
                    break;  | 
 | 
                }  | 
 | 
            }  | 
 | 
 | 
 | 
            if (dheCredentials == null) { | 
 | 
                throw chc.conContext.fatal(Alert.HANDSHAKE_FAILURE,  | 
 | 
                    "No DHE credentials negotiated for client key exchange");  | 
 | 
            }  | 
 | 
 | 
 | 
 | 
 | 
            DHEPossession dhePossession = new DHEPossession(  | 
 | 
                    dheCredentials, chc.sslContext.getSecureRandom());  | 
 | 
            chc.handshakePossessions.add(dhePossession);  | 
 | 
            DHClientKeyExchangeMessage ckem =  | 
 | 
                    new DHClientKeyExchangeMessage(chc);  | 
 | 
            if (SSLLogger.isOn && SSLLogger.isOn("ssl,handshake")) { | 
 | 
                SSLLogger.fine(  | 
 | 
                    "Produced DH ClientKeyExchange handshake message", ckem);  | 
 | 
            }  | 
 | 
 | 
 | 
              | 
 | 
            ckem.write(chc.handshakeOutput);  | 
 | 
            chc.handshakeOutput.flush();  | 
 | 
 | 
 | 
              | 
 | 
            SSLKeyExchange ke = SSLKeyExchange.valueOf(  | 
 | 
                    chc.negotiatedCipherSuite.keyExchange,  | 
 | 
                    chc.negotiatedProtocol);  | 
 | 
            if (ke == null) { | 
 | 
                  | 
 | 
                throw chc.conContext.fatal(Alert.INTERNAL_ERROR,  | 
 | 
                        "Not supported key exchange type");  | 
 | 
            } else { | 
 | 
                SSLKeyDerivation masterKD = ke.createKeyDerivation(chc);  | 
 | 
                SecretKey masterSecret =  | 
 | 
                        masterKD.deriveKey("MasterSecret", null); | 
 | 
                chc.handshakeSession.setMasterSecret(masterSecret);  | 
 | 
 | 
 | 
                SSLTrafficKeyDerivation kd =  | 
 | 
                        SSLTrafficKeyDerivation.valueOf(chc.negotiatedProtocol);  | 
 | 
                if (kd == null) { | 
 | 
                      | 
 | 
                    throw chc.conContext.fatal(Alert.INTERNAL_ERROR,  | 
 | 
                            "Not supported key derivation: " +  | 
 | 
                            chc.negotiatedProtocol);  | 
 | 
                } else { | 
 | 
                    chc.handshakeKeyDerivation =  | 
 | 
                        kd.createKeyDerivation(chc, masterSecret);  | 
 | 
                }  | 
 | 
            }  | 
 | 
 | 
 | 
              | 
 | 
            return null;  | 
 | 
        }  | 
 | 
    }  | 
 | 
 | 
 | 
      | 
 | 
 | 
 | 
     */  | 
 | 
    private static final  | 
 | 
            class DHClientKeyExchangeConsumer implements SSLConsumer { | 
 | 
          | 
 | 
        private DHClientKeyExchangeConsumer() { | 
 | 
            // blank  | 
 | 
        }  | 
 | 
 | 
 | 
        @Override  | 
 | 
        public void consume(ConnectionContext context,  | 
 | 
                ByteBuffer message) throws IOException { | 
 | 
              | 
 | 
            ServerHandshakeContext shc = (ServerHandshakeContext)context;  | 
 | 
 | 
 | 
            DHEPossession dhePossession = null;  | 
 | 
            for (SSLPossession possession : shc.handshakePossessions) { | 
 | 
                if (possession instanceof DHEPossession) { | 
 | 
                    dhePossession = (DHEPossession)possession;  | 
 | 
                    break;  | 
 | 
                }  | 
 | 
            }  | 
 | 
 | 
 | 
            if (dhePossession == null) { | 
 | 
                  | 
 | 
                throw shc.conContext.fatal(Alert.HANDSHAKE_FAILURE,  | 
 | 
                    "No expected DHE possessions for client key exchange");  | 
 | 
            }  | 
 | 
 | 
 | 
            SSLKeyExchange ke = SSLKeyExchange.valueOf(  | 
 | 
                    shc.negotiatedCipherSuite.keyExchange,  | 
 | 
                    shc.negotiatedProtocol);  | 
 | 
            if (ke == null) { | 
 | 
                  | 
 | 
                throw shc.conContext.fatal(Alert.INTERNAL_ERROR,  | 
 | 
                        "Not supported key exchange type");  | 
 | 
            }  | 
 | 
 | 
 | 
            DHClientKeyExchangeMessage ckem =  | 
 | 
                    new DHClientKeyExchangeMessage(shc, message);  | 
 | 
            if (SSLLogger.isOn && SSLLogger.isOn("ssl,handshake")) { | 
 | 
                SSLLogger.fine(  | 
 | 
                    "Consuming DH ClientKeyExchange handshake message", ckem);  | 
 | 
            }  | 
 | 
 | 
 | 
              | 
 | 
            try { | 
 | 
                DHParameterSpec params = dhePossession.publicKey.getParams();  | 
 | 
                DHPublicKeySpec spec = new DHPublicKeySpec(  | 
 | 
                        new BigInteger(1, ckem.y),  | 
 | 
                        params.getP(), params.getG());  | 
 | 
                KeyFactory kf = JsseJce.getKeyFactory("DiffieHellman"); | 
 | 
                DHPublicKey peerPublicKey =  | 
 | 
                        (DHPublicKey)kf.generatePublic(spec);  | 
 | 
 | 
 | 
                  | 
 | 
                if (!shc.algorithmConstraints.permits(  | 
 | 
                        EnumSet.of(CryptoPrimitive.KEY_AGREEMENT),  | 
 | 
                        peerPublicKey)) { | 
 | 
                    throw new SSLHandshakeException(  | 
 | 
                        "DHPublicKey does not comply to algorithm constraints");  | 
 | 
                }  | 
 | 
 | 
 | 
                NamedGroup namedGroup = NamedGroup.valueOf(params);  | 
 | 
                shc.handshakeCredentials.add(  | 
 | 
                        new DHECredentials(peerPublicKey, namedGroup));  | 
 | 
            } catch (GeneralSecurityException | java.io.IOException e) { | 
 | 
                throw (SSLHandshakeException)(new SSLHandshakeException(  | 
 | 
                        "Could not generate DHPublicKey").initCause(e));  | 
 | 
            }  | 
 | 
 | 
 | 
              | 
 | 
            SSLKeyDerivation masterKD = ke.createKeyDerivation(shc);  | 
 | 
            SecretKey masterSecret =  | 
 | 
                    masterKD.deriveKey("MasterSecret", null); | 
 | 
            shc.handshakeSession.setMasterSecret(masterSecret);  | 
 | 
 | 
 | 
            SSLTrafficKeyDerivation kd =  | 
 | 
                    SSLTrafficKeyDerivation.valueOf(shc.negotiatedProtocol);  | 
 | 
            if (kd == null) { | 
 | 
                  | 
 | 
                throw shc.conContext.fatal(Alert.INTERNAL_ERROR,  | 
 | 
                    "Not supported key derivation: " + shc.negotiatedProtocol);  | 
 | 
            } else { | 
 | 
                shc.handshakeKeyDerivation =  | 
 | 
                    kd.createKeyDerivation(shc, masterSecret);  | 
 | 
            }  | 
 | 
        }  | 
 | 
    }  | 
 | 
}  |