/*
 * Copyright (c) 2018, 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.IOException;
import java.math.BigInteger;
import java.nio.ByteBuffer;
import java.security.CryptoPrimitive;
import java.security.GeneralSecurityException;
import java.security.InvalidKeyException;
import java.security.KeyFactory;
import java.security.NoSuchAlgorithmException;
import java.security.Signature;
import java.security.SignatureException;
import java.security.interfaces.RSAPublicKey;
import java.security.spec.RSAPublicKeySpec;
import java.text.MessageFormat;
import java.util.EnumSet;
import java.util.Locale;
import sun.security.ssl.RSAKeyExchange.EphemeralRSACredentials;
import sun.security.ssl.RSAKeyExchange.EphemeralRSAPossession;
import sun.security.ssl.SSLHandshake.HandshakeMessage;
import sun.security.ssl.X509Authentication.X509Credentials;
import sun.security.ssl.X509Authentication.X509Possession;
import sun.misc.HexDumpEncoder;

Pack of the ServerKeyExchange handshake message.
/** * Pack of the ServerKeyExchange handshake message. */
final class RSAServerKeyExchange { static final SSLConsumer rsaHandshakeConsumer = new RSAServerKeyExchangeConsumer(); static final HandshakeProducer rsaHandshakeProducer = new RSAServerKeyExchangeProducer();
The ephemeral RSA ServerKeyExchange handshake message. Used for RSA_EXPORT, SSL 3.0 and TLS 1.0 only.
/** * The ephemeral RSA ServerKeyExchange handshake message. * * Used for RSA_EXPORT, SSL 3.0 and TLS 1.0 only. */
private static final class RSAServerKeyExchangeMessage extends HandshakeMessage { // public key encapsulated in this message private final byte[] modulus; // 1 to 2^16 - 1 bytes private final byte[] exponent; // 1 to 2^16 - 1 bytes // signature bytes, none-null as no anonymous RSA key exchange. private final byte[] paramsSignature; private RSAServerKeyExchangeMessage(HandshakeContext handshakeContext, X509Possession x509Possession, EphemeralRSAPossession rsaPossession) throws IOException { super(handshakeContext); // This happens in server side only. ServerHandshakeContext shc = (ServerHandshakeContext)handshakeContext; RSAPublicKey publicKey = rsaPossession.popPublicKey; RSAPublicKeySpec spec = JsseJce.getRSAPublicKeySpec(publicKey); this.modulus = Utilities.toByteArray(spec.getModulus()); this.exponent = Utilities.toByteArray(spec.getPublicExponent()); byte[] signature = null; try { Signature signer = RSASignature.getInstance(); signer.initSign(x509Possession.popPrivateKey, shc.sslContext.getSecureRandom()); updateSignature(signer, shc.clientHelloRandom.randomBytes, shc.serverHelloRandom.randomBytes); signature = signer.sign(); } catch (NoSuchAlgorithmException | InvalidKeyException | SignatureException ex) { throw shc.conContext.fatal(Alert.INTERNAL_ERROR, "Failed to sign ephemeral RSA parameters", ex); } this.paramsSignature = signature; } RSAServerKeyExchangeMessage(HandshakeContext handshakeContext, ByteBuffer m) throws IOException { super(handshakeContext); // This happens in client side only. ClientHandshakeContext chc = (ClientHandshakeContext)handshakeContext; this.modulus = Record.getBytes16(m); this.exponent = Record.getBytes16(m); this.paramsSignature = Record.getBytes16(m); X509Credentials x509Credentials = null; for (SSLCredentials cd : chc.handshakeCredentials) { if (cd instanceof X509Credentials) { x509Credentials = (X509Credentials)cd; break; } } if (x509Credentials == null) { throw chc.conContext.fatal(Alert.ILLEGAL_PARAMETER, "No RSA credentials negotiated for server key exchange"); } try { Signature signer = RSASignature.getInstance(); signer.initVerify(x509Credentials.popPublicKey); updateSignature(signer, chc.clientHelloRandom.randomBytes, chc.serverHelloRandom.randomBytes); if (!signer.verify(paramsSignature)) { throw chc.conContext.fatal(Alert.HANDSHAKE_FAILURE, "Invalid signature of RSA ServerKeyExchange message"); } } catch (NoSuchAlgorithmException | InvalidKeyException | SignatureException ex) { throw chc.conContext.fatal(Alert.INTERNAL_ERROR, "Failed to sign ephemeral RSA parameters", ex); } } @Override SSLHandshake handshakeType() { return SSLHandshake.SERVER_KEY_EXCHANGE; } @Override int messageLength() { return 6 + modulus.length + exponent.length + paramsSignature.length; } @Override void send(HandshakeOutStream hos) throws IOException { hos.putBytes16(modulus); hos.putBytes16(exponent); hos.putBytes16(paramsSignature); } @Override public String toString() { MessageFormat messageFormat = new MessageFormat( "\"RSA ServerKeyExchange\": '{'\n" + " \"parameters\": '{'\n" + " \"rsa_modulus\": '{'\n" + "{0}\n" + " '}',\n" + " \"rsa_exponent\": '{'\n" + "{1}\n" + " '}'\n" + " '}',\n" + " \"digital signature\": '{'\n" + " \"signature\": '{'\n" + "{2}\n" + " '}',\n" + " '}'\n" + "'}'", Locale.ENGLISH); HexDumpEncoder hexEncoder = new HexDumpEncoder(); Object[] messageFields = { Utilities.indent( hexEncoder.encodeBuffer(modulus), " "), Utilities.indent( hexEncoder.encodeBuffer(exponent), " "), Utilities.indent( hexEncoder.encodeBuffer(paramsSignature), " ") }; return messageFormat.format(messageFields); } /* * Hash the nonces and the ephemeral RSA public key. */ private void updateSignature(Signature signature, byte[] clntNonce, byte[] svrNonce) throws SignatureException { signature.update(clntNonce); signature.update(svrNonce); signature.update((byte)(modulus.length >> 8)); signature.update((byte)(modulus.length & 0x0ff)); signature.update(modulus); signature.update((byte)(exponent.length >> 8)); signature.update((byte)(exponent.length & 0x0ff)); signature.update(exponent); } }
The RSA "ServerKeyExchange" handshake message producer.
/** * The RSA "ServerKeyExchange" handshake message producer. */
private static final class RSAServerKeyExchangeProducer implements HandshakeProducer { // Prevent instantiation of this class. private RSAServerKeyExchangeProducer() { // blank } @Override public byte[] produce(ConnectionContext context, HandshakeMessage message) throws IOException { // The producing happens in server side only. ServerHandshakeContext shc = (ServerHandshakeContext)context; EphemeralRSAPossession rsaPossession = null; X509Possession x509Possession = null; for (SSLPossession possession : shc.handshakePossessions) { if (possession instanceof EphemeralRSAPossession) { rsaPossession = (EphemeralRSAPossession)possession; if (x509Possession != null) { break; } } else if (possession instanceof X509Possession) { x509Possession = (X509Possession)possession; if (rsaPossession != null) { break; } } } if (rsaPossession == null) { // The X.509 certificate itself should be used for RSA_EXPORT // key exchange. The ServerKeyExchange handshake message is // not needed. return null; } else if (x509Possession == null) { // unlikely throw shc.conContext.fatal(Alert.ILLEGAL_PARAMETER, "No RSA certificate negotiated for server key exchange"); } else if (!"RSA".equals( x509Possession.popPrivateKey.getAlgorithm())) { // unlikely throw shc.conContext.fatal(Alert.ILLEGAL_PARAMETER, "No X.509 possession can be used for " + "ephemeral RSA ServerKeyExchange"); } RSAServerKeyExchangeMessage skem = new RSAServerKeyExchangeMessage( shc, x509Possession, rsaPossession); if (SSLLogger.isOn && SSLLogger.isOn("ssl,handshake")) { SSLLogger.fine( "Produced RSA ServerKeyExchange handshake message", skem); } // Output the handshake message. skem.write(shc.handshakeOutput); shc.handshakeOutput.flush(); // The handshake message has been delivered. return null; } }
The RSA "ServerKeyExchange" handshake message consumer.
/** * The RSA "ServerKeyExchange" handshake message consumer. */
private static final class RSAServerKeyExchangeConsumer implements SSLConsumer { // Prevent instantiation of this class. private RSAServerKeyExchangeConsumer() { // blank } @Override public void consume(ConnectionContext context, ByteBuffer message) throws IOException { // The consuming happens in client side only. ClientHandshakeContext chc = (ClientHandshakeContext)context; RSAServerKeyExchangeMessage skem = new RSAServerKeyExchangeMessage(chc, message); if (SSLLogger.isOn && SSLLogger.isOn("ssl,handshake")) { SSLLogger.fine( "Consuming RSA ServerKeyExchange handshake message", skem); } // // validate // // check constraints of RSA PublicKey RSAPublicKey publicKey; try { KeyFactory kf = JsseJce.getKeyFactory("RSA"); RSAPublicKeySpec spec = new RSAPublicKeySpec( new BigInteger(1, skem.modulus), new BigInteger(1, skem.exponent)); publicKey = (RSAPublicKey)kf.generatePublic(spec); } catch (GeneralSecurityException gse) { throw chc.conContext.fatal(Alert.INSUFFICIENT_SECURITY, "Could not generate RSAPublicKey", gse); } if (!chc.algorithmConstraints.permits( EnumSet.of(CryptoPrimitive.KEY_AGREEMENT), publicKey)) { throw chc.conContext.fatal(Alert.INSUFFICIENT_SECURITY, "RSA ServerKeyExchange does not comply to " + "algorithm constraints"); } // // update // chc.handshakeCredentials.add( new EphemeralRSACredentials(publicKey)); // // produce // // Need no new handshake message producers here. } } }