|  |  | 
|  |  | 
|  |  | 
|  |  | 
|  |  | 
|  |  | 
|  |  | 
|  |  | 
|  |  | 
|  |  | 
|  |  | 
|  |  | 
|  |  | 
|  |  | 
|  |  | 
|  |  | 
|  |  | 
|  |  | 
|  |  | 
|  |  | 
|  |  | 
|  |  | 
|  |  | 
|  |  */ | 
|  |  | 
|  | 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.InvalidAlgorithmParameterException; | 
|  | import java.security.InvalidKeyException; | 
|  | import java.security.Key; | 
|  | import java.security.KeyFactory; | 
|  | import java.security.NoSuchAlgorithmException; | 
|  | import java.security.PrivateKey; | 
|  | import java.security.PublicKey; | 
|  | import java.security.Signature; | 
|  | import java.security.SignatureException; | 
|  | import java.text.MessageFormat; | 
|  | import java.util.EnumSet; | 
|  | import java.util.Locale; | 
|  | import java.util.Map; | 
|  | import javax.crypto.interfaces.DHPublicKey; | 
|  | import javax.crypto.spec.DHParameterSpec; | 
|  | import javax.crypto.spec.DHPublicKeySpec; | 
|  | 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.security.ssl.X509Authentication.X509Credentials; | 
|  | import sun.security.ssl.X509Authentication.X509Possession; | 
|  | import sun.misc.HexDumpEncoder; | 
|  | import sun.security.util.KeyUtil; | 
|  |  | 
|  |  | 
|  |  | 
|  |  */ | 
|  | final class DHServerKeyExchange { | 
|  |     static final SSLConsumer dhHandshakeConsumer = | 
|  |             new DHServerKeyExchangeConsumer(); | 
|  |     static final HandshakeProducer dhHandshakeProducer = | 
|  |             new DHServerKeyExchangeProducer(); | 
|  |  | 
|  |      | 
|  |  | 
|  |      */ | 
|  |     private static final | 
|  |             class DHServerKeyExchangeMessage extends HandshakeMessage { | 
|  |         // public key encapsulated in this message | 
|  |         private final byte[] p;         | 
|  |         private final byte[] g;         | 
|  |         private final byte[] y;         | 
|  |  | 
|  |          | 
|  |         private final boolean useExplicitSigAlgorithm; | 
|  |         private final SignatureScheme signatureScheme; | 
|  |  | 
|  |          | 
|  |         private final byte[] paramsSignature; | 
|  |  | 
|  |         DHServerKeyExchangeMessage( | 
|  |                 HandshakeContext handshakeContext) throws IOException { | 
|  |             super(handshakeContext); | 
|  |  | 
|  |              | 
|  |             ServerHandshakeContext shc = | 
|  |                     (ServerHandshakeContext)handshakeContext; | 
|  |  | 
|  |             DHEPossession dhePossession = null; | 
|  |             X509Possession x509Possession = null; | 
|  |             for (SSLPossession possession : shc.handshakePossessions) { | 
|  |                 if (possession instanceof DHEPossession) { | 
|  |                     dhePossession = (DHEPossession)possession; | 
|  |                     if (x509Possession != null) { | 
|  |                         break; | 
|  |                     } | 
|  |                 } else if (possession instanceof X509Possession) { | 
|  |                     x509Possession = (X509Possession)possession; | 
|  |                     if (dhePossession != null) { | 
|  |                         break; | 
|  |                     } | 
|  |                 } | 
|  |             } | 
|  |  | 
|  |             if (dhePossession == null) { | 
|  |                  | 
|  |                 throw shc.conContext.fatal(Alert.ILLEGAL_PARAMETER, | 
|  |                     "No DHE credentials negotiated for server key exchange"); | 
|  |             } | 
|  |             DHPublicKey publicKey = dhePossession.publicKey; | 
|  |             DHParameterSpec params = publicKey.getParams(); | 
|  |             this.p = Utilities.toByteArray(params.getP()); | 
|  |             this.g = Utilities.toByteArray(params.getG()); | 
|  |             this.y = Utilities.toByteArray(publicKey.getY()); | 
|  |  | 
|  |             if (x509Possession == null) { | 
|  |                  | 
|  |                 paramsSignature = null; | 
|  |                 signatureScheme = null; | 
|  |                 useExplicitSigAlgorithm = false; | 
|  |             } else { | 
|  |                 useExplicitSigAlgorithm = | 
|  |                         shc.negotiatedProtocol.useTLS12PlusSpec(); | 
|  |                 Signature signer = null; | 
|  |                 if (useExplicitSigAlgorithm) { | 
|  |                     Map.Entry<SignatureScheme, Signature> schemeAndSigner = | 
|  |                             SignatureScheme.getSignerOfPreferableAlgorithm( | 
|  |                                     shc.peerRequestedSignatureSchemes, | 
|  |                                     x509Possession, | 
|  |                                     shc.negotiatedProtocol); | 
|  |                     if (schemeAndSigner == null) { | 
|  |                         // Unlikely, the credentials generator should have | 
|  |                          | 
|  |                         throw shc.conContext.fatal(Alert.INTERNAL_ERROR, | 
|  |                                 "No supported signature algorithm for " + | 
|  |                                 x509Possession.popPrivateKey.getAlgorithm() + | 
|  |                                 "  key"); | 
|  |                     } else { | 
|  |                         signatureScheme = schemeAndSigner.getKey(); | 
|  |                         signer = schemeAndSigner.getValue(); | 
|  |                     } | 
|  |                 } else { | 
|  |                     signatureScheme = null; | 
|  |                     try { | 
|  |                         signer = getSignature( | 
|  |                                 x509Possession.popPrivateKey.getAlgorithm(), | 
|  |                                 x509Possession.popPrivateKey); | 
|  |                     } catch (NoSuchAlgorithmException | InvalidKeyException e) { | 
|  |                         throw shc.conContext.fatal(Alert.INTERNAL_ERROR, | 
|  |                             "Unsupported signature algorithm: " + | 
|  |                             x509Possession.popPrivateKey.getAlgorithm(), e); | 
|  |                     } | 
|  |                 } | 
|  |  | 
|  |                 byte[] signature = null; | 
|  |                 try { | 
|  |                     updateSignature(signer, shc.clientHelloRandom.randomBytes, | 
|  |                             shc.serverHelloRandom.randomBytes); | 
|  |                     signature = signer.sign(); | 
|  |                 } catch (SignatureException ex) { | 
|  |                     throw shc.conContext.fatal(Alert.INTERNAL_ERROR, | 
|  |                         "Failed to sign dhe parameters: " + | 
|  |                         x509Possession.popPrivateKey.getAlgorithm(), ex); | 
|  |                 } | 
|  |                 paramsSignature = signature; | 
|  |             } | 
|  |         } | 
|  |  | 
|  |         DHServerKeyExchangeMessage(HandshakeContext handshakeContext, | 
|  |                 ByteBuffer m) throws IOException { | 
|  |             super(handshakeContext); | 
|  |  | 
|  |              | 
|  |             ClientHandshakeContext chc = | 
|  |                     (ClientHandshakeContext)handshakeContext; | 
|  |  | 
|  |             this.p = Record.getBytes16(m); | 
|  |             this.g = Record.getBytes16(m); | 
|  |             this.y = Record.getBytes16(m); | 
|  |  | 
|  |             try { | 
|  |                 KeyUtil.validate(new DHPublicKeySpec( | 
|  |                         new BigInteger(1, y), | 
|  |                         new BigInteger(1, p), | 
|  |                         new BigInteger(1, p))); | 
|  |             } catch (InvalidKeyException ike) { | 
|  |                 throw chc.conContext.fatal(Alert.HANDSHAKE_FAILURE, | 
|  |                     "Invalid DH ServerKeyExchange: invalid parameters", ike); | 
|  |             } | 
|  |  | 
|  |             X509Credentials x509Credentials = null; | 
|  |             for (SSLCredentials cd : chc.handshakeCredentials) { | 
|  |                 if (cd instanceof X509Credentials) { | 
|  |                     x509Credentials = (X509Credentials)cd; | 
|  |                     break; | 
|  |                 } | 
|  |             } | 
|  |  | 
|  |             if (x509Credentials == null) { | 
|  |                  | 
|  |                 if (m.hasRemaining()) { | 
|  |                     throw chc.conContext.fatal(Alert.HANDSHAKE_FAILURE, | 
|  |                         "Invalid DH ServerKeyExchange: unknown extra data"); | 
|  |                 } | 
|  |  | 
|  |                 this.signatureScheme = null; | 
|  |                 this.paramsSignature = null; | 
|  |                 this.useExplicitSigAlgorithm = false; | 
|  |  | 
|  |                 return; | 
|  |             } | 
|  |  | 
|  |             this.useExplicitSigAlgorithm = | 
|  |                     chc.negotiatedProtocol.useTLS12PlusSpec(); | 
|  |             if (useExplicitSigAlgorithm) { | 
|  |                 int ssid = Record.getInt16(m); | 
|  |                 signatureScheme = SignatureScheme.valueOf(ssid); | 
|  |                 if (signatureScheme == null) { | 
|  |                     throw chc.conContext.fatal(Alert.HANDSHAKE_FAILURE, | 
|  |                             "Invalid signature algorithm (" + ssid + | 
|  |                             ") used in DH ServerKeyExchange handshake message"); | 
|  |                 } | 
|  |  | 
|  |                 if (!chc.localSupportedSignAlgs.contains(signatureScheme)) { | 
|  |                     throw chc.conContext.fatal(Alert.HANDSHAKE_FAILURE, | 
|  |                             "Unsupported signature algorithm (" + | 
|  |                             signatureScheme.name + | 
|  |                             ") used in DH ServerKeyExchange handshake message"); | 
|  |                 } | 
|  |             } else { | 
|  |                 this.signatureScheme = null; | 
|  |             } | 
|  |  | 
|  |              | 
|  |             this.paramsSignature = Record.getBytes16(m); | 
|  |             Signature signer; | 
|  |             if (useExplicitSigAlgorithm) { | 
|  |                 try { | 
|  |                     signer = signatureScheme.getVerifier( | 
|  |                             x509Credentials.popPublicKey); | 
|  |                 } catch (NoSuchAlgorithmException | InvalidKeyException | | 
|  |                         InvalidAlgorithmParameterException nsae) { | 
|  |                     throw chc.conContext.fatal(Alert.INTERNAL_ERROR, | 
|  |                             "Unsupported signature algorithm: " + | 
|  |                             signatureScheme.name, nsae); | 
|  |                 } | 
|  |             } else { | 
|  |                 try { | 
|  |                     signer = getSignature( | 
|  |                             x509Credentials.popPublicKey.getAlgorithm(), | 
|  |                             x509Credentials.popPublicKey); | 
|  |                 } catch (NoSuchAlgorithmException | InvalidKeyException e) { | 
|  |                     throw chc.conContext.fatal(Alert.INTERNAL_ERROR, | 
|  |                             "Unsupported signature algorithm: " + | 
|  |                             x509Credentials.popPublicKey.getAlgorithm(), e); | 
|  |                 } | 
|  |             } | 
|  |  | 
|  |             try { | 
|  |                 updateSignature(signer, | 
|  |                         chc.clientHelloRandom.randomBytes, | 
|  |                         chc.serverHelloRandom.randomBytes); | 
|  |  | 
|  |                 if (!signer.verify(paramsSignature)) { | 
|  |                     throw chc.conContext.fatal(Alert.HANDSHAKE_FAILURE, | 
|  |                         "Invalid signature on DH ServerKeyExchange message"); | 
|  |                 } | 
|  |             } catch (SignatureException ex) { | 
|  |                 throw chc.conContext.fatal(Alert.HANDSHAKE_FAILURE, | 
|  |                         "Cannot verify DH ServerKeyExchange signature", ex); | 
|  |             } | 
|  |         } | 
|  |  | 
|  |         @Override | 
|  |         public SSLHandshake handshakeType() { | 
|  |             return SSLHandshake.SERVER_KEY_EXCHANGE; | 
|  |         } | 
|  |  | 
|  |         @Override | 
|  |         public int messageLength() { | 
|  |             int sigLen = 0; | 
|  |             if (paramsSignature != null) { | 
|  |                 sigLen = 2 + paramsSignature.length; | 
|  |                 if (useExplicitSigAlgorithm) { | 
|  |                     sigLen += SignatureScheme.sizeInRecord(); | 
|  |                 } | 
|  |             } | 
|  |  | 
|  |             return 6 + p.length + g.length + y.length + sigLen; | 
|  |                     // 6: overhead for p, g, y values | 
|  |         } | 
|  |  | 
|  |         @Override | 
|  |         public void send(HandshakeOutStream hos) throws IOException { | 
|  |             hos.putBytes16(p); | 
|  |             hos.putBytes16(g); | 
|  |             hos.putBytes16(y); | 
|  |  | 
|  |             if (paramsSignature != null) { | 
|  |                 if (useExplicitSigAlgorithm) { | 
|  |                     hos.putInt16(signatureScheme.id); | 
|  |                 } | 
|  |  | 
|  |                 hos.putBytes16(paramsSignature); | 
|  |             } | 
|  |         } | 
|  |  | 
|  |         @Override | 
|  |         public String toString() { | 
|  |             if (paramsSignature == null) {     | 
|  |                 MessageFormat messageFormat = new MessageFormat( | 
|  |                     "\"DH ServerKeyExchange\": '{'\n" + | 
|  |                     "  \"parameters\": '{'\n" + | 
|  |                     "    \"dh_p\": '{'\n" + | 
|  |                     "{0}\n" + | 
|  |                     "    '}',\n" + | 
|  |                     "    \"dh_g\": '{'\n" + | 
|  |                     "{1}\n" + | 
|  |                     "    '}',\n" + | 
|  |                     "    \"dh_Ys\": '{'\n" + | 
|  |                     "{2}\n" + | 
|  |                     "    '}',\n" + | 
|  |                     "  '}'\n" + | 
|  |                     "'}'", | 
|  |                     Locale.ENGLISH); | 
|  |  | 
|  |                 HexDumpEncoder hexEncoder = new HexDumpEncoder(); | 
|  |                 Object[] messageFields = { | 
|  |                     Utilities.indent( | 
|  |                             hexEncoder.encodeBuffer(p), "      "), | 
|  |                     Utilities.indent( | 
|  |                             hexEncoder.encodeBuffer(g), "      "), | 
|  |                     Utilities.indent( | 
|  |                             hexEncoder.encodeBuffer(y), "      "), | 
|  |                 }; | 
|  |  | 
|  |                 return messageFormat.format(messageFields); | 
|  |             } | 
|  |  | 
|  |             if (useExplicitSigAlgorithm) { | 
|  |                 MessageFormat messageFormat = new MessageFormat( | 
|  |                     "\"DH ServerKeyExchange\": '{'\n" + | 
|  |                     "  \"parameters\": '{'\n" + | 
|  |                     "    \"dh_p\": '{'\n" + | 
|  |                     "{0}\n" + | 
|  |                     "    '}',\n" + | 
|  |                     "    \"dh_g\": '{'\n" + | 
|  |                     "{1}\n" + | 
|  |                     "    '}',\n" + | 
|  |                     "    \"dh_Ys\": '{'\n" + | 
|  |                     "{2}\n" + | 
|  |                     "    '}',\n" + | 
|  |                     "  '}',\n" + | 
|  |                     "  \"digital signature\":  '{'\n" + | 
|  |                     "    \"signature algorithm\": \"{3}\"\n" + | 
|  |                     "    \"signature\": '{'\n" + | 
|  |                     "{4}\n" + | 
|  |                     "    '}',\n" + | 
|  |                     "  '}'\n" + | 
|  |                     "'}'", | 
|  |                     Locale.ENGLISH); | 
|  |  | 
|  |                 HexDumpEncoder hexEncoder = new HexDumpEncoder(); | 
|  |                 Object[] messageFields = { | 
|  |                     Utilities.indent( | 
|  |                             hexEncoder.encodeBuffer(p), "      "), | 
|  |                     Utilities.indent( | 
|  |                             hexEncoder.encodeBuffer(g), "      "), | 
|  |                     Utilities.indent( | 
|  |                             hexEncoder.encodeBuffer(y), "      "), | 
|  |                     signatureScheme.name, | 
|  |                     Utilities.indent( | 
|  |                             hexEncoder.encodeBuffer(paramsSignature), "      ") | 
|  |                 }; | 
|  |  | 
|  |                 return messageFormat.format(messageFields); | 
|  |             } else { | 
|  |                 MessageFormat messageFormat = new MessageFormat( | 
|  |                     "\"DH ServerKeyExchange\": '{'\n" + | 
|  |                     "  \"parameters\": '{'\n" + | 
|  |                     "    \"dh_p\": '{'\n" + | 
|  |                     "{0}\n" + | 
|  |                     "    '}',\n" + | 
|  |                     "    \"dh_g\": '{'\n" + | 
|  |                     "{1}\n" + | 
|  |                     "    '}',\n" + | 
|  |                     "    \"dh_Ys\": '{'\n" + | 
|  |                     "{2}\n" + | 
|  |                     "    '}',\n" + | 
|  |                     "  '}',\n" + | 
|  |                     "  \"signature\": '{'\n" + | 
|  |                     "{3}\n" + | 
|  |                     "  '}'\n" + | 
|  |                     "'}'", | 
|  |                     Locale.ENGLISH); | 
|  |  | 
|  |                 HexDumpEncoder hexEncoder = new HexDumpEncoder(); | 
|  |                 Object[] messageFields = { | 
|  |                     Utilities.indent( | 
|  |                             hexEncoder.encodeBuffer(p), "      "), | 
|  |                     Utilities.indent( | 
|  |                             hexEncoder.encodeBuffer(g), "      "), | 
|  |                     Utilities.indent( | 
|  |                             hexEncoder.encodeBuffer(y), "      "), | 
|  |                     Utilities.indent( | 
|  |                             hexEncoder.encodeBuffer(paramsSignature), "    ") | 
|  |                 }; | 
|  |  | 
|  |                 return messageFormat.format(messageFields); | 
|  |             } | 
|  |         } | 
|  |  | 
|  |         private static Signature getSignature(String keyAlgorithm, | 
|  |                 Key key) throws NoSuchAlgorithmException, InvalidKeyException { | 
|  |             Signature signer = null; | 
|  |             switch (keyAlgorithm) { | 
|  |                 case "DSA": | 
|  |                     signer = JsseJce.getSignature(JsseJce.SIGNATURE_DSA); | 
|  |                     break; | 
|  |                 case "RSA": | 
|  |                     signer = RSASignature.getInstance(); | 
|  |                     break; | 
|  |                 default: | 
|  |                     throw new NoSuchAlgorithmException( | 
|  |                         "neither an RSA or a DSA key : " + keyAlgorithm); | 
|  |             } | 
|  |  | 
|  |             if (signer != null) { | 
|  |                 if (key instanceof PublicKey) { | 
|  |                     signer.initVerify((PublicKey)(key)); | 
|  |                 } else { | 
|  |                     signer.initSign((PrivateKey)key); | 
|  |                 } | 
|  |             } | 
|  |  | 
|  |             return signer; | 
|  |         } | 
|  |  | 
|  |          | 
|  |  | 
|  |          */ | 
|  |         private void updateSignature(Signature sig, byte[] clntNonce, | 
|  |                 byte[] svrNonce) throws SignatureException { | 
|  |             int tmp; | 
|  |  | 
|  |             sig.update(clntNonce); | 
|  |             sig.update(svrNonce); | 
|  |  | 
|  |             sig.update((byte)(p.length >> 8)); | 
|  |             sig.update((byte)(p.length & 0x0ff)); | 
|  |             sig.update(p); | 
|  |  | 
|  |             sig.update((byte)(g.length >> 8)); | 
|  |             sig.update((byte)(g.length & 0x0ff)); | 
|  |             sig.update(g); | 
|  |  | 
|  |             sig.update((byte)(y.length >> 8)); | 
|  |             sig.update((byte)(y.length & 0x0ff)); | 
|  |             sig.update(y); | 
|  |         } | 
|  |     } | 
|  |  | 
|  |      | 
|  |  | 
|  |      */ | 
|  |     static final class DHServerKeyExchangeProducer | 
|  |             implements HandshakeProducer { | 
|  |          | 
|  |         private DHServerKeyExchangeProducer() { | 
|  |             // blank | 
|  |         } | 
|  |  | 
|  |         @Override | 
|  |         public byte[] produce(ConnectionContext context, | 
|  |                 HandshakeMessage message) throws IOException { | 
|  |              | 
|  |             ServerHandshakeContext shc = (ServerHandshakeContext)context; | 
|  |             DHServerKeyExchangeMessage skem = | 
|  |                     new DHServerKeyExchangeMessage(shc); | 
|  |             if (SSLLogger.isOn && SSLLogger.isOn("ssl,handshake")) { | 
|  |                 SSLLogger.fine( | 
|  |                     "Produced DH ServerKeyExchange handshake message", skem); | 
|  |             } | 
|  |  | 
|  |              | 
|  |             skem.write(shc.handshakeOutput); | 
|  |             shc.handshakeOutput.flush(); | 
|  |  | 
|  |              | 
|  |             return null; | 
|  |         } | 
|  |     } | 
|  |  | 
|  |      | 
|  |  | 
|  |      */ | 
|  |     static final class DHServerKeyExchangeConsumer implements SSLConsumer { | 
|  |          | 
|  |         private DHServerKeyExchangeConsumer() { | 
|  |             // blank | 
|  |         } | 
|  |  | 
|  |         @Override | 
|  |         public void consume(ConnectionContext context, | 
|  |                 ByteBuffer message) throws IOException { | 
|  |              | 
|  |             ClientHandshakeContext chc = (ClientHandshakeContext)context; | 
|  |  | 
|  |             DHServerKeyExchangeMessage skem = | 
|  |                     new DHServerKeyExchangeMessage(chc, message); | 
|  |             if (SSLLogger.isOn && SSLLogger.isOn("ssl,handshake")) { | 
|  |                 SSLLogger.fine( | 
|  |                     "Consuming DH ServerKeyExchange handshake message", skem); | 
|  |             } | 
|  |  | 
|  |             // | 
|  |             // validate | 
|  |             // | 
|  |              | 
|  |             DHPublicKey publicKey; | 
|  |             try { | 
|  |                 KeyFactory kf = JsseJce.getKeyFactory("DiffieHellman"); | 
|  |                 DHPublicKeySpec spec = new DHPublicKeySpec( | 
|  |                         new BigInteger(1, skem.y), | 
|  |                         new BigInteger(1, skem.p), | 
|  |                         new BigInteger(1, skem.g)); | 
|  |                 publicKey = (DHPublicKey)kf.generatePublic(spec); | 
|  |             } catch (GeneralSecurityException gse) { | 
|  |                 throw chc.conContext.fatal(Alert.INSUFFICIENT_SECURITY, | 
|  |                     "Could not generate DHPublicKey", gse); | 
|  |             } | 
|  |  | 
|  |             if (!chc.algorithmConstraints.permits( | 
|  |                     EnumSet.of(CryptoPrimitive.KEY_AGREEMENT), publicKey)) { | 
|  |                 throw chc.conContext.fatal(Alert.INSUFFICIENT_SECURITY, | 
|  |                         "DH ServerKeyExchange does not comply to " + | 
|  |                         "algorithm constraints"); | 
|  |             } | 
|  |  | 
|  |             // | 
|  |             // update | 
|  |              | 
|  |             NamedGroup namedGroup = NamedGroup.valueOf(publicKey.getParams()); | 
|  |             chc.handshakeCredentials.add( | 
|  |                     new DHECredentials(publicKey, namedGroup)); | 
|  |  | 
|  |             // | 
|  |             // produce | 
|  |             // | 
|  |             // Need no new handshake message producers here. | 
|  |         } | 
|  |     } | 
|  | } | 
|  |  |