OpenJDK / amber / amber
changeset 7046:b3c01990cd1d
Merge
author | alanb |
---|---|
date | Tue, 02 Nov 2010 10:15:06 +0000 |
parents | 525f00c555b2 5e2d1edeb2c7 |
children | 38b1365f95b2 |
files | |
diffstat | 99 files changed, 8822 insertions(+), 1061 deletions(-) [+] |
line wrap: on
line diff
--- a/jdk/make/java/jli/Makefile Tue Nov 02 10:07:21 2010 +0000 +++ b/jdk/make/java/jli/Makefile Tue Nov 02 10:15:06 2010 +0000 @@ -148,14 +148,9 @@ # ifeq ($(PLATFORM), windows) -STATIC_LIBRARY_DIR = $(OBJDIR)/static -STATIC_LIBRARY_NAME = $(LIBPREFIX)$(LIBRARY).lib -STATIC_LIBRARY = $(STATIC_LIBRARY_DIR)/$(STATIC_LIBRARY_NAME) +STATIC_LIBRARY = $(OBJDIR)/static/$(LIBPREFIX)$(LIBRARY).lib -$(STATIC_LIBRARY_DIR): $(OBJDIR) - @$(MKDIR) $(STATIC_LIBRARY_DIR) - -$(STATIC_LIBRARY): $(STATIC_LIBRARY_DIR) +$(STATIC_LIBRARY): $(FILES_o) @$(prep-target) $(LIBEXE) -nologo -out:$@ $(FILES_o)
--- a/jdk/src/share/classes/com/sun/crypto/provider/AESCrypt.java Tue Nov 02 10:07:21 2010 +0000 +++ b/jdk/src/share/classes/com/sun/crypto/provider/AESCrypt.java Tue Nov 02 10:15:06 2010 +0000 @@ -1,5 +1,5 @@ /* - * Copyright (c) 2002, 2007, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2002, 2010, 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 @@ -253,7 +253,8 @@ for (j = 0; j < 8; j++) { if (AA[i][j] != 0) { AA[i][j] = (byte) - alog[(255 + log[AA[i][j] & 0xFF] - log[pivot & 0xFF]) % 255]; + alog[(255 + log[AA[i][j] & 0xFF] - log[pivot & 0xFF]) + % 255]; } } for (t = 0; t < 4; t++) {
--- a/jdk/src/share/classes/com/sun/crypto/provider/ARCFOURCipher.java Tue Nov 02 10:07:21 2010 +0000 +++ b/jdk/src/share/classes/com/sun/crypto/provider/ARCFOURCipher.java Tue Nov 02 10:15:06 2010 +0000 @@ -1,5 +1,5 @@ /* - * Copyright (c) 2003, 2009, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2003, 2010, 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 @@ -92,7 +92,8 @@ } // core crypt code. OFB style, so works for both encryption and decryption - private void crypt(byte[] in, int inOfs, int inLen, byte[] out, int outOfs) { + private void crypt(byte[] in, int inOfs, int inLen, byte[] out, + int outOfs) { if (is < 0) { // doFinal() was called, need to reset the cipher to initial state init(lastKey);
--- a/jdk/src/share/classes/com/sun/crypto/provider/DESedeCipher.java Tue Nov 02 10:07:21 2010 +0000 +++ b/jdk/src/share/classes/com/sun/crypto/provider/DESedeCipher.java Tue Nov 02 10:15:06 2010 +0000 @@ -1,5 +1,5 @@ /* - * Copyright (c) 1997, 2009, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 1997, 2010, 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 @@ -31,8 +31,8 @@ import javax.crypto.spec.*; /** - * This class implements the DESede algorithm (DES-EDE, tripleDES) in its various - * modes (<code>ECB</code>, <code>CFB</code>, <code>OFB</code>, + * This class implements the DESede algorithm (DES-EDE, tripleDES) in + * its various modes (<code>ECB</code>, <code>CFB</code>, <code>OFB</code>, * <code>CBC</code>, <code>PCBC</code>) and padding schemes * (<code>PKCS5Padding</code>, <code>NoPadding</code>, * <code>ISO10126Padding</code>).
--- a/jdk/src/share/classes/com/sun/crypto/provider/DHPrivateKey.java Tue Nov 02 10:07:21 2010 +0000 +++ b/jdk/src/share/classes/com/sun/crypto/provider/DHPrivateKey.java Tue Nov 02 10:15:06 2010 +0000 @@ -1,5 +1,5 @@ /* - * Copyright (c) 1997, 2007, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 1997, 2010, 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 @@ -190,7 +190,8 @@ ike.initCause(e); throw ike; } catch (IOException e) { - InvalidKeyException ike = new InvalidKeyException("Error parsing key encoding: " + e.getMessage()); + InvalidKeyException ike = new InvalidKeyException( + "Error parsing key encoding: " + e.getMessage()); ike.initCause(e); throw ike; } @@ -300,7 +301,8 @@ DerInputStream in = new DerInputStream(this.key); this.x = in.getBigInteger(); } catch (IOException e) { - InvalidKeyException ike = new InvalidKeyException("Error parsing key encoding: " + e.getMessage()); + InvalidKeyException ike = new InvalidKeyException( + "Error parsing key encoding: " + e.getMessage()); ike.initCause(e); throw ike; }
--- a/jdk/src/share/classes/com/sun/crypto/provider/DHPublicKey.java Tue Nov 02 10:07:21 2010 +0000 +++ b/jdk/src/share/classes/com/sun/crypto/provider/DHPublicKey.java Tue Nov 02 10:15:06 2010 +0000 @@ -1,5 +1,5 @@ /* - * Copyright (c) 1997, 2007, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 1997, 2010, 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 @@ -180,7 +180,8 @@ throw new InvalidKeyException("Private-value length too big"); } catch (IOException e) { - throw new InvalidKeyException("Error parsing key encoding: " + e.toString()); + throw new InvalidKeyException( + "Error parsing key encoding: " + e.toString()); } } @@ -281,7 +282,8 @@ DerInputStream in = new DerInputStream(this.key); this.y = in.getBigInteger(); } catch (IOException e) { - throw new InvalidKeyException("Error parsing key encoding: " + e.toString()); + throw new InvalidKeyException( + "Error parsing key encoding: " + e.toString()); } }
--- a/jdk/src/share/classes/com/sun/crypto/provider/JceKeyStore.java Tue Nov 02 10:07:21 2010 +0000 +++ b/jdk/src/share/classes/com/sun/crypto/provider/JceKeyStore.java Tue Nov 02 10:15:06 2010 +0000 @@ -1,5 +1,5 @@ /* - * Copyright (c) 1998, 2007, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 1998, 2010, 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 @@ -764,7 +764,8 @@ cf = (CertificateFactory)cfs.get(certType); } else { // create new certificate factory - cf = CertificateFactory.getInstance(certType); + cf = CertificateFactory.getInstance( + certType); // store the certificate factory so we can // reuse it later cfs.put(certType, cf); @@ -863,8 +864,9 @@ dis.readFully(actual); for (int i = 0; i < computed.length; i++) { if (computed[i] != actual[i]) { - throw new IOException("Keystore was tampered with, or " - + "password was incorrect"); + throw new IOException( + "Keystore was tampered with, or " + + "password was incorrect"); } } }
--- a/jdk/src/share/classes/com/sun/crypto/provider/OAEPParameters.java Tue Nov 02 10:07:21 2010 +0000 +++ b/jdk/src/share/classes/com/sun/crypto/provider/OAEPParameters.java Tue Nov 02 10:15:06 2010 +0000 @@ -1,5 +1,5 @@ /* - * Copyright (c) 2003, 2007, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2003, 2010, 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 @@ -139,7 +139,8 @@ if (!val.getOID().equals((Object) OID_MGF1)) { throw new IOException("Only MGF1 mgf is supported"); } - AlgorithmId params = AlgorithmId.parse(new DerValue(val.getEncodedParams())); + AlgorithmId params = AlgorithmId.parse( + new DerValue(val.getEncodedParams())); String mgfDigestName = convertToStandardName(params.getName()); if (mgfDigestName.equals("SHA-1")) { mgfSpec = MGF1ParameterSpec.SHA1; @@ -150,7 +151,8 @@ } else if (mgfDigestName.equals("SHA-512")) { mgfSpec = MGF1ParameterSpec.SHA512; } else { - throw new IOException("Unrecognized message digest algorithm"); + throw new IOException( + "Unrecognized message digest algorithm"); } } else if (data.isContextSpecific((byte) 0x02)) { // pSource algid
--- a/jdk/src/share/classes/com/sun/crypto/provider/PBKDF2KeyImpl.java Tue Nov 02 10:07:21 2010 +0000 +++ b/jdk/src/share/classes/com/sun/crypto/provider/PBKDF2KeyImpl.java Tue Nov 02 10:15:06 2010 +0000 @@ -1,5 +1,5 @@ /* - * Copyright (c) 2005, 2009, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2005, 2010, 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 @@ -121,8 +121,8 @@ this.key = deriveKey(prf, passwdBytes, salt, iterCount, keyLength); } - private static byte[] deriveKey(final Mac prf, final byte[] password, byte[] salt, - int iterCount, int keyLengthInBit) { + private static byte[] deriveKey(final Mac prf, final byte[] password, + byte[] salt, int iterCount, int keyLengthInBit) { int keyLength = keyLengthInBit/8; byte[] key = new byte[keyLength]; try { @@ -155,8 +155,9 @@ if (this == obj) return true; if (this.getClass() != obj.getClass()) return false; SecretKey sk = (SecretKey)obj; - return prf.getAlgorithm().equalsIgnoreCase(sk.getAlgorithm()) && - Arrays.equals(password, sk.getEncoded()); + return prf.getAlgorithm().equalsIgnoreCase( + sk.getAlgorithm()) && + Arrays.equals(password, sk.getEncoded()); } }; prf.init(macKey);
--- a/jdk/src/share/classes/com/sun/crypto/provider/PKCS12PBECipherCore.java Tue Nov 02 10:07:21 2010 +0000 +++ b/jdk/src/share/classes/com/sun/crypto/provider/PKCS12PBECipherCore.java Tue Nov 02 10:15:06 2010 +0000 @@ -1,5 +1,5 @@ /* - * Copyright (c) 2003, 2009, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2003, 2010, 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 @@ -206,7 +206,8 @@ (algo.equalsIgnoreCase("RC2")?"RC2_40":algo), "SunJCE"); } catch (GeneralSecurityException gse) { // should never happen - throw new RuntimeException("SunJCE provider is not configured properly"); + throw new RuntimeException( + "SunJCE provider is not configured properly"); } try { params.init(pbeSpec); @@ -316,7 +317,8 @@ try { paramSpec = params.getParameterSpec(PBEParameterSpec.class); } catch (InvalidParameterSpecException ipse) { - throw new InvalidAlgorithmParameterException("requires PBE parameters"); + throw new InvalidAlgorithmParameterException( + "requires PBE parameters"); } } implInit(opmode, key, paramSpec, random);
--- a/jdk/src/share/classes/com/sun/crypto/provider/SunJCE.java Tue Nov 02 10:07:21 2010 +0000 +++ b/jdk/src/share/classes/com/sun/crypto/provider/SunJCE.java Tue Nov 02 10:15:06 2010 +0000 @@ -1,5 +1,5 @@ /* - * Copyright (c) 1997, 2009, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 1997, 2010, 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 @@ -423,15 +423,31 @@ /* * SSL/TLS mechanisms + * + * These are strictly internal implementations and may + * be changed at any time. These names were chosen + * because PKCS11/SunPKCS11 does not yet have TLS1.2 + * mechanisms, and it will cause calls to come here. */ put("KeyGenerator.SunTlsPrf", - "com.sun.crypto.provider.TlsPrfGenerator"); - put("KeyGenerator.SunTlsRsaPremasterSecret", - "com.sun.crypto.provider.TlsRsaPremasterSecretGenerator"); + "com.sun.crypto.provider.TlsPrfGenerator$V10"); + put("KeyGenerator.SunTls12Prf", + "com.sun.crypto.provider.TlsPrfGenerator$V12"); + put("KeyGenerator.SunTlsMasterSecret", - "com.sun.crypto.provider.TlsMasterSecretGenerator"); + "com.sun.crypto.provider.TlsMasterSecretGenerator"); + put("Alg.Alias.KeyGenerator.SunTls12MasterSecret", + "SunTlsMasterSecret"); + put("KeyGenerator.SunTlsKeyMaterial", - "com.sun.crypto.provider.TlsKeyMaterialGenerator"); + "com.sun.crypto.provider.TlsKeyMaterialGenerator"); + put("Alg.Alias.KeyGenerator.SunTls12KeyMaterial", + "SunTlsKeyMaterial"); + + put("KeyGenerator.SunTlsRsaPremasterSecret", + "com.sun.crypto.provider.TlsRsaPremasterSecretGenerator"); + put("Alg.Alias.KeyGenerator.SunTls12RsaPremasterSecret", + "SunTlsRsaPremasterSecret"); return null; }
--- a/jdk/src/share/classes/com/sun/crypto/provider/TlsKeyMaterialGenerator.java Tue Nov 02 10:07:21 2010 +0000 +++ b/jdk/src/share/classes/com/sun/crypto/provider/TlsKeyMaterialGenerator.java Tue Nov 02 10:15:06 2010 +0000 @@ -1,5 +1,5 @@ /* - * Copyright (c) 2005, 2009, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2005, 2010, 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 @@ -65,12 +65,14 @@ } this.spec = (TlsKeyMaterialParameterSpec)params; if ("RAW".equals(spec.getMasterSecret().getFormat()) == false) { - throw new InvalidAlgorithmParameterException("Key format must be RAW"); + throw new InvalidAlgorithmParameterException( + "Key format must be RAW"); } - protocolVersion = (spec.getMajorVersion() << 8) | spec.getMinorVersion(); - if ((protocolVersion < 0x0300) || (protocolVersion > 0x0302)) { - throw new InvalidAlgorithmParameterException - ("Only SSL 3.0, TLS 1.0, and TLS 1.1 supported"); + protocolVersion = (spec.getMajorVersion() << 8) + | spec.getMinorVersion(); + if ((protocolVersion < 0x0300) || (protocolVersion > 0x0303)) { + throw new InvalidAlgorithmParameterException( + "Only SSL 3.0, TLS 1.0/1.1/1.2 supported"); } } @@ -80,8 +82,8 @@ protected SecretKey engineGenerateKey() { if (spec == null) { - throw new IllegalStateException - ("TlsKeyMaterialGenerator must be initialized"); + throw new IllegalStateException( + "TlsKeyMaterialGenerator must be initialized"); } try { return engineGenerateKey0(); @@ -99,8 +101,8 @@ SecretKey clientMacKey = null; SecretKey serverMacKey = null; SecretKey clientCipherKey = null; + SecretKey serverCipherKey = null; IvParameterSpec clientIv = null; - SecretKey serverCipherKey = null; IvParameterSpec serverIv = null; int macLength = spec.getMacKeyLength(); @@ -109,21 +111,33 @@ int keyLength = spec.getCipherKeyLength(); int ivLength = spec.getIvLength(); - int keyBlockLen = macLength + keyLength + (isExportable ? 0 : ivLength); + int keyBlockLen = macLength + keyLength + + (isExportable ? 0 : ivLength); keyBlockLen <<= 1; byte[] keyBlock = new byte[keyBlockLen]; - MessageDigest md5 = MessageDigest.getInstance("MD5"); - MessageDigest sha = MessageDigest.getInstance("SHA1"); + // These may be used again later for exportable suite calculations. + MessageDigest md5 = null; + MessageDigest sha = null; // generate key block - if (protocolVersion >= 0x0301) { - // TLS + if (protocolVersion >= 0x0303) { + // TLS 1.2 byte[] seed = concat(serverRandom, clientRandom); - keyBlock = doPRF(masterSecret, LABEL_KEY_EXPANSION, seed, + keyBlock = doTLS12PRF(masterSecret, LABEL_KEY_EXPANSION, seed, + keyBlockLen, spec.getPRFHashAlg(), + spec.getPRFHashLength(), spec.getPRFBlockSize()); + } else if (protocolVersion >= 0x0301) { + // TLS 1.0/1.1 + md5 = MessageDigest.getInstance("MD5"); + sha = MessageDigest.getInstance("SHA1"); + byte[] seed = concat(serverRandom, clientRandom); + keyBlock = doTLS10PRF(masterSecret, LABEL_KEY_EXPANSION, seed, keyBlockLen, md5, sha); } else { // SSL + md5 = MessageDigest.getInstance("MD5"); + sha = MessageDigest.getInstance("SHA1"); keyBlock = new byte[keyBlockLen]; byte[] tmp = new byte[20]; @@ -169,6 +183,7 @@ String alg = spec.getCipherAlgorithm(); + // cipher keys byte[] clientKeyBytes = new byte[keyLength]; System.arraycopy(keyBlock, ofs, clientKeyBytes, 0, keyLength); ofs += keyLength; @@ -182,6 +197,7 @@ clientCipherKey = new SecretKeySpec(clientKeyBytes, alg); serverCipherKey = new SecretKeySpec(serverKeyBytes, alg); + // IV keys if needed. if (ivLength != 0) { tmp = new byte[ivLength]; @@ -194,21 +210,28 @@ serverIv = new IvParameterSpec(tmp); } } else { + // if exportable suites, calculate the alternate // cipher key expansion and IV generation - if (protocolVersion >= 0x0301) { + if (protocolVersion >= 0x0302) { + // TLS 1.1+ + throw new RuntimeException( + "Internal Error: TLS 1.1+ should not be negotiating" + + "exportable ciphersuites"); + } else if (protocolVersion == 0x0301) { + // TLS 1.0 byte[] seed = concat(clientRandom, serverRandom); - tmp = doPRF(clientKeyBytes, LABEL_CLIENT_WRITE_KEY, seed, + tmp = doTLS10PRF(clientKeyBytes, LABEL_CLIENT_WRITE_KEY, seed, expandedKeyLength, md5, sha); clientCipherKey = new SecretKeySpec(tmp, alg); - tmp = doPRF(serverKeyBytes, LABEL_SERVER_WRITE_KEY, seed, + tmp = doTLS10PRF(serverKeyBytes, LABEL_SERVER_WRITE_KEY, seed, expandedKeyLength, md5, sha); serverCipherKey = new SecretKeySpec(tmp, alg); if (ivLength != 0) { tmp = new byte[ivLength]; - byte[] block = doPRF(null, LABEL_IV_BLOCK, seed, + byte[] block = doTLS10PRF(null, LABEL_IV_BLOCK, seed, ivLength << 1, md5, sha); System.arraycopy(block, 0, tmp, 0, ivLength); clientIv = new IvParameterSpec(tmp); @@ -216,6 +239,7 @@ serverIv = new IvParameterSpec(tmp); } } else { + // SSLv3 tmp = new byte[expandedKeyLength]; md5.update(clientKeyBytes);
--- a/jdk/src/share/classes/com/sun/crypto/provider/TlsMasterSecretGenerator.java Tue Nov 02 10:07:21 2010 +0000 +++ b/jdk/src/share/classes/com/sun/crypto/provider/TlsMasterSecretGenerator.java Tue Nov 02 10:15:06 2010 +0000 @@ -1,5 +1,5 @@ /* - * Copyright (c) 2005, 2009, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2005, 2010, 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 @@ -64,12 +64,14 @@ } this.spec = (TlsMasterSecretParameterSpec)params; if ("RAW".equals(spec.getPremasterSecret().getFormat()) == false) { - throw new InvalidAlgorithmParameterException("Key format must be RAW"); + throw new InvalidAlgorithmParameterException( + "Key format must be RAW"); } - protocolVersion = (spec.getMajorVersion() << 8) | spec.getMinorVersion(); - if ((protocolVersion < 0x0300) || (protocolVersion > 0x0302)) { - throw new InvalidAlgorithmParameterException - ("Only SSL 3.0, TLS 1.0, and TLS 1.1 supported"); + protocolVersion = (spec.getMajorVersion() << 8) + | spec.getMinorVersion(); + if ((protocolVersion < 0x0300) || (protocolVersion > 0x0303)) { + throw new InvalidAlgorithmParameterException( + "Only SSL 3.0, TLS 1.0/1.1/1.2 supported"); } } @@ -79,8 +81,8 @@ protected SecretKey engineGenerateKey() { if (spec == null) { - throw new IllegalStateException - ("TlsMasterSecretGenerator must be initialized"); + throw new IllegalStateException( + "TlsMasterSecretGenerator must be initialized"); } SecretKey premasterKey = spec.getPremasterSecret(); byte[] premaster = premasterKey.getEncoded(); @@ -103,7 +105,11 @@ if (protocolVersion >= 0x0301) { byte[] seed = concat(clientRandom, serverRandom); - master = doPRF(premaster, LABEL_MASTER_SECRET, seed, 48); + master = ((protocolVersion >= 0x0303) ? + doTLS12PRF(premaster, LABEL_MASTER_SECRET, seed, 48, + spec.getPRFHashAlg(), spec.getPRFHashLength(), + spec.getPRFBlockSize()) : + doTLS10PRF(premaster, LABEL_MASTER_SECRET, seed, 48)); } else { master = new byte[48]; MessageDigest md5 = MessageDigest.getInstance("MD5"); @@ -124,7 +130,8 @@ } - return new TlsMasterSecretKey(master, premasterMajor, premasterMinor); + return new TlsMasterSecretKey(master, premasterMajor, + premasterMinor); } catch (NoSuchAlgorithmException e) { throw new ProviderException(e); } catch (DigestException e) {
--- a/jdk/src/share/classes/com/sun/crypto/provider/TlsPrfGenerator.java Tue Nov 02 10:07:21 2010 +0000 +++ b/jdk/src/share/classes/com/sun/crypto/provider/TlsPrfGenerator.java Tue Nov 02 10:15:06 2010 +0000 @@ -1,5 +1,5 @@ /* - * Copyright (c) 2005, 2009, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2005, 2010, 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 @@ -37,11 +37,15 @@ /** * KeyGenerator implementation for the TLS PRF function. + * <p> + * This class duplicates the HMAC functionality (RFC 2104) with + * performance optimizations (e.g. XOR'ing keys with padding doesn't + * need to be redone for each HMAC operation). * * @author Andreas Sterbenz * @since 1.6 */ -public final class TlsPrfGenerator extends KeyGeneratorSpi { +abstract class TlsPrfGenerator extends KeyGeneratorSpi { // magic constants and utility functions, also used by other files // in this package @@ -69,8 +73,10 @@ * TLS HMAC "inner" and "outer" padding. This isn't a function * of the digest algorithm. */ - private static final byte[] HMAC_ipad = genPad((byte)0x36, 64); - private static final byte[] HMAC_opad = genPad((byte)0x5c, 64); + private static final byte[] HMAC_ipad64 = genPad((byte)0x36, 64); + private static final byte[] HMAC_ipad128 = genPad((byte)0x36, 128); + private static final byte[] HMAC_opad64 = genPad((byte)0x5c, 64); + private static final byte[] HMAC_opad128 = genPad((byte)0x5c, 128); // SSL3 magic mix constants ("A", "BB", "CCC", ...) final static byte[][] SSL3_CONST = genConst(); @@ -123,8 +129,8 @@ this.spec = (TlsPrfParameterSpec)params; SecretKey key = spec.getSecret(); if ((key != null) && ("RAW".equals(key.getFormat()) == false)) { - throw new InvalidAlgorithmParameterException - ("Key encoding format must be RAW"); + throw new InvalidAlgorithmParameterException( + "Key encoding format must be RAW"); } } @@ -132,17 +138,21 @@ throw new InvalidParameterException(MSG); } - protected SecretKey engineGenerateKey() { + SecretKey engineGenerateKey0(boolean tls12) { if (spec == null) { - throw new IllegalStateException - ("TlsPrfGenerator must be initialized"); + throw new IllegalStateException( + "TlsPrfGenerator must be initialized"); } SecretKey key = spec.getSecret(); byte[] secret = (key == null) ? null : key.getEncoded(); try { byte[] labelBytes = spec.getLabel().getBytes("UTF8"); int n = spec.getOutputLength(); - byte[] prfBytes = doPRF(secret, labelBytes, spec.getSeed(), n); + byte[] prfBytes = (tls12 ? + doTLS12PRF(secret, labelBytes, spec.getSeed(), n, + spec.getPRFHashAlg(), spec.getPRFHashLength(), + spec.getPRFBlockSize()) : + doTLS10PRF(secret, labelBytes, spec.getSeed(), n)); return new SecretKeySpec(prfBytes, "TlsPrf"); } catch (GeneralSecurityException e) { throw new ProviderException("Could not generate PRF", e); @@ -151,16 +161,67 @@ } } - static final byte[] doPRF(byte[] secret, byte[] labelBytes, byte[] seed, - int outputLength) throws NoSuchAlgorithmException, DigestException { + static byte[] doTLS12PRF(byte[] secret, byte[] labelBytes, + byte[] seed, int outputLength, + String prfHash, int prfHashLength, int prfBlockSize) + throws NoSuchAlgorithmException, DigestException { + if (prfHash == null) { + throw new NoSuchAlgorithmException("Unspecified PRF algorithm"); + } + MessageDigest prfMD = MessageDigest.getInstance(prfHash); + return doTLS12PRF(secret, labelBytes, seed, outputLength, + prfMD, prfHashLength, prfBlockSize); + } + + static byte[] doTLS12PRF(byte[] secret, byte[] labelBytes, + byte[] seed, int outputLength, + MessageDigest mdPRF, int mdPRFLen, int mdPRFBlockSize) + throws DigestException { + + if (secret == null) { + secret = B0; + } + + // If we have a long secret, digest it first. + if (secret.length > mdPRFBlockSize) { + secret = mdPRF.digest(secret); + } + + byte[] output = new byte[outputLength]; + byte [] ipad; + byte [] opad; + + switch (mdPRFBlockSize) { + case 64: + ipad = HMAC_ipad64.clone(); + opad = HMAC_opad64.clone(); + break; + case 128: + ipad = HMAC_ipad128.clone(); + opad = HMAC_opad128.clone(); + break; + default: + throw new DigestException("Unexpected block size."); + } + + // P_HASH(Secret, label + seed) + expand(mdPRF, mdPRFLen, secret, 0, secret.length, labelBytes, + seed, output, ipad, opad); + + return output; + } + + static byte[] doTLS10PRF(byte[] secret, byte[] labelBytes, + byte[] seed, int outputLength) throws NoSuchAlgorithmException, + DigestException { MessageDigest md5 = MessageDigest.getInstance("MD5"); MessageDigest sha = MessageDigest.getInstance("SHA1"); - return doPRF(secret, labelBytes, seed, outputLength, md5, sha); + return doTLS10PRF(secret, labelBytes, seed, outputLength, md5, sha); } - static final byte[] doPRF(byte[] secret, byte[] labelBytes, byte[] seed, - int outputLength, MessageDigest md5, MessageDigest sha) - throws DigestException { + static byte[] doTLS10PRF(byte[] secret, byte[] labelBytes, + byte[] seed, int outputLength, MessageDigest md5, + MessageDigest sha) throws DigestException { /* * Split the secret into two halves S1 and S2 of same length. * S1 is taken from the first half of the secret, S2 from the @@ -183,10 +244,12 @@ byte[] output = new byte[outputLength]; // P_MD5(S1, label + seed) - expand(md5, 16, secret, 0, seclen, labelBytes, seed, output); + expand(md5, 16, secret, 0, seclen, labelBytes, seed, output, + HMAC_ipad64.clone(), HMAC_opad64.clone()); // P_SHA-1(S2, label + seed) - expand(sha, 20, secret, off, seclen, labelBytes, seed, output); + expand(sha, 20, secret, off, seclen, labelBytes, seed, output, + HMAC_ipad64.clone(), HMAC_opad64.clone()); return output; } @@ -201,16 +264,13 @@ * @param seed the seed * @param output the output array */ - private static final void expand(MessageDigest digest, int hmacSize, + private static void expand(MessageDigest digest, int hmacSize, byte[] secret, int secOff, int secLen, byte[] label, byte[] seed, - byte[] output) throws DigestException { + byte[] output, byte[] pad1, byte[] pad2) throws DigestException { /* * modify the padding used, by XORing the key into our copy of that * padding. That's to avoid doing that for each HMAC computation. */ - byte[] pad1 = HMAC_ipad.clone(); - byte[] pad2 = HMAC_opad.clone(); - for (int i = 0; i < secLen; i++) { pad1[i] ^= secret[i + secOff]; pad2[i] ^= secret[i + secOff]; @@ -275,7 +335,34 @@ } remaining -= k; } - } + /** + * A KeyGenerator implementation that supports TLS 1.2. + * <p> + * TLS 1.2 uses a different hash algorithm than 1.0/1.1 for the PRF + * calculations. As of 2010, there is no PKCS11-level support for TLS + * 1.2 PRF calculations, and no known OS's have an internal variant + * we could use. Therefore for TLS 1.2, we are updating JSSE to request + * a different provider algorithm: "SunTls12Prf". If we reused the + * name "SunTlsPrf", the PKCS11 provider would need be updated to + * fail correctly when presented with the wrong version number + * (via Provider.Service.supportsParameters()), and add the + * appropriate supportsParamters() checks into KeyGenerators (not + * currently there). + */ + static public class V12 extends TlsPrfGenerator { + protected SecretKey engineGenerateKey() { + return engineGenerateKey0(true); + } + } + + /** + * A KeyGenerator implementation that supports TLS 1.0/1.1. + */ + static public class V10 extends TlsPrfGenerator { + protected SecretKey engineGenerateKey() { + return engineGenerateKey0(false); + } + } }
--- a/jdk/src/share/classes/com/sun/crypto/provider/TlsRsaPremasterSecretGenerator.java Tue Nov 02 10:07:21 2010 +0000 +++ b/jdk/src/share/classes/com/sun/crypto/provider/TlsRsaPremasterSecretGenerator.java Tue Nov 02 10:15:06 2010 +0000 @@ -1,5 +1,5 @@ /* - * Copyright (c) 2005, 2009, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2005, 2010, 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 @@ -69,8 +69,8 @@ protected SecretKey engineGenerateKey() { if (spec == null) { - throw new IllegalStateException - ("TlsRsaPremasterSecretGenerator must be initialized"); + throw new IllegalStateException( + "TlsRsaPremasterSecretGenerator must be initialized"); } if (random == null) { random = new SecureRandom();
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/jdk/src/share/classes/java/security/AlgorithmConstraints.java Tue Nov 02 10:15:06 2010 +0000 @@ -0,0 +1,108 @@ +/* + * Copyright (c) 2010, 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 java.security; + +import java.util.Set; + +/** + * This interface specifies constraints for cryptographic algorithms, + * keys (key sizes), and other algorithm parameters. + * <p> + * {@code AlgorithmConstraints} objects are immutable. An implementation + * of this interface should not provide methods that can change the state + * of an instance once it has been created. + * <p> + * Note that {@code AlgorithmConstraints} can be used to represent the + * restrictions described by the security properties + * {@code jdk.certpath.disabledAlgorithms} and + * {@code jdk.tls.disabledAlgorithms}, or could be used by a + * concrete {@code PKIXCertPathChecker} to check whether a specified + * certificate in the certification path contains the required algorithm + * constraints. + * + * @see javax.net.ssl.SSLParameters#getAlgorithmConstraints + * @see javax.net.ssl.SSLParameters#setAlgorithmConstraints(AlgorithmConstraints) + * + * @since 1.7 + */ + +public interface AlgorithmConstraints { + + /** + * Determines whether an algorithm is granted permission for the + * specified cryptographic primitives. + * + * @param primitives a set of cryptographic primitives + * @param algorithm the algorithm name + * @param parameters the algorithm parameters, or null if no additional + * parameters + * + * @return true if the algorithm is permitted and can be used for all + * of the specified cryptographic primitives + * + * @throws IllegalArgumentException if primitives or algorithm is null + * or empty + */ + public boolean permits(Set<CryptoPrimitive> primitives, + String algorithm, AlgorithmParameters parameters); + + /** + * Determines whether a key is granted permission for the specified + * cryptographic primitives. + * <p> + * This method is usually used to check key size and key usage. + * + * @param primitives a set of cryptographic primitives + * @param key the key + * + * @return true if the key can be used for all of the specified + * cryptographic primitives + * + * @throws IllegalArgumentException if primitives is null or empty, + * or the key is null + */ + public boolean permits(Set<CryptoPrimitive> primitives, Key key); + + /** + * Determines whether an algorithm and the corresponding key are granted + * permission for the specified cryptographic primitives. + * + * @param primitives a set of cryptographic primitives + * @param algorithm the algorithm name + * @param key the key + * @param parameters the algorithm parameters, or null if no additional + * parameters + * + * @return true if the key and the algorithm can be used for all of the + * specified cryptographic primitives + * + * @throws IllegalArgumentException if primitives or algorithm is null + * or empty, or the key is null + */ + public boolean permits(Set<CryptoPrimitive> primitives, + String algorithm, Key key, AlgorithmParameters parameters); + +}
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/jdk/src/share/classes/java/security/CryptoPrimitive.java Tue Nov 02 10:15:06 2010 +0000 @@ -0,0 +1,83 @@ +/* + * Copyright (c) 2010, 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 java.security; + +/** + * An enumeration of cryptographic primitives. + * + * @since 1.7 + */ +public enum CryptoPrimitive { + /** + * Hash function + */ + MESSAGE_DIGEST, + + /** + * Cryptographic random number generator + */ + SECURE_RANDOM, + + /** + * Symmetric primitive: block cipher + */ + BLOCK_CIPHER, + + /** + * Symmetric primitive: stream cipher + */ + STREAM_CIPHER, + + /** + * Symmetric primitive: message authentication code + */ + MAC, + + /** + * Symmetric primitive: key wrap + */ + KEY_WRAP, + + /** + * Asymmetric primitive: public key encryption + */ + PUBLIC_KEY_ENCRYPTION, + + /** + * Asymmetric primitive: signature scheme + */ + SIGNATURE, + + /** + * Asymmetric primitive: key encapsulation mechanism + */ + KEY_ENCAPSULATION, + + /** + * Asymmetric primitive: key agreement and key distribution + */ + KEY_AGREEMENT +}
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/jdk/src/share/classes/javax/net/ssl/ExtendedSSLSession.java Tue Nov 02 10:15:06 2010 +0000 @@ -0,0 +1,86 @@ +/* + * Copyright (c) 2010, 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 javax.net.ssl; + +/** + * Extends the <code>SSLSession</code> interface to support additional + * session attributes. + * + * @since 1.7 + */ +public abstract class ExtendedSSLSession implements SSLSession { + /** + * Obtains an array of supported signature algorithms that the local side + * is willing to use. + * <p> + * Note: this method is used to indicate to the peer which signature + * algorithms may be used for digital signatures in TLS 1.2. It is + * not meaningful for TLS versions prior to 1.2. + * <p> + * The signature algorithm name must be a standard Java Security + * name (such as "SHA1withRSA", "SHA256withECDSA", and so on). + * See Appendix A in the <a href= + * "../../../technotes/guides/security/crypto/CryptoSpec.html#AppA"> + * Java Cryptography Architecture API Specification & Reference </a> + * for information about standard algorithm names. + * <p> + * Note: the local supported signature algorithms should conform to + * the algorithm constraints specified by + * {@link SSLParameters#getAlgorithmConstraints getAlgorithmConstraints()} + * method in <code>SSLParameters</code>. + * + * @return An array of supported signature algorithms, in descending + * order of preference. The return value is an empty array if + * no signature algorithm is supported. + * + * @see SSLParameters#getAlgorithmConstraints + */ + public abstract String[] getLocalSupportedSignatureAlgorithms(); + + /** + * Obtains an array of supported signature algorithms that the peer is + * able to use. + * <p> + * Note: this method is used to indicate to the local side which signature + * algorithms may be used for digital signatures in TLS 1.2. It is + * not meaningful for TLS versions prior to 1.2. + * <p> + * The signature algorithm name must be a standard Java Security + * name (such as "SHA1withRSA", "SHA256withECDSA", and so on). + * See Appendix A in the <a href= + * "../../../technotes/guides/security/crypto/CryptoSpec.html#AppA"> + * Java Cryptography Architecture API Specification & Reference </a> + * for information about standard algorithm names. + * + * @return An array of supported signature algorithms, in descending + * order of preference. The return value is an empty array if + * the peer has not sent the supported signature algorithms. + * + * @see X509KeyManager + * @see X509ExtendedKeyManager + */ + public abstract String[] getPeerSupportedSignatureAlgorithms(); +}
--- a/jdk/src/share/classes/javax/net/ssl/HttpsURLConnection.java Tue Nov 02 10:07:21 2010 +0000 +++ b/jdk/src/share/classes/javax/net/ssl/HttpsURLConnection.java Tue Nov 02 10:15:06 2010 +0000 @@ -1,5 +1,5 @@ /* - * Copyright (c) 1999, 2005, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 1999, 2010, 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 @@ -188,19 +188,8 @@ * <p> * The default implementation will deny such connections. */ - private static HostnameVerifier defaultHostnameVerifier; - - /** - * Initialize the default <code>HostnameVerifier</code>. - */ - static { - try { - defaultHostnameVerifier = - new sun.net.www.protocol.https.DefaultHostnameVerifier(); - } catch (NoClassDefFoundError e) { - defaultHostnameVerifier = new DefaultHostnameVerifier(); - } - } + private static HostnameVerifier defaultHostnameVerifier = + new DefaultHostnameVerifier(); /* * The initial default <code>HostnameVerifier</code>. Should be
--- a/jdk/src/share/classes/javax/net/ssl/SSLEngine.java Tue Nov 02 10:07:21 2010 +0000 +++ b/jdk/src/share/classes/javax/net/ssl/SSLEngine.java Tue Nov 02 10:15:06 2010 +0000 @@ -1,5 +1,5 @@ /* - * Copyright (c) 2003, 2006, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2003, 2010, 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 @@ -968,6 +968,47 @@ /** + * Returns the {@code SSLSession} being constructed during a SSL/TLS + * handshake. + * <p> + * TLS protocols may negotiate parameters that are needed when using + * an instance of this class, but before the {@code SSLSession} has + * been completely initialized and made available via {@code getSession}. + * For example, the list of valid signature algorithms may restrict + * the type of certificates that can used during TrustManager + * decisions, or the maximum TLS fragment packet sizes can be + * resized to better support the network environment. + * <p> + * This method provides early access to the {@code SSLSession} being + * constructed. Depending on how far the handshake has progressed, + * some data may not yet be available for use. For example, if a + * remote server will be sending a Certificate chain, but that chain + * has yet not been processed, the {@code getPeerCertificates} + * method of {@code SSLSession} will throw a + * SSLPeerUnverifiedException. Once that chain has been processed, + * {@code getPeerCertificates} will return the proper value. + * + * @see SSLSocket + * @see SSLSession + * @see ExtendedSSLSession + * @see X509ExtendedKeyManager + * @see X509ExtendedTrustManager + * + * @return null if this instance is not currently handshaking, or + * if the current handshake has not progressed far enough to + * create a basic SSLSession. Otherwise, this method returns the + * {@code SSLSession} currently being negotiated. + * @throws UnsupportedOperationException if the underlying provider + * does not implement the operation. + * + * @since 1.7 + */ + public SSLSession getHandshakeSession() { + throw new UnsupportedOperationException(); + } + + + /** * Initiates handshaking (initial or renegotiation) on this SSLEngine. * <P> * This method is not needed for the initial handshake, as the
--- a/jdk/src/share/classes/javax/net/ssl/SSLParameters.java Tue Nov 02 10:07:21 2010 +0000 +++ b/jdk/src/share/classes/javax/net/ssl/SSLParameters.java Tue Nov 02 10:15:06 2010 +0000 @@ -1,5 +1,5 @@ /* - * Copyright (c) 2005, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2005, 2010, 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 @@ -25,24 +25,29 @@ package javax.net.ssl; +import java.security.AlgorithmConstraints; + /** * Encapsulates parameters for an SSL/TLS connection. The parameters * are the list of ciphersuites to be accepted in an SSL/TLS handshake, - * the list of protocols to be allowed, and whether SSL/TLS servers should - * request or require client authentication. - * - * <p>SSLParameters can be created via the constructors in this class. + * the list of protocols to be allowed, the endpoint identification + * algorithm during SSL/TLS handshaking, the algorithm constraints and + * whether SSL/TLS servers should request or require client authentication. + * <p> + * SSLParameters can be created via the constructors in this class. * Objects can also be obtained using the <code>getSSLParameters()</code> * methods in * {@link SSLSocket#getSSLParameters SSLSocket} and + * {@link SSLServerSocket#getSSLParameters SSLServerSocket} and * {@link SSLEngine#getSSLParameters SSLEngine} or the * {@link SSLContext#getDefaultSSLParameters getDefaultSSLParameters()} and * {@link SSLContext#getSupportedSSLParameters getSupportedSSLParameters()} * methods in <code>SSLContext</code>. - * - * <P>SSLParameters can be applied to a connection via the methods + * <p> + * SSLParameters can be applied to a connection via the methods * {@link SSLSocket#setSSLParameters SSLSocket.setSSLParameters()} and - * {@link SSLEngine#setSSLParameters SSLEngine.getSSLParameters()}. + * {@link SSLServerSocket#setSSLParameters SSLServerSocket.setSSLParameters()} + * and {@link SSLEngine#setSSLParameters SSLEngine.getSSLParameters()}. * * @see SSLSocket * @see SSLEngine @@ -56,11 +61,13 @@ private String[] protocols; private boolean wantClientAuth; private boolean needClientAuth; + private String identificationAlgorithm; + private AlgorithmConstraints algorithmConstraints; /** * Constructs SSLParameters. - * - * <p>The cipherSuites and protocols values are set to <code>null</code>, + * <p> + * The cipherSuites and protocols values are set to <code>null</code>, * wantClientAuth and needClientAuth are set to <code>false</code>. */ public SSLParameters() { @@ -69,6 +76,7 @@ /** * Constructs SSLParameters from the specified array of ciphersuites. + * <p> * Calling this constructor is equivalent to calling the no-args * constructor followed by * <code>setCipherSuites(cipherSuites);</code>. @@ -82,6 +90,7 @@ /** * Constructs SSLParameters from the specified array of ciphersuites * and protocols. + * <p> * Calling this constructor is equivalent to calling the no-args * constructor followed by * <code>setCipherSuites(cipherSuites); setProtocols(protocols);</code>. @@ -178,4 +187,71 @@ this.needClientAuth = needClientAuth; } + /** + * Returns the cryptographic algorithm constraints. + * + * @return the cryptographic algorithm constraints, or null if the + * constraints have not been set + * + * @see #setAlgorithmConstraints(AlgorithmConstraints) + * + * @since 1.7 + */ + public AlgorithmConstraints getAlgorithmConstraints() { + return algorithmConstraints; + } + + /** + * Sets the cryptographic algorithm constraints, which will be used + * in addition to any configured by the runtime environment. + * <p> + * If the <code>constraints</code> parameter is non-null, every + * cryptographic algorithm, key and algorithm parameters used in the + * SSL/TLS handshake must be permitted by the constraints. + * + * @param constraints the algorithm constraints (or null) + * + * @since 1.7 + */ + public void setAlgorithmConstraints(AlgorithmConstraints constraints) { + // the constraints object is immutable + this.algorithmConstraints = constraints; + } + + /** + * Gets the endpoint identification algorithm. + * + * @return the endpoint identification algorithm, or null if none + * has been set. + * + * @see X509ExtendedTrustManager + * @see #setEndpointIdentificationAlgorithm(String) + * + * @since 1.7 + */ + public String getEndpointIdentificationAlgorithm() { + return identificationAlgorithm; + } + + /** + * Sets the endpoint identification algorithm. + * <p> + * If the <code>algorithm</code> parameter is non-null or non-empty, the + * endpoint identification/verification procedures must be handled during + * SSL/TLS handshaking. This is to prevent man-in-the-middle attacks. + * + * @param algorithm The standard string name of the endpoint + * identification algorithm (or null). See Appendix A in the <a href= + * "../../../technotes/guides/security/crypto/CryptoSpec.html#AppA"> + * Java Cryptography Architecture API Specification & Reference </a> + * for information about standard algorithm names. + * + * @see X509ExtendedTrustManager + * + * @since 1.7 + */ + public void setEndpointIdentificationAlgorithm(String algorithm) { + this.identificationAlgorithm = algorithm; + } + }
--- a/jdk/src/share/classes/javax/net/ssl/SSLServerSocket.java Tue Nov 02 10:07:21 2010 +0000 +++ b/jdk/src/share/classes/javax/net/ssl/SSLServerSocket.java Tue Nov 02 10:15:06 2010 +0000 @@ -1,5 +1,5 @@ /* - * Copyright (c) 1997, 2008, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 1997, 2010, 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 @@ -56,8 +56,8 @@ * @since 1.4 * @author David Brownell */ -public abstract class SSLServerSocket extends ServerSocket -{ +public abstract class SSLServerSocket extends ServerSocket { + /** * Used only by subclasses. * <P> @@ -449,8 +449,79 @@ * * @return true indicates that sessions may be created; this * is the default. false indicates that an existing - * session must be resumed. + * session must be resumed * @see #setEnableSessionCreation(boolean) */ public abstract boolean getEnableSessionCreation(); + + /** + * Returns the SSLParameters in effect for newly accepted connections. + * The ciphersuites and protocols of the returned SSLParameters + * are always non-null. + * + * @return the SSLParameters in effect for newly accepted connections + * + * @see #setSSLParameters(SSLParameters) + * + * @since 1.7 + */ + public SSLParameters getSSLParameters() { + SSLParameters parameters = new SSLParameters(); + + parameters.setCipherSuites(getEnabledCipherSuites()); + parameters.setProtocols(getEnabledProtocols()); + if (getNeedClientAuth()) { + parameters.setNeedClientAuth(true); + } else if (getWantClientAuth()) { + parameters.setWantClientAuth(true); + } + + return parameters; + } + + /** + * Applies SSLParameters to newly accepted connections. + * + * <p>This means: + * <ul> + * <li>if <code>params.getCipherSuites()</code> is non-null, + * <code>setEnabledCipherSuites()</code> is called with that value + * <li>if <code>params.getProtocols()</code> is non-null, + * <code>setEnabledProtocols()</code> is called with that value + * <li>if <code>params.getNeedClientAuth()</code> or + * <code>params.getWantClientAuth()</code> return <code>true</code>, + * <code>setNeedClientAuth(true)</code> and + * <code>setWantClientAuth(true)</code> are called, respectively; + * otherwise <code>setWantClientAuth(false)</code> is called. + * </ul> + * + * @param params the parameters + * @throws IllegalArgumentException if the setEnabledCipherSuites() or + * the setEnabledProtocols() call fails + * + * @see #getSSLParameters() + * + * @since 1.7 + */ + public void setSSLParameters(SSLParameters params) { + String[] s; + s = params.getCipherSuites(); + if (s != null) { + setEnabledCipherSuites(s); + } + + s = params.getProtocols(); + if (s != null) { + setEnabledProtocols(s); + } + + if (params.getNeedClientAuth()) { + setNeedClientAuth(true); + } else if (params.getWantClientAuth()) { + setWantClientAuth(true); + } else { + setWantClientAuth(false); + } + } + }
--- a/jdk/src/share/classes/javax/net/ssl/SSLSocket.java Tue Nov 02 10:07:21 2010 +0000 +++ b/jdk/src/share/classes/javax/net/ssl/SSLSocket.java Tue Nov 02 10:15:06 2010 +0000 @@ -1,5 +1,5 @@ /* - * Copyright (c) 1997, 2008, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 1997, 2010, 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 @@ -371,6 +371,51 @@ /** + * Returns the {@code SSLSession} being constructed during a SSL/TLS + * handshake. + * <p> + * TLS protocols may negotiate parameters that are needed when using + * an instance of this class, but before the {@code SSLSession} has + * been completely initialized and made available via {@code getSession}. + * For example, the list of valid signature algorithms may restrict + * the type of certificates that can used during TrustManager + * decisions, or the maximum TLS fragment packet sizes can be + * resized to better support the network environment. + * <p> + * This method provides early access to the {@code SSLSession} being + * constructed. Depending on how far the handshake has progressed, + * some data may not yet be available for use. For example, if a + * remote server will be sending a Certificate chain, but that chain + * has yet not been processed, the {@code getPeerCertificates} + * method of {@code SSLSession} will throw a + * SSLPeerUnverifiedException. Once that chain has been processed, + * {@code getPeerCertificates} will return the proper value. + * <p> + * Unlike {@link #getSession()}, this method does not initiate the + * initial handshake and does not block until handshaking is + * complete. + * + * @see SSLEngine + * @see SSLSession + * @see ExtendedSSLSession + * @see X509ExtendedKeyManager + * @see X509ExtendedTrustManager + * + * @return null if this instance is not currently handshaking, or + * if the current handshake has not progressed far enough to + * create a basic SSLSession. Otherwise, this method returns the + * {@code SSLSession} currently being negotiated. + * @throws UnsupportedOperationException if the underlying provider + * does not implement the operation. + * + * @since 1.7 + */ + public SSLSession getHandshakeSession() { + throw new UnsupportedOperationException(); + } + + + /** * Registers an event listener to receive notifications that an * SSL handshake has completed on this connection. *
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/jdk/src/share/classes/javax/net/ssl/X509ExtendedTrustManager.java Tue Nov 02 10:15:06 2010 +0000 @@ -0,0 +1,234 @@ +/* + * Copyright (c) 2010, 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 javax.net.ssl; + +import java.net.Socket; +import javax.net.ssl.X509TrustManager; + +import java.security.cert.X509Certificate; +import java.security.cert.CertificateException; + +/** + * Extensions to the <code>X509TrustManager</code> interface to support + * SSL/TLS connection sensitive trust management. + * <p> + * To prevent man-in-the-middle attacks, hostname checks can be done + * to verify that the hostname in an end-entity certificate matches the + * targeted hostname. TLS does not require such checks, but some protocols + * over TLS (such as HTTPS) do. In earlier versions of the JDK, the + * certificate chain checks were done at the SSL/TLS layer, and the hostname + * verification checks were done at the layer over TLS. This class allows + * for the checking to be done during a single call to this class. + * <p> + * RFC 2830 defines the server identification specification for the "LDAPS" + * algorithm. RFC 2818 defines both the server identification and the + * client identification specification for the "HTTPS" algorithm. + * + * @see X509TrustManager + * @see HostnameVerifier + * + * @since 1.7 + */ +public abstract class X509ExtendedTrustManager implements X509TrustManager { + /** + * Given the partial or complete certificate chain provided by the + * peer, build and validate the certificate path based on the + * authentication type and ssl parameters. + * <p> + * The authentication type is determined by the actual certificate + * used. For instance, if RSAPublicKey is used, the authType + * should be "RSA". Checking is case-sensitive. + * <p> + * If the <code>socket</code> parameter is an instance of + * {@link javax.net.SSLSocket}, and the endpoint identification + * algorithm of the <code>SSLParameters</code> is non-empty, to prevent + * man-in-the-middle attacks, the address that the <code>socket</code> + * connected to should be checked against the peer's identity presented + * in the end-entity X509 certificate, as specified in the endpoint + * identification algorithm. + * <p> + * If the <code>socket</code> parameter is an instance of + * {@link javax.net.SSLSocket}, and the algorithm constraints of the + * <code>SSLParameters</code> is non-null, for every certificate in the + * certification path, fields such as subject public key, the signature + * algorithm, key usage, extended key usage, etc. need to conform to the + * algorithm constraints in place on this socket. + * + * @param chain the peer certificate chain + * @param authType the key exchange algorithm used + * @param socket the socket used for this connection. This parameter + * can be null, which indicates that implementations need not check + * the ssl parameters + * @throws IllegalArgumentException if null or zero-length array is passed + * in for the <code>chain</code> parameter or if null or zero-length + * string is passed in for the <code>authType</code> parameter + * @throws CertificateException if the certificate chain is not trusted + * by this TrustManager + * + * @see SSLParameters#getEndpointIdentificationProtocol + * @see SSLParameters#setEndpointIdentificationProtocol(String) + * @see SSLParameters#getAlgorithmConstraints + * @see SSLParameters#setAlgorithmConstraints(AlgorithmConstraints) + */ + public abstract void checkClientTrusted(X509Certificate[] chain, + String authType, Socket socket) throws CertificateException; + + /** + * Given the partial or complete certificate chain provided by the + * peer, build and validate the certificate path based on the + * authentication type and ssl parameters. + * <p> + * The authentication type is the key exchange algorithm portion + * of the cipher suites represented as a String, such as "RSA", + * "DHE_DSS". Note: for some exportable cipher suites, the key + * exchange algorithm is determined at run time during the + * handshake. For instance, for TLS_RSA_EXPORT_WITH_RC4_40_MD5, + * the authType should be RSA_EXPORT when an ephemeral RSA key is + * used for the key exchange, and RSA when the key from the server + * certificate is used. Checking is case-sensitive. + * <p> + * If the <code>socket</code> parameter is an instance of + * {@link javax.net.SSLSocket}, and the endpoint identification + * algorithm of the <code>SSLParameters</code> is non-empty, to prevent + * man-in-the-middle attacks, the address that the <code>socket</code> + * connected to should be checked against the peer's identity presented + * in the end-entity X509 certificate, as specified in the endpoint + * identification algorithm. + * <p> + * If the <code>socket</code> parameter is an instance of + * {@link javax.net.SSLSocket}, and the algorithm constraints of the + * <code>SSLParameters</code> is non-null, for every certificate in the + * certification path, fields such as subject public key, the signature + * algorithm, key usage, extended key usage, etc. need to conform to the + * algorithm constraints in place on this socket. + * + * @param chain the peer certificate chain + * @param authType the key exchange algorithm used + * @param socket the socket used for this connection. This parameter + * can be null, which indicates that implementations need not check + * the ssl parameters + * @throws IllegalArgumentException if null or zero-length array is passed + * in for the <code>chain</code> parameter or if null or zero-length + * string is passed in for the <code>authType</code> parameter + * @throws CertificateException if the certificate chain is not trusted + * by this TrustManager + * + * @see SSLParameters#getEndpointIdentificationProtocol + * @see SSLParameters#setEndpointIdentificationProtocol(String) + * @see SSLParameters#getAlgorithmConstraints + * @see SSLParameters#setAlgorithmConstraints(AlgorithmConstraints) + */ + public abstract void checkServerTrusted(X509Certificate[] chain, + String authType, Socket socket) throws CertificateException; + + /** + * Given the partial or complete certificate chain provided by the + * peer, build and validate the certificate path based on the + * authentication type and ssl parameters. + * <p> + * The authentication type is determined by the actual certificate + * used. For instance, if RSAPublicKey is used, the authType + * should be "RSA". Checking is case-sensitive. + * <p> + * If the <code>engine</code> parameter is available, and the endpoint + * identification algorithm of the <code>SSLParameters</code> is + * non-empty, to prevent man-in-the-middle attacks, the address that + * the <code>engine</code> connected to should be checked against + * the peer's identity presented in the end-entity X509 certificate, + * as specified in the endpoint identification algorithm. + * <p> + * If the <code>engine</code> parameter is available, and the algorithm + * constraints of the <code>SSLParameters</code> is non-null, for every + * certificate in the certification path, fields such as subject public + * key, the signature algorithm, key usage, extended key usage, etc. + * need to conform to the algorithm constraints in place on this engine. + * + * @param chain the peer certificate chain + * @param authType the key exchange algorithm used + * @param engine the engine used for this connection. This parameter + * can be null, which indicates that implementations need not check + * the ssl parameters + * @throws IllegalArgumentException if null or zero-length array is passed + * in for the <code>chain</code> parameter or if null or zero-length + * string is passed in for the <code>authType</code> parameter + * @throws CertificateException if the certificate chain is not trusted + * by this TrustManager + * + * @see SSLParameters#getEndpointIdentificationProtocol + * @see SSLParameters#setEndpointIdentificationProtocol(String) + * @see SSLParameters#getAlgorithmConstraints + * @see SSLParameters#setAlgorithmConstraints(AlgorithmConstraints) + */ + public abstract void checkClientTrusted(X509Certificate[] chain, + String authType, SSLEngine engine) throws CertificateException; + + /** + * Given the partial or complete certificate chain provided by the + * peer, build and validate the certificate path based on the + * authentication type and ssl parameters. + * <p> + * The authentication type is the key exchange algorithm portion + * of the cipher suites represented as a String, such as "RSA", + * "DHE_DSS". Note: for some exportable cipher suites, the key + * exchange algorithm is determined at run time during the + * handshake. For instance, for TLS_RSA_EXPORT_WITH_RC4_40_MD5, + * the authType should be RSA_EXPORT when an ephemeral RSA key is + * used for the key exchange, and RSA when the key from the server + * certificate is used. Checking is case-sensitive. + * <p> + * If the <code>engine</code> parameter is available, and the endpoint + * identification algorithm of the <code>SSLParameters</code> is + * non-empty, to prevent man-in-the-middle attacks, the address that + * the <code>engine</code> connected to should be checked against + * the peer's identity presented in the end-entity X509 certificate, + * as specified in the endpoint identification algorithm. + * <p> + * If the <code>engine</code> parameter is available, and the algorithm + * constraints of the <code>SSLParameters</code> is non-null, for every + * certificate in the certification path, fields such as subject public + * key, the signature algorithm, key usage, extended key usage, etc. + * need to conform to the algorithm constraints in place on this engine. + * + * @param chain the peer certificate chain + * @param authType the key exchange algorithm used + * @param engine the engine used for this connection. This parameter + * can be null, which indicates that implementations need not check + * the ssl parameters + * @throws IllegalArgumentException if null or zero-length array is passed + * in for the <code>chain</code> parameter or if null or zero-length + * string is passed in for the <code>authType</code> parameter + * @throws CertificateException if the certificate chain is not trusted + * by this TrustManager + * + * @see SSLParameters#getEndpointIdentificationProtocol + * @see SSLParameters#setEndpointIdentificationProtocol(String) + * @see SSLParameters#getAlgorithmConstraints + * @see SSLParameters#setAlgorithmConstraints(AlgorithmConstraints) + */ + public abstract void checkServerTrusted(X509Certificate[] chain, + String authType, SSLEngine engine) throws CertificateException; + +}
--- a/jdk/src/share/classes/sun/net/www/protocol/https/HttpsClient.java Tue Nov 02 10:07:21 2010 +0000 +++ b/jdk/src/share/classes/sun/net/www/protocol/https/HttpsClient.java Tue Nov 02 10:15:06 2010 +0000 @@ -1,5 +1,5 @@ /* - * Copyright (c) 2001, 2009, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2001, 2010, 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 @@ -109,6 +109,10 @@ // HTTPS uses a different default port number than HTTP. private static final int httpsPortNumber = 443; + // default HostnameVerifier class canonical name + private static final String defaultHVCanonicalName = + "javax.net.ssl.HttpsURLConnection.DefaultHostnameVerifier"; + /** Returns the default HTTPS port (443) */ @Override protected int getDefaultPort() { return httpsPortNumber; } @@ -427,13 +431,93 @@ } s.addHandshakeCompletedListener(this); - // if the HostnameVerifier is not set, try to enable endpoint - // identification during handshaking - boolean enabledIdentification = false; - if (hv instanceof DefaultHostnameVerifier && - (s instanceof SSLSocketImpl) && - ((SSLSocketImpl)s).trySetHostnameVerification("HTTPS")) { - enabledIdentification = true; + // We have two hostname verification approaches. One is in + // SSL/TLS socket layer, where the algorithm is configured with + // SSLParameters.setEndpointIdentificationAlgorithm(), and the + // hostname verification is done by X509ExtendedTrustManager when + // the algorithm is "HTTPS". The other one is in HTTPS layer, + // where the algorithm is customized by + // HttpsURLConnection.setHostnameVerifier(), and the hostname + // verification is done by HostnameVerifier when the default + // rules for hostname verification fail. + // + // The relationship between two hostname verification approaches + // likes the following: + // + // | EIA algorithm + // +---------------------------------------------- + // | null | HTTPS | LDAP/other | + // ------------------------------------------------------------- + // | |1 |2 |3 | + // HNV | default | Set HTTPS EIA | use EIA | HTTPS | + // |-------------------------------------------------------- + // | non - |4 |5 |6 | + // | default | HTTPS/HNV | use EIA | HTTPS/HNV | + // ------------------------------------------------------------- + // + // Abbreviation: + // EIA: the endpoint identification algorithm in SSL/TLS + // socket layer + // HNV: the hostname verification object in HTTPS layer + // Notes: + // case 1. default HNV and EIA is null + // Set EIA as HTTPS, hostname check done in SSL/TLS + // layer. + // case 2. default HNV and EIA is HTTPS + // Use existing EIA, hostname check done in SSL/TLS + // layer. + // case 3. default HNV and EIA is other than HTTPS + // Use existing EIA, EIA check done in SSL/TLS + // layer, then do HTTPS check in HTTPS layer. + // case 4. non-default HNV and EIA is null + // No EIA, no EIA check done in SSL/TLS layer, then do + // HTTPS check in HTTPS layer using HNV as override. + // case 5. non-default HNV and EIA is HTTPS + // Use existing EIA, hostname check done in SSL/TLS + // layer. No HNV override possible. We will review this + // decision and may update the architecture for JDK 7. + // case 6. non-default HNV and EIA is other than HTTPS + // Use existing EIA, EIA check done in SSL/TLS layer, + // then do HTTPS check in HTTPS layer as override. + boolean needToCheckSpoofing = true; + String identification = + s.getSSLParameters().getEndpointIdentificationAlgorithm(); + if (identification != null && identification.length() != 0) { + if (identification.equalsIgnoreCase("HTTPS")) { + // Do not check server identity again out of SSLSocket, + // the endpoint will be identified during TLS handshaking + // in SSLSocket. + needToCheckSpoofing = false; + } // else, we don't understand the identification algorithm, + // need to check URL spoofing here. + } else { + boolean isDefaultHostnameVerifier = false; + + // We prefer to let the SSLSocket do the spoof checks, but if + // the application has specified a HostnameVerifier (HNV), + // we will always use that. + if (hv != null) { + String canonicalName = hv.getClass().getCanonicalName(); + if (canonicalName != null && + canonicalName.equalsIgnoreCase(defaultHVCanonicalName)) { + isDefaultHostnameVerifier = true; + } + } else { + // Unlikely to happen! As the behavior is the same as the + // default hostname verifier, so we prefer to let the + // SSLSocket do the spoof checks. + isDefaultHostnameVerifier = true; + } + + if (isDefaultHostnameVerifier) { + // If the HNV is the default from HttpsURLConnection, we + // will do the spoof checks in SSLSocket. + SSLParameters paramaters = s.getSSLParameters(); + paramaters.setEndpointIdentificationAlgorithm("HTTPS"); + s.setSSLParameters(paramaters); + + needToCheckSpoofing = false; + } } s.startHandshake(); @@ -449,7 +533,7 @@ } // check URL spoofing if it has not been checked under handshaking - if (!enabledIdentification) { + if (needToCheckSpoofing) { checkURLSpoofing(hv); } } else { @@ -463,8 +547,7 @@ // Server identity checking is done according to RFC 2818: HTTP over TLS // Section 3.1 Server Identity private void checkURLSpoofing(HostnameVerifier hostnameVerifier) - throws IOException - { + throws IOException { // // Get authenticated server name, if any //
--- a/jdk/src/share/classes/sun/security/internal/interfaces/TlsMasterSecret.java Tue Nov 02 10:07:21 2010 +0000 +++ b/jdk/src/share/classes/sun/security/internal/interfaces/TlsMasterSecret.java Tue Nov 02 10:15:06 2010 +0000 @@ -1,5 +1,5 @@ /* - * Copyright (c) 2005, 2007, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2005, 2010, 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 @@ -38,7 +38,8 @@ * * @since 1.6 * @author Andreas Sterbenz - * @deprecated Sun JDK internal use only --- WILL BE REMOVED in Dolphin (JDK 7) + * @deprecated Sun JDK internal use only --- WILL BE REMOVED in a future + * release. */ @Deprecated public interface TlsMasterSecret extends SecretKey {
--- a/jdk/src/share/classes/sun/security/internal/spec/TlsKeyMaterialParameterSpec.java Tue Nov 02 10:07:21 2010 +0000 +++ b/jdk/src/share/classes/sun/security/internal/spec/TlsKeyMaterialParameterSpec.java Tue Nov 02 10:15:06 2010 +0000 @@ -39,7 +39,8 @@ * * @since 1.6 * @author Andreas Sterbenz - * @deprecated Sun JDK internal use only --- WILL BE REMOVED in Dolphin (JDK 7) + * @deprecated Sun JDK internal use only --- WILL BE REMOVED in a future + * release. */ @Deprecated public class TlsKeyMaterialParameterSpec implements AlgorithmParameterSpec { @@ -50,6 +51,9 @@ private final String cipherAlgorithm; private final int cipherKeyLength, ivLength, macKeyLength; private final int expandedCipherKeyLength; // == 0 for domestic ciphersuites + private final String prfHashAlg; + private final int prfHashLength; + private final int prfBlockSize; /** * Constructs a new TlsKeyMaterialParameterSpec. @@ -71,6 +75,12 @@ * @param ivLength the length in bytes of the initialization vector * to be generated, or 0 if no initialization vector is required * @param macKeyLength the length in bytes of the MAC key to be generated + * @param prfHashAlg the name of the TLS PRF hash algorithm to use. + * Used only for TLS 1.2+. TLS1.1 and earlier use a fixed PRF. + * @param prfHashLength the output length of the TLS PRF hash algorithm. + * Used only for TLS 1.2+. + * @param prfBlockSize the input block size of the TLS PRF hash algorithm. + * Used only for TLS 1.2+. * * @throws NullPointerException if masterSecret, clientRandom, * serverRandom, or cipherAlgorithm are null @@ -82,7 +92,8 @@ public TlsKeyMaterialParameterSpec(SecretKey masterSecret, int majorVersion, int minorVersion, byte[] clientRandom, byte[] serverRandom, String cipherAlgorithm, int cipherKeyLength, - int expandedCipherKeyLength, int ivLength, int macKeyLength) { + int expandedCipherKeyLength, int ivLength, int macKeyLength, + String prfHashAlg, int prfHashLength, int prfBlockSize) { if (masterSecret.getAlgorithm().equals("TlsMasterSecret") == false) { throw new IllegalArgumentException("Not a TLS master secret"); } @@ -101,6 +112,9 @@ this.expandedCipherKeyLength = checkSign(expandedCipherKeyLength); this.ivLength = checkSign(ivLength); this.macKeyLength = checkSign(macKeyLength); + this.prfHashAlg = prfHashAlg; + this.prfHashLength = prfHashLength; + this.prfBlockSize = prfBlockSize; } private static int checkSign(int k) { @@ -216,4 +230,30 @@ return macKeyLength; } + /** + * Obtains the PRF hash algorithm to use in the PRF calculation. + * + * @return the hash algorithm. + */ + public String getPRFHashAlg() { + return prfHashAlg; + } + + /** + * Obtains the length of the PRF hash algorithm. + * + * @return the hash algorithm length. + */ + public int getPRFHashLength() { + return prfHashLength; + } + + /** + * Obtains the block size of the PRF hash algorithm. + * + * @return the hash algorithm block size + */ + public int getPRFBlockSize() { + return prfBlockSize; + } }
--- a/jdk/src/share/classes/sun/security/internal/spec/TlsKeyMaterialSpec.java Tue Nov 02 10:07:21 2010 +0000 +++ b/jdk/src/share/classes/sun/security/internal/spec/TlsKeyMaterialSpec.java Tue Nov 02 10:15:06 2010 +0000 @@ -1,5 +1,5 @@ /* - * Copyright (c) 2005, 2007, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2005, 2010, 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 @@ -39,7 +39,8 @@ * * @since 1.6 * @author Andreas Sterbenz - * @deprecated Sun JDK internal use only --- WILL BE REMOVED in Dolphin (JDK 7) + * @deprecated Sun JDK internal use only --- WILL BE REMOVED in a future + * release. */ @Deprecated public class TlsKeyMaterialSpec implements KeySpec, SecretKey { @@ -80,7 +81,8 @@ */ public TlsKeyMaterialSpec(SecretKey clientMacKey, SecretKey serverMacKey, SecretKey clientCipherKey, SecretKey serverCipherKey) { - this(clientMacKey, serverMacKey, clientCipherKey, null, serverCipherKey, null); + this(clientMacKey, serverMacKey, clientCipherKey, null, + serverCipherKey, null); } /**
--- a/jdk/src/share/classes/sun/security/internal/spec/TlsMasterSecretParameterSpec.java Tue Nov 02 10:07:21 2010 +0000 +++ b/jdk/src/share/classes/sun/security/internal/spec/TlsMasterSecretParameterSpec.java Tue Nov 02 10:15:06 2010 +0000 @@ -39,7 +39,8 @@ * * @since 1.6 * @author Andreas Sterbenz - * @deprecated Sun JDK internal use only --- WILL BE REMOVED in Dolphin (JDK 7) + * @deprecated Sun JDK internal use only --- WILL BE REMOVED in a future + * release. */ @Deprecated public class TlsMasterSecretParameterSpec implements AlgorithmParameterSpec { @@ -47,6 +48,9 @@ private final SecretKey premasterSecret; private final int majorVersion, minorVersion; private final byte[] clientRandom, serverRandom; + private final String prfHashAlg; + private final int prfHashLength; + private final int prfBlockSize; /** * Constructs a new TlsMasterSecretParameterSpec. @@ -60,6 +64,12 @@ * @param minorVersion the minor number of the protocol version * @param clientRandom the client's random value * @param serverRandom the server's random value + * @param prfHashAlg the name of the TLS PRF hash algorithm to use. + * Used only for TLS 1.2+. TLS1.1 and earlier use a fixed PRF. + * @param prfHashLength the output length of the TLS PRF hash algorithm. + * Used only for TLS 1.2+. + * @param prfBlockSize the input block size of the TLS PRF hash algorithm. + * Used only for TLS 1.2+. * * @throws NullPointerException if premasterSecret, clientRandom, * or serverRandom are null @@ -68,7 +78,8 @@ */ public TlsMasterSecretParameterSpec(SecretKey premasterSecret, int majorVersion, int minorVersion, - byte[] clientRandom, byte[] serverRandom) { + byte[] clientRandom, byte[] serverRandom, + String prfHashAlg, int prfHashLength, int prfBlockSize) { if (premasterSecret == null) { throw new NullPointerException("premasterSecret must not be null"); } @@ -77,6 +88,9 @@ this.minorVersion = checkVersion(minorVersion); this.clientRandom = clientRandom.clone(); this.serverRandom = serverRandom.clone(); + this.prfHashAlg = prfHashAlg; + this.prfHashLength = prfHashLength; + this.prfBlockSize = prfBlockSize; } static int checkVersion(int version) { @@ -132,4 +146,30 @@ return serverRandom.clone(); } + /** + * Obtains the PRF hash algorithm to use in the PRF calculation. + * + * @return the hash algorithm. + */ + public String getPRFHashAlg() { + return prfHashAlg; + } + + /** + * Obtains the length of the PRF hash algorithm. + * + * @return the hash algorithm length. + */ + public int getPRFHashLength() { + return prfHashLength; + } + + /** + * Obtains the block size of the PRF hash algorithm. + * + * @return the hash algorithm block size. + */ + public int getPRFBlockSize() { + return prfBlockSize; + } }
--- a/jdk/src/share/classes/sun/security/internal/spec/TlsPrfParameterSpec.java Tue Nov 02 10:07:21 2010 +0000 +++ b/jdk/src/share/classes/sun/security/internal/spec/TlsPrfParameterSpec.java Tue Nov 02 10:15:06 2010 +0000 @@ -1,5 +1,5 @@ /* - * Copyright (c) 2005, 2007, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2005, 2010, 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 @@ -38,7 +38,8 @@ * * @since 1.6 * @author Andreas Sterbenz - * @deprecated Sun JDK internal use only --- WILL BE REMOVED in Dolphin (JDK 7) + * @deprecated Sun JDK internal use only --- WILL BE REMOVED in a future + * release. */ @Deprecated public class TlsPrfParameterSpec implements AlgorithmParameterSpec { @@ -47,6 +48,9 @@ private final String label; private final byte[] seed; private final int outputLength; + private final String prfHashAlg; + private final int prfHashLength; + private final int prfBlockSize; /** * Constructs a new TlsPrfParameterSpec. @@ -55,11 +59,19 @@ * @param label the label to use in the calculation * @param seed the random seed to use in the calculation * @param outputLength the length in bytes of the output key to be produced + * @param prfHashAlg the name of the TLS PRF hash algorithm to use. + * Used only for TLS 1.2+. TLS1.1 and earlier use a fixed PRF. + * @param prfHashLength the output length of the TLS PRF hash algorithm. + * Used only for TLS 1.2+. + * @param prfBlockSize the input block size of the TLS PRF hash algorithm. + * Used only for TLS 1.2+. * * @throws NullPointerException if label or seed is null * @throws IllegalArgumentException if outputLength is negative */ - public TlsPrfParameterSpec(SecretKey secret, String label, byte[] seed, int outputLength) { + public TlsPrfParameterSpec(SecretKey secret, String label, + byte[] seed, int outputLength, + String prfHashAlg, int prfHashLength, int prfBlockSize) { if ((label == null) || (seed == null)) { throw new NullPointerException("label and seed must not be null"); } @@ -70,6 +82,9 @@ this.label = label; this.seed = seed.clone(); this.outputLength = outputLength; + this.prfHashAlg = prfHashAlg; + this.prfHashLength = prfHashLength; + this.prfBlockSize = prfBlockSize; } /** @@ -110,4 +125,33 @@ return outputLength; } + /** + * Obtains the PRF hash algorithm to use in the PRF calculation. + * + * @return the hash algorithm, or null if no algorithm was specified. + */ + public String getPRFHashAlg() { + return prfHashAlg; + } + + /** + * Obtains the length of PRF hash algorithm. + * + * It would have been preferred to use MessageDigest.getDigestLength(), + * but the API does not require implementations to support the method. + * + * @return the hash algorithm length. + */ + public int getPRFHashLength() { + return prfHashLength; + } + + /** + * Obtains the length of PRF hash algorithm. + * + * @return the hash algorithm length. + */ + public int getPRFBlockSize() { + return prfBlockSize; + } }
--- a/jdk/src/share/classes/sun/security/internal/spec/TlsRsaPremasterSecretParameterSpec.java Tue Nov 02 10:07:21 2010 +0000 +++ b/jdk/src/share/classes/sun/security/internal/spec/TlsRsaPremasterSecretParameterSpec.java Tue Nov 02 10:15:06 2010 +0000 @@ -1,5 +1,5 @@ /* - * Copyright (c) 2005, 2007, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2005, 2010, 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 @@ -36,10 +36,12 @@ * * @since 1.6 * @author Andreas Sterbenz - * @deprecated Sun JDK internal use only --- WILL BE REMOVED in Dolphin (JDK 7) + * @deprecated Sun JDK internal use only --- WILL BE REMOVED in a future + * release. */ @Deprecated -public class TlsRsaPremasterSecretParameterSpec implements AlgorithmParameterSpec { +public class TlsRsaPremasterSecretParameterSpec + implements AlgorithmParameterSpec { private final int majorVersion; private final int minorVersion; @@ -58,10 +60,12 @@ * @throws IllegalArgumentException if minorVersion or majorVersion are * negative or larger than 255 */ - public TlsRsaPremasterSecretParameterSpec(int majorVersion, int minorVersion) { - this.majorVersion = TlsMasterSecretParameterSpec.checkVersion(majorVersion); - this.minorVersion = TlsMasterSecretParameterSpec.checkVersion(minorVersion); - } + public TlsRsaPremasterSecretParameterSpec(int majorVersion, + int minorVersion) { + this.majorVersion = + TlsMasterSecretParameterSpec.checkVersion(majorVersion); + this.minorVersion = + TlsMasterSecretParameterSpec.checkVersion(minorVersion); } /** * Returns the major version.
--- a/jdk/src/share/classes/sun/security/pkcs11/SunPKCS11.java Tue Nov 02 10:07:21 2010 +0000 +++ b/jdk/src/share/classes/sun/security/pkcs11/SunPKCS11.java Tue Nov 02 10:15:06 2010 +0000 @@ -1,5 +1,5 @@ /* - * Copyright (c) 2003, 2009, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2003, 2010, 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 @@ -655,6 +655,25 @@ d(SIG, "SHA512withRSA", P11Signature, m(CKM_SHA512_RSA_PKCS, CKM_RSA_PKCS, CKM_RSA_X_509)); + /* + * TLS 1.2 uses a different hash algorithm than 1.0/1.1 for the + * PRF calculations. As of 2010, there is no PKCS11-level + * support for TLS 1.2 PRF calculations, and no known OS's have + * an internal variant we could use. Therefore for TLS 1.2, we + * are updating JSSE to request different provider algorithms + * (e.g. "SunTls12Prf"), and currently only SunJCE has these + * TLS 1.2 algorithms. + * + * If we reused the names such as "SunTlsPrf", the PKCS11 + * providers would need be updated to fail correctly when + * presented with the wrong version number (via + * Provider.Service.supportsParameters()), and we would also + * need to add the appropriate supportsParamters() checks into + * KeyGenerators (not currently there). + * + * In the future, if PKCS11 support is added, we will restructure + * this. + */ d(KG, "SunTlsRsaPremasterSecret", "sun.security.pkcs11.P11TlsRsaPremasterSecretGenerator", m(CKM_SSL3_PRE_MASTER_KEY_GEN, CKM_TLS_PRE_MASTER_KEY_GEN)); @@ -887,7 +906,8 @@ return (aliases == null) ? null : Arrays.asList(aliases); } - public Object newInstance(Object param) throws NoSuchAlgorithmException { + public Object newInstance(Object param) + throws NoSuchAlgorithmException { if (token.isValid() == false) { throw new NoSuchAlgorithmException("Token has been removed"); }
--- a/jdk/src/share/classes/sun/security/provider/certpath/AlgorithmChecker.java Tue Nov 02 10:07:21 2010 +0000 +++ b/jdk/src/share/classes/sun/security/provider/certpath/AlgorithmChecker.java Tue Nov 02 10:15:06 2010 +0000 @@ -1,5 +1,5 @@ /* - * Copyright (c) 2009, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2009, 2010, 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 @@ -25,95 +25,336 @@ package sun.security.provider.certpath; -import java.util.Set; +import java.security.AlgorithmConstraints; +import java.security.CryptoPrimitive; import java.util.Collection; -import java.util.Locale; +import java.util.Collections; +import java.util.Set; +import java.util.EnumSet; +import java.util.HashSet; +import java.math.BigInteger; +import java.security.PublicKey; +import java.security.KeyFactory; +import java.security.AlgorithmParameters; +import java.security.NoSuchAlgorithmException; +import java.security.GeneralSecurityException; import java.security.cert.Certificate; +import java.security.cert.X509CRL; import java.security.cert.X509Certificate; -import java.security.cert.X509CRL; +import java.security.cert.PKIXCertPathChecker; +import java.security.cert.TrustAnchor; +import java.security.cert.CRLException; +import java.security.cert.CertificateException; import java.security.cert.CertPathValidatorException; -import java.security.cert.PKIXCertPathChecker; +import java.io.IOException; +import java.security.interfaces.*; +import java.security.spec.*; +import sun.security.util.DisabledAlgorithmConstraints; +import sun.security.x509.X509CertImpl; +import sun.security.x509.X509CRLImpl; import sun.security.x509.AlgorithmId; /** - * AlgorithmChecker is a <code>PKIXCertPathChecker</code> that checks that - * the signature algorithm of the specified certificate is not disabled. + * A <code>PKIXCertPathChecker</code> implementation to check whether a + * specified certificate contains the required algorithm constraints. + * <p> + * Certificate fields such as the subject public key, the signature + * algorithm, key usage, extended key usage, etc. need to conform to + * the specified algorithm constraints. * - * @author Xuelei Fan + * @see PKIXCertPathChecker + * @see PKIXParameters */ final public class AlgorithmChecker extends PKIXCertPathChecker { - // the disabled algorithms - private static final String[] disabledAlgorithms = new String[] {"md2"}; + private final AlgorithmConstraints constraints; + private final PublicKey trustedPubKey; + private PublicKey prevPubKey; - // singleton instance - static final AlgorithmChecker INSTANCE = new AlgorithmChecker(); + private final static Set<CryptoPrimitive> SIGNATURE_PRIMITIVE_SET = + EnumSet.of(CryptoPrimitive.SIGNATURE); + + private final static DisabledAlgorithmConstraints + certPathDefaultConstraints = new DisabledAlgorithmConstraints( + DisabledAlgorithmConstraints.PROPERTY_CERTPATH_DISABLED_ALGS); /** - * Default Constructor + * Create a new <code>AlgorithmChecker</code> with the algorithm + * constraints specified in security property + * "jdk.certpath.disabledAlgorithms". + * + * @param anchor the trust anchor selected to validate the target + * certificate */ - private AlgorithmChecker() { - // do nothing + public AlgorithmChecker(TrustAnchor anchor) { + this(anchor, certPathDefaultConstraints); + } + + /** + * Create a new <code>AlgorithmChecker</code> with the + * given {@code AlgorithmConstraints}. + * <p> + * Note that this constructor will be used to check a certification + * path where the trust anchor is unknown, or a certificate list which may + * contain the trust anchor. This constructor is used by SunJSSE. + * + * @param constraints the algorithm constraints (or null) + */ + public AlgorithmChecker(AlgorithmConstraints constraints) { + this.prevPubKey = null; + this.trustedPubKey = null; + this.constraints = constraints; } /** - * Return a AlgorithmChecker instance. + * Create a new <code>AlgorithmChecker</code> with the + * given <code>TrustAnchor</code> and <code>AlgorithmConstraints</code>. + * + * @param anchor the trust anchor selected to validate the target + * certificate + * @param constraints the algorithm constraints (or null) + * + * @throws IllegalArgumentException if the <code>anchor</code> is null */ - static AlgorithmChecker getInstance() { - return INSTANCE; + public AlgorithmChecker(TrustAnchor anchor, + AlgorithmConstraints constraints) { + + if (anchor == null) { + throw new IllegalArgumentException( + "The trust anchor cannot be null"); + } + + if (anchor.getTrustedCert() != null) { + this.trustedPubKey = anchor.getTrustedCert().getPublicKey(); + } else { + this.trustedPubKey = anchor.getCAPublicKey(); + } + + this.prevPubKey = trustedPubKey; + this.constraints = constraints; } - /** - * Initializes the internal state of the checker from parameters - * specified in the constructor. - */ + @Override public void init(boolean forward) throws CertPathValidatorException { - // do nothing + // Note that this class does not support forward mode. + if (!forward) { + if (trustedPubKey != null) { + prevPubKey = trustedPubKey; + } else { + prevPubKey = null; + } + } else { + throw new + CertPathValidatorException("forward checking not supported"); + } } + @Override public boolean isForwardCheckingSupported() { + // Note that as this class does not support forward mode, the method + // will always returns false. return false; } + @Override public Set<String> getSupportedExtensions() { return null; } - /** - * Checks the signature algorithm of the specified certificate. - */ - public void check(Certificate cert, Collection<String> unresolvedCritExts) + @Override + public void check(Certificate cert, + Collection<String> unresolvedCritExts) throws CertPathValidatorException { - check(cert); - } + + if (!(cert instanceof X509Certificate) || constraints == null) { + // ignore the check for non-x.509 certificate or null constraints + return; + } + + X509CertImpl x509Cert = null; + try { + x509Cert = X509CertImpl.toImpl((X509Certificate)cert); + } catch (CertificateException ce) { + throw new CertPathValidatorException(ce); + } + + PublicKey currPubKey = x509Cert.getPublicKey(); + String currSigAlg = x509Cert.getSigAlgName(); + + AlgorithmId algorithmId = null; + try { + algorithmId = (AlgorithmId)x509Cert.get(X509CertImpl.SIG_ALG); + } catch (CertificateException ce) { + throw new CertPathValidatorException(ce); + } + + AlgorithmParameters currSigAlgParams = algorithmId.getParameters(); + + // Check the current signature algorithm + if (!constraints.permits( + SIGNATURE_PRIMITIVE_SET, + currSigAlg, currSigAlgParams)) { + throw new CertPathValidatorException( + "Algorithm constraints check failed: " + currSigAlg); + } + + // check the key usage and key size + boolean[] keyUsage = x509Cert.getKeyUsage(); + if (keyUsage != null && keyUsage.length < 9) { + throw new CertPathValidatorException( + "incorrect KeyUsage extension"); + } + + if (keyUsage != null) { + Set<CryptoPrimitive> primitives = + EnumSet.noneOf(CryptoPrimitive.class); + + if (keyUsage[0] || keyUsage[1] || keyUsage[5] || keyUsage[6]) { + // keyUsage[0]: KeyUsage.digitalSignature + // keyUsage[1]: KeyUsage.nonRepudiation + // keyUsage[5]: KeyUsage.keyCertSign + // keyUsage[6]: KeyUsage.cRLSign + primitives.add(CryptoPrimitive.SIGNATURE); + } + + if (keyUsage[2]) { // KeyUsage.keyEncipherment + primitives.add(CryptoPrimitive.KEY_ENCAPSULATION); + } + + if (keyUsage[3]) { // KeyUsage.dataEncipherment + primitives.add(CryptoPrimitive.PUBLIC_KEY_ENCRYPTION); + } - public static void check(Certificate cert) - throws CertPathValidatorException { - X509Certificate xcert = (X509Certificate)cert; - check(xcert.getSigAlgName()); + if (keyUsage[4]) { // KeyUsage.keyAgreement + primitives.add(CryptoPrimitive.KEY_AGREEMENT); + } + + // KeyUsage.encipherOnly and KeyUsage.decipherOnly are + // undefined in the absence of the keyAgreement bit. + + if (!primitives.isEmpty()) { + if (!constraints.permits(primitives, currPubKey)) { + throw new CertPathValidatorException( + "algorithm constraints check failed"); + } + } + } + + // Check with previous cert for signature algorithm and public key + if (prevPubKey != null) { + if (currSigAlg != null) { + if (!constraints.permits( + SIGNATURE_PRIMITIVE_SET, + currSigAlg, prevPubKey, currSigAlgParams)) { + throw new CertPathValidatorException( + "Algorithm constraints check failed: " + currSigAlg); + } + } + + // Inherit key parameters from previous key + if (currPubKey instanceof DSAPublicKey && + ((DSAPublicKey)currPubKey).getParams() == null) { + // Inherit DSA parameters from previous key + if (!(prevPubKey instanceof DSAPublicKey)) { + throw new CertPathValidatorException("Input key is not " + + "of a appropriate type for inheriting parameters"); + } + + DSAParams params = ((DSAPublicKey)prevPubKey).getParams(); + if (params == null) { + throw new CertPathValidatorException( + "Key parameters missing"); + } + + try { + BigInteger y = ((DSAPublicKey)currPubKey).getY(); + KeyFactory kf = KeyFactory.getInstance("DSA"); + DSAPublicKeySpec ks = new DSAPublicKeySpec(y, + params.getP(), + params.getQ(), + params.getG()); + currPubKey = kf.generatePublic(ks); + } catch (GeneralSecurityException e) { + throw new CertPathValidatorException("Unable to generate " + + "key with inherited parameters: " + e.getMessage(), e); + } + } + } + + // reset the previous public key + prevPubKey = currPubKey; + + // check the extended key usage, ignore the check now + // List<String> extendedKeyUsages = x509Cert.getExtendedKeyUsage(); + + // DO NOT remove any unresolved critical extensions } - static void check(AlgorithmId aid) throws CertPathValidatorException { - check(aid.getName()); - } - - static void check(X509CRL crl) throws CertPathValidatorException { - check(crl.getSigAlgName()); - } + /** + * Try to set the trust anchor of the checker. + * <p> + * If there is no trust anchor specified and the checker has not started, + * set the trust anchor. + * + * @param anchor the trust anchor selected to validate the target + * certificate + */ + void trySetTrustAnchor(TrustAnchor anchor) { + // Don't bother if the check has started or trust anchor has already + // specified. + if (prevPubKey == null) { + if (anchor == null) { + throw new IllegalArgumentException( + "The trust anchor cannot be null"); + } - private static void check(String algName) - throws CertPathValidatorException { - - String lowerCaseAlgName = algName.toLowerCase(Locale.ENGLISH); - - for (String disabled : disabledAlgorithms) { - // checking the signature algorithm name - if (lowerCaseAlgName.indexOf(disabled) != -1) { - throw new CertPathValidatorException( - "algorithm check failed: " + algName + " is disabled"); + // Don't bother to change the trustedPubKey. + if (anchor.getTrustedCert() != null) { + prevPubKey = anchor.getTrustedCert().getPublicKey(); + } else { + prevPubKey = anchor.getCAPublicKey(); } } } + /** + * Check the signature algorithm with the specified public key. + * + * @param key the public key to verify the CRL signature + * @param crl the target CRL + */ + static void check(PublicKey key, X509CRL crl) + throws CertPathValidatorException { + + X509CRLImpl x509CRLImpl = null; + try { + x509CRLImpl = X509CRLImpl.toImpl(crl); + } catch (CRLException ce) { + throw new CertPathValidatorException(ce); + } + + AlgorithmId algorithmId = x509CRLImpl.getSigAlgId(); + check(key, algorithmId); + } + + /** + * Check the signature algorithm with the specified public key. + * + * @param key the public key to verify the CRL signature + * @param crl the target CRL + */ + static void check(PublicKey key, AlgorithmId algorithmId) + throws CertPathValidatorException { + String sigAlgName = algorithmId.getName(); + AlgorithmParameters sigAlgParams = algorithmId.getParameters(); + + if (!certPathDefaultConstraints.permits( + SIGNATURE_PRIMITIVE_SET, sigAlgName, key, sigAlgParams)) { + throw new CertPathValidatorException( + "algorithm check failed: " + sigAlgName + " is disabled"); + } + } + } +
--- a/jdk/src/share/classes/sun/security/provider/certpath/DistributionPointFetcher.java Tue Nov 02 10:07:21 2010 +0000 +++ b/jdk/src/share/classes/sun/security/provider/certpath/DistributionPointFetcher.java Tue Nov 02 10:15:06 2010 +0000 @@ -1,5 +1,5 @@ /* - * Copyright (c) 2002, 2009, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2002, 2010, 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 @@ -289,16 +289,6 @@ X500Name certIssuer = (X500Name) certImpl.getIssuerDN(); X500Name crlIssuer = (X500Name) crlImpl.getIssuerDN(); - // check the crl signature algorithm - try { - AlgorithmChecker.check(crl); - } catch (CertPathValidatorException cpve) { - if (debug != null) { - debug.println("CRL signature algorithm check failed: " + cpve); - } - return false; - } - // if crlIssuer is set, verify that it matches the issuer of the // CRL and the CRL contains an IDP extension with the indirectCRL // boolean asserted. Otherwise, verify that the CRL issuer matches the @@ -637,6 +627,16 @@ } } + // check the crl signature algorithm + try { + AlgorithmChecker.check(prevKey, crl); + } catch (CertPathValidatorException cpve) { + if (debug != null) { + debug.println("CRL signature algorithm check failed: " + cpve); + } + return false; + } + // validate the signature on the CRL try { crl.verify(prevKey, provider);
--- a/jdk/src/share/classes/sun/security/provider/certpath/ForwardBuilder.java Tue Nov 02 10:07:21 2010 +0000 +++ b/jdk/src/share/classes/sun/security/provider/certpath/ForwardBuilder.java Tue Nov 02 10:15:06 2010 +0000 @@ -1,5 +1,5 @@ /* - * Copyright (c) 2000, 2009, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2000, 2010, 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 @@ -719,11 +719,6 @@ /* we don't perform any validation of the trusted cert */ if (!isTrustedCert) { /* - * check that the signature algorithm is not disabled. - */ - AlgorithmChecker.check(cert); - - /* * Check CRITICAL private extensions for user checkers that * support forward checking (forwardCheckers) and remove * ones we know how to check.
--- a/jdk/src/share/classes/sun/security/provider/certpath/OCSPChecker.java Tue Nov 02 10:07:21 2010 +0000 +++ b/jdk/src/share/classes/sun/security/provider/certpath/OCSPChecker.java Tue Nov 02 10:15:06 2010 +0000 @@ -1,5 +1,5 @@ /* - * Copyright (c) 2003, 2009, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2003, 2010, 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 @@ -327,6 +327,10 @@ "(set using the OCSP security properties)."); } + // The algorithm constraints of the OCSP trusted responder certificate + // does not need to be checked in this code. The constraints will be + // checked when the responder's certificate is validated. + CertId certId = null; OCSPResponse response = null; try {
--- a/jdk/src/share/classes/sun/security/provider/certpath/OCSPResponse.java Tue Nov 02 10:07:21 2010 +0000 +++ b/jdk/src/share/classes/sun/security/provider/certpath/OCSPResponse.java Tue Nov 02 10:15:06 2010 +0000 @@ -1,5 +1,5 @@ /* - * Copyright (c) 2003, 2009, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2003, 2010, 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 @@ -32,6 +32,7 @@ import java.security.cert.CertificateParsingException; import java.security.cert.CertPathValidatorException; import java.security.cert.CRLReason; +import java.security.cert.TrustAnchor; import java.security.cert.X509Certificate; import java.util.Collections; import java.util.Date; @@ -371,6 +372,13 @@ "OCSP responses", cpe); } + // Check algorithm constraints specified in security property + // "jdk.certpath.disabledAlgorithms". + AlgorithmChecker algChecker = new AlgorithmChecker( + new TrustAnchor(responderCert, null)); + algChecker.init(false); + algChecker.check(cert, Collections.<String>emptySet()); + // check the validity try { if (dateCheckedAgainst == null) { @@ -422,6 +430,10 @@ // Confirm that the signed response was generated using the public // key from the trusted responder cert if (responderCert != null) { + // Check algorithm constraints specified in security property + // "jdk.certpath.disabledAlgorithms". + AlgorithmChecker.check(responderCert.getPublicKey(), sigAlgId); + if (!verifyResponse(responseDataDer, responderCert, sigAlgId, signature)) { throw new CertPathValidatorException(
--- a/jdk/src/share/classes/sun/security/provider/certpath/PKIXCertPathValidator.java Tue Nov 02 10:07:21 2010 +0000 +++ b/jdk/src/share/classes/sun/security/provider/certpath/PKIXCertPathValidator.java Tue Nov 02 10:15:06 2010 +0000 @@ -1,5 +1,5 @@ /* - * Copyright (c) 2000, 2009, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2000, 2010, 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 @@ -275,7 +275,7 @@ int certPathLen = certList.size(); basicChecker = new BasicChecker(anchor, testDate, sigProvider, false); - AlgorithmChecker algorithmChecker= AlgorithmChecker.getInstance(); + AlgorithmChecker algorithmChecker = new AlgorithmChecker(anchor); KeyChecker keyChecker = new KeyChecker(certPathLen, pkixParam.getTargetCertConstraints()); ConstraintsChecker constraintsChecker =
--- a/jdk/src/share/classes/sun/security/provider/certpath/ReverseBuilder.java Tue Nov 02 10:07:21 2010 +0000 +++ b/jdk/src/share/classes/sun/security/provider/certpath/ReverseBuilder.java Tue Nov 02 10:15:06 2010 +0000 @@ -1,5 +1,5 @@ /* - * Copyright (c) 2000, 2009, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2000, 2010, 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 @@ -347,9 +347,6 @@ return; } - /* check that the signature algorithm is not disabled. */ - AlgorithmChecker.check(cert); - /* * check for looping - abort a loop if * ((we encounter the same certificate twice) AND @@ -470,9 +467,16 @@ if (unresolvedCritExts == null) { unresolvedCritExts = Collections.<String>emptySet(); } + + /* + * Check that the signature algorithm is not disabled. + */ + currentState.algorithmChecker.check(cert, unresolvedCritExts); + for (PKIXCertPathChecker checker : currentState.userCheckers) { checker.check(cert, unresolvedCritExts); } + /* * Look at the remaining extensions and remove any ones we have * already checked. If there are any left, throw an exception!
--- a/jdk/src/share/classes/sun/security/provider/certpath/ReverseState.java Tue Nov 02 10:07:21 2010 +0000 +++ b/jdk/src/share/classes/sun/security/provider/certpath/ReverseState.java Tue Nov 02 10:15:06 2010 +0000 @@ -1,5 +1,5 @@ /* - * Copyright (c) 2000, 2006, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2000, 2010, 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 @@ -96,6 +96,9 @@ /* the checker used for revocation status */ public CrlRevocationChecker crlChecker; + /* the algorithm checker */ + AlgorithmChecker algorithmChecker; + /* the trust anchor used to validate the path */ TrustAnchor trustAnchor; @@ -241,6 +244,14 @@ updateState(anchor.getCAPublicKey(), caName); } + // The user specified AlgorithmChecker may not be + // able to set the trust anchor until now. + for (PKIXCertPathChecker checker : userCheckers) { + if (checker instanceof AlgorithmChecker) { + ((AlgorithmChecker)checker).trySetTrustAnchor(anchor); + } + } + init = false; }
--- a/jdk/src/share/classes/sun/security/provider/certpath/SunCertPathBuilder.java Tue Nov 02 10:07:21 2010 +0000 +++ b/jdk/src/share/classes/sun/security/provider/certpath/SunCertPathBuilder.java Tue Nov 02 10:15:06 2010 +0000 @@ -1,5 +1,5 @@ /* - * Copyright (c) 2000, 2009, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2000, 2010, 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 @@ -302,6 +302,7 @@ // init the crl checker currentState.crlChecker = new CrlRevocationChecker(null, buildParams, null, onlyEECert); + currentState.algorithmChecker = new AlgorithmChecker(anchor); try { depthFirstSearchReverse(null, currentState, new ReverseBuilder(buildParams, targetSubjectDN), adjacencyList, @@ -475,29 +476,41 @@ userCheckers.add(mustCheck, policyChecker); mustCheck++; + // add the algorithm checker + userCheckers.add(mustCheck, + new AlgorithmChecker(builder.trustAnchor)); + mustCheck++; + if (nextState.keyParamsNeeded()) { PublicKey rootKey = cert.getPublicKey(); if (builder.trustAnchor.getTrustedCert() == null) { rootKey = builder.trustAnchor.getCAPublicKey(); if (debug != null) - debug.println("SunCertPathBuilder.depthFirstSearchForward" + - " using buildParams public key: " + - rootKey.toString()); + debug.println( + "SunCertPathBuilder.depthFirstSearchForward " + + "using buildParams public key: " + + rootKey.toString()); } TrustAnchor anchor = new TrustAnchor (cert.getSubjectX500Principal(), rootKey, null); + + // add the basic checker basicChecker = new BasicChecker(anchor, builder.date, buildParams.getSigProvider(), true); userCheckers.add(mustCheck, basicChecker); mustCheck++; + + // add the crl revocation checker if (buildParams.isRevocationEnabled()) { userCheckers.add(mustCheck, new CrlRevocationChecker (anchor, buildParams, null, onlyEECert)); mustCheck++; } } + // Why we don't need BasicChecker and CrlRevocationChecker + // if nextState.keyParamsNeeded() is false? for (int i=0; i<appendedCerts.size(); i++) { X509Certificate currCert = appendedCerts.get(i); @@ -513,10 +526,18 @@ for (int j=0; j<userCheckers.size(); j++) { PKIXCertPathChecker currChecker = userCheckers.get(j); if (j < mustCheck || - !currChecker.isForwardCheckingSupported()) - { + !currChecker.isForwardCheckingSupported()) { if (i == 0) { currChecker.init(false); + + // The user specified + // AlgorithmChecker may not be + // able to set the trust anchor until now. + if (j >= mustCheck && + currChecker instanceof AlgorithmChecker) { + ((AlgorithmChecker)currChecker). + trySetTrustAnchor(builder.trustAnchor); + } } try {
--- a/jdk/src/share/classes/sun/security/rsa/RSASignature.java Tue Nov 02 10:07:21 2010 +0000 +++ b/jdk/src/share/classes/sun/security/rsa/RSASignature.java Tue Nov 02 10:15:06 2010 +0000 @@ -1,5 +1,5 @@ /* - * Copyright (c) 2003, 2006, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2003, 2010, 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 @@ -49,7 +49,7 @@ public abstract class RSASignature extends SignatureSpi { // we sign an ASN.1 SEQUENCE of AlgorithmId and digest - // it has the form 30:xx:30:0c:[digestOID]:05:00:04:xx:[digest] + // it has the form 30:xx:30:xx:[digestOID]:05:00:04:xx:[digest] // this means the encoded length is (8 + digestOID.length + digest.length) private static final int baseLength = 8; @@ -104,7 +104,8 @@ // initialize for signing. See JCA doc protected void engineInitSign(PrivateKey privateKey, SecureRandom random) throws InvalidKeyException { - RSAPrivateKey rsaKey = (RSAPrivateKey)RSAKeyFactory.toRSAKey(privateKey); + RSAPrivateKey rsaKey = + (RSAPrivateKey)RSAKeyFactory.toRSAKey(privateKey); this.privateKey = rsaKey; this.publicKey = null; initCommon(rsaKey, random); @@ -212,7 +213,8 @@ DerOutputStream out = new DerOutputStream(); new AlgorithmId(oid).encode(out); out.putOctetString(digest); - DerValue result = new DerValue(DerValue.tag_Sequence, out.toByteArray()); + DerValue result = + new DerValue(DerValue.tag_Sequence, out.toByteArray()); return result.toByteArray(); } @@ -229,7 +231,8 @@ } AlgorithmId algId = AlgorithmId.parse(values[0]); if (algId.getOID().equals(oid) == false) { - throw new IOException("ObjectIdentifier mismatch: " + algId.getOID()); + throw new IOException("ObjectIdentifier mismatch: " + + algId.getOID()); } if (algId.getEncodedParams() != null) { throw new IOException("Unexpected AlgorithmId parameters");
--- a/jdk/src/share/classes/sun/security/ssl/CipherSuite.java Tue Nov 02 10:07:21 2010 +0000 +++ b/jdk/src/share/classes/sun/security/ssl/CipherSuite.java Tue Nov 02 10:15:06 2010 +0000 @@ -38,6 +38,7 @@ import sun.security.ssl.CipherSuite.*; import static sun.security.ssl.CipherSuite.KeyExchange.*; +import static sun.security.ssl.CipherSuite.PRF.*; import static sun.security.ssl.JsseJce.*; /** @@ -102,12 +103,15 @@ // by default final int priority; - // key exchange, bulk cipher, and mac algorithms. See those classes below. + // key exchange, bulk cipher, mac and prf algorithms. See those + // classes below. final KeyExchange keyExchange; final BulkCipher cipher; final MacAlg macAlg; + final PRF prfAlg; // whether a CipherSuite qualifies as exportable under 512/40 bit rules. + // TLS 1.1+ (RFC 4346) must not negotiate to these suites. final boolean exportable; // true iff implemented and enabled at compile time @@ -116,9 +120,15 @@ // obsoleted since protocol version final int obsoleted; + // supported since protocol version + final int supported; + + /** + * Constructor for implemented CipherSuites. + */ private CipherSuite(String name, int id, int priority, KeyExchange keyExchange, BulkCipher cipher, - boolean allowed, int obsoleted) { + boolean allowed, int obsoleted, int supported, PRF prfAlg) { this.name = name; this.id = id; this.priority = priority; @@ -129,6 +139,10 @@ macAlg = M_MD5; } else if (name.endsWith("_SHA")) { macAlg = M_SHA; + } else if (name.endsWith("_SHA256")) { + macAlg = M_SHA256; + } else if (name.endsWith("_SHA384")) { + macAlg = M_SHA384; } else if (name.endsWith("_NULL")) { macAlg = M_NULL; } else if (name.endsWith("_SCSV")) { @@ -142,8 +156,13 @@ allowed &= cipher.allowed; this.allowed = allowed; this.obsoleted = obsoleted; + this.supported = supported; + this.prfAlg = prfAlg; } + /** + * Constructor for unimplemented CipherSuites. + */ private CipherSuite(String name, int id) { this.name = name; this.id = id; @@ -155,6 +174,8 @@ this.macAlg = null; this.exportable = false; this.obsoleted = ProtocolVersion.LIMIT_MAX_VALUE; + this.supported = ProtocolVersion.LIMIT_MIN_VALUE; + this.prfAlg = P_NONE; } /** @@ -236,12 +257,17 @@ return nameMap.values(); } + /* + * Use this method when all of the values need to be specified. + * This is primarily used when defining a new ciphersuite for + * TLS 1.2+ that doesn't use the "default" PRF. + */ private static void add(String name, int id, int priority, KeyExchange keyExchange, BulkCipher cipher, - boolean allowed, int obsoleted) { + boolean allowed, int obsoleted, int supported, PRF prf) { CipherSuite c = new CipherSuite(name, id, priority, keyExchange, - cipher, allowed, obsoleted); + cipher, allowed, obsoleted, supported, prf); if (idMap.put(id, c) != null) { throw new RuntimeException("Duplicate ciphersuite definition: " + id + ", " + name); @@ -254,12 +280,41 @@ } } + /* + * Use this method when there is no lower protocol limit where this + * suite can be used, and the PRF is P_SHA256. That is, the + * existing ciphersuites. From RFC 5246: + * + * All cipher suites in this document use P_SHA256. + */ + private static void add(String name, int id, int priority, + KeyExchange keyExchange, BulkCipher cipher, + boolean allowed, int obsoleted) { + // If this is an obsoleted suite, then don't let the TLS 1.2 + // protocol have a valid PRF value. + PRF prf = P_SHA256; + if (obsoleted < ProtocolVersion.TLS12.v) { + prf = P_NONE; + } + + add(name, id, priority, keyExchange, cipher, allowed, obsoleted, + ProtocolVersion.LIMIT_MIN_VALUE, prf); + } + + /* + * Use this method when there is no upper protocol limit. That is, + * suites which have not been obsoleted. + */ private static void add(String name, int id, int priority, KeyExchange keyExchange, BulkCipher cipher, boolean allowed) { add(name, id, priority, keyExchange, cipher, allowed, ProtocolVersion.LIMIT_MAX_VALUE); } + /* + * Use this method to define an unimplemented suite. This provides + * a number<->name mapping that can be used for debugging. + */ private static void add(String name, int id) { CipherSuite c = new CipherSuite(name, id); if (idMap.put(id, c) != null) { @@ -459,7 +514,7 @@ /** * An SSL/TLS key MAC algorithm. * - * Also contains a factory method to obtain in initialized MAC + * Also contains a factory method to obtain an initialized MAC * for this algorithm. */ final static class MacAlg { @@ -519,6 +574,48 @@ final static MacAlg M_NULL = new MacAlg("NULL", 0); final static MacAlg M_MD5 = new MacAlg("MD5", 16); final static MacAlg M_SHA = new MacAlg("SHA", 20); + final static MacAlg M_SHA256 = new MacAlg("SHA256", 32); + final static MacAlg M_SHA384 = new MacAlg("SHA384", 48); + + // PRFs (PseudoRandom Function) from TLS specifications. + // + // TLS 1.1- uses a single MD5/SHA1-based PRF algorithm for generating + // the necessary material. + // + // In TLS 1.2+, all existing/known CipherSuites use SHA256, however + // new Ciphersuites (e.g. RFC 5288) can define specific PRF hash + // algorithms. + static enum PRF { + + // PRF algorithms + P_NONE( "NONE", 0, 0), + P_SHA256("SHA-256", 32, 64), + P_SHA384("SHA-384", 48, 128), + P_SHA512("SHA-512", 64, 128); // not currently used. + + // PRF characteristics + private final String prfHashAlg; + private final int prfHashLength; + private final int prfBlockSize; + + PRF(String prfHashAlg, int prfHashLength, int prfBlockSize) { + this.prfHashAlg = prfHashAlg; + this.prfHashLength = prfHashLength; + this.prfBlockSize = prfBlockSize; + } + + String getPRFHashAlg() { + return prfHashAlg; + } + + int getPRFHashLength() { + return prfHashLength; + } + + int getPRFBlockSize() { + return prfBlockSize; + } + } static { idMap = new HashMap<Integer,CipherSuite>(); @@ -769,161 +866,199 @@ // They are listed in preference order, most preferred first. int p = DEFAULT_SUITES_PRIORITY * 2; + // shorten names to fit the following table cleanly. + int max = ProtocolVersion.LIMIT_MAX_VALUE; + int tls11 = ProtocolVersion.TLS11.v; + int tls12 = ProtocolVersion.TLS12.v; + + // ID Key Exchange Cipher A obs suprt PRF + // ====== ============ ========= = === ===== ======== + add("TLS_RSA_WITH_AES_128_CBC_SHA256", + 0x003c, --p, K_RSA, B_AES_128, T, max, tls12, P_SHA256); + add("TLS_RSA_WITH_AES_256_CBC_SHA256", + 0x003d, --p, K_RSA, B_AES_256, T, max, tls12, P_SHA256); + add("TLS_DHE_DSS_WITH_AES_128_CBC_SHA256", + 0x0040, --p, K_DHE_DSS, B_AES_128, T, max, tls12, P_SHA256); + add("TLS_DHE_RSA_WITH_AES_128_CBC_SHA256", + 0x0067, --p, K_DHE_RSA, B_AES_128, T, max, tls12, P_SHA256); + add("TLS_DHE_DSS_WITH_AES_256_CBC_SHA256", + 0x006a, --p, K_DHE_DSS, B_AES_256, T, max, tls12, P_SHA256); + add("TLS_DHE_RSA_WITH_AES_256_CBC_SHA256", + 0x006b, --p, K_DHE_RSA, B_AES_256, T, max, tls12, P_SHA256); + + add("TLS_ECDHE_ECDSA_WITH_AES_128_CBC_SHA256", + 0xc023, --p, K_ECDHE_ECDSA, B_AES_128, T, max, tls12, P_SHA256); + add("TLS_ECDHE_ECDSA_WITH_AES_256_CBC_SHA384", + 0xc024, --p, K_ECDHE_ECDSA, B_AES_256, T, max, tls12, P_SHA384); + add("TLS_ECDH_ECDSA_WITH_AES_128_CBC_SHA256", + 0xc025, --p, K_ECDH_ECDSA, B_AES_128, T, max, tls12, P_SHA256); + add("TLS_ECDH_ECDSA_WITH_AES_256_CBC_SHA384", + 0xc026, --p, K_ECDH_ECDSA, B_AES_256, T, max, tls12, P_SHA384); + add("TLS_ECDHE_RSA_WITH_AES_128_CBC_SHA256", + 0xc027, --p, K_ECDHE_RSA, B_AES_128, T, max, tls12, P_SHA256); + add("TLS_ECDHE_RSA_WITH_AES_256_CBC_SHA384", + 0xc028, --p, K_ECDHE_RSA, B_AES_256, T, max, tls12, P_SHA384); + add("TLS_ECDH_RSA_WITH_AES_128_CBC_SHA256", + 0xc029, --p, K_ECDH_RSA, B_AES_128, T, max, tls12, P_SHA256); + add("TLS_ECDH_RSA_WITH_AES_256_CBC_SHA384", + 0xc02a, --p, K_ECDH_RSA, B_AES_256, T, max, tls12, P_SHA384); + add("SSL_RSA_WITH_RC4_128_MD5", - 0x0004, --p, K_RSA, B_RC4_128, N); + 0x0004, --p, K_RSA, B_RC4_128, N); add("SSL_RSA_WITH_RC4_128_SHA", - 0x0005, --p, K_RSA, B_RC4_128, N); + 0x0005, --p, K_RSA, B_RC4_128, N); add("TLS_RSA_WITH_AES_128_CBC_SHA", - 0x002f, --p, K_RSA, B_AES_128, T); + 0x002f, --p, K_RSA, B_AES_128, T); add("TLS_RSA_WITH_AES_256_CBC_SHA", - 0x0035, --p, K_RSA, B_AES_256, T); + 0x0035, --p, K_RSA, B_AES_256, T); add("TLS_ECDH_ECDSA_WITH_RC4_128_SHA", - 0xC002, --p, K_ECDH_ECDSA, B_RC4_128, N); + 0xC002, --p, K_ECDH_ECDSA, B_RC4_128, N); add("TLS_ECDH_ECDSA_WITH_AES_128_CBC_SHA", - 0xC004, --p, K_ECDH_ECDSA, B_AES_128, T); + 0xC004, --p, K_ECDH_ECDSA, B_AES_128, T); add("TLS_ECDH_ECDSA_WITH_AES_256_CBC_SHA", - 0xC005, --p, K_ECDH_ECDSA, B_AES_256, T); + 0xC005, --p, K_ECDH_ECDSA, B_AES_256, T); add("TLS_ECDH_RSA_WITH_RC4_128_SHA", - 0xC00C, --p, K_ECDH_RSA, B_RC4_128, N); + 0xC00C, --p, K_ECDH_RSA, B_RC4_128, N); add("TLS_ECDH_RSA_WITH_AES_128_CBC_SHA", - 0xC00E, --p, K_ECDH_RSA, B_AES_128, T); + 0xC00E, --p, K_ECDH_RSA, B_AES_128, T); add("TLS_ECDH_RSA_WITH_AES_256_CBC_SHA", - 0xC00F, --p, K_ECDH_RSA, B_AES_256, T); + 0xC00F, --p, K_ECDH_RSA, B_AES_256, T); add("TLS_ECDHE_ECDSA_WITH_RC4_128_SHA", - 0xC007, --p, K_ECDHE_ECDSA,B_RC4_128, N); + 0xC007, --p, K_ECDHE_ECDSA, B_RC4_128, N); add("TLS_ECDHE_ECDSA_WITH_AES_128_CBC_SHA", - 0xC009, --p, K_ECDHE_ECDSA,B_AES_128, T); + 0xC009, --p, K_ECDHE_ECDSA, B_AES_128, T); add("TLS_ECDHE_ECDSA_WITH_AES_256_CBC_SHA", - 0xC00A, --p, K_ECDHE_ECDSA,B_AES_256, T); + 0xC00A, --p, K_ECDHE_ECDSA, B_AES_256, T); add("TLS_ECDHE_RSA_WITH_RC4_128_SHA", - 0xC011, --p, K_ECDHE_RSA, B_RC4_128, N); + 0xC011, --p, K_ECDHE_RSA, B_RC4_128, N); add("TLS_ECDHE_RSA_WITH_AES_128_CBC_SHA", - 0xC013, --p, K_ECDHE_RSA, B_AES_128, T); + 0xC013, --p, K_ECDHE_RSA, B_AES_128, T); add("TLS_ECDHE_RSA_WITH_AES_256_CBC_SHA", - 0xC014, --p, K_ECDHE_RSA, B_AES_256, T); + 0xC014, --p, K_ECDHE_RSA, B_AES_256, T); add("TLS_DHE_RSA_WITH_AES_128_CBC_SHA", - 0x0033, --p, K_DHE_RSA, B_AES_128, T); + 0x0033, --p, K_DHE_RSA, B_AES_128, T); add("TLS_DHE_RSA_WITH_AES_256_CBC_SHA", - 0x0039, --p, K_DHE_RSA, B_AES_256, T); + 0x0039, --p, K_DHE_RSA, B_AES_256, T); add("TLS_DHE_DSS_WITH_AES_128_CBC_SHA", - 0x0032, --p, K_DHE_DSS, B_AES_128, T); + 0x0032, --p, K_DHE_DSS, B_AES_128, T); add("TLS_DHE_DSS_WITH_AES_256_CBC_SHA", - 0x0038, --p, K_DHE_DSS, B_AES_256, T); + 0x0038, --p, K_DHE_DSS, B_AES_256, T); add("SSL_RSA_WITH_3DES_EDE_CBC_SHA", - 0x000a, --p, K_RSA, B_3DES, T); + 0x000a, --p, K_RSA, B_3DES, T); add("TLS_ECDH_ECDSA_WITH_3DES_EDE_CBC_SHA", - 0xC003, --p, K_ECDH_ECDSA, B_3DES, T); + 0xC003, --p, K_ECDH_ECDSA, B_3DES, T); add("TLS_ECDH_RSA_WITH_3DES_EDE_CBC_SHA", - 0xC00D, --p, K_ECDH_RSA, B_3DES, T); + 0xC00D, --p, K_ECDH_RSA, B_3DES, T); add("TLS_ECDHE_ECDSA_WITH_3DES_EDE_CBC_SHA", - 0xC008, --p, K_ECDHE_ECDSA,B_3DES, T); + 0xC008, --p, K_ECDHE_ECDSA, B_3DES, T); add("TLS_ECDHE_RSA_WITH_3DES_EDE_CBC_SHA", - 0xC012, --p, K_ECDHE_RSA, B_3DES, T); + 0xC012, --p, K_ECDHE_RSA, B_3DES, T); add("SSL_DHE_RSA_WITH_3DES_EDE_CBC_SHA", - 0x0016, --p, K_DHE_RSA, B_3DES, T); + 0x0016, --p, K_DHE_RSA, B_3DES, T); add("SSL_DHE_DSS_WITH_3DES_EDE_CBC_SHA", - 0x0013, --p, K_DHE_DSS, B_3DES, N); - add("SSL_RSA_WITH_DES_CBC_SHA", - 0x0009, --p, K_RSA, B_DES, N); - add("SSL_DHE_RSA_WITH_DES_CBC_SHA", - 0x0015, --p, K_DHE_RSA, B_DES, N); - add("SSL_DHE_DSS_WITH_DES_CBC_SHA", - 0x0012, --p, K_DHE_DSS, B_DES, N); - add("SSL_RSA_EXPORT_WITH_RC4_40_MD5", - 0x0003, --p, K_RSA_EXPORT, B_RC4_40, N, - ProtocolVersion.TLS11.v); - add("SSL_RSA_EXPORT_WITH_DES40_CBC_SHA", - 0x0008, --p, K_RSA_EXPORT, B_DES_40, N, - ProtocolVersion.TLS11.v); - add("SSL_DHE_RSA_EXPORT_WITH_DES40_CBC_SHA", - 0x0014, --p, K_DHE_RSA, B_DES_40, N, - ProtocolVersion.TLS11.v); - add("SSL_DHE_DSS_EXPORT_WITH_DES40_CBC_SHA", - 0x0011, --p, K_DHE_DSS, B_DES_40, N, - ProtocolVersion.TLS11.v); + 0x0013, --p, K_DHE_DSS, B_3DES, N); // Renegotiation protection request Signalling Cipher Suite Value (SCSV) add("TLS_EMPTY_RENEGOTIATION_INFO_SCSV", - 0x00ff, --p, K_SCSV, B_NULL, T); + 0x00ff, --p, K_SCSV, B_NULL, T); // Definition of the CipherSuites that are supported but not enabled // by default. // They are listed in preference order, preferred first. p = DEFAULT_SUITES_PRIORITY; + // weak single-DES cipher suites + add("SSL_RSA_WITH_DES_CBC_SHA", + 0x0009, --p, K_RSA, B_DES, N, tls12); + add("SSL_DHE_RSA_WITH_DES_CBC_SHA", + 0x0015, --p, K_DHE_RSA, B_DES, N, tls12); + add("SSL_DHE_DSS_WITH_DES_CBC_SHA", + 0x0012, --p, K_DHE_DSS, B_DES, N, tls12); + // Anonymous key exchange and the NULL ciphers add("SSL_RSA_WITH_NULL_MD5", - 0x0001, --p, K_RSA, B_NULL, N); + 0x0001, --p, K_RSA, B_NULL, N); add("SSL_RSA_WITH_NULL_SHA", - 0x0002, --p, K_RSA, B_NULL, N); + 0x0002, --p, K_RSA, B_NULL, N); + add("TLS_RSA_WITH_NULL_SHA256", + 0x003b, --p, K_RSA, B_NULL, N, max, tls12, P_SHA256); + add("TLS_ECDH_ECDSA_WITH_NULL_SHA", - 0xC001, --p, K_ECDH_ECDSA, B_NULL, N); + 0xC001, --p, K_ECDH_ECDSA, B_NULL, N); add("TLS_ECDH_RSA_WITH_NULL_SHA", - 0xC00B, --p, K_ECDH_RSA, B_NULL, N); + 0xC00B, --p, K_ECDH_RSA, B_NULL, N); add("TLS_ECDHE_ECDSA_WITH_NULL_SHA", - 0xC006, --p, K_ECDHE_ECDSA,B_NULL, N); + 0xC006, --p, K_ECDHE_ECDSA, B_NULL, N); add("TLS_ECDHE_RSA_WITH_NULL_SHA", - 0xC010, --p, K_ECDHE_RSA, B_NULL, N); + 0xC010, --p, K_ECDHE_RSA, B_NULL, N); add("SSL_DH_anon_WITH_RC4_128_MD5", - 0x0018, --p, K_DH_ANON, B_RC4_128, N); + 0x0018, --p, K_DH_ANON, B_RC4_128, N); add("TLS_DH_anon_WITH_AES_128_CBC_SHA", - 0x0034, --p, K_DH_ANON, B_AES_128, N); + 0x0034, --p, K_DH_ANON, B_AES_128, N); add("TLS_DH_anon_WITH_AES_256_CBC_SHA", - 0x003a, --p, K_DH_ANON, B_AES_256, N); + 0x003a, --p, K_DH_ANON, B_AES_256, N); add("SSL_DH_anon_WITH_3DES_EDE_CBC_SHA", - 0x001b, --p, K_DH_ANON, B_3DES, N); + 0x001b, --p, K_DH_ANON, B_3DES, N); add("SSL_DH_anon_WITH_DES_CBC_SHA", - 0x001a, --p, K_DH_ANON, B_DES, N); + 0x001a, --p, K_DH_ANON, B_DES, N, tls12); + + add("TLS_DH_anon_WITH_AES_128_CBC_SHA256", + 0x006c, --p, K_DH_ANON, B_AES_128, N, max, tls12, P_SHA256); + add("TLS_DH_anon_WITH_AES_256_CBC_SHA256", + 0x006d, --p, K_DH_ANON, B_AES_256, N, max, tls12, P_SHA256); add("TLS_ECDH_anon_WITH_RC4_128_SHA", - 0xC016, --p, K_ECDH_ANON, B_RC4_128, N); + 0xC016, --p, K_ECDH_ANON, B_RC4_128, N); add("TLS_ECDH_anon_WITH_AES_128_CBC_SHA", - 0xC018, --p, K_ECDH_ANON, B_AES_128, T); + 0xC018, --p, K_ECDH_ANON, B_AES_128, T); add("TLS_ECDH_anon_WITH_AES_256_CBC_SHA", - 0xC019, --p, K_ECDH_ANON, B_AES_256, T); + 0xC019, --p, K_ECDH_ANON, B_AES_256, T); add("TLS_ECDH_anon_WITH_3DES_EDE_CBC_SHA", - 0xC017, --p, K_ECDH_ANON, B_3DES, T); + 0xC017, --p, K_ECDH_ANON, B_3DES, T); add("SSL_DH_anon_EXPORT_WITH_RC4_40_MD5", - 0x0017, --p, K_DH_ANON, B_RC4_40, N, - ProtocolVersion.TLS11.v); + 0x0017, --p, K_DH_ANON, B_RC4_40, N, tls11); add("SSL_DH_anon_EXPORT_WITH_DES40_CBC_SHA", - 0x0019, --p, K_DH_ANON, B_DES_40, N, - ProtocolVersion.TLS11.v); + 0x0019, --p, K_DH_ANON, B_DES_40, N, tls11); add("TLS_ECDH_anon_WITH_NULL_SHA", - 0xC015, --p, K_ECDH_ANON, B_NULL, N); + 0xC015, --p, K_ECDH_ANON, B_NULL, N); + + add("SSL_RSA_EXPORT_WITH_RC4_40_MD5", + 0x0003, --p, K_RSA_EXPORT, B_RC4_40, N, tls11); + add("SSL_RSA_EXPORT_WITH_DES40_CBC_SHA", + 0x0008, --p, K_RSA_EXPORT, B_DES_40, N, tls11); + add("SSL_DHE_RSA_EXPORT_WITH_DES40_CBC_SHA", + 0x0014, --p, K_DHE_RSA, B_DES_40, N, tls11); + add("SSL_DHE_DSS_EXPORT_WITH_DES40_CBC_SHA", + 0x0011, --p, K_DHE_DSS, B_DES_40, N, tls11); // Supported Kerberos ciphersuites from RFC2712 add("TLS_KRB5_WITH_RC4_128_SHA", - 0x0020, --p, K_KRB5, B_RC4_128, N); + 0x0020, --p, K_KRB5, B_RC4_128, N); add("TLS_KRB5_WITH_RC4_128_MD5", - 0x0024, --p, K_KRB5, B_RC4_128, N); + 0x0024, --p, K_KRB5, B_RC4_128, N); add("TLS_KRB5_WITH_3DES_EDE_CBC_SHA", - 0x001f, --p, K_KRB5, B_3DES, N); + 0x001f, --p, K_KRB5, B_3DES, N); add("TLS_KRB5_WITH_3DES_EDE_CBC_MD5", - 0x0023, --p, K_KRB5, B_3DES, N); + 0x0023, --p, K_KRB5, B_3DES, N); add("TLS_KRB5_WITH_DES_CBC_SHA", - 0x001e, --p, K_KRB5, B_DES, N); + 0x001e, --p, K_KRB5, B_DES, N, tls12); add("TLS_KRB5_WITH_DES_CBC_MD5", - 0x0022, --p, K_KRB5, B_DES, N); + 0x0022, --p, K_KRB5, B_DES, N, tls12); add("TLS_KRB5_EXPORT_WITH_RC4_40_SHA", - 0x0028, --p, K_KRB5_EXPORT, B_RC4_40, N, - ProtocolVersion.TLS11.v); + 0x0028, --p, K_KRB5_EXPORT, B_RC4_40, N, tls11); add("TLS_KRB5_EXPORT_WITH_RC4_40_MD5", - 0x002b, --p, K_KRB5_EXPORT, B_RC4_40, N, - ProtocolVersion.TLS11.v); + 0x002b, --p, K_KRB5_EXPORT, B_RC4_40, N, tls11); add("TLS_KRB5_EXPORT_WITH_DES_CBC_40_SHA", - 0x0026, --p, K_KRB5_EXPORT, B_DES_40, N, - ProtocolVersion.TLS11.v); + 0x0026, --p, K_KRB5_EXPORT, B_DES_40, N, tls11); add("TLS_KRB5_EXPORT_WITH_DES_CBC_40_MD5", - 0x0029, --p, K_KRB5_EXPORT, B_DES_40, N, - ProtocolVersion.TLS11.v); + 0x0029, --p, K_KRB5_EXPORT, B_DES_40, N, tls11); /* * Other values from the TLS Cipher Suite Registry, as of August 2010. @@ -1006,19 +1141,10 @@ add("TLS_DH_RSA_WITH_AES_128_CBC_SHA", 0x0031); add("TLS_DH_DSS_WITH_AES_256_CBC_SHA", 0x0036); add("TLS_DH_RSA_WITH_AES_256_CBC_SHA", 0x0037); - add("TLS_RSA_WITH_NULL_SHA256", 0x003b); - add("TLS_RSA_WITH_AES_128_CBC_SHA256", 0x003c); - add("TLS_RSA_WITH_AES_256_CBC_SHA256", 0x003d); add("TLS_DH_DSS_WITH_AES_128_CBC_SHA256", 0x003e); add("TLS_DH_RSA_WITH_AES_128_CBC_SHA256", 0x003f); - add("TLS_DHE_DSS_WITH_AES_128_CBC_SHA256", 0x0040); - add("TLS_DHE_RSA_WITH_AES_128_CBC_SHA256", 0x0067); add("TLS_DH_DSS_WITH_AES_256_CBC_SHA256", 0x0068); add("TLS_DH_RSA_WITH_AES_256_CBC_SHA256", 0x0069); - add("TLS_DHE_DSS_WITH_AES_256_CBC_SHA256", 0x006a); - add("TLS_DHE_RSA_WITH_AES_256_CBC_SHA256", 0x006b); - add("TLS_DH_anon_WITH_AES_128_CBC_SHA256", 0x006c); - add("TLS_DH_anon_WITH_AES_256_CBC_SHA256", 0x006d); // Unsupported cipher suites from RFC 5288 add("TLS_RSA_WITH_AES_128_GCM_SHA256", 0x009c); @@ -1092,14 +1218,6 @@ add("TLS_SRP_SHA_DSS_WITH_AES_256_CBC_SHA", 0xc022); // Unsupported cipher suites from RFC 5289 - add("TLS_ECDHE_ECDSA_WITH_AES_128_CBC_SHA256", 0xc023); - add("TLS_ECDHE_ECDSA_WITH_AES_256_CBC_SHA384", 0xc024); - add("TLS_ECDH_ECDSA_WITH_AES_128_CBC_SHA256", 0xc025); - add("TLS_ECDH_ECDSA_WITH_AES_256_CBC_SHA384", 0xc026); - add("TLS_ECDHE_RSA_WITH_AES_128_CBC_SHA256", 0xc027); - add("TLS_ECDHE_RSA_WITH_AES_256_CBC_SHA384", 0xc028); - add("TLS_ECDH_RSA_WITH_AES_128_CBC_SHA256", 0xc029); - add("TLS_ECDH_RSA_WITH_AES_256_CBC_SHA384", 0xc02a); add("TLS_ECDHE_ECDSA_WITH_AES_128_GCM_SHA256", 0xc02b); add("TLS_ECDHE_ECDSA_WITH_AES_256_GCM_SHA384", 0xc02c); add("TLS_ECDH_ECDSA_WITH_AES_128_GCM_SHA256", 0xc02d);
--- a/jdk/src/share/classes/sun/security/ssl/ClientHandshaker.java Tue Nov 02 10:07:21 2010 +0000 +++ b/jdk/src/share/classes/sun/security/ssl/ClientHandshaker.java Tue Nov 02 10:15:06 2010 +0000 @@ -23,7 +23,6 @@ * questions. */ - package sun.security.ssl; import java.io.*; @@ -45,12 +44,12 @@ import javax.security.auth.Subject; -import com.sun.net.ssl.internal.ssl.X509ExtendedTrustManager; - import sun.security.ssl.HandshakeMessage.*; import sun.security.ssl.CipherSuite.*; import static sun.security.ssl.CipherSuite.KeyExchange.*; +import sun.net.util.IPAddressUtil; + /** * ClientHandshaker does the protocol handshaking from the point * of view of a client. It is driven asychronously by handshake messages @@ -89,6 +88,10 @@ */ private ProtocolVersion maxProtocolVersion; + // To switch off the SNI extension. + private final static boolean enableSNIExtension = + Debug.getBooleanProperty("jsse.enableSNIExtension", true); + /* * Constructors */ @@ -190,7 +193,8 @@ } break; case K_DH_ANON: - this.serverKeyExchange(new DH_ServerKeyExchange(input)); + this.serverKeyExchange(new DH_ServerKeyExchange( + input, protocolVersion)); break; case K_DHE_DSS: case K_DHE_RSA: @@ -198,7 +202,8 @@ this.serverKeyExchange(new DH_ServerKeyExchange( input, serverKey, clnt_random.random_bytes, svr_random.random_bytes, - messageLen)); + messageLen, + localSupportedSignAlgs, protocolVersion)); } catch (GeneralSecurityException e) { throwSSLException("Server key", e); } @@ -209,7 +214,8 @@ try { this.serverKeyExchange(new ECDH_ServerKeyExchange (input, serverKey, clnt_random.random_bytes, - svr_random.random_bytes)); + svr_random.random_bytes, + localSupportedSignAlgs, protocolVersion)); } catch (GeneralSecurityException e) { throwSSLException("Server key", e); } @@ -219,8 +225,9 @@ case K_DH_DSS: case K_ECDH_ECDSA: case K_ECDH_RSA: - throw new SSLProtocolException("Protocol violation: server sent" - + " a server key exchange message for key exchange " + keyExchange); + throw new SSLProtocolException( + "Protocol violation: server sent a server key exchange" + + "message for key exchange " + keyExchange); case K_KRB5: case K_KRB5_EXPORT: throw new SSLProtocolException( @@ -243,10 +250,32 @@ "Client certificate requested for "+ "kerberos cipher suite."); } - certRequest = new CertificateRequest(input); + certRequest = new CertificateRequest(input, protocolVersion); if (debug != null && Debug.isOn("handshake")) { certRequest.print(System.out); } + + if (protocolVersion.v >= ProtocolVersion.TLS12.v) { + Collection<SignatureAndHashAlgorithm> peerSignAlgs = + certRequest.getSignAlgorithms(); + if (peerSignAlgs == null || peerSignAlgs.isEmpty()) { + throw new SSLHandshakeException( + "No peer supported signature algorithms"); + } + + Collection<SignatureAndHashAlgorithm> supportedPeerSignAlgs = + SignatureAndHashAlgorithm.getSupportedAlgorithms( + peerSignAlgs); + if (supportedPeerSignAlgs.isEmpty()) { + throw new SSLHandshakeException( + "No supported signature and hash algorithm in common"); + } + + setPeerSupportedSignAlgs(supportedPeerSignAlgs); + session.setPeerSupportedSignatureAlgorithms( + supportedPeerSignAlgs); + } + break; case HandshakeMessage.ht_server_hello_done: @@ -254,7 +283,8 @@ break; case HandshakeMessage.ht_finished: - this.serverFinished(new Finished(protocolVersion, input)); + this.serverFinished( + new Finished(protocolVersion, input, cipherSuite)); break; default: @@ -351,6 +381,9 @@ mesgVersion); } + handshakeHash.protocolDetermined( + mesgVersion.v >= ProtocolVersion.TLS12.v); + // Set protocolVersion and propagate to SSLSocket and the // Handshake streams setVersion(mesgVersion); @@ -426,10 +459,13 @@ if (isNegotiable(mesg.cipherSuite) == false) { fatalSE(Alerts.alert_illegal_parameter, - "Server selected improper ciphersuite " + cipherSuite); + "Server selected improper ciphersuite " + mesg.cipherSuite); } setCipherSuite(mesg.cipherSuite); + if (protocolVersion.v >= ProtocolVersion.TLS12.v) { + handshakeHash.setFinishedAlg(cipherSuite.prfAlg.getPRFHashAlg()); + } if (mesg.compression_method != 0) { fatalSE(Alerts.alert_illegal_parameter, @@ -508,7 +544,6 @@ if (debug != null && Debug.isOn("session")) { System.out.println("%% Server resumed " + session); } - return; } else { // we wanted to resume, but the server refused session = null; @@ -519,11 +554,21 @@ } } + if (resumingSession && session != null) { + if (protocolVersion.v >= ProtocolVersion.TLS12.v) { + handshakeHash.setCertificateVerifyAlg(null); + } + + setHandshakeSessionSE(session); + return; + } + // check extensions for (HelloExtension ext : mesg.extensions.list()) { ExtensionType type = ext.type; if ((type != ExtensionType.EXT_ELLIPTIC_CURVES) && (type != ExtensionType.EXT_EC_POINT_FORMATS) + && (type != ExtensionType.EXT_SERVER_NAME) && (type != ExtensionType.EXT_RENEGOTIATION_INFO)) { fatalSE(Alerts.alert_unsupported_extension, "Server sent an unsupported extension: " + type); @@ -532,7 +577,9 @@ // Create a new session, we need to do the full handshake session = new SSLSessionImpl(protocolVersion, cipherSuite, + getLocalSupportedSignAlgs(), mesg.sessionId, getHostSE(), getPortSE()); + setHandshakeSessionSE(session); if (debug != null && Debug.isOn("handshake")) { System.out.println("** " + cipherSuite); } @@ -568,11 +615,13 @@ if (debug != null && Debug.isOn("handshake")) { mesg.print(System.out); } - dh = new DHCrypt(mesg.getModulus(), mesg.getBase(), sslContext.getSecureRandom()); + dh = new DHCrypt(mesg.getModulus(), mesg.getBase(), + sslContext.getSecureRandom()); serverDH = mesg.getServerPublicKey(); } - private void serverKeyExchange(ECDH_ServerKeyExchange mesg) throws IOException { + private void serverKeyExchange(ECDH_ServerKeyExchange mesg) + throws IOException { if (debug != null && Debug.isOn("handshake")) { mesg.print(System.out); } @@ -666,9 +715,13 @@ PublicKey publicKey = certs[0].getPublicKey(); // for EC, make sure we use a supported named curve if (publicKey instanceof ECPublicKey) { - ECParameterSpec params = ((ECPublicKey)publicKey).getParams(); - int index = SupportedEllipticCurvesExtension.getCurveIndex(params); - if (!SupportedEllipticCurvesExtension.isSupported(index)) { + ECParameterSpec params = + ((ECPublicKey)publicKey).getParams(); + int index = + SupportedEllipticCurvesExtension.getCurveIndex( + params); + if (!SupportedEllipticCurvesExtension.isSupported( + index)) { publicKey = null; } } @@ -814,8 +867,9 @@ throw new IOException("Hostname is required" + " to use Kerberos cipher suites"); } - KerberosClientKeyExchange kerberosMsg = new KerberosClientKeyExchange - (hostname, isLoopbackSE(), getAccSE(), protocolVersion, + KerberosClientKeyExchange kerberosMsg = + new KerberosClientKeyExchange( + hostname, isLoopbackSE(), getAccSE(), protocolVersion, sslContext.getSecureRandom()); // Record the principals involved in exchange session.setPeerPrincipal(kerberosMsg.getPeerPrincipal()); @@ -862,7 +916,8 @@ case K_KRB5_EXPORT: byte[] secretBytes = ((KerberosClientKeyExchange)m2).getUnencryptedPreMasterSecret(); - preMasterSecret = new SecretKeySpec(secretBytes, "TlsPremasterSecret"); + preMasterSecret = new SecretKeySpec(secretBytes, + "TlsPremasterSecret"); break; case K_DHE_RSA: case K_DHE_DSS: @@ -879,7 +934,8 @@ preMasterSecret = ecdh.getAgreedSecret(serverKey); break; default: - throw new IOException("Internal error: unknown key exchange " + keyExchange); + throw new IOException("Internal error: unknown key exchange " + + keyExchange); } calculateKeys(preMasterSecret, null); @@ -897,9 +953,32 @@ if (signingKey != null) { CertificateVerify m3; try { + SignatureAndHashAlgorithm preferableSignatureAlgorithm = null; + if (protocolVersion.v >= ProtocolVersion.TLS12.v) { + preferableSignatureAlgorithm = + SignatureAndHashAlgorithm.getPreferableAlgorithm( + peerSupportedSignAlgs, signingKey.getAlgorithm()); + + if (preferableSignatureAlgorithm == null) { + throw new SSLHandshakeException( + "No supported signature algorithm"); + } + + String hashAlg = + SignatureAndHashAlgorithm.getHashAlgorithmName( + preferableSignatureAlgorithm); + if (hashAlg == null || hashAlg.length() == 0) { + throw new SSLHandshakeException( + "No supported hash algorithm"); + } + + handshakeHash.setCertificateVerifyAlg(hashAlg); + } + m3 = new CertificateVerify(protocolVersion, handshakeHash, signingKey, session.getMasterSecret(), - sslContext.getSecureRandom()); + sslContext.getSecureRandom(), + preferableSignatureAlgorithm); } catch (GeneralSecurityException e) { fatalSE(Alerts.alert_handshake_failure, "Error signing certificate verify", e); @@ -911,6 +990,10 @@ } m3.write(output); output.doHashes(); + } else { + if (protocolVersion.v >= ProtocolVersion.TLS12.v) { + handshakeHash.setCertificateVerifyAlg(null); + } } /* @@ -931,8 +1014,8 @@ mesg.print(System.out); } - boolean verified = mesg.verify(protocolVersion, handshakeHash, - Finished.SERVER, session.getMasterSecret()); + boolean verified = mesg.verify(handshakeHash, Finished.SERVER, + session.getMasterSecret()); if (!verified) { fatalSE(Alerts.alert_illegal_parameter, @@ -989,7 +1072,7 @@ private void sendChangeCipherAndFinish(boolean finishedTag) throws IOException { Finished mesg = new Finished(protocolVersion, handshakeHash, - Finished.CLIENT, session.getMasterSecret()); + Finished.CLIENT, session.getMasterSecret(), cipherSuite); /* * Send the change_cipher_spec message, then the Finished message @@ -1134,11 +1217,49 @@ throw new SSLHandshakeException("No negotiable cipher suite"); } + // Not a TLS1.2+ handshake + // For SSLv2Hello, HandshakeHash.reset() will be called, so we + // cannot call HandshakeHash.protocolDetermined() here. As it does + // not follow the spec that HandshakeHash.reset() can be only be + // called before protocolDetermined. + // if (maxProtocolVersion.v < ProtocolVersion.TLS12.v) { + // handshakeHash.protocolDetermined(false); + // } + // create the ClientHello message ClientHello clientHelloMessage = new ClientHello( sslContext.getSecureRandom(), maxProtocolVersion, sessionId, cipherSuites); + // add signature_algorithm extension + if (maxProtocolVersion.v >= ProtocolVersion.TLS12.v) { + // we will always send the signature_algorithm extension + Collection<SignatureAndHashAlgorithm> localSignAlgs = + getLocalSupportedSignAlgs(); + if (localSignAlgs.isEmpty()) { + throw new SSLHandshakeException( + "No supported signature algorithm"); + } + + clientHelloMessage.addSignatureAlgorithmsExtension(localSignAlgs); + } + + // add server_name extension + if (enableSNIExtension) { + // We cannot use the hostname resolved from name services. For + // virtual hosting, multiple hostnames may be bound to the same IP + // address, so the hostname resolved from name services is not + // reliable. + String hostname = getRawHostnameSE(); + + // we only allow FQDN + if (hostname != null && hostname.indexOf('.') > 0 && + !IPAddressUtil.isIPv4LiteralAddress(hostname) && + !IPAddressUtil.isIPv6LiteralAddress(hostname)) { + clientHelloMessage.addServerNameIndicationExtension(hostname); + } + } + // reset the client random cookie clnt_random = clientHelloMessage.clnt_random; @@ -1194,26 +1315,23 @@ keyExchangeString = keyExchange.name; } - String identificator = getHostnameVerificationSE(); if (tm instanceof X509ExtendedTrustManager) { - ((X509ExtendedTrustManager)tm).checkServerTrusted( - (peerCerts != null ? - peerCerts.clone() : - null), + if (conn != null) { + ((X509ExtendedTrustManager)tm).checkServerTrusted( + peerCerts.clone(), keyExchangeString, - getHostSE(), - identificator); + conn); + } else { + ((X509ExtendedTrustManager)tm).checkServerTrusted( + peerCerts.clone(), + keyExchangeString, + engine); + } } else { - if (identificator != null) { - throw new RuntimeException( - "trust manager does not support peer identification"); - } - - tm.checkServerTrusted( - (peerCerts != null ? - peerCerts.clone() : - peerCerts), - keyExchangeString); + // Unlikely to happen, because we have wrapped the old + // X509TrustManager with the new X509ExtendedTrustManager. + throw new CertificateException( + "Improper X509TrustManager implementation"); } } catch (CertificateException e) { // This will throw an exception, so include the original error.
--- a/jdk/src/share/classes/sun/security/ssl/HandshakeHash.java Tue Nov 02 10:07:21 2010 +0000 +++ b/jdk/src/share/classes/sun/security/ssl/HandshakeHash.java Tue Nov 02 10:15:06 2010 +0000 @@ -1,5 +1,5 @@ /* - * Copyright (c) 2002, 2007, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2002, 2010, 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 @@ -26,7 +26,13 @@ package sun.security.ssl; +import java.io.ByteArrayOutputStream; import java.security.*; +import java.util.Arrays; +import java.util.LinkedList; +import java.util.List; +import java.util.Locale; +import java.util.Set; /** * Abstraction for the SSL/TLS hash of all handshake messages that is @@ -36,51 +42,161 @@ * * This class transparently deals with cloneable and non-cloneable digests. * + * This class now supports TLS 1.2 also. The key difference for TLS 1.2 + * is that you cannot determine the hash algorithms for CertificateVerify + * at a early stage. On the other hand, it's simpler than TLS 1.1 (and earlier) + * that there is no messy MD5+SHA1 digests. + * + * You need to obey these conventions when using this class: + * + * 1. protocolDetermined(boolean isTLS12) should be called when the negotiated + * protocol version is determined. + * + * 2. Before protocolDetermined() is called, only update(), reset(), + * restrictCertificateVerifyAlgs(), setFinishedAlg(), and + * setCertificateVerifyAlg() can be called. + * + * 3. After protocolDetermined(*) is called. reset() cannot be called. + * + * 4. After protocolDetermined(false) is called, getFinishedHash() and + * getCertificateVerifyHash() cannot be called. After protocolDetermined(true) + * is called, getMD5Clone() and getSHAClone() cannot be called. + * + * 5. getMD5Clone() and getSHAClone() can only be called after + * protocolDetermined(false) is called. + * + * 6. getFinishedHash() and getCertificateVerifyHash() can only be called after + * all protocolDetermined(true), setCertificateVerifyAlg() and setFinishedAlg() + * have been called. If a CertificateVerify message is to be used, call + * setCertificateVerifyAlg() with the hash algorithm as the argument. + * Otherwise, you still must call setCertificateVerifyAlg(null) before + * calculating any hash value. + * + * Suggestions: Call protocolDetermined(), restrictCertificateVerifyAlgs(), + * setFinishedAlg(), and setCertificateVerifyAlg() as early as possible. + * + * Example: + * <pre> + * HandshakeHash hh = new HandshakeHash(...) + * hh.update(clientHelloBytes); + * hh.setFinishedAlg("SHA-256"); + * hh.update(serverHelloBytes); + * ... + * hh.setCertificateVerifyAlg("SHA-384"); + * hh.update(CertificateVerifyBytes); + * byte[] cvDigest = hh.getCertificateVerifyHash(); + * ... + * hh.update(finished1); + * byte[] finDigest1 = hh.getFinishedHash(); + * hh.update(finished2); + * byte[] finDigest2 = hh.getFinishedHash(); + * </pre> + * If no CertificateVerify message is to be used, call + * <pre> + * hh.setCertificateVerifyAlg(null); + * </pre> + * This call can be made once you are certain that this message + * will never be used. */ final class HandshakeHash { - private final MessageDigest md5, sha; + // Common + + // -1: unknown + // 1: <=TLS 1.1 + // 2: TLS 1.2 + private int version = -1; + private ByteArrayOutputStream data = new ByteArrayOutputStream(); + private final boolean isServer; + + // For TLS 1.1 + private MessageDigest md5, sha; + private final int clonesNeeded; // needs to be saved for later use + + // For TLS 1.2 + // cvAlgDetermined == true means setCertificateVerifyAlg() is called + private boolean cvAlgDetermined = false; + private String cvAlg; + private MessageDigest finMD; /** * Create a new HandshakeHash. needCertificateVerify indicates whether - * a hash for the certificate verify message is required. + * a hash for the certificate verify message is required. The argument + * algs is a set of all possible hash algorithms that might be used in + * TLS 1.2. If the caller is sure that TLS 1.2 won't be used or no + * CertificateVerify message will be used, leave it null or empty. */ - HandshakeHash(boolean needCertificateVerify) { - int n = needCertificateVerify ? 3 : 2; - try { - md5 = CloneableDigest.getDigest("MD5", n); - sha = CloneableDigest.getDigest("SHA", n); - } catch (NoSuchAlgorithmException e) { - throw new RuntimeException - ("Algorithm MD5 or SHA not available", e); + HandshakeHash(boolean isServer, boolean needCertificateVerify, + Set<String> algs) { + this.isServer = isServer; + clonesNeeded = needCertificateVerify ? 3 : 2; + } + void update(byte[] b, int offset, int len) { + switch (version) { + case 1: + md5.update(b, offset, len); + sha.update(b, offset, len); + break; + default: + if (finMD != null) { + finMD.update(b, offset, len); + } + data.write(b, offset, len); + break; } } - void update(byte b) { - md5.update(b); - sha.update(b); - } - - void update(byte[] b, int offset, int len) { - md5.update(b, offset, len); - sha.update(b, offset, len); - } - /** - * Reset the remaining digests. Note this does *not* reset the numbe of + * Reset the remaining digests. Note this does *not* reset the number of * digest clones that can be obtained. Digests that have already been * cloned and are gone remain gone. */ void reset() { - md5.reset(); - sha.reset(); + if (version != -1) { + throw new RuntimeException( + "reset() can be only be called before protocolDetermined"); + } + data.reset(); } + + void protocolDetermined(boolean isTLS12) { + + // Do not set again, will ignore + if (version != -1) return; + + version = isTLS12 ? 2 : 1; + switch (version) { + case 1: + // initiate md5, sha and call update on saved array + try { + md5 = CloneableDigest.getDigest("MD5", clonesNeeded); + sha = CloneableDigest.getDigest("SHA", clonesNeeded); + } catch (NoSuchAlgorithmException e) { + throw new RuntimeException + ("Algorithm MD5 or SHA not available", e); + } + byte[] bytes = data.toByteArray(); + update(bytes, 0, bytes.length); + break; + case 2: + break; + } + } + + ///////////////////////////////////////////////////////////// + // Below are old methods for pre-TLS 1.1 + ///////////////////////////////////////////////////////////// + /** * Return a new MD5 digest updated with all data hashed so far. */ MessageDigest getMD5Clone() { + if (version != 1) { + throw new RuntimeException( + "getMD5Clone() can be only be called for TLS 1.1"); + } return cloneDigest(md5); } @@ -88,6 +204,10 @@ * Return a new SHA digest updated with all data hashed so far. */ MessageDigest getSHAClone() { + if (version != 1) { + throw new RuntimeException( + "getSHAClone() can be only be called for TLS 1.1"); + } return cloneDigest(sha); } @@ -100,6 +220,181 @@ } } + ///////////////////////////////////////////////////////////// + // Below are new methods for TLS 1.2 + ///////////////////////////////////////////////////////////// + + private static String normalizeAlgName(String alg) { + alg = alg.toUpperCase(Locale.US); + if (alg.startsWith("SHA")) { + if (alg.length() == 3) { + return "SHA-1"; + } + if (alg.charAt(3) != '-') { + return "SHA-" + alg.substring(3); + } + } + return alg; + } + /** + * Specifies the hash algorithm used in Finished. This should be called + * based in info in ServerHello. + * Can be called multiple times. + */ + void setFinishedAlg(String s) { + if (s == null) { + throw new RuntimeException( + "setFinishedAlg's argument cannot be null"); + } + + // Can be called multiple times, but only set once + if (finMD != null) return; + + try { + finMD = CloneableDigest.getDigest(normalizeAlgName(s), 2); + } catch (NoSuchAlgorithmException e) { + throw new Error(e); + } + finMD.update(data.toByteArray()); + } + + /** + * Restricts the possible algorithms for the CertificateVerify. Called by + * the server based on info in CertRequest. The argument must be a subset + * of the argument with the same name in the constructor. The method can be + * called multiple times. If the caller is sure that no CertificateVerify + * message will be used, leave this argument null or empty. + */ + void restrictCertificateVerifyAlgs(Set<String> algs) { + if (version == 1) { + throw new RuntimeException( + "setCertificateVerifyAlg() cannot be called for TLS 1.1"); + } + // Not used yet + } + + /** + * Specifies the hash algorithm used in CertificateVerify. + * Can be called multiple times. + */ + void setCertificateVerifyAlg(String s) { + + // Can be called multiple times, but only set once + if (cvAlgDetermined) return; + + cvAlg = s == null ? null : normalizeAlgName(s); + cvAlgDetermined = true; + } + + byte[] getAllHandshakeMessages() { + return data.toByteArray(); + } + + /** + * Calculates the hash in the CertificateVerify. Must be called right + * after setCertificateVerifyAlg() + */ + /*byte[] getCertificateVerifyHash() { + throw new Error("Do not call getCertificateVerifyHash()"); + }*/ + + /** + * Calculates the hash in Finished. Must be called after setFinishedAlg(). + * This method can be called twice, for Finished messages of the server + * side and client side respectively. + */ + byte[] getFinishedHash() { + try { + return cloneDigest(finMD).digest(); + } catch (Exception e) { + throw new Error("BAD"); + } + } + + //////////////////////////////////////////////////////////////// + // TEST + //////////////////////////////////////////////////////////////// + + public static void main(String[] args) throws Exception { + Test t = new Test(); + t.test(null, "SHA-256"); + t.test("", "SHA-256"); + t.test("SHA-1", "SHA-256"); + t.test("SHA-256", "SHA-256"); + t.test("SHA-384", "SHA-256"); + t.test("SHA-512", "SHA-256"); + t.testSame("sha", "SHA-1"); + t.testSame("SHA", "SHA-1"); + t.testSame("SHA1", "SHA-1"); + t.testSame("SHA-1", "SHA-1"); + t.testSame("SHA256", "SHA-256"); + t.testSame("SHA-256", "SHA-256"); + } + + static class Test { + void update(HandshakeHash hh, String s) { + hh.update(s.getBytes(), 0, s.length()); + } + static byte[] digest(String alg, String data) throws Exception { + return MessageDigest.getInstance(alg).digest(data.getBytes()); + } + static void equals(byte[] b1, byte[] b2) { + if (!Arrays.equals(b1, b2)) { + throw new RuntimeException("Bad"); + } + } + void testSame(String a, String a2) { + System.out.println("testSame: " + a + " " + a2); + if (!HandshakeHash.normalizeAlgName(a).equals(a2)) { + throw new RuntimeException("Bad"); + } + } + /** + * Special convention: when it's certain that CV will not be used at the + * very beginning, use null as cvAlg. If known at a late stage, use "". + */ + void test(String cvAlg, String finAlg) throws Exception { + System.out.println("test: " + cvAlg + " " + finAlg); + byte[] cv = null, f1, f2; + HandshakeHash hh = new HandshakeHash(true, true, null); + if (cvAlg == null) { + hh.setCertificateVerifyAlg(cvAlg); + } + + update(hh, "ClientHello,"); + hh.reset(); + update(hh, "ClientHellov2,"); + hh.setFinishedAlg(finAlg); + + // Useless calls + hh.setFinishedAlg("SHA-1"); + hh.setFinishedAlg("SHA-512"); + + update(hh, "More,"); + if (cvAlg != null) { + if (cvAlg.isEmpty()) cvAlg = null; + hh.setCertificateVerifyAlg(cvAlg); + } + + // Useless calls + hh.setCertificateVerifyAlg("SHA-1"); + hh.setCertificateVerifyAlg(null); + + hh.protocolDetermined(true); + + if (cvAlg != null) { + cv = hh.getAllHandshakeMessages(); + equals(cv, "ClientHellov2,More,".getBytes()); + } + + update(hh, "FIN1,"); + f1 = hh.getFinishedHash(); + equals(f1, digest(finAlg, "ClientHellov2,More,FIN1,")); + update(hh, "FIN2,"); + f2 = hh.getFinishedHash(); + equals(f2, digest(finAlg, "ClientHellov2,More,FIN1,FIN2,")); + } + } } /**
--- a/jdk/src/share/classes/sun/security/ssl/HandshakeMessage.java Tue Nov 02 10:07:21 2010 +0000 +++ b/jdk/src/share/classes/sun/security/ssl/HandshakeMessage.java Tue Nov 02 10:15:06 2010 +0000 @@ -1,5 +1,5 @@ /* - * Copyright (c) 1996, 2010, Oracle and/or its affiliates. All rights reserved. + * copyright (c) 1996, 2010, 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 @@ -41,15 +41,12 @@ import javax.crypto.KeyGenerator; import javax.crypto.SecretKey; -import javax.crypto.spec.SecretKeySpec; import javax.net.ssl.*; -import sun.security.action.GetPropertyAction; - import sun.security.internal.spec.TlsPrfParameterSpec; - import sun.security.ssl.CipherSuite.*; +import static sun.security.ssl.CipherSuite.PRF.*; /** * Many data structures are involved in the handshake messages. These @@ -258,6 +255,27 @@ extensions.add(renegotiationInfo); } + // add server_name extension + void addServerNameIndicationExtension(String hostname) { + // We would have checked that the hostname ia a FQDN. + ArrayList<String> hostnames = new ArrayList<String>(1); + hostnames.add(hostname); + + try { + extensions.add(new ServerNameExtension(hostnames)); + } catch (IOException ioe) { + // ignore the exception and return + } + } + + // add signature_algorithm extension + void addSignatureAlgorithmsExtension( + Collection<SignatureAndHashAlgorithm> algorithms) { + HelloExtension signatureAlgorithm = + new SignatureAlgorithmsExtension(algorithms); + extensions.add(signatureAlgorithm); + } + @Override int messageType() { return ht_client_hello; } @@ -290,7 +308,8 @@ s.println("*** ClientHello, " + protocolVersion); if (debug != null && Debug.isOn("verbose")) { - s.print ("RandomCookie: "); clnt_random.print(s); + s.print("RandomCookie: "); + clnt_random.print(s); s.print("Session ID: "); s.println(sessionId); @@ -327,7 +346,8 @@ // empty } - ServerHello(HandshakeInStream input, int messageLength) throws IOException { + ServerHello(HandshakeInStream input, int messageLength) + throws IOException { protocolVersion = ProtocolVersion.valueOf(input.getInt8(), input.getInt8()); svr_random = new RandomCookie(input); @@ -367,7 +387,8 @@ s.println("*** ServerHello, " + protocolVersion); if (debug != null && Debug.isOn("verbose")) { - s.print ("RandomCookie: "); svr_random.print(s); + s.print("RandomCookie: "); + svr_random.print(s); int i; @@ -425,8 +446,8 @@ } v.add(cf.generateCertificate(new ByteArrayInputStream(cert))); } catch (CertificateException e) { - throw (SSLProtocolException)new SSLProtocolException - (e.getMessage()).initCause(e); + throw (SSLProtocolException)new SSLProtocolException( + e.getMessage()).initCause(e); } } @@ -469,7 +490,7 @@ } X509Certificate[] getCertificateChain() { - return chain; + return chain.clone(); } } @@ -597,9 +618,9 @@ try { KeyFactory kfac = JsseJce.getKeyFactory("RSA"); // modulus and exponent are always positive - RSAPublicKeySpec kspec = new RSAPublicKeySpec - (new BigInteger(1, rsa_modulus), - new BigInteger(1, rsa_exponent)); + RSAPublicKeySpec kspec = new RSAPublicKeySpec( + new BigInteger(1, rsa_modulus), + new BigInteger(1, rsa_exponent)); return kfac.generatePublic(kspec); } catch (Exception e) { throw new RuntimeException(e); @@ -667,6 +688,12 @@ private byte signature []; + // protocol version being established using this ServerKeyExchange message + ProtocolVersion protocolVersion; + + // the preferable signature algorithm used by this ServerKeyExchange message + private SignatureAndHashAlgorithm preferableSignatureAlgorithm; + /* Return the Diffie-Hellman modulus */ BigInteger getModulus() { return new BigInteger(1, dh_p); @@ -712,8 +739,11 @@ * Construct from initialized DH key object, for DH_anon * key exchange. */ - DH_ServerKeyExchange(DHCrypt obj) { - getValues(obj); + DH_ServerKeyExchange(DHCrypt obj, ProtocolVersion protocolVersion) { + this.protocolVersion = protocolVersion; + this.preferableSignatureAlgorithm = null; + + setValues(obj); signature = null; } @@ -723,22 +753,33 @@ * key exchange. (Constructor called by server.) */ DH_ServerKeyExchange(DHCrypt obj, PrivateKey key, byte clntNonce[], - byte svrNonce[], SecureRandom sr) throws GeneralSecurityException { + byte svrNonce[], SecureRandom sr, + SignatureAndHashAlgorithm signAlgorithm, + ProtocolVersion protocolVersion) throws GeneralSecurityException { - getValues(obj); + this.protocolVersion = protocolVersion; + + setValues(obj); Signature sig; - if (key.getAlgorithm().equals("DSA")) { - sig = JsseJce.getSignature(JsseJce.SIGNATURE_DSA); + if (protocolVersion.v >= ProtocolVersion.TLS12.v) { + this.preferableSignatureAlgorithm = signAlgorithm; + sig = JsseJce.getSignature(signAlgorithm.getAlgorithmName()); } else { - sig = RSASignature.getInstance(); + this.preferableSignatureAlgorithm = null; + if (key.getAlgorithm().equals("DSA")) { + sig = JsseJce.getSignature(JsseJce.SIGNATURE_DSA); + } else { + sig = RSASignature.getInstance(); + } } + sig.initSign(key, sr); updateSignature(sig, clntNonce, svrNonce); signature = sig.sign(); } - private void getValues(DHCrypt obj) { + private void setValues(DHCrypt obj) { dh_p = toByteArray(obj.getModulus()); dh_g = toByteArray(obj.getBase()); dh_Ys = toByteArray(obj.getPublicKey()); @@ -749,7 +790,12 @@ * stream, as if sent from server to client for use with * DH_anon key exchange */ - DH_ServerKeyExchange(HandshakeInStream input) throws IOException { + DH_ServerKeyExchange(HandshakeInStream input, + ProtocolVersion protocolVersion) throws IOException { + + this.protocolVersion = protocolVersion; + this.preferableSignatureAlgorithm = null; + dh_p = input.getBytes16(); dh_g = input.getBytes16(); dh_Ys = input.getBytes16(); @@ -762,13 +808,38 @@ * DHE_DSS or DHE_RSA key exchange. (Called by client.) */ DH_ServerKeyExchange(HandshakeInStream input, PublicKey publicKey, - byte clntNonce[], byte svrNonce[], int messageSize) + byte clntNonce[], byte svrNonce[], int messageSize, + Collection<SignatureAndHashAlgorithm> localSupportedSignAlgs, + ProtocolVersion protocolVersion) throws IOException, GeneralSecurityException { + this.protocolVersion = protocolVersion; + + // read params: ServerDHParams dh_p = input.getBytes16(); dh_g = input.getBytes16(); dh_Ys = input.getBytes16(); + // read the signature and hash algorithm + if (protocolVersion.v >= ProtocolVersion.TLS12.v) { + int hash = input.getInt8(); // hash algorithm + int signature = input.getInt8(); // signature algorithm + + preferableSignatureAlgorithm = + SignatureAndHashAlgorithm.valueOf(hash, signature, 0); + + // Is it a local supported signature algorithm? + if (!localSupportedSignAlgs.contains( + preferableSignatureAlgorithm)) { + throw new SSLHandshakeException( + "Unsupported SignatureAndHashAlgorithm in " + + "ServerKeyExchange message"); + } + } else { + this.preferableSignatureAlgorithm = null; + } + + // read the signature byte signature[]; if (dhKeyExchangeFix) { signature = input.getBytes16(); @@ -783,12 +854,17 @@ Signature sig; String algorithm = publicKey.getAlgorithm(); - if (algorithm.equals("DSA")) { - sig = JsseJce.getSignature(JsseJce.SIGNATURE_DSA); - } else if (algorithm.equals("RSA")) { - sig = RSASignature.getInstance(); + if (protocolVersion.v >= ProtocolVersion.TLS12.v) { + sig = JsseJce.getSignature( + preferableSignatureAlgorithm.getAlgorithmName()); } else { - throw new SSLKeyException("neither an RSA or a DSA key"); + if (algorithm.equals("DSA")) { + sig = JsseJce.getSignature(JsseJce.SIGNATURE_DSA); + } else if (algorithm.equals("RSA")) { + sig = RSASignature.getInstance(); + } else { + throw new SSLKeyException("neither an RSA or a DSA key"); + } } sig.initVerify(publicKey); @@ -805,12 +881,18 @@ temp += dh_p.length; temp += dh_g.length; temp += dh_Ys.length; + if (signature != null) { + if (protocolVersion.v >= ProtocolVersion.TLS12.v) { + temp += SignatureAndHashAlgorithm.sizeInRecord(); + } + temp += signature.length; if (dhKeyExchangeFix) { temp += 2; } } + return temp; } @@ -818,7 +900,13 @@ s.putBytes16(dh_p); s.putBytes16(dh_g); s.putBytes16(dh_Ys); + if (signature != null) { + if (protocolVersion.v >= ProtocolVersion.TLS12.v) { + s.putInt8(preferableSignatureAlgorithm.getHashValue()); + s.putInt8(preferableSignatureAlgorithm.getSignatureValue()); + } + if (dhKeyExchangeFix) { s.putBytes16(signature); } else { @@ -838,6 +926,11 @@ if (signature == null) { s.println("Anonymous"); } else { + if (protocolVersion.v >= ProtocolVersion.TLS12.v) { + s.println("Signature Algorithm " + + preferableSignatureAlgorithm.getAlgorithmName()); + } + s.println("Signed with a DSA or RSA public key"); } } @@ -871,9 +964,19 @@ // public key object encapsulated in this message private ECPublicKey publicKey; + // protocol version being established using this ServerKeyExchange message + ProtocolVersion protocolVersion; + + // the preferable signature algorithm used by this ServerKeyExchange message + private SignatureAndHashAlgorithm preferableSignatureAlgorithm; + ECDH_ServerKeyExchange(ECDHCrypt obj, PrivateKey privateKey, - byte[] clntNonce, byte[] svrNonce, SecureRandom sr) - throws GeneralSecurityException { + byte[] clntNonce, byte[] svrNonce, SecureRandom sr, + SignatureAndHashAlgorithm signAlgorithm, + ProtocolVersion protocolVersion) throws GeneralSecurityException { + + this.protocolVersion = protocolVersion; + publicKey = (ECPublicKey)obj.getPublicKey(); ECParameterSpec params = publicKey.getParams(); ECPoint point = publicKey.getW(); @@ -885,8 +988,14 @@ return; } - Signature sig = getSignature(privateKey.getAlgorithm()); - sig.initSign(privateKey); + Signature sig; + if (protocolVersion.v >= ProtocolVersion.TLS12.v) { + this.preferableSignatureAlgorithm = signAlgorithm; + sig = JsseJce.getSignature(signAlgorithm.getAlgorithmName()); + } else { + sig = getSignature(privateKey.getAlgorithm()); + } + sig.initSign(privateKey); // where is the SecureRandom? updateSignature(sig, clntNonce, svrNonce); signatureBytes = sig.sign(); @@ -896,49 +1005,87 @@ * Parse an ECDH server key exchange message. */ ECDH_ServerKeyExchange(HandshakeInStream input, PublicKey signingKey, - byte[] clntNonce, byte[] svrNonce) + byte[] clntNonce, byte[] svrNonce, + Collection<SignatureAndHashAlgorithm> localSupportedSignAlgs, + ProtocolVersion protocolVersion) throws IOException, GeneralSecurityException { + + this.protocolVersion = protocolVersion; + + // read params: ServerECDHParams int curveType = input.getInt8(); ECParameterSpec parameters; // These parsing errors should never occur as we negotiated // the supported curves during the exchange of the Hello messages. if (curveType == CURVE_NAMED_CURVE) { curveId = input.getInt16(); - if (SupportedEllipticCurvesExtension.isSupported(curveId) == false) { - throw new SSLHandshakeException("Unsupported curveId: " + curveId); + if (SupportedEllipticCurvesExtension.isSupported(curveId) + == false) { + throw new SSLHandshakeException( + "Unsupported curveId: " + curveId); } - String curveOid = SupportedEllipticCurvesExtension.getCurveOid(curveId); + String curveOid = + SupportedEllipticCurvesExtension.getCurveOid(curveId); if (curveOid == null) { - throw new SSLHandshakeException("Unknown named curve: " + curveId); + throw new SSLHandshakeException( + "Unknown named curve: " + curveId); } parameters = JsseJce.getECParameterSpec(curveOid); if (parameters == null) { - throw new SSLHandshakeException("Unsupported curve: " + curveOid); + throw new SSLHandshakeException( + "Unsupported curve: " + curveOid); } } else { - throw new SSLHandshakeException("Unsupported ECCurveType: " + curveType); + throw new SSLHandshakeException( + "Unsupported ECCurveType: " + curveType); } pointBytes = input.getBytes8(); ECPoint point = JsseJce.decodePoint(pointBytes, parameters.getCurve()); KeyFactory factory = JsseJce.getKeyFactory("EC"); - publicKey = (ECPublicKey)factory.generatePublic(new ECPublicKeySpec(point, parameters)); + publicKey = (ECPublicKey)factory.generatePublic( + new ECPublicKeySpec(point, parameters)); if (signingKey == null) { // ECDH_anon return; } - // verify the signature + // read the signature and hash algorithm + if (protocolVersion.v >= ProtocolVersion.TLS12.v) { + int hash = input.getInt8(); // hash algorithm + int signature = input.getInt8(); // signature algorithm + + preferableSignatureAlgorithm = + SignatureAndHashAlgorithm.valueOf(hash, signature, 0); + + // Is it a local supported signature algorithm? + if (!localSupportedSignAlgs.contains( + preferableSignatureAlgorithm)) { + throw new SSLHandshakeException( + "Unsupported SignatureAndHashAlgorithm in " + + "ServerKeyExchange message"); + } + } + + // read the signature signatureBytes = input.getBytes16(); - Signature sig = getSignature(signingKey.getAlgorithm()); + + // verify the signature + Signature sig; + if (protocolVersion.v >= ProtocolVersion.TLS12.v) { + sig = JsseJce.getSignature( + preferableSignatureAlgorithm.getAlgorithmName()); + } else { + sig = getSignature(signingKey.getAlgorithm()); + } sig.initVerify(signingKey); updateSignature(sig, clntNonce, svrNonce); if (sig.verify(signatureBytes) == false ) { - throw new SSLKeyException - ("Invalid signature on ECDH server key exchange message"); + throw new SSLKeyException( + "Invalid signature on ECDH server key exchange message"); } } @@ -949,7 +1096,8 @@ return publicKey; } - private static Signature getSignature(String keyAlgorithm) throws NoSuchAlgorithmException { + private static Signature getSignature(String keyAlgorithm) + throws NoSuchAlgorithmException { if (keyAlgorithm.equals("EC")) { return JsseJce.getSignature(JsseJce.SIGNATURE_ECDSA); } else if (keyAlgorithm.equals("RSA")) { @@ -973,6 +1121,11 @@ int messageLength() { int sigLen = (signatureBytes == null) ? 0 : 2 + signatureBytes.length; + + if (protocolVersion.v >= ProtocolVersion.TLS12.v) { + sigLen += SignatureAndHashAlgorithm.sizeInRecord(); + } + return 4 + pointBytes.length + sigLen; } @@ -980,6 +1133,11 @@ s.putInt8(CURVE_NAMED_CURVE); s.putInt16(curveId); s.putBytes8(pointBytes); + if (protocolVersion.v >= ProtocolVersion.TLS12.v) { + s.putInt8(preferableSignatureAlgorithm.getHashValue()); + s.putInt8(preferableSignatureAlgorithm.getSignatureValue()); + } + if (signatureBytes != null) { s.putBytes16(signatureBytes); } @@ -989,6 +1147,11 @@ s.println("*** ECDH ServerKeyExchange"); if (debug != null && Debug.isOn("verbose")) { + if (protocolVersion.v >= ProtocolVersion.TLS12.v) { + s.println("Signature Algorithm " + + preferableSignatureAlgorithm.getAlgorithmName()); + } + s.println("Server key: " + publicKey); } } @@ -1014,8 +1177,8 @@ try { return new X500Principal(name); } catch (IllegalArgumentException e) { - throw (SSLProtocolException)new SSLProtocolException - (e.getMessage()).initCause(e); + throw (SSLProtocolException)new SSLProtocolException( + e.getMessage()).initCause(e); } } @@ -1038,12 +1201,25 @@ * * Authenticated servers may ask clients to authenticate themselves * in turn, using this message. + * + * Prior to TLS 1.2, the structure of the message is defined as: + * struct { + * ClientCertificateType certificate_types<1..2^8-1>; + * DistinguishedName certificate_authorities<0..2^16-1>; + * } CertificateRequest; + * + * In TLS 1.2, the structure is changed to: + * struct { + * ClientCertificateType certificate_types<1..2^8-1>; + * SignatureAndHashAlgorithm + * supported_signature_algorithms<2^16-1>; + * DistinguishedName certificate_authorities<0..2^16-1>; + * } CertificateRequest; + * */ static final class CertificateRequest extends HandshakeMessage { - int messageType() { return ht_certificate_request; } - // enum ClientCertificateType static final int cct_rsa_sign = 1; static final int cct_dss_sign = 2; @@ -1068,8 +1244,21 @@ DistinguishedName authorities []; // 3 to 2^16 - 1 // ... "3" because that's the smallest DER-encoded X500 DN - CertificateRequest(X509Certificate ca[], KeyExchange keyExchange) - throws IOException { + // protocol version being established using this CertificateRequest message + ProtocolVersion protocolVersion; + + // supported_signature_algorithms for TLS 1.2 or later + private Collection<SignatureAndHashAlgorithm> algorithms; + + // length of supported_signature_algorithms + private int algorithmsLen; + + CertificateRequest(X509Certificate ca[], KeyExchange keyExchange, + Collection<SignatureAndHashAlgorithm> signAlgs, + ProtocolVersion protocolVersion) throws IOException { + + this.protocolVersion = protocolVersion; + // always use X500Principal authorities = new DistinguishedName[ca.length]; for (int i = 0; i < ca.length; i++) { @@ -1081,10 +1270,63 @@ // needs to be adapted to take keyExchange into account. // We only request ECDSA client auth if we have ECC crypto available. this.types = JsseJce.isEcAvailable() ? TYPES_ECC : TYPES_NO_ECC; + + // Use supported_signature_algorithms for TLS 1.2 or later. + if (protocolVersion.v >= ProtocolVersion.TLS12.v) { + if (signAlgs == null || signAlgs.isEmpty()) { + throw new SSLProtocolException( + "No supported signature algorithms"); + } + + algorithms = new ArrayList<SignatureAndHashAlgorithm>(signAlgs); + algorithmsLen = + SignatureAndHashAlgorithm.sizeInRecord() * algorithms.size(); + } else { + algorithms = new ArrayList<SignatureAndHashAlgorithm>(); + algorithmsLen = 0; + } } - CertificateRequest(HandshakeInStream input) throws IOException { + CertificateRequest(HandshakeInStream input, + ProtocolVersion protocolVersion) throws IOException { + + this.protocolVersion = protocolVersion; + + // Read the certificate_types. types = input.getBytes8(); + + // Read the supported_signature_algorithms for TLS 1.2 or later. + if (protocolVersion.v >= ProtocolVersion.TLS12.v) { + algorithmsLen = input.getInt16(); + if (algorithmsLen < 2) { + throw new SSLProtocolException( + "Invalid supported_signature_algorithms field"); + } + + algorithms = new ArrayList<SignatureAndHashAlgorithm>(); + int remains = algorithmsLen; + int sequence = 0; + while (remains > 1) { // needs at least two bytes + int hash = input.getInt8(); // hash algorithm + int signature = input.getInt8(); // signature algorithm + + SignatureAndHashAlgorithm algorithm = + SignatureAndHashAlgorithm.valueOf(hash, signature, + ++sequence); + algorithms.add(algorithm); + remains -= 2; // one byte for hash, one byte for signature + } + + if (remains != 0) { + throw new SSLProtocolException( + "Invalid supported_signature_algorithms field"); + } + } else { + algorithms = new ArrayList<SignatureAndHashAlgorithm>(); + algorithmsLen = 0; + } + + // read the certificate_authorities int len = input.getInt16(); ArrayList<DistinguishedName> v = new ArrayList<DistinguishedName>(); while (len >= 3) { @@ -1108,31 +1350,58 @@ return ret; } - int messageLength() - { - int len; + Collection<SignatureAndHashAlgorithm> getSignAlgorithms() { + return algorithms; + } + + @Override + int messageType() { + return ht_certificate_request; + } - len = 1 + types.length + 2; - for (int i = 0; i < authorities.length; i++) + @Override + int messageLength() { + int len = 1 + types.length + 2; + + if (protocolVersion.v >= ProtocolVersion.TLS12.v) { + len += algorithmsLen + 2; + } + + for (int i = 0; i < authorities.length; i++) { len += authorities[i].length(); + } + return len; } - void send(HandshakeOutStream output) throws IOException - { - int len = 0; + @Override + void send(HandshakeOutStream output) throws IOException { + // put certificate_types + output.putBytes8(types); - for (int i = 0; i < authorities.length; i++) + // put supported_signature_algorithms + if (protocolVersion.v >= ProtocolVersion.TLS12.v) { + output.putInt16(algorithmsLen); + for (SignatureAndHashAlgorithm algorithm : algorithms) { + output.putInt8(algorithm.getHashValue()); // hash + output.putInt8(algorithm.getSignatureValue()); // signature + } + } + + // put certificate_authorities + int len = 0; + for (int i = 0; i < authorities.length; i++) { len += authorities[i].length(); + } - output.putBytes8(types); output.putInt16(len); - for (int i = 0; i < authorities.length; i++) + for (int i = 0; i < authorities.length; i++) { authorities[i].send(output); + } } - void print(PrintStream s) throws IOException - { + @Override + void print(PrintStream s) throws IOException { s.println("*** CertificateRequest"); if (debug != null && Debug.isOn("verbose")) { @@ -1166,6 +1435,20 @@ } s.println(); + if (protocolVersion.v >= ProtocolVersion.TLS12.v) { + StringBuffer buffer = new StringBuffer(); + boolean opened = false; + for (SignatureAndHashAlgorithm signAlg : algorithms) { + if (opened) { + buffer.append(", " + signAlg.getAlgorithmName()); + } else { + buffer.append(signAlg.getAlgorithmName()); + opened = true; + } + } + s.println("Supported Signature Algorithms: " + buffer); + } + s.println("Cert Authorities:"); if (authorities.length == 0) { s.println("<Empty>"); @@ -1224,18 +1507,34 @@ */ static final class CertificateVerify extends HandshakeMessage { - int messageType() { return ht_certificate_verify; } + // the signature bytes + private byte[] signature; - 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) throws GeneralSecurityException { + CertificateVerify(ProtocolVersion protocolVersion, + HandshakeHash handshakeHash, PrivateKey privateKey, + SecretKey masterSecret, SecureRandom sr, + SignatureAndHashAlgorithm signAlgorithm) + throws GeneralSecurityException { + + this.protocolVersion = protocolVersion; + String algorithm = privateKey.getAlgorithm(); - Signature sig = getSignature(protocolVersion, algorithm); + 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); @@ -1245,11 +1544,41 @@ // // Unmarshal the signed data from the input stream. // - CertificateVerify(HandshakeInStream input) throws IOException { + 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. */ @@ -1257,7 +1586,13 @@ HandshakeHash handshakeHash, PublicKey publicKey, SecretKey masterSecret) throws GeneralSecurityException { String algorithm = publicKey.getAlgorithm(); - Signature sig = getSignature(protocolVersion, algorithm); + 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); @@ -1291,25 +1626,35 @@ ProtocolVersion protocolVersion, HandshakeHash handshakeHash, String algorithm, SecretKey masterKey) throws SignatureException { - MessageDigest md5Clone = handshakeHash.getMD5Clone(); - MessageDigest shaClone = handshakeHash.getSHAClone(); - boolean tls = protocolVersion.v >= ProtocolVersion.TLS10.v; + if (algorithm.equals("RSA")) { - if (tls) { - // nothing to do - } else { // SSLv3 - updateDigest(md5Clone, MD5_pad1, MD5_pad2, masterKey); - updateDigest(shaClone, SHA_pad1, SHA_pad2, masterKey); + 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()); } - // need to use these hashes directly - RSASignature.setHashes(sig, md5Clone, shaClone); } else { // DSA, ECDSA - if (tls) { - // nothing to do - } else { // SSLv3 - updateDigest(shaClone, SHA_pad1, SHA_pad2, masterKey); + 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()); } - sig.update(shaClone.digest()); } } @@ -1319,7 +1664,8 @@ * all preceding handshake messages. * Used by the Finished class as well. */ - static void updateDigest(MessageDigest md, byte[] pad1, byte[] pad2, + 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. @@ -1395,26 +1741,54 @@ methodCache.put(clazz, r); } if (r == NULL_OBJECT) { - throw new Exception("Digest does not support implUpdate(SecretKey)"); + 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); + throw new RuntimeException( + "Could not obtain encoded key and " + + "MessageDigest cannot digest key", e); } } - int messageLength() { - return 2 + signature.length; + @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()); + } + } } } @@ -1453,19 +1827,29 @@ 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) { - verifyData = getFinished(protocolVersion, handshakeHash, sender, - master); + 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) - throws IOException { + 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); @@ -1477,18 +1861,16 @@ * both client and server are fully in sync, and that the handshake * computations have been successful. */ - boolean verify(ProtocolVersion protocolVersion, - HandshakeHash handshakeHash, int sender, SecretKey master) { - byte[] myFinished = getFinished(protocolVersion, handshakeHash, - sender, master); + 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 static byte[] getFinished(ProtocolVersion protocolVersion, - HandshakeHash handshakeHash, int sender, SecretKey masterKey) { + private byte[] getFinished(HandshakeHash handshakeHash, + int sender, SecretKey masterKey) { byte[] sslLabel; String tlsLabel; if (sender == CLIENT) { @@ -1500,23 +1882,53 @@ } else { throw new RuntimeException("Invalid sender: " + sender); } - MessageDigest md5Clone = handshakeHash.getMD5Clone(); - MessageDigest shaClone = handshakeHash.getSHAClone(); + if (protocolVersion.v >= ProtocolVersion.TLS10.v) { - // TLS + // TLS 1.0+ try { - byte[] seed = new byte[36]; - md5Clone.digest(seed, 0, 16); - shaClone.digest(seed, 16, 20); + 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); - TlsPrfParameterSpec spec = new TlsPrfParameterSpec - (masterKey, tlsLabel, seed, 12); - KeyGenerator prf = JsseJce.getKeyGenerator("SunTlsPrf"); - prf.init(spec); - SecretKey prfKey = prf.generateKey(); + 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"); + throw new ProviderException( + "Invalid PRF output, format must be RAW"); } byte[] finished = prfKey.getEncoded(); return finished; @@ -1525,6 +1937,8 @@ } } 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];
--- a/jdk/src/share/classes/sun/security/ssl/Handshaker.java Tue Nov 02 10:07:21 2010 +0000 +++ b/jdk/src/share/classes/sun/security/ssl/Handshaker.java Tue Nov 02 10:15:06 2010 +0000 @@ -29,13 +29,12 @@ import java.io.*; import java.util.*; import java.security.*; -import java.security.MessageDigest; import java.security.NoSuchAlgorithmException; import java.security.AccessController; +import java.security.AlgorithmConstraints; import java.security.AccessControlContext; import java.security.PrivilegedExceptionAction; import java.security.PrivilegedActionException; -import java.security.cert.X509Certificate; import javax.crypto.*; import javax.crypto.spec.*; @@ -49,6 +48,8 @@ import sun.security.ssl.HandshakeMessage.*; import sun.security.ssl.CipherSuite.*; +import static sun.security.ssl.CipherSuite.PRF.*; + /** * Handshaker ... processes handshake records from an SSL V3.0 * data stream, handling all the details of the handshake protocol. @@ -80,6 +81,20 @@ // List of enabled CipherSuites private CipherSuiteList enabledCipherSuites; + // The endpoint identification protocol + String identificationProtocol; + + // The cryptographic algorithm constraints + private AlgorithmConstraints algorithmConstraints = null; + + // Local supported signature and algorithms + Collection<SignatureAndHashAlgorithm> localSupportedSignAlgs; + + // Peer supported signature and algorithms + Collection<SignatureAndHashAlgorithm> peerSupportedSignAlgs; + + /* + /* * List of active protocols * @@ -98,6 +113,7 @@ private CipherSuiteList activeCipherSuites; private boolean isClient; + private boolean needCertVerify; SSLSocketImpl conn = null; SSLEngineImpl engine = null; @@ -110,10 +126,6 @@ RandomCookie clnt_random, svr_random; SSLSessionImpl session; - // Temporary MD5 and SHA message digests. Must always be left - // in reset state after use. - private MessageDigest md5Tmp, shaTmp; - // current CipherSuite. Never null, initially SSL_NULL_WITH_NULL_NULL CipherSuite cipherSuite; @@ -208,6 +220,7 @@ this.sslContext = context; this.isClient = isClient; + this.needCertVerify = needCertVerify; this.activeProtocolVersion = activeProtocolVersion; this.isInitialHandshake = isInitialHandshake; this.secureRenegotiation = secureRenegotiation; @@ -217,23 +230,12 @@ invalidated = false; setCipherSuite(CipherSuite.C_NULL); - - md5Tmp = JsseJce.getMD5(); - shaTmp = JsseJce.getSHA(); - - // - // We accumulate digests of the handshake messages so that - // we can read/write CertificateVerify and Finished messages, - // getting assurance against some particular active attacks. - // - handshakeHash = new HandshakeHash(needCertVerify); - setEnabledProtocols(enabledProtocols); if (conn != null) { - conn.getAppInputStream().r.setHandshakeHash(handshakeHash); + algorithmConstraints = new SSLAlgorithmConstraints(conn, true); } else { // engine != null - engine.inputRecord.setHandshakeHash(handshakeHash); + algorithmConstraints = new SSLAlgorithmConstraints(engine, true); } @@ -285,6 +287,14 @@ } } + String getRawHostnameSE() { + if (conn != null) { + return conn.getRawHostname(); + } else { + return engine.getPeerHost(); + } + } + String getHostSE() { if (conn != null) { return conn.getHost(); @@ -330,14 +340,6 @@ } } - String getHostnameVerificationSE() { - if (conn != null) { - return conn.getHostnameVerification(); - } else { - return engine.getHostnameVerification(); - } - } - AccessControlContext getAccSE() { if (conn != null) { return conn.getAcc(); @@ -366,7 +368,6 @@ output.r.setVersion(protocolVersion); } - /** * Set the enabled protocols. Called from the constructor or * SSLSocketImpl/SSLEngineImpl.setEnabledProtocols() (if the @@ -390,6 +391,49 @@ this.enabledCipherSuites = enabledCipherSuites; } + /** + * Set the algorithm constraints. Called from the constructor or + * SSLSocketImpl/SSLEngineImpl.setAlgorithmConstraints() (if the + * handshake is not yet in progress). + */ + void setAlgorithmConstraints(AlgorithmConstraints algorithmConstraints) { + activeCipherSuites = null; + activeProtocols = null; + + this.algorithmConstraints = + new SSLAlgorithmConstraints(algorithmConstraints); + this.localSupportedSignAlgs = null; + } + + Collection<SignatureAndHashAlgorithm> getLocalSupportedSignAlgs() { + if (localSupportedSignAlgs == null) { + localSupportedSignAlgs = + SignatureAndHashAlgorithm.getSupportedAlgorithms( + algorithmConstraints); + } + + return localSupportedSignAlgs; + } + + void setPeerSupportedSignAlgs( + Collection<SignatureAndHashAlgorithm> algorithms) { + peerSupportedSignAlgs = + new ArrayList<SignatureAndHashAlgorithm>(algorithms); + } + + Collection<SignatureAndHashAlgorithm> getPeerSupportedSignAlgs() { + return peerSupportedSignAlgs; + } + + + /** + * Set the identification protocol. Called from the constructor or + * SSLSocketImpl/SSLEngineImpl.setIdentificationProtocol() (if the + * handshake is not yet in progress). + */ + void setIdentificationProtocol(String protocol) { + this.identificationProtocol = protocol; + } /** * Prior to handshaking, activate the handshake and initialize the version, @@ -426,16 +470,27 @@ helloVersion = activeProtocols.helloVersion; } + // We accumulate digests of the handshake messages so that + // we can read/write CertificateVerify and Finished messages, + // getting assurance against some particular active attacks. + Set<String> localSupportedHashAlgorithms = + SignatureAndHashAlgorithm.getHashAlgorithmNames( + getLocalSupportedSignAlgs()); + handshakeHash = new HandshakeHash(!isClient, needCertVerify, + localSupportedHashAlgorithms); + + // Generate handshake input/output stream. input = new HandshakeInStream(handshakeHash); - if (conn != null) { output = new HandshakeOutStream(protocolVersion, helloVersion, handshakeHash, conn); + conn.getAppInputStream().r.setHandshakeHash(handshakeHash); conn.getAppInputStream().r.setHelloVersion(helloVersion); conn.getAppOutputStream().r.setHelloVersion(helloVersion); } else { output = new HandshakeOutStream(protocolVersion, helloVersion, handshakeHash, engine); + engine.inputRecord.setHandshakeHash(handshakeHash); engine.inputRecord.setHelloVersion(helloVersion); engine.outputRecord.setHelloVersion(helloVersion); } @@ -501,7 +556,7 @@ * * Therefore, when the active protocols only include TLS 1.1 or later, * the client cannot request to negotiate those obsoleted cipher - * suites, that's, the obsoleted suites should not be included in the + * suites. That is, the obsoleted suites should not be included in the * client hello. So we need to create a subset of the enabled cipher * suites, the active cipher suites, which does not contain obsoleted * cipher suites of the minimum active protocol. @@ -518,11 +573,21 @@ if (!(activeProtocols.collection().isEmpty()) && activeProtocols.min.v != ProtocolVersion.NONE.v) { for (CipherSuite suite : enabledCipherSuites.collection()) { - if (suite.obsoleted > activeProtocols.min.v) { - suites.add(suite); - } else if (debug != null && Debug.isOn("handshake")) { - System.out.println( - "Ignoring obsoleted cipher suite: " + suite); + if (suite.obsoleted > activeProtocols.min.v && + suite.supported <= activeProtocols.max.v) { + if (algorithmConstraints.permits( + EnumSet.of(CryptoPrimitive.KEY_AGREEMENT), + suite.name, null)) { + suites.add(suite); + } + } else if (debug != null && Debug.isOn("verbose")) { + if (suite.obsoleted <= activeProtocols.min.v) { + System.out.println( + "Ignoring obsoleted cipher suite: " + suite); + } else { + System.out.println( + "Ignoring unsupported cipher suite: " + suite); + } } } } @@ -550,14 +615,27 @@ ProtocolList getActiveProtocols() { if (activeProtocols == null) { ArrayList<ProtocolVersion> protocols = - new ArrayList<ProtocolVersion>(3); + new ArrayList<ProtocolVersion>(4); for (ProtocolVersion protocol : enabledProtocols.collection()) { boolean found = false; for (CipherSuite suite : enabledCipherSuites.collection()) { - if (suite.isAvailable() && suite.obsoleted > protocol.v) { - protocols.add(protocol); - found = true; - break; + if (suite.isAvailable() && suite.obsoleted > protocol.v && + suite.supported <= protocol.v) { + if (algorithmConstraints.permits( + EnumSet.of(CryptoPrimitive.KEY_AGREEMENT), + suite.name, null)) { + protocols.add(protocol); + found = true; + break; + } else if (debug != null && Debug.isOn("verbose")) { + System.out.println( + "Ignoring disabled cipher suite: " + suite + + " for " + protocol); + } + } else if (debug != null && Debug.isOn("verbose")) { + System.out.println( + "Ignoring unsupported cipher suite: " + suite + + " for " + protocol); } } if (!found && (debug != null) && Debug.isOn("handshake")) { @@ -673,6 +751,17 @@ } /* + * Set the handshake session + */ + void setHandshakeSessionSE(SSLSessionImpl handshakeSession) { + if (conn != null) { + conn.setHandshakeSession(handshakeSession); + } else { + engine.setHandshakeSession(handshakeSession); + } + } + + /* * Returns true if renegotiation is in use for this connection. */ boolean isSecureRenegotiation() { @@ -798,7 +887,7 @@ */ boolean started() { return state >= 0; // 0: HandshakeMessage.ht_hello_request - // 1: HandshakeMessage.ht_hello_request + // 1: HandshakeMessage.ht_client_hello } @@ -926,10 +1015,6 @@ private SecretKey calculateMasterSecret(SecretKey preMasterSecret, ProtocolVersion requestedVersion) { - TlsMasterSecretParameterSpec spec = new TlsMasterSecretParameterSpec - (preMasterSecret, protocolVersion.major, protocolVersion.minor, - clnt_random.random_bytes, svr_random.random_bytes); - if (debug != null && Debug.isOn("keygen")) { HexDumpEncoder dump = new HexDumpEncoder(); @@ -942,15 +1027,37 @@ // benefit to doing it twice } + // What algs/params do we need to use? + String masterAlg; + PRF prf; + + if (protocolVersion.v >= ProtocolVersion.TLS12.v) { + masterAlg = "SunTls12MasterSecret"; + prf = cipherSuite.prfAlg; + } else { + masterAlg = "SunTlsMasterSecret"; + prf = P_NONE; + } + + String prfHashAlg = prf.getPRFHashAlg(); + int prfHashLength = prf.getPRFHashLength(); + int prfBlockSize = prf.getPRFBlockSize(); + + TlsMasterSecretParameterSpec spec = new TlsMasterSecretParameterSpec( + preMasterSecret, protocolVersion.major, protocolVersion.minor, + clnt_random.random_bytes, svr_random.random_bytes, + prfHashAlg, prfHashLength, prfBlockSize); + SecretKey masterSecret; try { - KeyGenerator kg = JsseJce.getKeyGenerator("SunTlsMasterSecret"); + KeyGenerator kg = JsseJce.getKeyGenerator(masterAlg); kg.init(spec); masterSecret = kg.generateKey(); } catch (GeneralSecurityException e) { // For RSA premaster secrets, do not signal a protocol error // due to the Bleichenbacher attack. See comments further down. - if (!preMasterSecret.getAlgorithm().equals("TlsRsaPremasterSecret")) { + if (!preMasterSecret.getAlgorithm().equals( + "TlsRsaPremasterSecret")) { throw new ProviderException(e); } @@ -1056,14 +1163,31 @@ BulkCipher cipher = cipherSuite.cipher; int expandedKeySize = is_exportable ? cipher.expandedKeySize : 0; - TlsKeyMaterialParameterSpec spec = new TlsKeyMaterialParameterSpec - (masterKey, protocolVersion.major, protocolVersion.minor, + // Which algs/params do we need to use? + String keyMaterialAlg; + PRF prf; + + if (protocolVersion.v >= ProtocolVersion.TLS12.v) { + keyMaterialAlg = "SunTls12KeyMaterial"; + prf = cipherSuite.prfAlg; + } else { + keyMaterialAlg = "SunTlsKeyMaterial"; + prf = P_NONE; + } + + String prfHashAlg = prf.getPRFHashAlg(); + int prfHashLength = prf.getPRFHashLength(); + int prfBlockSize = prf.getPRFBlockSize(); + + TlsKeyMaterialParameterSpec spec = new TlsKeyMaterialParameterSpec( + masterKey, protocolVersion.major, protocolVersion.minor, clnt_random.random_bytes, svr_random.random_bytes, cipher.algorithm, cipher.keySize, expandedKeySize, - cipher.ivSize, hashSize); + cipher.ivSize, hashSize, + prfHashAlg, prfHashLength, prfBlockSize); try { - KeyGenerator kg = JsseJce.getKeyGenerator("SunTlsKeyMaterial"); + KeyGenerator kg = JsseJce.getKeyGenerator(keyMaterialAlg); kg.init(spec); TlsKeyMaterialSpec keySpec = (TlsKeyMaterialSpec)kg.generateKey();
--- a/jdk/src/share/classes/sun/security/ssl/HelloExtensions.java Tue Nov 02 10:07:21 2010 +0000 +++ b/jdk/src/share/classes/sun/security/ssl/HelloExtensions.java Tue Nov 02 10:15:06 2010 +0000 @@ -50,7 +50,8 @@ * * . UnknownExtension: used to represent all parsed extensions that we do not * explicitly support. - * . ServerNameExtension: partially implemented server_name extension. + * . ServerNameExtension: the server_name extension. + * . SignatureAlgorithmsExtension: the signature_algorithms extension. * . SupportedEllipticCurvesExtension: the ECC supported curves extension. * . SupportedEllipticPointFormatsExtension: the ECC supported point formats * (compressed/uncompressed) extension. @@ -78,6 +79,8 @@ HelloExtension extension; if (extType == ExtensionType.EXT_SERVER_NAME) { extension = new ServerNameExtension(s, extlen); + } else if (extType == ExtensionType.EXT_SIGNATURE_ALGORITHMS) { + extension = new SignatureAlgorithmsExtension(s, extlen); } else if (extType == ExtensionType.EXT_ELLIPTIC_CURVES) { extension = new SupportedEllipticCurvesExtension(s, extlen); } else if (extType == ExtensionType.EXT_EC_POINT_FORMATS) { @@ -266,31 +269,102 @@ } public String toString() { - return "Unsupported extension " + type + ", data: " + Debug.toString(data); + return "Unsupported extension " + type + ", data: " + + Debug.toString(data); } } /* - * Support for the server_name extension is incomplete. Parsing is implemented - * so that we get nicer debug output, but we neither send it nor do we do - * act on it if we receive it. + * [RFC4366] To facilitate secure connections to servers that host multiple + * 'virtual' servers at a single underlying network address, clients MAY + * include an extension of type "server_name" in the (extended) client hello. + * The "extension_data" field of this extension SHALL contain "ServerNameList" + * where: + * + * struct { + * NameType name_type; + * select (name_type) { + * case host_name: HostName; + * } name; + * } ServerName; + * + * enum { + * host_name(0), (255) + * } NameType; + * + * opaque HostName<1..2^16-1>; + * + * struct { + * ServerName server_name_list<1..2^16-1> + * } ServerNameList; */ final class ServerNameExtension extends HelloExtension { final static int NAME_HOST_NAME = 0; private List<ServerName> names; + private int listLength; // ServerNameList length + + ServerNameExtension(List<String> hostnames) throws IOException { + super(ExtensionType.EXT_SERVER_NAME); + + listLength = 0; + names = new ArrayList<ServerName>(hostnames.size()); + for (String hostname : hostnames) { + if (hostname != null && hostname.length() != 0) { + // we only support DNS hostname now. + ServerName serverName = + new ServerName(NAME_HOST_NAME, hostname); + names.add(serverName); + listLength += serverName.length; + } + } + + // As we only support DNS hostname now, the hostname list must + // not contain more than one hostname + if (names.size() > 1) { + throw new SSLProtocolException( + "The ServerNameList MUST NOT contain more than " + + "one name of the same name_type"); + } + + // We only need to add "server_name" extension in ClientHello unless + // we support SNI in server side in the future. It is possible that + // the SNI is empty in ServerHello. As we don't support SNI in + // ServerHello now, we will throw exception for empty list for now. + if (listLength == 0) { + throw new SSLProtocolException( + "The ServerNameList cannot be empty"); + } + } ServerNameExtension(HandshakeInStream s, int len) throws IOException { super(ExtensionType.EXT_SERVER_NAME); - names = new ArrayList<ServerName>(); - while (len > 0) { - ServerName name = new ServerName(s); - names.add(name); - len -= name.length + 2; + + int remains = len; + if (len >= 2) { // "server_name" extension in ClientHello + listLength = s.getInt16(); // ServerNameList length + if (listLength == 0 || listLength + 2 != len) { + throw new SSLProtocolException( + "Invalid " + type + " extension"); + } + + remains -= 2; + names = new ArrayList<ServerName>(); + while (remains > 0) { + ServerName name = new ServerName(s); + names.add(name); + remains -= name.length; + + // we may need to check the duplicated ServerName type + } + } else if (len == 0) { // "server_name" extension in ServerHello + listLength = 0; + names = Collections.<ServerName>emptyList(); } - if (len != 0) { + + if (remains != 0) { throw new SSLProtocolException("Invalid server_name extension"); } } @@ -301,10 +375,19 @@ final byte[] data; final String hostname; + ServerName(int type, String hostname) throws IOException { + this.type = type; // NameType + this.hostname = hostname; + this.data = hostname.getBytes("UTF8"); // HostName + this.length = data.length + 3; // NameType: 1 byte + // HostName length: 2 bytes + } + ServerName(HandshakeInStream s) throws IOException { - length = s.getInt16(); // ServerNameList length type = s.getInt8(); // NameType data = s.getBytes16(); // HostName (length read in getBytes16) + length = data.length + 3; // NameType: 1 byte + // HostName length: 2 bytes if (type == NAME_HOST_NAME) { hostname = new String(data, "UTF8"); } else { @@ -322,15 +405,29 @@ } int length() { - throw new RuntimeException("not yet supported"); + return listLength == 0 ? 4 : 6 + listLength; } void send(HandshakeOutStream s) throws IOException { - throw new RuntimeException("not yet supported"); + s.putInt16(type.id); + s.putInt16(listLength + 2); + if (listLength != 0) { + s.putInt16(listLength); + + for (ServerName name : names) { + s.putInt8(name.type); // NameType + s.putBytes16(name.data); // HostName + } + } } public String toString() { - return "Unsupported extension " + type + ", " + names.toString(); + StringBuffer buffer = new StringBuffer(); + for (ServerName name : names) { + buffer.append("[" + name + "]"); + } + + return "Extension " + type + ", server_name: " + buffer; } } @@ -523,7 +620,8 @@ final static int FMT_ANSIX962_COMPRESSED_CHAR2 = 2; static final HelloExtension DEFAULT = - new SupportedEllipticPointFormatsExtension(new byte[] {FMT_UNCOMPRESSED}); + new SupportedEllipticPointFormatsExtension( + new byte[] {FMT_UNCOMPRESSED}); private final byte[] formats; @@ -665,3 +763,105 @@ } } + +/* + * [RFC5246] The client uses the "signature_algorithms" extension to + * indicate to the server which signature/hash algorithm pairs may be + * used in digital signatures. The "extension_data" field of this + * extension contains a "supported_signature_algorithms" value. + * + * enum { + * none(0), md5(1), sha1(2), sha224(3), sha256(4), sha384(5), + * sha512(6), (255) + * } HashAlgorithm; + * + * enum { anonymous(0), rsa(1), dsa(2), ecdsa(3), (255) } + * SignatureAlgorithm; + * + * struct { + * HashAlgorithm hash; + * SignatureAlgorithm signature; + * } SignatureAndHashAlgorithm; + * + * SignatureAndHashAlgorithm + * supported_signature_algorithms<2..2^16-2>; + */ +final class SignatureAlgorithmsExtension extends HelloExtension { + + private Collection<SignatureAndHashAlgorithm> algorithms; + private int algorithmsLen; // length of supported_signature_algorithms + + SignatureAlgorithmsExtension( + Collection<SignatureAndHashAlgorithm> signAlgs) { + + super(ExtensionType.EXT_SIGNATURE_ALGORITHMS); + + algorithms = new ArrayList<SignatureAndHashAlgorithm>(signAlgs); + algorithmsLen = + SignatureAndHashAlgorithm.sizeInRecord() * algorithms.size(); + } + + SignatureAlgorithmsExtension(HandshakeInStream s, int len) + throws IOException { + super(ExtensionType.EXT_SIGNATURE_ALGORITHMS); + + algorithmsLen = s.getInt16(); + if (algorithmsLen == 0 || algorithmsLen + 2 != len) { + throw new SSLProtocolException("Invalid " + type + " extension"); + } + + algorithms = new ArrayList<SignatureAndHashAlgorithm>(); + int remains = algorithmsLen; + int sequence = 0; + while (remains > 1) { // needs at least two bytes + int hash = s.getInt8(); // hash algorithm + int signature = s.getInt8(); // signature algorithm + + SignatureAndHashAlgorithm algorithm = + SignatureAndHashAlgorithm.valueOf(hash, signature, ++sequence); + algorithms.add(algorithm); + remains -= 2; // one byte for hash, one byte for signature + } + + if (remains != 0) { + throw new SSLProtocolException("Invalid server_name extension"); + } + } + + Collection<SignatureAndHashAlgorithm> getSignAlgorithms() { + return algorithms; + } + + @Override + int length() { + return 6 + algorithmsLen; + } + + @Override + void send(HandshakeOutStream s) throws IOException { + s.putInt16(type.id); + s.putInt16(algorithmsLen + 2); + s.putInt16(algorithmsLen); + + for (SignatureAndHashAlgorithm algorithm : algorithms) { + s.putInt8(algorithm.getHashValue()); // HashAlgorithm + s.putInt8(algorithm.getSignatureValue()); // SignatureAlgorithm + } + } + + @Override + public String toString() { + StringBuffer buffer = new StringBuffer(); + boolean opened = false; + for (SignatureAndHashAlgorithm signAlg : algorithms) { + if (opened) { + buffer.append(", " + signAlg.getAlgorithmName()); + } else { + buffer.append(signAlg.getAlgorithmName()); + opened = true; + } + } + + return "Extension " + type + ", signature_algorithms: " + buffer; + } +}
--- a/jdk/src/share/classes/sun/security/ssl/MAC.java Tue Nov 02 10:07:21 2010 +0000 +++ b/jdk/src/share/classes/sun/security/ssl/MAC.java Tue Nov 02 10:15:06 2010 +0000 @@ -105,6 +105,10 @@ algorithm = tls ? "HmacMD5" : "SslMacMD5"; } else if (macAlg == M_SHA) { algorithm = tls ? "HmacSHA1" : "SslMacSHA1"; + } else if (macAlg == M_SHA256) { + algorithm = "HmacSHA256"; // TLS 1.2+ + } else if (macAlg == M_SHA384) { + algorithm = "HmacSHA384"; // TLS 1.2+ } else { throw new RuntimeException("Unknown Mac " + macAlg); } @@ -204,7 +208,8 @@ * Compute based on either buffer type, either bb.position/limit * or buf/offset/len. */ - private byte[] compute(byte type, ByteBuffer bb, byte[] buf, int offset, int len) { + private byte[] compute(byte type, ByteBuffer bb, byte[] buf, + int offset, int len) { if (macSize == 0) { return nullMAC;
--- a/jdk/src/share/classes/sun/security/ssl/ProtocolList.java Tue Nov 02 10:07:21 2010 +0000 +++ b/jdk/src/share/classes/sun/security/ssl/ProtocolList.java Tue Nov 02 10:15:06 2010 +0000 @@ -181,7 +181,8 @@ if (SunJSSE.isFIPS()) { SUPPORTED = new ProtocolList(new String[] { ProtocolVersion.TLS10.name, - ProtocolVersion.TLS11.name + ProtocolVersion.TLS11.name, + ProtocolVersion.TLS12.name }); SERVER_DEFAULT = SUPPORTED; @@ -193,10 +194,21 @@ ProtocolVersion.SSL20Hello.name, ProtocolVersion.SSL30.name, ProtocolVersion.TLS10.name, - ProtocolVersion.TLS11.name + ProtocolVersion.TLS11.name, + ProtocolVersion.TLS12.name }); SERVER_DEFAULT = SUPPORTED; + + /* + * RFC 5246 says that sending SSLv2 backward-compatible + * hello SHOULD NOT be done any longer. + * + * We are not enabling TLS 1.1/1.2 by default yet on clients + * out of concern for interop with existing + * SSLv3/TLS1.0-only servers. When these versions of TLS + * gain more traction, we'll enable them. + */ CLIENT_DEFAULT = new ProtocolList(new String[] { ProtocolVersion.SSL30.name, ProtocolVersion.TLS10.name
--- a/jdk/src/share/classes/sun/security/ssl/ProtocolVersion.java Tue Nov 02 10:07:21 2010 +0000 +++ b/jdk/src/share/classes/sun/security/ssl/ProtocolVersion.java Tue Nov 02 10:15:06 2010 +0000 @@ -50,6 +50,9 @@ // The limit of maximum protocol version final static int LIMIT_MAX_VALUE = 0xFFFF; + // The limit of minimum protocol version + final static int LIMIT_MIN_VALUE = 0x0000; + // Dummy protocol version value for invalid SSLSession final static ProtocolVersion NONE = new ProtocolVersion(-1, "NONE"); @@ -74,8 +77,8 @@ // minimum version we implement (SSL 3.0) final static ProtocolVersion MIN = FIPS ? TLS10 : SSL30; - // maximum version we implement (TLS 1.1) - final static ProtocolVersion MAX = TLS11; + // maximum version we implement (TLS 1.2) + final static ProtocolVersion MAX = TLS12; // ProtocolVersion to use by default (TLS 1.0) final static ProtocolVersion DEFAULT = TLS10;
--- a/jdk/src/share/classes/sun/security/ssl/RSAClientKeyExchange.java Tue Nov 02 10:07:21 2010 +0000 +++ b/jdk/src/share/classes/sun/security/ssl/RSAClientKeyExchange.java Tue Nov 02 10:15:06 2010 +0000 @@ -100,8 +100,9 @@ } try { - KeyGenerator kg = - JsseJce.getKeyGenerator("SunTlsRsaPremasterSecret"); + String s = ((protocolVersion.v >= ProtocolVersion.TLS12.v) ? + "SunTls12RsaPremasterSecret" : "SunTlsRsaPremasterSecret"); + KeyGenerator kg = JsseJce.getKeyGenerator(s); kg.init(new TlsRsaPremasterSecretParameterSpec(major, minor)); preMaster = kg.generateKey(); @@ -242,8 +243,9 @@ // generate a premaster secret with the specified version number static SecretKey generateDummySecret(ProtocolVersion version) { try { - KeyGenerator kg = - JsseJce.getKeyGenerator("SunTlsRsaPremasterSecret"); + String s = ((version.v >= ProtocolVersion.TLS12.v) ? + "SunTls12RsaPremasterSecret" : "SunTlsRsaPremasterSecret"); + KeyGenerator kg = JsseJce.getKeyGenerator(s); kg.init(new TlsRsaPremasterSecretParameterSpec (version.major, version.minor)); return kg.generateKey();
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/jdk/src/share/classes/sun/security/ssl/SSLAlgorithmConstraints.java Tue Nov 02 10:15:06 2010 +0000 @@ -0,0 +1,469 @@ +/* + * Copyright (c) 2010, 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.security.AlgorithmConstraints; +import java.security.CryptoPrimitive; +import java.security.AlgorithmParameters; + +import javax.net.ssl.*; + +import java.security.Key; + +import java.util.Set; +import java.util.HashSet; + +import sun.security.util.DisabledAlgorithmConstraints; +import sun.security.ssl.CipherSuite.*; + +/** + * Algorithm constraints for disabled algorithms property + * + * See the "jdk.certpath.disabledAlgorithms" specification in java.security + * for the syntax of the disabled algorithm string. + */ +final class SSLAlgorithmConstraints implements AlgorithmConstraints { + private final static AlgorithmConstraints tlsDisabledAlgConstraints = + new TLSDisabledAlgConstraints(); + private final static AlgorithmConstraints x509DisabledAlgConstraints = + new X509DisabledAlgConstraints(); + private AlgorithmConstraints userAlgConstraints = null; + private AlgorithmConstraints peerAlgConstraints = null; + + private boolean enabledX509DisabledAlgConstraints = true; + + SSLAlgorithmConstraints(AlgorithmConstraints algorithmConstraints) { + userAlgConstraints = algorithmConstraints; + } + + SSLAlgorithmConstraints(SSLSocket socket, + boolean withDefaultCertPathConstraints) { + if (socket != null) { + userAlgConstraints = + socket.getSSLParameters().getAlgorithmConstraints(); + } + + if (!withDefaultCertPathConstraints) { + enabledX509DisabledAlgConstraints = false; + } + } + + SSLAlgorithmConstraints(SSLEngine engine, + boolean withDefaultCertPathConstraints) { + if (engine != null) { + userAlgConstraints = + engine.getSSLParameters().getAlgorithmConstraints(); + } + + if (!withDefaultCertPathConstraints) { + enabledX509DisabledAlgConstraints = false; + } + } + + SSLAlgorithmConstraints(SSLSocket socket, String[] supportedAlgorithms, + boolean withDefaultCertPathConstraints) { + if (socket != null) { + userAlgConstraints = + socket.getSSLParameters().getAlgorithmConstraints(); + peerAlgConstraints = + new SupportedSignatureAlgorithmConstraints(supportedAlgorithms); + } + + if (!withDefaultCertPathConstraints) { + enabledX509DisabledAlgConstraints = false; + } + } + + SSLAlgorithmConstraints(SSLEngine engine, String[] supportedAlgorithms, + boolean withDefaultCertPathConstraints) { + if (engine != null) { + userAlgConstraints = + engine.getSSLParameters().getAlgorithmConstraints(); + peerAlgConstraints = + new SupportedSignatureAlgorithmConstraints(supportedAlgorithms); + } + + if (!withDefaultCertPathConstraints) { + enabledX509DisabledAlgConstraints = false; + } + } + + public boolean permits(Set<CryptoPrimitive> primitives, + String algorithm, AlgorithmParameters parameters) { + + boolean permitted = true; + + if (peerAlgConstraints != null) { + permitted = peerAlgConstraints.permits( + primitives, algorithm, parameters); + } + + if (permitted && userAlgConstraints != null) { + permitted = userAlgConstraints.permits( + primitives, algorithm, parameters); + } + + if (permitted) { + permitted = tlsDisabledAlgConstraints.permits( + primitives, algorithm, parameters); + } + + if (permitted && enabledX509DisabledAlgConstraints) { + permitted = x509DisabledAlgConstraints.permits( + primitives, algorithm, parameters); + } + + return permitted; + } + + public boolean permits(Set<CryptoPrimitive> primitives, Key key) { + + boolean permitted = true; + + if (peerAlgConstraints != null) { + permitted = peerAlgConstraints.permits(primitives, key); + } + + if (permitted && userAlgConstraints != null) { + permitted = userAlgConstraints.permits(primitives, key); + } + + if (permitted) { + permitted = tlsDisabledAlgConstraints.permits(primitives, key); + } + + if (permitted && enabledX509DisabledAlgConstraints) { + permitted = x509DisabledAlgConstraints.permits(primitives, key); + } + + return permitted; + } + + public boolean permits(Set<CryptoPrimitive> primitives, + String algorithm, Key key, AlgorithmParameters parameters) { + + boolean permitted = true; + + if (peerAlgConstraints != null) { + permitted = peerAlgConstraints.permits( + primitives, algorithm, key, parameters); + } + + if (permitted && userAlgConstraints != null) { + permitted = userAlgConstraints.permits( + primitives, algorithm, key, parameters); + } + + if (permitted) { + permitted = tlsDisabledAlgConstraints.permits( + primitives, algorithm, key, parameters); + } + + if (permitted && enabledX509DisabledAlgConstraints) { + permitted = x509DisabledAlgConstraints.permits( + primitives, algorithm, key, parameters); + } + + return permitted; + } + + + static private class SupportedSignatureAlgorithmConstraints + implements AlgorithmConstraints { + // supported signature algorithms + private String[] supportedAlgorithms; + + SupportedSignatureAlgorithmConstraints(String[] supportedAlgorithms) { + if (supportedAlgorithms != null) { + this.supportedAlgorithms = supportedAlgorithms.clone(); + } else { + this.supportedAlgorithms = null; + } + } + + public boolean permits(Set<CryptoPrimitive> primitives, + String algorithm, AlgorithmParameters parameters) { + + if (algorithm == null || algorithm.length() == 0) { + throw new IllegalArgumentException( + "No algorithm name specified"); + } + + if (primitives == null || primitives.isEmpty()) { + throw new IllegalArgumentException( + "No cryptographic primitive specified"); + } + + if (supportedAlgorithms == null || + supportedAlgorithms.length == 0) { + return false; + } + + // trim the MGF part: <digest>with<encryption>and<mgf> + int position = algorithm.indexOf("and"); + if (position > 0) { + algorithm = algorithm.substring(0, position); + } + + for (String supportedAlgorithm : supportedAlgorithms) { + if (algorithm.equalsIgnoreCase(supportedAlgorithm)) { + return true; + } + } + + return false; + } + + final public boolean permits(Set<CryptoPrimitive> primitives, Key key) { + return true; + } + + final public boolean permits(Set<CryptoPrimitive> primitives, + String algorithm, Key key, AlgorithmParameters parameters) { + + if (algorithm == null || algorithm.length() == 0) { + throw new IllegalArgumentException( + "No algorithm name specified"); + } + + return permits(primitives, algorithm, parameters); + } + } + + static private class BasicDisabledAlgConstraints + extends DisabledAlgorithmConstraints { + BasicDisabledAlgConstraints(String propertyName) { + super(propertyName); + } + + protected Set<String> decomposes(KeyExchange keyExchange, + boolean forCertPathOnly) { + Set<String> components = new HashSet<String>(); + switch (keyExchange) { + case K_NULL: + if (!forCertPathOnly) { + components.add("NULL"); + } + break; + case K_RSA: + components.add("RSA"); + break; + case K_RSA_EXPORT: + components.add("RSA"); + components.add("RSA_EXPORT"); + break; + case K_DH_RSA: + components.add("RSA"); + components.add("DH"); + components.add("DiffieHellman"); + components.add("DH_RSA"); + break; + case K_DH_DSS: + components.add("DSA"); + components.add("DSS"); + components.add("DH"); + components.add("DiffieHellman"); + components.add("DH_DSS"); + break; + case K_DHE_DSS: + components.add("DSA"); + components.add("DSS"); + components.add("DH"); + components.add("DHE"); + components.add("DiffieHellman"); + components.add("DHE_DSS"); + break; + case K_DHE_RSA: + components.add("RSA"); + components.add("DH"); + components.add("DHE"); + components.add("DiffieHellman"); + components.add("DHE_RSA"); + break; + case K_DH_ANON: + if (!forCertPathOnly) { + components.add("ANON"); + components.add("DH"); + components.add("DiffieHellman"); + components.add("DH_ANON"); + } + break; + case K_ECDH_ECDSA: + components.add("ECDH"); + components.add("ECDSA"); + components.add("ECDH_ECDSA"); + break; + case K_ECDH_RSA: + components.add("ECDH"); + components.add("RSA"); + components.add("ECDH_RSA"); + break; + case K_ECDHE_ECDSA: + components.add("ECDHE"); + components.add("ECDSA"); + components.add("ECDHE_ECDSA"); + break; + case K_ECDHE_RSA: + components.add("ECDHE"); + components.add("RSA"); + components.add("ECDHE_RSA"); + break; + case K_ECDH_ANON: + if (!forCertPathOnly) { + components.add("ECDH"); + components.add("ANON"); + components.add("ECDH_ANON"); + } + break; + case K_KRB5: + if (!forCertPathOnly) { + components.add("KRB5"); + } + break; + case K_KRB5_EXPORT: + if (!forCertPathOnly) { + components.add("KRB5_EXPORT"); + } + break; + default: + // ignore + } + + return components; + } + + protected Set<String> decomposes(BulkCipher bulkCipher) { + Set<String> components = new HashSet<String>(); + + if (bulkCipher.transformation != null) { + components.addAll(super.decomposes(bulkCipher.transformation)); + } + + return components; + } + + protected Set<String> decomposes(MacAlg macAlg) { + Set<String> components = new HashSet<String>(); + + if (macAlg == CipherSuite.M_MD5) { + components.add("MD5"); + components.add("HmacMD5"); + } else if (macAlg == CipherSuite.M_SHA) { + components.add("SHA1"); + components.add("SHA-1"); + components.add("HmacSHA1"); + } else if (macAlg == CipherSuite.M_SHA256) { + components.add("SHA256"); + components.add("SHA-256"); + components.add("HmacSHA256"); + } else if (macAlg == CipherSuite.M_SHA384) { + components.add("SHA384"); + components.add("SHA-384"); + components.add("HmacSHA384"); + } + + return components; + } + } + + static private class TLSDisabledAlgConstraints + extends BasicDisabledAlgConstraints { + + TLSDisabledAlgConstraints() { + super(DisabledAlgorithmConstraints.PROPERTY_TLS_DISABLED_ALGS); + } + + @Override + protected Set<String> decomposes(String algorithm) { + if (algorithm.startsWith("SSL_") || algorithm.startsWith("TLS_")) { + CipherSuite cipherSuite = null; + try { + cipherSuite = CipherSuite.valueOf(algorithm); + } catch (IllegalArgumentException iae) { + // ignore: unknown or unsupported ciphersuite + } + + if (cipherSuite != null) { + Set<String> components = new HashSet<String>(); + + if(cipherSuite.keyExchange != null) { + components.addAll( + decomposes(cipherSuite.keyExchange, false)); + } + + if (cipherSuite.cipher != null) { + components.addAll(decomposes(cipherSuite.cipher)); + } + + if (cipherSuite.macAlg != null) { + components.addAll(decomposes(cipherSuite.macAlg)); + } + + return components; + } + } + + return super.decomposes(algorithm); + } + } + + static private class X509DisabledAlgConstraints + extends BasicDisabledAlgConstraints { + + X509DisabledAlgConstraints() { + super(DisabledAlgorithmConstraints.PROPERTY_CERTPATH_DISABLED_ALGS); + } + + @Override + protected Set<String> decomposes(String algorithm) { + if (algorithm.startsWith("SSL_") || algorithm.startsWith("TLS_")) { + CipherSuite cipherSuite = null; + try { + cipherSuite = CipherSuite.valueOf(algorithm); + } catch (IllegalArgumentException iae) { + // ignore: unknown or unsupported ciphersuite + } + + if (cipherSuite != null) { + Set<String> components = new HashSet<String>(); + + if(cipherSuite.keyExchange != null) { + components.addAll( + decomposes(cipherSuite.keyExchange, true)); + } + + // Certification path algorithm constraints do not apply + // to cipherSuite.cipher and cipherSuite.macAlg. + + return components; + } + } + + return super.decomposes(algorithm); + } + } +} +
--- a/jdk/src/share/classes/sun/security/ssl/SSLContextImpl.java Tue Nov 02 10:07:21 2010 +0000 +++ b/jdk/src/share/classes/sun/security/ssl/SSLContextImpl.java Tue Nov 02 10:15:06 2010 +0000 @@ -1,5 +1,5 @@ /* - * Copyright (c) 1999, 2007, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 1999, 2010, 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 @@ -27,11 +27,15 @@ import java.net.Socket; +import java.util.*; import java.security.*; import java.security.cert.*; +import java.security.cert.Certificate; import javax.net.ssl.*; +import sun.security.provider.certpath.AlgorithmChecker; + public class SSLContextImpl extends SSLContextSpi { private static final Debug debug = Debug.getInstance("ssl"); @@ -82,7 +86,8 @@ if (sr == null) { secureRandom = JsseJce.getSecureRandom(); } else { - if (SunJSSE.isFIPS() && (sr.getProvider() != SunJSSE.cryptoProvider)) { + if (SunJSSE.isFIPS() && + (sr.getProvider() != SunJSSE.cryptoProvider)) { throw new KeyManagementException ("FIPS mode: SecureRandom must be from provider " + SunJSSE.cryptoProvider.getName()); @@ -111,11 +116,18 @@ // We only use the first instance of X509TrustManager passed to us. for (int i = 0; tm != null && i < tm.length; i++) { if (tm[i] instanceof X509TrustManager) { - if (SunJSSE.isFIPS() && !(tm[i] instanceof X509TrustManagerImpl)) { + if (SunJSSE.isFIPS() && + !(tm[i] instanceof X509TrustManagerImpl)) { throw new KeyManagementException ("FIPS mode: only SunJSSE TrustManagers may be used"); } - return (X509TrustManager)tm[i]; + + if (tm[i] instanceof X509ExtendedTrustManager) { + return (X509TrustManager)tm[i]; + } else { + return new AbstractTrustManagerWrapper( + (X509TrustManager)tm[i]); + } } } @@ -153,7 +165,7 @@ "SSLContext.init(): need an " + "X509ExtendedKeyManager for SSLEngine use"); } - return new AbstractWrapper((X509KeyManager)km); + return new AbstractKeyManagerWrapper((X509KeyManager)km); } // nothing found, return a dummy X509ExtendedKeyManager @@ -217,9 +229,179 @@ } + +final class AbstractTrustManagerWrapper extends X509ExtendedTrustManager + implements X509TrustManager { + + private final X509TrustManager tm; + + AbstractTrustManagerWrapper(X509TrustManager tm) { + this.tm = tm; + } + + @Override + public void checkClientTrusted(X509Certificate[] chain, String authType) + throws CertificateException { + tm.checkClientTrusted(chain, authType); + } + + @Override + public void checkServerTrusted(X509Certificate[] chain, String authType) + throws CertificateException { + tm.checkServerTrusted(chain, authType); + } + + @Override + public X509Certificate[] getAcceptedIssuers() { + return tm.getAcceptedIssuers(); + } + + @Override + public void checkClientTrusted(X509Certificate[] chain, String authType, + Socket socket) throws CertificateException { + tm.checkClientTrusted(chain, authType); + checkAdditionalTrust(chain, authType, socket, true); + } + + @Override + public void checkServerTrusted(X509Certificate[] chain, String authType, + Socket socket) throws CertificateException { + tm.checkServerTrusted(chain, authType); + checkAdditionalTrust(chain, authType, socket, false); + } + + @Override + public void checkClientTrusted(X509Certificate[] chain, String authType, + SSLEngine engine) throws CertificateException { + tm.checkClientTrusted(chain, authType); + checkAdditionalTrust(chain, authType, engine, true); + } + + @Override + public void checkServerTrusted(X509Certificate[] chain, String authType, + SSLEngine engine) throws CertificateException { + tm.checkServerTrusted(chain, authType); + checkAdditionalTrust(chain, authType, engine, false); + } + + private void checkAdditionalTrust(X509Certificate[] chain, String authType, + Socket socket, boolean isClient) throws CertificateException { + if (socket != null && socket.isConnected() && + socket instanceof SSLSocket) { + + SSLSocket sslSocket = (SSLSocket)socket; + SSLSession session = sslSocket.getHandshakeSession(); + if (session == null) { + throw new CertificateException("No handshake session"); + } + + // check endpoint identity + String identityAlg = sslSocket.getSSLParameters(). + getEndpointIdentificationAlgorithm(); + if (identityAlg != null && identityAlg.length() != 0) { + String hostname = session.getPeerHost(); + X509TrustManagerImpl.checkIdentity( + hostname, chain[0], identityAlg); + } + + // try the best to check the algorithm constraints + ProtocolVersion protocolVersion = + ProtocolVersion.valueOf(session.getProtocol()); + AlgorithmConstraints constraints = null; + if (protocolVersion.v >= ProtocolVersion.TLS12.v) { + if (session instanceof ExtendedSSLSession) { + ExtendedSSLSession extSession = + (ExtendedSSLSession)session; + String[] peerSupportedSignAlgs = + extSession.getLocalSupportedSignatureAlgorithms(); + + constraints = new SSLAlgorithmConstraints( + sslSocket, peerSupportedSignAlgs, true); + } else { + constraints = + new SSLAlgorithmConstraints(sslSocket, true); + } + } else { + constraints = new SSLAlgorithmConstraints(sslSocket, true); + } + + AlgorithmChecker checker = new AlgorithmChecker(constraints); + try { + checker.init(false); + + // a forward checker, need to check from trust to target + for (int i = chain.length - 1; i >= 0; i--) { + Certificate cert = chain[i]; + // We don't care about the unresolved critical extensions. + checker.check(cert, Collections.<String>emptySet()); + } + } catch (CertPathValidatorException cpve) { + throw new CertificateException( + "Certificates does not conform to algorithm constraints"); + } + } + } + + private void checkAdditionalTrust(X509Certificate[] chain, String authType, + SSLEngine engine, boolean isClient) throws CertificateException { + if (engine != null) { + SSLSession session = engine.getHandshakeSession(); + if (session == null) { + throw new CertificateException("No handshake session"); + } + + // check endpoint identity + String identityAlg = engine.getSSLParameters(). + getEndpointIdentificationAlgorithm(); + if (identityAlg != null && identityAlg.length() != 0) { + String hostname = session.getPeerHost(); + X509TrustManagerImpl.checkIdentity( + hostname, chain[0], identityAlg); + } + + // try the best to check the algorithm constraints + ProtocolVersion protocolVersion = + ProtocolVersion.valueOf(session.getProtocol()); + AlgorithmConstraints constraints = null; + if (protocolVersion.v >= ProtocolVersion.TLS12.v) { + if (session instanceof ExtendedSSLSession) { + ExtendedSSLSession extSession = + (ExtendedSSLSession)session; + String[] peerSupportedSignAlgs = + extSession.getLocalSupportedSignatureAlgorithms(); + + constraints = new SSLAlgorithmConstraints( + engine, peerSupportedSignAlgs, true); + } else { + constraints = + new SSLAlgorithmConstraints(engine, true); + } + } else { + constraints = new SSLAlgorithmConstraints(engine, true); + } + + AlgorithmChecker checker = new AlgorithmChecker(constraints); + try { + checker.init(false); + + // A forward checker, need to check from trust to target + for (int i = chain.length - 1; i >= 0; i--) { + Certificate cert = chain[i]; + // We don't care about the unresolved critical extensions. + checker.check(cert, Collections.<String>emptySet()); + } + } catch (CertPathValidatorException cpve) { + throw new CertificateException( + "Certificates does not conform to algorithm constraints"); + } + } + } +} + // Dummy X509TrustManager implementation, rejects all peer certificates. // Used if the application did not specify a proper X509TrustManager. -final class DummyX509TrustManager implements X509TrustManager { +final class DummyX509TrustManager extends X509ExtendedTrustManager + implements X509TrustManager { static final X509TrustManager INSTANCE = new DummyX509TrustManager(); @@ -234,6 +416,7 @@ * validated and is trusted for client SSL authentication. * If not, it throws an exception. */ + @Override public void checkClientTrusted(X509Certificate[] chain, String authType) throws CertificateException { throw new CertificateException( @@ -247,6 +430,7 @@ * validated and is trusted for server SSL authentication. * If not, it throws an exception. */ + @Override public void checkServerTrusted(X509Certificate[] chain, String authType) throws CertificateException { throw new CertificateException( @@ -257,19 +441,48 @@ * Return an array of issuer certificates which are trusted * for authenticating peers. */ + @Override public X509Certificate[] getAcceptedIssuers() { return new X509Certificate[0]; } + + @Override + public void checkClientTrusted(X509Certificate[] chain, String authType, + Socket socket) throws CertificateException { + throw new CertificateException( + "No X509TrustManager implementation available"); + } + + @Override + public void checkServerTrusted(X509Certificate[] chain, String authType, + Socket socket) throws CertificateException { + throw new CertificateException( + "No X509TrustManager implementation available"); + } + + @Override + public void checkClientTrusted(X509Certificate[] chain, String authType, + SSLEngine engine) throws CertificateException { + throw new CertificateException( + "No X509TrustManager implementation available"); + } + + @Override + public void checkServerTrusted(X509Certificate[] chain, String authType, + SSLEngine engine) throws CertificateException { + throw new CertificateException( + "No X509TrustManager implementation available"); + } } /* * A wrapper class to turn a X509KeyManager into an X509ExtendedKeyManager */ -final class AbstractWrapper extends X509ExtendedKeyManager { +final class AbstractKeyManagerWrapper extends X509ExtendedKeyManager { private final X509KeyManager km; - AbstractWrapper(X509KeyManager km) { + AbstractKeyManagerWrapper(X509KeyManager km) { this.km = km; }
--- a/jdk/src/share/classes/sun/security/ssl/SSLEngineImpl.java Tue Nov 02 10:07:21 2010 +0000 +++ b/jdk/src/share/classes/sun/security/ssl/SSLEngineImpl.java Tue Nov 02 10:15:06 2010 +0000 @@ -200,8 +200,10 @@ * is associated with a session at the same time. (TLS/IETF may * change that to add client authentication w/o new key exchg.) */ - private SSLSessionImpl sess; - private Handshaker handshaker; + private Handshaker handshaker; + private SSLSessionImpl sess; + private volatile SSLSessionImpl handshakeSession; + /* * Client authentication be off, requested, or required. @@ -248,9 +250,11 @@ // The cipher suites enabled for use on this connection. private CipherSuiteList enabledCipherSuites; - // hostname identification algorithm, the hostname identification is - // disabled by default. - private String identificationAlg = null; + // the endpoint identification protocol + private String identificationProtocol = null; + + // The cryptographic algorithm constraints + private AlgorithmConstraints algorithmConstraints = null; // Have we been told whether we're client or server? private boolean serverModeSet = false; @@ -344,6 +348,7 @@ sslContext = ctx; sess = SSLSessionImpl.nullSession; + handshakeSession = null; /* * State is cs_START until we initialize the handshaker. @@ -1023,6 +1028,7 @@ serverVerifyData = handshaker.getServerVerifyData(); sess = handshaker.getSession(); + handshakeSession = null; if (!writer.hasOutboundData()) { hsStatus = HandshakeStatus.FINISHED; } @@ -1528,6 +1534,15 @@ return sess; } + @Override + synchronized public SSLSession getHandshakeSession() { + return handshakeSession; + } + + synchronized void setHandshakeSession(SSLSessionImpl session) { + handshakeSession = session; + } + /** * Returns a delegated <code>Runnable</code> task for * this <code>SSLEngine</code>. @@ -1629,6 +1644,9 @@ inboundDone = true; sess.invalidate(); + if (handshakeSession != null) { + handshakeSession.invalidate(); + } /* * If we haven't even started handshaking yet, no need @@ -1971,7 +1989,7 @@ /** * Returns the protocols that are supported by this implementation. * A subset of the supported protocols may be enabled for this connection - * @ returns an array of protocol names. + * @return an array of protocol names. */ public String[] getSupportedProtocols() { return ProtocolList.getSupported().toStringArray(); @@ -1998,28 +2016,31 @@ } /** - * Try to configure the endpoint identification algorithm of the engine. - * - * @param identificationAlgorithm the algorithm used to check the - * endpoint identity. - * @return true if the identification algorithm configuration success. + * Returns the SSLParameters in effect for this SSLEngine. */ - synchronized public boolean trySetHostnameVerification( - String identificationAlgorithm) { - if (sslContext.getX509TrustManager() instanceof - X509ExtendedTrustManager) { - this.identificationAlg = identificationAlgorithm; - return true; - } else { - return false; - } + synchronized public SSLParameters getSSLParameters() { + SSLParameters params = super.getSSLParameters(); + + // the super implementation does not handle the following parameters + params.setEndpointIdentificationAlgorithm(identificationProtocol); + params.setAlgorithmConstraints(algorithmConstraints); + + return params; } /** - * Returns the endpoint identification algorithm of the engine. + * Applies SSLParameters to this engine. */ - synchronized public String getHostnameVerification() { - return identificationAlg; + synchronized public void setSSLParameters(SSLParameters params) { + super.setSSLParameters(params); + + // the super implementation does not handle the following parameters + identificationProtocol = params.getEndpointIdentificationAlgorithm(); + algorithmConstraints = params.getAlgorithmConstraints(); + if ((handshaker != null) && !handshaker.started()) { + handshaker.setIdentificationProtocol(identificationProtocol); + handshaker.setAlgorithmConstraints(algorithmConstraints); + } } /**
--- a/jdk/src/share/classes/sun/security/ssl/SSLServerSocketImpl.java Tue Nov 02 10:07:21 2010 +0000 +++ b/jdk/src/share/classes/sun/security/ssl/SSLServerSocketImpl.java Tue Nov 02 10:15:06 2010 +0000 @@ -31,11 +31,14 @@ import java.net.Socket; import java.net.ServerSocket; +import java.security.AlgorithmConstraints; + import java.util.*; import javax.net.ServerSocketFactory; import javax.net.ssl.SSLException; import javax.net.ssl.SSLServerSocket; +import javax.net.ssl.SSLParameters; /** @@ -83,6 +86,12 @@ /* could enabledCipherSuites ever complete handshaking? */ private boolean checkedEnabled = false; + // the endpoint identification protocol to use by default + private String identificationProtocol = null; + + // The cryptographic algorithm constraints + private AlgorithmConstraints algorithmConstraints = null; + /** * Create an SSL server socket on a port, using a non-default * authentication context and a specified connection backlog. @@ -273,6 +282,30 @@ } /** + * Returns the SSLParameters in effect for newly accepted connections. + */ + synchronized public SSLParameters getSSLParameters() { + SSLParameters params = super.getSSLParameters(); + + // the super implementation does not handle the following parameters + params.setEndpointIdentificationAlgorithm(identificationProtocol); + params.setAlgorithmConstraints(algorithmConstraints); + + return params; + } + + /** + * Applies SSLParameters to newly accepted connections. + */ + synchronized public void setSSLParameters(SSLParameters params) { + super.setSSLParameters(params); + + // the super implementation does not handle the following parameters + identificationProtocol = params.getEndpointIdentificationAlgorithm(); + algorithmConstraints = params.getAlgorithmConstraints(); + } + + /** * Accept a new SSL connection. This server identifies itself with * information provided in the authentication context which was * presented during construction. @@ -280,7 +313,7 @@ public Socket accept() throws IOException { SSLSocketImpl s = new SSLSocketImpl(sslContext, useServerMode, enabledCipherSuites, doClientAuth, enableSessionCreation, - enabledProtocols); + enabledProtocols, identificationProtocol, algorithmConstraints); implAccept(s); s.doneConnect();
--- a/jdk/src/share/classes/sun/security/ssl/SSLSessionImpl.java Tue Nov 02 10:07:21 2010 +0000 +++ b/jdk/src/share/classes/sun/security/ssl/SSLSessionImpl.java Tue Nov 02 10:15:06 2010 +0000 @@ -1,5 +1,5 @@ /* - * Copyright (c) 1996, 2008, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 1996, 2010, 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 @@ -31,6 +31,8 @@ import java.util.Enumeration; import java.util.Hashtable; import java.util.Vector; +import java.util.Arrays; +import java.util.Collection; import java.security.Principal; import java.security.PrivateKey; @@ -47,6 +49,8 @@ import javax.net.ssl.SSLPeerUnverifiedException; import javax.net.ssl.SSLSession; import javax.net.ssl.SSLPermission; +import javax.net.ssl.SSLException; +import javax.net.ssl.ExtendedSSLSession; import javax.security.auth.x500.X500Principal; @@ -71,7 +75,7 @@ * * @author David Brownell */ -final class SSLSessionImpl implements SSLSession { +final class SSLSessionImpl extends ExtendedSSLSession { /* * we only really need a single null session @@ -89,7 +93,7 @@ private final SessionId sessionId; private X509Certificate[] peerCerts; private byte compressionMethod; - private final CipherSuite cipherSuite; + private CipherSuite cipherSuite; private SecretKey masterSecret; /* @@ -105,6 +109,8 @@ private boolean invalidated; private X509Certificate[] localCerts; private PrivateKey localPrivateKey; + private String[] localSupportedSignAlgs; + private String[] peerSupportedSignAlgs; // Principals for non-certificate based cipher suites private Principal peerPrincipal; @@ -132,8 +138,8 @@ * first opened and before handshaking begins. */ private SSLSessionImpl() { - this(ProtocolVersion.NONE, CipherSuite.C_NULL, - new SessionId(false, null), null, -1); + this(ProtocolVersion.NONE, CipherSuite.C_NULL, null, + new SessionId(false, null), null, -1); } /* @@ -142,8 +148,9 @@ * is intended mostly for use by serves. */ SSLSessionImpl(ProtocolVersion protocolVersion, CipherSuite cipherSuite, + Collection<SignatureAndHashAlgorithm> algorithms, SecureRandom generator, String host, int port) { - this(protocolVersion, cipherSuite, + this(protocolVersion, cipherSuite, algorithms, new SessionId(defaultRejoinable, generator), host, port); } @@ -151,6 +158,7 @@ * Record a new session, using a given cipher spec and session ID. */ SSLSessionImpl(ProtocolVersion protocolVersion, CipherSuite cipherSuite, + Collection<SignatureAndHashAlgorithm> algorithms, SessionId id, String host, int port) { this.protocolVersion = protocolVersion; sessionId = id; @@ -161,9 +169,11 @@ this.host = host; this.port = port; sessionCount = ++counter; + localSupportedSignAlgs = + SignatureAndHashAlgorithm.getAlgorithmNames(algorithms); if (debug != null && Debug.isOn("session")) { - System.out.println("%% Created: " + this); + System.out.println("%% Initialized: " + this); } } @@ -196,6 +206,12 @@ localPrivateKey = privateKey; } + void setPeerSupportedSignatureAlgorithms( + Collection<SignatureAndHashAlgorithm> algorithms) { + peerSupportedSignAlgs = + SignatureAndHashAlgorithm.getAlgorithmNames(algorithms); + } + /** * Set the peer principal. */ @@ -293,6 +309,17 @@ } /** + * Resets the cipher spec in use on this session + */ + void setSuite(CipherSuite suite) { + cipherSuite = suite; + + if (debug != null && Debug.isOn("session")) { + System.out.println("%% Negotiating: " + this); + } + } + + /** * Returns the name of the cipher suite in use on this session */ public String getCipherSuite() { @@ -718,6 +745,30 @@ return getPacketBufferSize() - Record.headerSize; } + /** + * Gets an array of supported signature algorithms that the local side is + * willing to verify. + */ + public String[] getLocalSupportedSignatureAlgorithms() { + if (localSupportedSignAlgs != null) { + return localSupportedSignAlgs.clone(); + } + + return new String[0]; + } + + /** + * Gets an array of supported signature algorithms that the peer is + * able to verify. + */ + public String[] getPeerSupportedSignatureAlgorithms() { + if (peerSupportedSignAlgs != null) { + return peerSupportedSignAlgs.clone(); + } + + return new String[0]; + } + /** Returns a string representation of this SSL session */ public String toString() { return "[Session-" + sessionCount
--- a/jdk/src/share/classes/sun/security/ssl/SSLSocketImpl.java Tue Nov 02 10:07:21 2010 +0000 +++ b/jdk/src/share/classes/sun/security/ssl/SSLSocketImpl.java Tue Nov 02 10:15:06 2010 +0000 @@ -32,6 +32,7 @@ import java.security.AccessController; import java.security.AccessControlContext; import java.security.PrivilegedAction; +import java.security.AlgorithmConstraints; import java.util.*; import java.util.concurrent.TimeUnit; import java.util.concurrent.locks.ReentrantLock; @@ -199,12 +200,22 @@ private boolean autoClose = true; private AccessControlContext acc; + /* + * We cannot use the hostname resolved from name services. For + * virtual hosting, multiple hostnames may be bound to the same IP + * address, so the hostname resolved from name services is not + * reliable. + */ + private String rawHostname; + // The cipher suites enabled for use on this connection. private CipherSuiteList enabledCipherSuites; - // hostname identification algorithm, the hostname identification is - // disabled by default. - private String identificationAlg = null; + // The endpoint identification protocol + private String identificationProtocol = null; + + // The cryptographic algorithm constraints + private AlgorithmConstraints algorithmConstraints = null; /* * READ ME * READ ME * READ ME * READ ME * READ ME * READ ME * @@ -314,8 +325,9 @@ * is associated with a session at the same time. (TLS/IETF may * change that to add client authentication w/o new key exchg.) */ - private SSLSessionImpl sess; - private Handshaker handshaker; + private Handshaker handshaker; + private SSLSessionImpl sess; + private volatile SSLSessionImpl handshakeSession; /* @@ -376,6 +388,7 @@ throws IOException, UnknownHostException { super(); this.host = host; + this.rawHostname = host; init(context, false); SocketAddress socketAddress = host != null ? new InetSocketAddress(host, port) : @@ -418,6 +431,7 @@ throws IOException, UnknownHostException { super(); this.host = host; + this.rawHostname = host; init(context, false); bind(new InetSocketAddress(localAddr, localPort)); SocketAddress socketAddress = @@ -457,11 +471,15 @@ */ SSLSocketImpl(SSLContextImpl context, boolean serverMode, CipherSuiteList suites, byte clientAuth, - boolean sessionCreation, ProtocolList protocols) - throws IOException { + boolean sessionCreation, ProtocolList protocols, + String identificationProtocol, + AlgorithmConstraints algorithmConstraints) throws IOException { + super(); doClientAuth = clientAuth; enableSessionCreation = sessionCreation; + this.identificationProtocol = identificationProtocol; + this.algorithmConstraints = algorithmConstraints; init(context, serverMode); /* @@ -508,6 +526,7 @@ throw new SocketException("Underlying socket is not connected"); } this.host = host; + this.rawHostname = host; init(context, false); this.autoClose = autoClose; doneConnect(); @@ -519,6 +538,7 @@ private void init(SSLContextImpl context, boolean isServer) { sslContext = context; sess = SSLSessionImpl.nullSession; + handshakeSession = null; /* * role is as specified, state is START until after @@ -957,6 +977,7 @@ serverVerifyData = handshaker.getServerVerifyData(); sess = handshaker.getSession(); + handshakeSession = null; handshaker = null; connectionState = cs_DATA; @@ -1732,6 +1753,9 @@ input.r.close(); } sess.invalidate(); + if (handshakeSession != null) { + handshakeSession.invalidate(); + } int oldState = connectionState; connectionState = cs_ERROR; @@ -1972,9 +1996,14 @@ return host; } + synchronized String getRawHostname() { + return rawHostname; + } + // ONLY used by HttpsClient to setup the URI specified hostname synchronized public void setHost(String host) { this.host = host; + this.rawHostname = host; } /** @@ -2045,6 +2074,15 @@ } } + @Override + synchronized public SSLSession getHandshakeSession() { + return handshakeSession; + } + + synchronized void setHandshakeSession(SSLSessionImpl session) { + handshakeSession = session; + } + /** * Controls whether new connections may cause creation of new SSL * sessions. @@ -2230,7 +2268,7 @@ /** * Returns the protocols that are supported by this implementation. * A subset of the supported protocols may be enabled for this connection - * @ returns an array of protocol names. + * @return an array of protocol names. */ public String[] getSupportedProtocols() { return ProtocolList.getSupported().toStringArray(); @@ -2306,28 +2344,31 @@ } /** - * Try to configure the endpoint identification algorithm of the socket. - * - * @param identificationAlgorithm the algorithm used to check the - * endpoint identity. - * @return true if the identification algorithm configuration success. + * Returns the SSLParameters in effect for this SSLSocket. */ - synchronized public boolean trySetHostnameVerification( - String identificationAlgorithm) { - if (sslContext.getX509TrustManager() instanceof - X509ExtendedTrustManager) { - this.identificationAlg = identificationAlgorithm; - return true; - } else { - return false; - } + synchronized public SSLParameters getSSLParameters() { + SSLParameters params = super.getSSLParameters(); + + // the super implementation does not handle the following parameters + params.setEndpointIdentificationAlgorithm(identificationProtocol); + params.setAlgorithmConstraints(algorithmConstraints); + + return params; } /** - * Returns the endpoint identification algorithm of the socket. + * Applies SSLParameters to this socket. */ - synchronized public String getHostnameVerification() { - return identificationAlg; + synchronized public void setSSLParameters(SSLParameters params) { + super.setSSLParameters(params); + + // the super implementation does not handle the following parameters + identificationProtocol = params.getEndpointIdentificationAlgorithm(); + algorithmConstraints = params.getAlgorithmConstraints(); + if ((handshaker != null) && !handshaker.started()) { + handshaker.setIdentificationProtocol(identificationProtocol); + handshaker.setAlgorithmConstraints(algorithmConstraints); + } } //
--- a/jdk/src/share/classes/sun/security/ssl/ServerHandshaker.java Tue Nov 02 10:07:21 2010 +0000 +++ b/jdk/src/share/classes/sun/security/ssl/ServerHandshaker.java Tue Nov 02 10:15:06 2010 +0000 @@ -40,10 +40,9 @@ import javax.security.auth.Subject; -import com.sun.net.ssl.internal.ssl.X509ExtendedTrustManager; - import sun.security.ssl.HandshakeMessage.*; import sun.security.ssl.CipherSuite.*; +import sun.security.ssl.SignatureAndHashAlgorithm.*; import static sun.security.ssl.CipherSuite.*; import static sun.security.ssl.CipherSuite.KeyExchange.*; @@ -92,6 +91,9 @@ private SupportedEllipticCurvesExtension supportedCurves; + // the preferable signature algorithm used by ServerKeyExchange message + SignatureAndHashAlgorithm preferableSignatureAlgorithm; + /* * Constructor ... use the keys found in the auth context. */ @@ -233,11 +235,13 @@ break; case HandshakeMessage.ht_certificate_verify: - this.clientCertificateVerify(new CertificateVerify(input)); + this.clientCertificateVerify(new CertificateVerify(input, + localSupportedSignAlgs, protocolVersion)); break; case HandshakeMessage.ht_finished: - this.clientFinished(new Finished(protocolVersion, input)); + this.clientFinished( + new Finished(protocolVersion, input, cipherSuite)); break; default: @@ -419,6 +423,9 @@ "Client requested protocol " + clientRequestedVersion + " not enabled or not supported"); } + + handshakeHash.protocolDetermined( + selectedVersion.v >= ProtocolVersion.TLS12.v); setVersion(selectedVersion); m1.protocolVersion = protocolVersion; @@ -562,14 +569,71 @@ if (!enableNewSession) { throw new SSLException("Client did not resume a session"); } + supportedCurves = (SupportedEllipticCurvesExtension) mesg.extensions.get(ExtensionType.EXT_ELLIPTIC_CURVES); + + // We only need to handle the "signature_algorithm" extension + // for full handshakes and TLS 1.2 or later. + if (protocolVersion.v >= ProtocolVersion.TLS12.v) { + SignatureAlgorithmsExtension signAlgs = + (SignatureAlgorithmsExtension)mesg.extensions.get( + ExtensionType.EXT_SIGNATURE_ALGORITHMS); + if (signAlgs != null) { + Collection<SignatureAndHashAlgorithm> peerSignAlgs = + signAlgs.getSignAlgorithms(); + if (peerSignAlgs == null || peerSignAlgs.isEmpty()) { + throw new SSLHandshakeException( + "No peer supported signature algorithms"); + } + + Collection<SignatureAndHashAlgorithm> + supportedPeerSignAlgs = + SignatureAndHashAlgorithm.getSupportedAlgorithms( + peerSignAlgs); + if (supportedPeerSignAlgs.isEmpty()) { + throw new SSLHandshakeException( + "No supported signature and hash algorithm " + + "in common"); + } + + setPeerSupportedSignAlgs(supportedPeerSignAlgs); + } // else, need to use peer implicit supported signature algs + } + + session = new SSLSessionImpl(protocolVersion, CipherSuite.C_NULL, + getLocalSupportedSignAlgs(), + sslContext.getSecureRandom(), + getHostAddressSE(), getPortSE()); + + if (protocolVersion.v >= ProtocolVersion.TLS12.v) { + if (peerSupportedSignAlgs != null) { + session.setPeerSupportedSignatureAlgorithms( + peerSupportedSignAlgs); + } // else, we will set the implicit peer supported signature + // algorithms in chooseCipherSuite() + } + + // set the handshake session + setHandshakeSessionSE(session); + + // choose cipher suite and corresponding private key chooseCipherSuite(mesg); - session = new SSLSessionImpl(protocolVersion, cipherSuite, - sslContext.getSecureRandom(), - getHostAddressSE(), getPortSE()); + + session.setSuite(cipherSuite); session.setLocalPrivateKey(privateKey); + // chooseCompression(mesg); + } else { + // set the handshake session + setHandshakeSessionSE(session); + } + + if (protocolVersion.v >= ProtocolVersion.TLS12.v) { + if (resumingSession) { + handshakeHash.setCertificateVerifyAlg(null); + } + handshakeHash.setFinishedAlg(cipherSuite.prfAlg.getPRFHashAlg()); } m1.cipherSuite = cipherSuite; @@ -689,14 +753,16 @@ privateKey, clnt_random.random_bytes, svr_random.random_bytes, - sslContext.getSecureRandom()); + sslContext.getSecureRandom(), + preferableSignatureAlgorithm, + protocolVersion); } catch (GeneralSecurityException e) { throwSSLException("Error generating DH server key exchange", e); m3 = null; // make compiler happy } break; case K_DH_ANON: - m3 = new DH_ServerKeyExchange(dh); + m3 = new DH_ServerKeyExchange(dh, protocolVersion); break; case K_ECDHE_RSA: case K_ECDHE_ECDSA: @@ -706,9 +772,12 @@ privateKey, clnt_random.random_bytes, svr_random.random_bytes, - sslContext.getSecureRandom()); + sslContext.getSecureRandom(), + preferableSignatureAlgorithm, + protocolVersion); } catch (GeneralSecurityException e) { - throwSSLException("Error generating ECDH server key exchange", e); + throwSSLException( + "Error generating ECDH server key exchange", e); m3 = null; // make compiler happy } break; @@ -737,21 +806,48 @@ // Needed only if server requires client to authenticate self. // Illegal for anonymous flavors, so we need to check that. // - if (keyExchange == K_KRB5 || keyExchange == K_KRB5_EXPORT) { - // CertificateRequest is omitted for Kerberos ciphers + // CertificateRequest is omitted for Kerberos ciphers + if (doClientAuth != SSLEngineImpl.clauth_none && + keyExchange != K_DH_ANON && keyExchange != K_ECDH_ANON && + keyExchange != K_KRB5 && keyExchange != K_KRB5_EXPORT) { - } else if (doClientAuth != SSLEngineImpl.clauth_none && - keyExchange != K_DH_ANON && keyExchange != K_ECDH_ANON) { CertificateRequest m4; X509Certificate caCerts[]; + Collection<SignatureAndHashAlgorithm> localSignAlgs = null; + if (protocolVersion.v >= ProtocolVersion.TLS12.v) { + // We currently use all local upported signature and hash + // algorithms. However, to minimize the computation cost + // of requested hash algorithms, we may use a restricted + // set of signature algorithms in the future. + localSignAlgs = getLocalSupportedSignAlgs(); + if (localSignAlgs.isEmpty()) { + throw new SSLHandshakeException( + "No supported signature algorithm"); + } + + Set<String> localHashAlgs = + SignatureAndHashAlgorithm.getHashAlgorithmNames( + localSignAlgs); + if (localHashAlgs.isEmpty()) { + throw new SSLHandshakeException( + "No supported signature algorithm"); + } + handshakeHash.restrictCertificateVerifyAlgs(localHashAlgs); + } + caCerts = sslContext.getX509TrustManager().getAcceptedIssuers(); - m4 = new CertificateRequest(caCerts, keyExchange); + m4 = new CertificateRequest(caCerts, keyExchange, + localSignAlgs, protocolVersion); if (debug != null && Debug.isOn("handshake")) { m4.print(System.out); } m4.write(output); + } else { + if (protocolVersion.v >= ProtocolVersion.TLS12.v) { + handshakeHash.setCertificateVerifyAlg(null); + } } /* @@ -826,11 +922,16 @@ return false; } - // TLSv1.1 must not negotiate the exportable weak cipher suites. + // must not negotiate the obsoleted weak cipher suites. if (protocolVersion.v >= suite.obsoleted) { return false; } + // must not negotiate unsupported cipher suites. + if (protocolVersion.v < suite.supported) { + return false; + } + KeyExchange keyExchange = suite.keyExchange; // null out any existing references @@ -840,36 +941,136 @@ tempPrivateKey = null; tempPublicKey = null; + Collection<SignatureAndHashAlgorithm> supportedSignAlgs = null; + if (protocolVersion.v >= ProtocolVersion.TLS12.v) { + if (peerSupportedSignAlgs != null) { + supportedSignAlgs = peerSupportedSignAlgs; + } else { + SignatureAndHashAlgorithm algorithm = null; + + // we may optimize the performance + switch (keyExchange) { + // If the negotiated key exchange algorithm is one of + // (RSA, DHE_RSA, DH_RSA, RSA_PSK, ECDH_RSA, ECDHE_RSA), + // behave as if client had sent the value {sha1,rsa}. + case K_RSA: + case K_DHE_RSA: + case K_DH_RSA: + // case K_RSA_PSK: + case K_ECDH_RSA: + case K_ECDHE_RSA: + algorithm = SignatureAndHashAlgorithm.valueOf( + HashAlgorithm.SHA1.value, + SignatureAlgorithm.RSA.value, 0); + break; + // If the negotiated key exchange algorithm is one of + // (DHE_DSS, DH_DSS), behave as if the client had + // sent the value {sha1,dsa}. + case K_DHE_DSS: + case K_DH_DSS: + algorithm = SignatureAndHashAlgorithm.valueOf( + HashAlgorithm.SHA1.value, + SignatureAlgorithm.DSA.value, 0); + break; + // If the negotiated key exchange algorithm is one of + // (ECDH_ECDSA, ECDHE_ECDSA), behave as if the client + // had sent value {sha1,ecdsa}. + case K_ECDH_ECDSA: + case K_ECDHE_ECDSA: + algorithm = SignatureAndHashAlgorithm.valueOf( + HashAlgorithm.SHA1.value, + SignatureAlgorithm.ECDSA.value, 0); + break; + default: + // no peer supported signature algorithms + } + + if (algorithm == null) { + supportedSignAlgs = + Collections.<SignatureAndHashAlgorithm>emptySet(); + } else { + supportedSignAlgs = + new ArrayList<SignatureAndHashAlgorithm>(1); + supportedSignAlgs.add(algorithm); + } + + // Sets the peer supported signature algorithm to use in KM + // temporarily. + session.setPeerSupportedSignatureAlgorithms(supportedSignAlgs); + } + } + switch (keyExchange) { case K_RSA: + // need RSA certs for authentication + if (setupPrivateKeyAndChain("RSA") == false) { + return false; + } + break; case K_RSA_EXPORT: - case K_DHE_RSA: - case K_ECDHE_RSA: // need RSA certs for authentication if (setupPrivateKeyAndChain("RSA") == false) { return false; } - if (keyExchange == K_RSA_EXPORT) { - try { - if (JsseJce.getRSAKeyLength(certs[0].getPublicKey()) > 512) { - if (!setupEphemeralRSAKeys(suite.exportable)) { - return false; - } - } - } catch (RuntimeException e) { - // could not determine keylength, ignore key + try { + if (JsseJce.getRSAKeyLength(certs[0].getPublicKey()) > 512) { + if (!setupEphemeralRSAKeys(suite.exportable)) { + return false; + } + } + } catch (RuntimeException e) { + // could not determine keylength, ignore key + return false; + } + break; + case K_DHE_RSA: + // get preferable peer signature algorithm for server key exchange + if (protocolVersion.v >= ProtocolVersion.TLS12.v) { + preferableSignatureAlgorithm = + SignatureAndHashAlgorithm.getPreferableAlgorithm( + supportedSignAlgs, "RSA"); + if (preferableSignatureAlgorithm == null) { return false; } - } else if (keyExchange == K_DHE_RSA) { - setupEphemeralDHKeys(suite.exportable); - } else if (keyExchange == K_ECDHE_RSA) { - if (setupEphemeralECDHKeys() == false) { + } + + // need RSA certs for authentication + if (setupPrivateKeyAndChain("RSA") == false) { + return false; + } + setupEphemeralDHKeys(suite.exportable); + break; + case K_ECDHE_RSA: + // get preferable peer signature algorithm for server key exchange + if (protocolVersion.v >= ProtocolVersion.TLS12.v) { + preferableSignatureAlgorithm = + SignatureAndHashAlgorithm.getPreferableAlgorithm( + supportedSignAlgs, "RSA"); + if (preferableSignatureAlgorithm == null) { return false; } - } // else nothing more to do for K_RSA + } + + // need RSA certs for authentication + if (setupPrivateKeyAndChain("RSA") == false) { + return false; + } + if (setupEphemeralECDHKeys() == false) { + return false; + } break; case K_DHE_DSS: + // get preferable peer signature algorithm for server key exchange + if (protocolVersion.v >= ProtocolVersion.TLS12.v) { + preferableSignatureAlgorithm = + SignatureAndHashAlgorithm.getPreferableAlgorithm( + supportedSignAlgs, "DSA"); + if (preferableSignatureAlgorithm == null) { + return false; + } + } + // need DSS certs for authentication if (setupPrivateKeyAndChain("DSA") == false) { return false; @@ -877,6 +1078,16 @@ setupEphemeralDHKeys(suite.exportable); break; case K_ECDHE_ECDSA: + // get preferable peer signature algorithm for server key exchange + if (protocolVersion.v >= ProtocolVersion.TLS12.v) { + preferableSignatureAlgorithm = + SignatureAndHashAlgorithm.getPreferableAlgorithm( + supportedSignAlgs, "ECDSA"); + if (preferableSignatureAlgorithm == null) { + return false; + } + } + // need EC cert signed using EC if (setupPrivateKeyAndChain("EC_EC") == false) { return false; @@ -921,6 +1132,14 @@ throw new RuntimeException("Unrecognized cipherSuite: " + suite); } setCipherSuite(suite); + + // set the peer implicit supported signature algorithms + if (protocolVersion.v >= ProtocolVersion.TLS12.v) { + if (peerSupportedSignAlgs == null) { + setPeerSupportedSignAlgs(supportedSignAlgs); + // we had alreay update the session + } + } return true; } @@ -1170,6 +1389,24 @@ mesg.print(System.out); } + if (protocolVersion.v >= ProtocolVersion.TLS12.v) { + SignatureAndHashAlgorithm signAlg = + mesg.getPreferableSignatureAlgorithm(); + if (signAlg == null) { + throw new SSLHandshakeException( + "Illegal CertificateVerify message"); + } + + String hashAlg = + SignatureAndHashAlgorithm.getHashAlgorithmName(signAlg); + if (hashAlg == null || hashAlg.length() == 0) { + throw new SSLHandshakeException( + "No supported hash algorithm"); + } + + handshakeHash.setCertificateVerifyAlg(hashAlg); + } + try { PublicKey publicKey = session.getPeerCertificates()[0].getPublicKey(); @@ -1225,8 +1462,8 @@ * Verify the client's message with the "before" digest of messages, * and forget about continuing to use that digest. */ - boolean verified = mesg.verify(protocolVersion, handshakeHash, - Finished.CLIENT, session.getMasterSecret()); + boolean verified = mesg.verify(handshakeHash, Finished.CLIENT, + session.getMasterSecret()); if (!verified) { fatalSE(Alerts.alert_handshake_failure, @@ -1281,7 +1518,7 @@ output.flush(); Finished mesg = new Finished(protocolVersion, handshakeHash, - Finished.SERVER, session.getMasterSecret()); + Finished.SERVER, session.getMasterSecret(), cipherSuite); /* * Send the change_cipher_spec record; then our Finished handshake @@ -1351,7 +1588,8 @@ * ServerKeyExchange) message that was sent to it by the server. That's * decrypted using the private key before we get here. */ - private SecretKey clientKeyExchange(RSAClientKeyExchange mesg) throws IOException { + private SecretKey clientKeyExchange(RSAClientKeyExchange mesg) + throws IOException { if (debug != null && Debug.isOn("handshake")) { mesg.print(System.out); @@ -1379,6 +1617,11 @@ * not *REQUIRED*, this is an acceptable condition.) */ if (doClientAuth == SSLEngineImpl.clauth_requested) { + // Smart (aka stupid) to forecast that no CertificateVerify + // message will be received. + if (protocolVersion.v >= ProtocolVersion.TLS12.v) { + handshakeHash.setCertificateVerifyAlg(null); + } return; } else { fatalSE(Alerts.alert_bad_certificate, @@ -1405,26 +1648,23 @@ authType = "UNKNOWN"; } - String identificator = getHostnameVerificationSE(); if (tm instanceof X509ExtendedTrustManager) { - ((X509ExtendedTrustManager)tm).checkClientTrusted( - (peerCerts != null ? - peerCerts.clone() : - null), + if (conn != null) { + ((X509ExtendedTrustManager)tm).checkClientTrusted( + peerCerts.clone(), authType, - getHostSE(), - identificator); + conn); + } else { + ((X509ExtendedTrustManager)tm).checkClientTrusted( + peerCerts.clone(), + authType, + engine); + } } else { - if (identificator != null) { - throw new RuntimeException( - "trust manager does not support peer identification"); - } - - tm.checkClientTrusted( - (peerCerts != null ? - peerCerts.clone() : - peerCerts), - authType); + // Unlikely to happen, because we have wrapped the old + // X509TrustManager with the new X509ExtendedTrustManager. + throw new CertificateException( + "Improper X509TrustManager implementation"); } } catch (CertificateException e) { // This will throw an exception, so include the original error.
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/jdk/src/share/classes/sun/security/ssl/SignatureAndHashAlgorithm.java Tue Nov 02 10:15:06 2010 +0000 @@ -0,0 +1,389 @@ +/* + * Copyright (c) 2010, 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.security.AlgorithmConstraints; +import java.security.CryptoPrimitive; + +import java.util.Set; +import java.util.HashSet; +import java.util.Map; +import java.util.EnumSet; +import java.util.TreeMap; +import java.util.Collection; +import java.util.Collections; +import java.util.ArrayList; + +/** + * Signature and hash algorithm. + * + * [RFC5246] The client uses the "signature_algorithms" extension to + * indicate to the server which signature/hash algorithm pairs may be + * used in digital signatures. The "extension_data" field of this + * extension contains a "supported_signature_algorithms" value. + * + * enum { + * none(0), md5(1), sha1(2), sha224(3), sha256(4), sha384(5), + * sha512(6), (255) + * } HashAlgorithm; + * + * enum { anonymous(0), rsa(1), dsa(2), ecdsa(3), (255) } + * SignatureAlgorithm; + * + * struct { + * HashAlgorithm hash; + * SignatureAlgorithm signature; + * } SignatureAndHashAlgorithm; + */ +final class SignatureAndHashAlgorithm { + + // minimum priority for default enabled algorithms + final static int SUPPORTED_ALG_PRIORITY_MAX_NUM = 0x00F0; + + // performance optimization + private final static Set<CryptoPrimitive> SIGNATURE_PRIMITIVE_SET = + EnumSet.of(CryptoPrimitive.SIGNATURE); + + // supported pairs of signature and hash algorithm + private final static Map<Integer, SignatureAndHashAlgorithm> supportedMap; + private final static Map<Integer, SignatureAndHashAlgorithm> priorityMap; + + // the hash algorithm + private HashAlgorithm hash; + + // the signature algorithm + private SignatureAlgorithm signature; + + // id in 16 bit MSB format, i.e. 0x0603 for SHA512withECDSA + private int id; + + // the standard algorithm name, for example "SHA512withECDSA" + private String algorithm; + + // Priority for the preference order. The lower the better. + // + // If the algorithm is unsupported, its priority should be bigger + // than SUPPORTED_ALG_PRIORITY_MAX_NUM. + private int priority; + + // constructor for supported algorithm + private SignatureAndHashAlgorithm(HashAlgorithm hash, + SignatureAlgorithm signature, String algorithm, int priority) { + this.hash = hash; + this.signature = signature; + this.algorithm = algorithm; + this.id = ((hash.value & 0xFF) << 8) | (signature.value & 0xFF); + this.priority = priority; + } + + // constructor for unsupported algorithm + private SignatureAndHashAlgorithm(String algorithm, int id, int sequence) { + this.hash = HashAlgorithm.valueOf((id >> 8) & 0xFF); + this.signature = SignatureAlgorithm.valueOf(id & 0xFF); + this.algorithm = algorithm; + this.id = id; + + // add one more to the sequece number, in case that the number is zero + this.priority = SUPPORTED_ALG_PRIORITY_MAX_NUM + sequence + 1; + } + + // Note that we do not use the sequence argument for supported algorithms, + // so please don't sort by comparing the objects read from handshake + // messages. + static SignatureAndHashAlgorithm valueOf(int hash, + int signature, int sequence) { + hash &= 0xFF; + signature &= 0xFF; + + int id = (hash << 8) | signature; + SignatureAndHashAlgorithm signAlg = supportedMap.get(id); + if (signAlg == null) { + // unsupported algorithm + signAlg = new SignatureAndHashAlgorithm( + "Unknown (hash:0x" + Integer.toString(hash, 16) + + ", signature:0x" + Integer.toString(signature, 16) + ")", + id, sequence); + } + + return signAlg; + } + + int getHashValue() { + return (id >> 8) & 0xFF; + } + + int getSignatureValue() { + return id & 0xFF; + } + + String getAlgorithmName() { + return algorithm; + } + + // return the size of a SignatureAndHashAlgorithm structure in TLS record + static int sizeInRecord() { + return 2; + } + + // Get local supported algorithm collection complying to + // algorithm constraints + static Collection<SignatureAndHashAlgorithm> + getSupportedAlgorithms(AlgorithmConstraints constraints) { + + Collection<SignatureAndHashAlgorithm> supported = + new ArrayList<SignatureAndHashAlgorithm>(); + synchronized (priorityMap) { + for (SignatureAndHashAlgorithm sigAlg : priorityMap.values()) { + if (sigAlg.priority <= SUPPORTED_ALG_PRIORITY_MAX_NUM && + constraints.permits(SIGNATURE_PRIMITIVE_SET, + sigAlg.algorithm, null)) { + supported.add(sigAlg); + } + } + } + + return supported; + } + + // Get supported algorithm collection from an untrusted collection + static Collection<SignatureAndHashAlgorithm> getSupportedAlgorithms( + Collection<SignatureAndHashAlgorithm> algorithms ) { + Collection<SignatureAndHashAlgorithm> supported = + new ArrayList<SignatureAndHashAlgorithm>(); + for (SignatureAndHashAlgorithm sigAlg : algorithms) { + if (sigAlg.priority <= SUPPORTED_ALG_PRIORITY_MAX_NUM) { + supported.add(sigAlg); + } + } + + return supported; + } + + static String[] getAlgorithmNames( + Collection<SignatureAndHashAlgorithm> algorithms) { + ArrayList<String> algorithmNames = new ArrayList<String>(); + if (algorithms != null) { + for (SignatureAndHashAlgorithm sigAlg : algorithms) { + algorithmNames.add(sigAlg.algorithm); + } + } + + String[] array = new String[algorithmNames.size()]; + return algorithmNames.toArray(array); + } + + static Set<String> getHashAlgorithmNames( + Collection<SignatureAndHashAlgorithm> algorithms) { + Set<String> algorithmNames = new HashSet<String>(); + if (algorithms != null) { + for (SignatureAndHashAlgorithm sigAlg : algorithms) { + if (sigAlg.hash.value > 0) { + algorithmNames.add(sigAlg.hash.standardName); + } + } + } + + return algorithmNames; + } + + static String getHashAlgorithmName(SignatureAndHashAlgorithm algorithm) { + return algorithm.hash.standardName; + } + + private static void supports(HashAlgorithm hash, + SignatureAlgorithm signature, String algorithm, int priority) { + + SignatureAndHashAlgorithm pair = + new SignatureAndHashAlgorithm(hash, signature, algorithm, priority); + if (supportedMap.put(pair.id, pair) != null) { + throw new RuntimeException( + "Duplicate SignatureAndHashAlgorithm definition, id: " + + pair.id); + } + if (priorityMap.put(pair.priority, pair) != null) { + throw new RuntimeException( + "Duplicate SignatureAndHashAlgorithm definition, priority: " + + pair.priority); + } + } + + static SignatureAndHashAlgorithm getPreferableAlgorithm( + Collection<SignatureAndHashAlgorithm> algorithms, String expected) { + + if (expected == null && !algorithms.isEmpty()) { + for (SignatureAndHashAlgorithm sigAlg : algorithms) { + if (sigAlg.priority <= SUPPORTED_ALG_PRIORITY_MAX_NUM) { + return sigAlg; + } + } + + return null; // no supported algorithm + } + + + for (SignatureAndHashAlgorithm algorithm : algorithms) { + int signValue = algorithm.id & 0xFF; + if ((expected.equalsIgnoreCase("dsa") && + signValue == SignatureAlgorithm.DSA.value) || + (expected.equalsIgnoreCase("rsa") && + signValue == SignatureAlgorithm.RSA.value) || + (expected.equalsIgnoreCase("ecdsa") && + signValue == SignatureAlgorithm.ECDSA.value) || + (expected.equalsIgnoreCase("ec") && + signValue == SignatureAlgorithm.ECDSA.value)) { + return algorithm; + } + } + + return null; + } + + static enum HashAlgorithm { + UNDEFINED("undefined", "", -1), + NONE( "none", "NONE", 0), + MD5( "md5", "MD5", 1), + SHA1( "sha1", "SHA-1", 2), + SHA224( "sha224", "SHA-224", 3), + SHA256( "sha256", "SHA-256", 4), + SHA384( "sha384", "SHA-384", 5), + SHA512( "sha512", "SHA-512", 6); + + final String name; // not the standard signature algorithm name + // except the UNDEFINED, other names are defined + // by TLS 1.2 protocol + final String standardName; // the standard MessageDigest algorithm name + final int value; + + private HashAlgorithm(String name, String standardName, int value) { + this.name = name; + this.standardName = standardName; + this.value = value; + } + + static HashAlgorithm valueOf(int value) { + HashAlgorithm algorithm = UNDEFINED; + switch (value) { + case 0: + algorithm = NONE; + break; + case 1: + algorithm = MD5; + break; + case 2: + algorithm = SHA1; + break; + case 3: + algorithm = SHA224; + break; + case 4: + algorithm = SHA256; + break; + case 5: + algorithm = SHA384; + break; + case 6: + algorithm = SHA512; + break; + } + + return algorithm; + } + } + + static enum SignatureAlgorithm { + UNDEFINED("undefined", -1), + ANONYMOUS("anonymous", 0), + RSA( "rsa", 1), + DSA( "dsa", 2), + ECDSA( "ecdsa", 3); + + final String name; // not the standard signature algorithm name + // except the UNDEFINED, other names are defined + // by TLS 1.2 protocol + final int value; + + private SignatureAlgorithm(String name, int value) { + this.name = name; + this.value = value; + } + + static SignatureAlgorithm valueOf(int value) { + SignatureAlgorithm algorithm = UNDEFINED; + switch (value) { + case 0: + algorithm = ANONYMOUS; + break; + case 1: + algorithm = RSA; + break; + case 2: + algorithm = DSA; + break; + case 3: + algorithm = ECDSA; + break; + } + + return algorithm; + } + } + + static { + supportedMap = Collections.synchronizedSortedMap( + new TreeMap<Integer, SignatureAndHashAlgorithm>()); + priorityMap = Collections.synchronizedSortedMap( + new TreeMap<Integer, SignatureAndHashAlgorithm>()); + + synchronized (supportedMap) { + int p = SUPPORTED_ALG_PRIORITY_MAX_NUM; + supports(HashAlgorithm.MD5, SignatureAlgorithm.RSA, + "MD5withRSA", --p); + supports(HashAlgorithm.SHA1, SignatureAlgorithm.DSA, + "SHA1withDSA", --p); + supports(HashAlgorithm.SHA1, SignatureAlgorithm.RSA, + "SHA1withRSA", --p); + supports(HashAlgorithm.SHA1, SignatureAlgorithm.ECDSA, + "SHA1withECDSA", --p); + supports(HashAlgorithm.SHA224, SignatureAlgorithm.RSA, + "SHA224withRSA", --p); + supports(HashAlgorithm.SHA224, SignatureAlgorithm.ECDSA, + "SHA224withECDSA", --p); + supports(HashAlgorithm.SHA256, SignatureAlgorithm.RSA, + "SHA256withRSA", --p); + supports(HashAlgorithm.SHA256, SignatureAlgorithm.ECDSA, + "SHA256withECDSA", --p); + supports(HashAlgorithm.SHA384, SignatureAlgorithm.RSA, + "SHA384withRSA", --p); + supports(HashAlgorithm.SHA384, SignatureAlgorithm.ECDSA, + "SHA384withECDSA", --p); + supports(HashAlgorithm.SHA512, SignatureAlgorithm.RSA, + "SHA512withRSA", --p); + supports(HashAlgorithm.SHA512, SignatureAlgorithm.ECDSA, + "SHA512withECDSA", --p); + } + } +} +
--- a/jdk/src/share/classes/sun/security/ssl/SunJSSE.java Tue Nov 02 10:07:21 2010 +0000 +++ b/jdk/src/share/classes/sun/security/ssl/SunJSSE.java Tue Nov 02 10:15:06 2010 +0000 @@ -129,7 +129,8 @@ return t; } - private SunJSSE(java.security.Provider cryptoProvider, String providerName) { + private SunJSSE(java.security.Provider cryptoProvider, + String providerName) { super("SunJSSE", 1.6d, fipsInfo + providerName + ")"); subclassCheck(); if (cryptoProvider == null) { @@ -213,6 +214,8 @@ "sun.security.ssl.SSLContextImpl"); put("SSLContext.TLSv1.1", "sun.security.ssl.SSLContextImpl"); + put("SSLContext.TLSv1.2", + "sun.security.ssl.SSLContextImpl"); put("SSLContext.Default", "sun.security.ssl.DefaultSSLContextImpl");
--- a/jdk/src/share/classes/sun/security/ssl/TrustManagerFactoryImpl.java Tue Nov 02 10:07:21 2010 +0000 +++ b/jdk/src/share/classes/sun/security/ssl/TrustManagerFactoryImpl.java Tue Nov 02 10:15:06 2010 +0000 @@ -1,5 +1,5 @@ /* - * Copyright (c) 1999, 2007, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 1999, 2010, 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
--- a/jdk/src/share/classes/sun/security/ssl/X509KeyManagerImpl.java Tue Nov 02 10:07:21 2010 +0000 +++ b/jdk/src/share/classes/sun/security/ssl/X509KeyManagerImpl.java Tue Nov 02 10:15:06 2010 +0000 @@ -1,5 +1,5 @@ /* - * Copyright (c) 2004, 2007, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2004, 2010, 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 @@ -38,6 +38,8 @@ import javax.net.ssl.*; +import sun.security.provider.certpath.AlgorithmChecker; + /** * The new X509 key manager implementation. The main differences to the * old SunX509 key manager are: @@ -111,36 +113,98 @@ public String chooseClientAlias(String[] keyTypes, Principal[] issuers, Socket socket) { - return chooseAlias(getKeyTypes(keyTypes), issuers, CheckType.CLIENT); + return chooseAlias(getKeyTypes(keyTypes), issuers, CheckType.CLIENT, + getAlgorithmConstraints(socket)); } public String chooseEngineClientAlias(String[] keyTypes, Principal[] issuers, SSLEngine engine) { - return chooseAlias(getKeyTypes(keyTypes), issuers, CheckType.CLIENT); + return chooseAlias(getKeyTypes(keyTypes), issuers, CheckType.CLIENT, + getAlgorithmConstraints(engine)); } public String chooseServerAlias(String keyType, Principal[] issuers, Socket socket) { - return chooseAlias(getKeyTypes(keyType), issuers, CheckType.SERVER); + return chooseAlias(getKeyTypes(keyType), issuers, CheckType.SERVER, + getAlgorithmConstraints(socket)); } public String chooseEngineServerAlias(String keyType, Principal[] issuers, SSLEngine engine) { - return chooseAlias(getKeyTypes(keyType), issuers, CheckType.SERVER); + return chooseAlias(getKeyTypes(keyType), issuers, CheckType.SERVER, + getAlgorithmConstraints(engine)); } public String[] getClientAliases(String keyType, Principal[] issuers) { - return getAliases(keyType, issuers, CheckType.CLIENT); + return getAliases(keyType, issuers, CheckType.CLIENT, null); } public String[] getServerAliases(String keyType, Principal[] issuers) { - return getAliases(keyType, issuers, CheckType.SERVER); + return getAliases(keyType, issuers, CheckType.SERVER, null); } // // implementation private methods // + // Gets algorithm constraints of the socket. + private AlgorithmConstraints getAlgorithmConstraints(Socket socket) { + if (socket != null && socket.isConnected() && + socket instanceof SSLSocket) { + + SSLSocket sslSocket = (SSLSocket)socket; + SSLSession session = sslSocket.getHandshakeSession(); + + if (session != null) { + ProtocolVersion protocolVersion = + ProtocolVersion.valueOf(session.getProtocol()); + if (protocolVersion.v >= ProtocolVersion.TLS12.v) { + String[] peerSupportedSignAlgs = null; + + if (session instanceof ExtendedSSLSession) { + ExtendedSSLSession extSession = + (ExtendedSSLSession)session; + peerSupportedSignAlgs = + extSession.getPeerSupportedSignatureAlgorithms(); + } + + return new SSLAlgorithmConstraints( + sslSocket, peerSupportedSignAlgs, true); + } + } + + return new SSLAlgorithmConstraints(sslSocket, true); + } + + return new SSLAlgorithmConstraints((SSLSocket)null, true); + } + + // Gets algorithm constraints of the engine. + private AlgorithmConstraints getAlgorithmConstraints(SSLEngine engine) { + if (engine != null) { + SSLSession session = engine.getHandshakeSession(); + if (session != null) { + ProtocolVersion protocolVersion = + ProtocolVersion.valueOf(session.getProtocol()); + if (protocolVersion.v >= ProtocolVersion.TLS12.v) { + String[] peerSupportedSignAlgs = null; + + if (session instanceof ExtendedSSLSession) { + ExtendedSSLSession extSession = + (ExtendedSSLSession)session; + peerSupportedSignAlgs = + extSession.getPeerSupportedSignatureAlgorithms(); + } + + return new SSLAlgorithmConstraints( + engine, peerSupportedSignAlgs, true); + } + } + } + + return new SSLAlgorithmConstraints(engine, true); + } + // we construct the alias we return to JSSE as seen in the code below // a unique id is included to allow us to reliably cache entries // between the calls to getCertificateChain() and getPrivateKey() @@ -196,6 +260,13 @@ private static class KeyType { final String keyAlgorithm; + + // In TLS 1.2, the signature algorithm has been obsoleted by the + // supported_signature_algorithms, and the certificate type no longer + // restricts the algorithm used to sign the certificate. + // However, because we don't support certificate type checking other + // than rsa_sign, dss_sign and ecdsa_sign, we don't have to check the + // protocol version here. final String sigKeyAlgorithm; KeyType(String algorithm) { @@ -218,7 +289,8 @@ } if (chain.length > 1) { // if possible, check the public key in the issuer cert - return sigKeyAlgorithm.equals(chain[1].getPublicKey().getAlgorithm()); + return sigKeyAlgorithm.equals( + chain[1].getPublicKey().getAlgorithm()); } else { // Check the signature algorithm of the certificate itself. // Look for the "withRSA" in "SHA1withRSA", etc. @@ -231,7 +303,8 @@ } private static List<KeyType> getKeyTypes(String ... keyTypes) { - if ((keyTypes == null) || (keyTypes.length == 0) || (keyTypes[0] == null)) { + if ((keyTypes == null) || + (keyTypes.length == 0) || (keyTypes[0] == null)) { return null; } List<KeyType> list = new ArrayList<KeyType>(keyTypes.length); @@ -254,8 +327,8 @@ * with appropriate key usage to certs with the wrong key usage. * return the first one of them. */ - private String chooseAlias(List<KeyType> keyTypeList, - Principal[] issuers, CheckType checkType) { + private String chooseAlias(List<KeyType> keyTypeList, Principal[] issuers, + CheckType checkType, AlgorithmConstraints constraints) { if (keyTypeList == null || keyTypeList.size() == 0) { return null; } @@ -264,8 +337,8 @@ List<EntryStatus> allResults = null; for (int i = 0, n = builders.size(); i < n; i++) { try { - List<EntryStatus> results = - getAliases(i, keyTypeList, issuerSet, false, checkType); + List<EntryStatus> results = getAliases(i, keyTypeList, + issuerSet, false, checkType, constraints); if (results != null) { // the results will either be a single perfect match // or 1 or more imperfect matches @@ -308,7 +381,7 @@ * The perfect matches will be first in the array. */ public String[] getAliases(String keyType, Principal[] issuers, - CheckType checkType) { + CheckType checkType, AlgorithmConstraints constraints) { if (keyType == null) { return null; } @@ -318,8 +391,8 @@ List<EntryStatus> allResults = null; for (int i = 0, n = builders.size(); i < n; i++) { try { - List<EntryStatus> results = - getAliases(i, keyTypeList, issuerSet, true, checkType); + List<EntryStatus> results = getAliases(i, keyTypeList, + issuerSet, true, checkType, constraints); if (results != null) { if (allResults == null) { allResults = new ArrayList<EntryStatus>(); @@ -438,7 +511,8 @@ try { // check extended key usage List<String> certEku = cert.getExtendedKeyUsage(); - if ((certEku != null) && Collections.disjoint(validEku, certEku)) { + if ((certEku != null) && + Collections.disjoint(validEku, certEku)) { // if extension present and it does not contain any of // the valid EKU OIDs, return extension_mismatch return CheckResult.EXTENSION_MISMATCH; @@ -534,7 +608,8 @@ */ private List<EntryStatus> getAliases(int builderIndex, List<KeyType> keyTypes, Set<Principal> issuerSet, - boolean findAll, CheckType checkType) throws Exception { + boolean findAll, CheckType checkType, + AlgorithmConstraints constraints) throws Exception { Builder builder = builders.get(builderIndex); KeyStore ks = builder.getKeyStore(); List<EntryStatus> results = null; @@ -552,6 +627,19 @@ // must be secret key entry, ignore continue; } + + boolean incompatible = false; + for (Certificate cert : chain) { + if (cert instanceof X509Certificate == false) { + // not an X509Certificate, ignore this alias + incompatible = true; + break; + } + } + if (incompatible) { + continue; + } + // check keytype int keyIndex = -1; int j = 0; @@ -573,10 +661,6 @@ if (issuerSet != null) { boolean found = false; for (Certificate cert : chain) { - if (cert instanceof X509Certificate == false) { - // not an X509Certificate, ignore this entry - break; - } X509Certificate xcert = (X509Certificate)cert; if (issuerSet.contains(xcert.getIssuerX500Principal())) { found = true; @@ -591,6 +675,19 @@ continue; } } + + // check the algorithm constraints + if (constraints != null && + !conformsToAlgorithmConstraints(constraints, chain)) { + + if (useDebug) { + debug.println("Ignoring alias " + alias + + ": certificate list does not conform to " + + "algorithm constraints"); + } + continue; + } + if (date == null) { date = new Date(); } @@ -616,4 +713,29 @@ return results; } + private static boolean conformsToAlgorithmConstraints( + AlgorithmConstraints constraints, Certificate[] chain) { + + AlgorithmChecker checker = new AlgorithmChecker(constraints); + try { + checker.init(false); + } catch (CertPathValidatorException cpve) { + // unlikely to happen + return false; + } + + // It is a forward checker, so we need to check from trust to target. + for (int i = chain.length - 1; i >= 0; i--) { + Certificate cert = chain[i]; + try { + // We don't care about the unresolved critical extensions. + checker.check(cert, Collections.<String>emptySet()); + } catch (CertPathValidatorException cpve) { + return false; + } + } + + return true; + } + }
--- a/jdk/src/share/classes/sun/security/ssl/X509TrustManagerImpl.java Tue Nov 02 10:07:21 2010 +0000 +++ b/jdk/src/share/classes/sun/security/ssl/X509TrustManagerImpl.java Tue Nov 02 10:15:06 2010 +0000 @@ -1,5 +1,5 @@ /* - * Copyright (c) 1997, 2007, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 1997, 2010, 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 @@ -26,14 +26,15 @@ package sun.security.ssl; +import java.net.Socket; +import javax.net.ssl.SSLSession; + import java.util.*; import java.security.*; import java.security.cert.*; import javax.net.ssl.*; -import com.sun.net.ssl.internal.ssl.X509ExtendedTrustManager; - import sun.security.validator.*; import sun.security.util.HostnameChecker; @@ -41,7 +42,7 @@ /** * This class implements the SunJSSE X.509 trust manager using the internal * validator API in J2SE core. The logic in this class is minimal.<p> - * + * <p> * This class supports both the Simple validation algorithm from previous * JSSE versions and PKIX validation. Currently, it is not possible for the * application to specify PKIX parameters other than trust anchors. This will @@ -50,19 +51,10 @@ * classes. * * @author Andreas Sterbenz - * @author Xuelei Fan */ final class X509TrustManagerImpl extends X509ExtendedTrustManager implements X509TrustManager { - /** - * Flag indicating whether to enable revocation check for the PKIX trust - * manager. Typically, this will only work if the PKIX implementation - * supports CRL distribution points as we do not manually setup CertStores. - */ - private final static boolean checkRevocation = - Debug.getBooleanProperty("com.sun.net.ssl.checkRevocation", false); - private final String validatorType; /** @@ -103,6 +95,199 @@ showTrustedCerts(); } + @Override + public void checkClientTrusted(X509Certificate chain[], String authType) + throws CertificateException { + checkTrusted(chain, authType, (Socket)null, true); + } + + @Override + public void checkServerTrusted(X509Certificate chain[], String authType) + throws CertificateException { + checkTrusted(chain, authType, (Socket)null, false); + } + + @Override + public X509Certificate[] getAcceptedIssuers() { + X509Certificate[] certsArray = new X509Certificate[trustedCerts.size()]; + trustedCerts.toArray(certsArray); + return certsArray; + } + + @Override + public void checkClientTrusted(X509Certificate[] chain, String authType, + Socket socket) throws CertificateException { + checkTrusted(chain, authType, socket, true); + } + + @Override + public void checkServerTrusted(X509Certificate[] chain, String authType, + Socket socket) throws CertificateException { + checkTrusted(chain, authType, socket, false); + } + + @Override + public void checkClientTrusted(X509Certificate[] chain, String authType, + SSLEngine engine) throws CertificateException { + checkTrusted(chain, authType, engine, true); + } + + @Override + public void checkServerTrusted(X509Certificate[] chain, String authType, + SSLEngine engine) throws CertificateException { + checkTrusted(chain, authType, engine, false); + } + + private Validator checkTrustedInit(X509Certificate[] chain, + String authType, boolean isClient) { + if (chain == null || chain.length == 0) { + throw new IllegalArgumentException( + "null or zero-length certificate chain"); + } + + if (authType == null || authType.length() == 0) { + throw new IllegalArgumentException( + "null or zero-length authentication type"); + } + + Validator v = null; + if (isClient) { + v = clientValidator; + if (v == null) { + synchronized (this) { + v = clientValidator; + if (v == null) { + v = getValidator(Validator.VAR_TLS_CLIENT); + clientValidator = v; + } + } + } + } else { + // assume double checked locking with a volatile flag works + // (guaranteed under the new Tiger memory model) + v = serverValidator; + if (v == null) { + synchronized (this) { + v = serverValidator; + if (v == null) { + v = getValidator(Validator.VAR_TLS_SERVER); + serverValidator = v; + } + } + } + } + + return v; + } + + + private void checkTrusted(X509Certificate[] chain, String authType, + Socket socket, boolean isClient) throws CertificateException { + Validator v = checkTrustedInit(chain, authType, isClient); + + AlgorithmConstraints constraints = null; + if ((socket != null) && socket.isConnected() && + (socket instanceof SSLSocket)) { + + SSLSocket sslSocket = (SSLSocket)socket; + SSLSession session = sslSocket.getHandshakeSession(); + if (session == null) { + throw new CertificateException("No handshake session"); + } + + // check endpoint identity + String identityAlg = sslSocket.getSSLParameters(). + getEndpointIdentificationAlgorithm(); + if (identityAlg != null && identityAlg.length() != 0) { + String hostname = session.getPeerHost(); + checkIdentity(hostname, chain[0], identityAlg); + } + + // create the algorithm constraints + ProtocolVersion protocolVersion = + ProtocolVersion.valueOf(session.getProtocol()); + if (protocolVersion.v >= ProtocolVersion.TLS12.v) { + if (session instanceof ExtendedSSLSession) { + ExtendedSSLSession extSession = + (ExtendedSSLSession)session; + String[] localSupportedSignAlgs = + extSession.getLocalSupportedSignatureAlgorithms(); + + constraints = new SSLAlgorithmConstraints( + sslSocket, localSupportedSignAlgs, false); + } else { + constraints = + new SSLAlgorithmConstraints(sslSocket, false); + } + } else { + constraints = new SSLAlgorithmConstraints(sslSocket, false); + } + } + + X509Certificate[] trustedChain = null; + if (isClient) { + trustedChain = validate(v, chain, constraints, null); + } else { + trustedChain = validate(v, chain, constraints, authType); + } + if (debug != null && Debug.isOn("trustmanager")) { + System.out.println("Found trusted certificate:"); + System.out.println(trustedChain[trustedChain.length - 1]); + } + } + + private void checkTrusted(X509Certificate[] chain, String authType, + SSLEngine engine, boolean isClient) throws CertificateException { + Validator v = checkTrustedInit(chain, authType, isClient); + + AlgorithmConstraints constraints = null; + if (engine != null) { + SSLSession session = engine.getHandshakeSession(); + if (session == null) { + throw new CertificateException("No handshake session"); + } + + // check endpoint identity + String identityAlg = engine.getSSLParameters(). + getEndpointIdentificationAlgorithm(); + if (identityAlg != null && identityAlg.length() != 0) { + String hostname = session.getPeerHost(); + checkIdentity(hostname, chain[0], identityAlg); + } + + // create the algorithm constraints + ProtocolVersion protocolVersion = + ProtocolVersion.valueOf(session.getProtocol()); + if (protocolVersion.v >= ProtocolVersion.TLS12.v) { + if (session instanceof ExtendedSSLSession) { + ExtendedSSLSession extSession = + (ExtendedSSLSession)session; + String[] localSupportedSignAlgs = + extSession.getLocalSupportedSignatureAlgorithms(); + + constraints = new SSLAlgorithmConstraints( + engine, localSupportedSignAlgs, false); + } else { + constraints = + new SSLAlgorithmConstraints(engine, false); + } + } else { + constraints = new SSLAlgorithmConstraints(engine, false); + } + } + + X509Certificate[] trustedChain = null; + if (isClient) { + trustedChain = validate(v, chain, constraints, null); + } else { + trustedChain = validate(v, chain, constraints, authType); + } + if (debug != null && Debug.isOn("trustmanager")) { + System.out.println("Found trusted certificate:"); + System.out.println(trustedChain[trustedChain.length - 1]); + } + } + private void showTrustedCerts() { if (debug != null && Debug.isOn("trustmanager")) { for (X509Certificate cert : trustedCerts) { @@ -127,13 +312,6 @@ Validator v; if (pkixParams == null) { v = Validator.getInstance(validatorType, variant, trustedCerts); - // if the PKIX validator is created from a KeyStore, - // disable revocation checking - if (v instanceof PKIXValidator) { - PKIXValidator pkixValidator = (PKIXValidator)v; - pkixValidator.getParameters().setRevocationEnabled - (checkRevocation); - } } else { v = Validator.getInstance(validatorType, variant, pkixParams); } @@ -141,150 +319,35 @@ } private static X509Certificate[] validate(Validator v, - X509Certificate[] chain, String authType) throws CertificateException { + X509Certificate[] chain, AlgorithmConstraints constraints, + String authType) throws CertificateException { Object o = JsseJce.beginFipsProvider(); try { - return v.validate(chain, null, authType); + return v.validate(chain, null, constraints, authType); } finally { JsseJce.endFipsProvider(o); } } - /** - * Returns true if the client certificate can be trusted. + /* + * Identify the peer by its certificate and hostname. * - * @param chain certificates which establish an identity for the client. - * Chains of arbitrary length are supported, and certificates - * marked internally as trusted will short-circuit signature checks. - * @throws IllegalArgumentException if null or zero-length chain - * is passed in for the chain parameter or if null or zero-length - * string is passed in for the authType parameter. - * @throws CertificateException if the certificate chain is not trusted - * by this TrustManager. - */ - public void checkClientTrusted(X509Certificate chain[], String authType) - throws CertificateException { - if (chain == null || chain.length == 0) { - throw new IllegalArgumentException( - "null or zero-length certificate chain"); - } - if (authType == null || authType.length() == 0) { - throw new IllegalArgumentException( - "null or zero-length authentication type"); - } - - // assume double checked locking with a volatile flag works - // (guaranteed under the new Tiger memory model) - Validator v = clientValidator; - if (v == null) { - synchronized (this) { - v = clientValidator; - if (v == null) { - v = getValidator(Validator.VAR_TLS_CLIENT); - clientValidator = v; - } - } - } - X509Certificate[] trustedChain = validate(v, chain, null); - if (debug != null && Debug.isOn("trustmanager")) { - System.out.println("Found trusted certificate:"); - System.out.println(trustedChain[trustedChain.length - 1]); - } - } - - /** - * Returns true if the server certifcate can be trusted. - * - * @param chain certificates which establish an identity for the server. - * Chains of arbitrary length are supported, and certificates - * marked internally as trusted will short-circuit signature checks. - * @throws IllegalArgumentException if null or zero-length chain - * is passed in for the chain parameter or if null or zero-length - * string is passed in for the authType parameter. - * @throws CertificateException if the certificate chain is not trusted - * by this TrustManager. + * Lifted from sun.net.www.protocol.https.HttpsClient. */ - public void checkServerTrusted(X509Certificate chain[], String authType) - throws CertificateException { - if (chain == null || chain.length == 0) { - throw new IllegalArgumentException( - "null or zero-length certificate chain"); - } - if (authType == null || authType.length() == 0) { - throw new IllegalArgumentException( - "null or zero-length authentication type"); - } - - // assume double checked locking with a volatile flag works - // (guaranteed under the new Tiger memory model) - Validator v = serverValidator; - if (v == null) { - synchronized (this) { - v = serverValidator; - if (v == null) { - v = getValidator(Validator.VAR_TLS_SERVER); - serverValidator = v; - } - } - } - X509Certificate[] trustedChain = validate(v, chain, authType); - if (debug != null && Debug.isOn("trustmanager")) { - System.out.println("Found trusted certificate:"); - System.out.println(trustedChain[trustedChain.length - 1]); - } - } - - /** - * Returns a list of CAs accepted to authenticate entities for the - * specified purpose. - * - * @param purpose activity for which CAs should be trusted - * @return list of CAs accepted for authenticating such tasks - */ - public X509Certificate[] getAcceptedIssuers() { - X509Certificate[] certsArray = new X509Certificate[trustedCerts.size()]; - trustedCerts.toArray(certsArray); - return certsArray; - } - - /** - * Given the partial or complete certificate chain provided by the - * peer, check its identity and build a certificate path to a trusted - * root, return if it can be validated and is trusted for client SSL - * authentication based on the authentication type. - */ - public void checkClientTrusted(X509Certificate[] chain, String authType, - String hostname, String algorithm) throws CertificateException { - checkClientTrusted(chain, authType); - checkIdentity(hostname, chain[0], algorithm); - } - - /** - * Given the partial or complete certificate chain provided by the - * peer, check its identity and build a certificate path to a trusted - * root, return if it can be validated and is trusted for server SSL - * authentication based on the authentication type. - */ - public void checkServerTrusted(X509Certificate[] chain, String authType, - String hostname, String algorithm) throws CertificateException { - checkServerTrusted(chain, authType); - checkIdentity(hostname, chain[0], algorithm); - } - - // Identify the peer by its certificate and hostname. - private void checkIdentity(String hostname, X509Certificate cert, - String algorithm) throws CertificateException { + static void checkIdentity(String hostname, X509Certificate cert, + String algorithm) throws CertificateException { if (algorithm != null && algorithm.length() != 0) { // if IPv6 strip off the "[]" - if (hostname != null && hostname.startsWith("[") && - hostname.endsWith("]")) { - hostname = hostname.substring(1, hostname.length()-1); + if ((hostname != null) && hostname.startsWith("[") && + hostname.endsWith("]")) { + hostname = hostname.substring(1, hostname.length() - 1); } if (algorithm.equalsIgnoreCase("HTTPS")) { HostnameChecker.getInstance(HostnameChecker.TYPE_TLS).match( hostname, cert); - } else if (algorithm.equalsIgnoreCase("LDAP")) { + } else if (algorithm.equalsIgnoreCase("LDAP") || + algorithm.equalsIgnoreCase("LDAPS")) { HostnameChecker.getInstance(HostnameChecker.TYPE_LDAP).match( hostname, cert); } else {
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/jdk/src/share/classes/sun/security/util/DisabledAlgorithmConstraints.java Tue Nov 02 10:15:06 2010 +0000 @@ -0,0 +1,486 @@ +/* + * Copyright (c) 2010, 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.util; + +import java.security.AlgorithmConstraints; +import java.security.CryptoPrimitive; +import java.security.AlgorithmParameters; + +import java.security.Key; +import java.security.Security; +import java.security.PrivilegedAction; +import java.security.AccessController; +import java.security.interfaces.ECKey; +import java.security.interfaces.RSAKey; +import java.security.interfaces.DSAKey; +import javax.crypto.SecretKey; +import javax.crypto.interfaces.DHKey; + +import java.util.Locale; +import java.util.Set; +import java.util.Collections; +import java.util.HashSet; +import java.util.Map; +import java.util.HashMap; +import java.util.regex.Pattern; +import java.util.regex.Matcher; + +/** + * Algorithm constraints for disabled algorithms property + * + * See the "jdk.certpath.disabledAlgorithms" specification in java.security + * for the syntax of the disabled algorithm string. + */ +public class DisabledAlgorithmConstraints implements AlgorithmConstraints { + + // the known security property, jdk.certpath.disabledAlgorithms + public final static String PROPERTY_CERTPATH_DISABLED_ALGS = + "jdk.certpath.disabledAlgorithms"; + + // the known security property, jdk.tls.disabledAlgorithms + public final static String PROPERTY_TLS_DISABLED_ALGS = + "jdk.tls.disabledAlgorithms"; + + private static Map<String, String[]> disabledAlgorithmsMap = + Collections.synchronizedMap(new HashMap<String, String[]>()); + private static Map<String, KeySizeConstraints> keySizeConstraintsMap = + Collections.synchronizedMap(new HashMap<String, KeySizeConstraints>()); + + private String[] disabledAlgorithms; + private KeySizeConstraints keySizeConstraints; + + /** + * Initialize algorithm constraints with the specified security property. + * + * @param propertyName the security property name that define the disabled + * algorithm constraints + */ + public DisabledAlgorithmConstraints(String propertyName) { + synchronized (disabledAlgorithmsMap) { + if(!disabledAlgorithmsMap.containsKey(propertyName)) { + loadDisabledAlgorithmsMap(propertyName); + } + + disabledAlgorithms = disabledAlgorithmsMap.get(propertyName); + keySizeConstraints = keySizeConstraintsMap.get(propertyName); + } + } + + @Override + final public boolean permits(Set<CryptoPrimitive> primitives, + String algorithm, AlgorithmParameters parameters) { + + if (algorithm == null || algorithm.length() == 0) { + throw new IllegalArgumentException("No algorithm name specified"); + } + + if (primitives == null || primitives.isEmpty()) { + throw new IllegalArgumentException( + "No cryptographic primitive specified"); + } + + Set<String> elements = null; + for (String disabled : disabledAlgorithms) { + if (disabled == null || disabled.isEmpty()) { + continue; + } + + // check the full name + if (disabled.equalsIgnoreCase(algorithm)) { + return false; + } + + // decompose the algorithm into sub-elements + if (elements == null) { + elements = decomposes(algorithm); + } + + // check the items of the algorithm + for (String element : elements) { + if (disabled.equalsIgnoreCase(element)) { + return false; + } + } + } + + return true; + } + + @Override + final public boolean permits(Set<CryptoPrimitive> primitives, Key key) { + return checkConstraints(primitives, "", key, null); + } + + @Override + final public boolean permits(Set<CryptoPrimitive> primitives, + String algorithm, Key key, AlgorithmParameters parameters) { + + if (algorithm == null || algorithm.length() == 0) { + throw new IllegalArgumentException("No algorithm name specified"); + } + + return checkConstraints(primitives, algorithm, key, parameters); + } + + /** + * Decompose the standard algorithm name into sub-elements. + * <p> + * For example, we need to decompose "SHA1WithRSA" into "SHA1" and "RSA" + * so that we can check the "SHA1" and "RSA" algorithm constraints + * separately. + * <p> + * Please override the method if need to support more name pattern. + */ + protected Set<String> decomposes(String algorithm) { + if (algorithm == null || algorithm.length() == 0) { + return new HashSet<String>(); + } + + // algorithm/mode/padding + Pattern transPattern = Pattern.compile("/"); + String[] transTockens = transPattern.split(algorithm); + + Set<String> elements = new HashSet<String>(); + for (String transTocken : transTockens) { + if (transTocken == null || transTocken.length() == 0) { + continue; + } + + // PBEWith<digest>And<encryption> + // PBEWith<prf>And<encryption> + // OAEPWith<digest>And<mgf>Padding + // <digest>with<encryption> + // <digest>with<encryption>and<mgf> + Pattern pattern = + Pattern.compile("with|and", Pattern.CASE_INSENSITIVE); + String[] tokens = pattern.split(transTocken); + + for (String token : tokens) { + if (token == null || token.length() == 0) { + continue; + } + + elements.add(token); + } + } + + // In Java standard algorithm name specification, for different + // purpose, the SHA-1 and SHA-2 algorithm names are different. For + // example, for MessageDigest, the standard name is "SHA-256", while + // for Signature, the digest algorithm component is "SHA256" for + // signature algorithm "SHA256withRSA". So we need to check both + // "SHA-256" and "SHA256" to make the right constraint checking. + + // handle special name: SHA-1 and SHA1 + if (elements.contains("SHA1") && !elements.contains("SHA-1")) { + elements.add("SHA-1"); + } + if (elements.contains("SHA-1") && !elements.contains("SHA1")) { + elements.add("SHA1"); + } + + // handle special name: SHA-224 and SHA224 + if (elements.contains("SHA224") && !elements.contains("SHA-224")) { + elements.add("SHA-224"); + } + if (elements.contains("SHA-224") && !elements.contains("SHA224")) { + elements.add("SHA224"); + } + + // handle special name: SHA-256 and SHA256 + if (elements.contains("SHA256") && !elements.contains("SHA-256")) { + elements.add("SHA-256"); + } + if (elements.contains("SHA-256") && !elements.contains("SHA256")) { + elements.add("SHA256"); + } + + // handle special name: SHA-384 and SHA384 + if (elements.contains("SHA384") && !elements.contains("SHA-384")) { + elements.add("SHA-384"); + } + if (elements.contains("SHA-384") && !elements.contains("SHA384")) { + elements.add("SHA384"); + } + + // handle special name: SHA-512 and SHA512 + if (elements.contains("SHA512") && !elements.contains("SHA-512")) { + elements.add("SHA-512"); + } + if (elements.contains("SHA-512") && !elements.contains("SHA512")) { + elements.add("SHA512"); + } + + return elements; + } + + // Check algorithm constraints + private boolean checkConstraints(Set<CryptoPrimitive> primitives, + String algorithm, Key key, AlgorithmParameters parameters) { + + // check the key parameter, it cannot be null. + if (key == null) { + throw new IllegalArgumentException("The key cannot be null"); + } + + // check the target algorithm + if (algorithm != null && algorithm.length() != 0) { + if (!permits(primitives, algorithm, parameters)) { + return false; + } + } + + // check the key algorithm + if (!permits(primitives, key.getAlgorithm(), null)) { + return false; + } + + // check the key constraints + if (keySizeConstraints.disables(key)) { + return false; + } + + return true; + } + + // Get disabled algorithm constraints from the specified security property. + private static void loadDisabledAlgorithmsMap( + final String propertyName) { + + String property = AccessController.doPrivileged( + new PrivilegedAction<String>() { + public String run() { + return Security.getProperty(propertyName); + } + }); + + String[] algorithmsInProperty = null; + + if (property != null && !property.isEmpty()) { + + // remove double quote marks from beginning/end of the property + if (property.charAt(0) == '"' && + property.charAt(property.length() - 1) == '"') { + property = property.substring(1, property.length() - 1); + } + + algorithmsInProperty = property.split(","); + for (int i = 0; i < algorithmsInProperty.length; i++) { + algorithmsInProperty[i] = algorithmsInProperty[i].trim(); + } + } + + // map the disabled algorithms + if (algorithmsInProperty == null) { + algorithmsInProperty = new String[0]; + } + disabledAlgorithmsMap.put(propertyName, algorithmsInProperty); + + // map the key constraints + KeySizeConstraints keySizeConstraints = + new KeySizeConstraints(algorithmsInProperty); + keySizeConstraintsMap.put(propertyName, keySizeConstraints); + } + + /** + * key constraints + */ + private static class KeySizeConstraints { + private static final Pattern pattern = Pattern.compile( + "(\\S+)\\s+keySize\\s*(<=|<|==|!=|>|>=)\\s*(\\d+)"); + + private Map<String, Set<KeySizeConstraint>> constraintsMap = + Collections.synchronizedMap( + new HashMap<String, Set<KeySizeConstraint>>()); + + public KeySizeConstraints(String[] restrictions) { + for (String restriction : restrictions) { + if (restriction == null || restriction.isEmpty()) { + continue; + } + + Matcher matcher = pattern.matcher(restriction); + if (matcher.matches()) { + String algorithm = matcher.group(1); + + KeySizeConstraint.Operator operator = + KeySizeConstraint.Operator.of(matcher.group(2)); + int length = Integer.parseInt(matcher.group(3)); + + algorithm = algorithm.toLowerCase(Locale.ENGLISH); + + synchronized (constraintsMap) { + if (!constraintsMap.containsKey(algorithm)) { + constraintsMap.put(algorithm, + new HashSet<KeySizeConstraint>()); + } + + Set<KeySizeConstraint> constraintSet = + constraintsMap.get(algorithm); + KeySizeConstraint constraint = + new KeySizeConstraint(operator, length); + constraintSet.add(constraint); + } + } + } + } + + // Does this KeySizeConstraints disable the specified key? + public boolean disables(Key key) { + String algorithm = key.getAlgorithm().toLowerCase(Locale.ENGLISH); + synchronized (constraintsMap) { + if (constraintsMap.containsKey(algorithm)) { + Set<KeySizeConstraint> constraintSet = + constraintsMap.get(algorithm); + for (KeySizeConstraint constraint : constraintSet) { + if (constraint.disables(key)) { + return true; + } + } + } + } + + return false; + } + } + + /** + * Key size constraint. + * + * e.g. "keysize <= 1024" + */ + private static class KeySizeConstraint { + // operator + static enum Operator { + EQ, // "==" + NE, // "!=" + LT, // "<" + LE, // "<=" + GT, // ">" + GE; // ">=" + + static Operator of(String s) { + switch (s) { + case "==": + return EQ; + case "!=": + return NE; + case "<": + return LT; + case "<=": + return LE; + case ">": + return GT; + case ">=": + return GE; + } + + throw new IllegalArgumentException( + s + " is not a legal Operator"); + } + } + + private int minSize; // the minimal available key size + private int maxSize; // the maximal available key size + private int prohibitedSize = -1; // unavailable key sizes + + public KeySizeConstraint(Operator operator, int length) { + switch (operator) { + case EQ: // an unavailable key size + this.minSize = 0; + this.maxSize = Integer.MAX_VALUE; + prohibitedSize = length; + break; + case NE: + this.minSize = length; + this.maxSize = length; + break; + case LT: + this.minSize = length; + this.maxSize = Integer.MAX_VALUE; + break; + case LE: + this.minSize = length + 1; + this.maxSize = Integer.MAX_VALUE; + break; + case GT: + this.minSize = 0; + this.maxSize = length; + break; + case GE: + this.minSize = 0; + this.maxSize = length > 1 ? (length - 1) : 0; + break; + default: + // unlikely to happen + this.minSize = Integer.MAX_VALUE; + this.maxSize = -1; + } + } + + // Does this key constraint disable the specified key? + public boolean disables(Key key) { + int size = -1; + + // it is a SecretKey + if (key instanceof SecretKey) { + SecretKey sk = (SecretKey)key; + if (sk.getFormat().equals("RAW") && sk.getEncoded() != null) { + size = sk.getEncoded().length * 8; + + } + } + + // it is an asymmetric key + if (key instanceof RSAKey) { + RSAKey pubk = (RSAKey)key; + size = pubk.getModulus().bitLength(); + } else if (key instanceof ECKey) { + ECKey pubk = (ECKey)key; + size = pubk.getParams().getOrder().bitLength(); + } else if (key instanceof DSAKey) { + DSAKey pubk = (DSAKey)key; + size = pubk.getParams().getP().bitLength(); + } else if (key instanceof DHKey) { + DHKey pubk = (DHKey)key; + size = pubk.getParams().getP().bitLength(); + } // else, it is not a key we know. + + if (size == 0) { + return true; // we don't allow any key of size 0. + } + + if (size >= 0) { + return ((size < minSize) || (size > maxSize) || + (prohibitedSize == size)); + } + + return false; + } + } + +} +
--- a/jdk/src/share/classes/sun/security/validator/PKIXValidator.java Tue Nov 02 10:07:21 2010 +0000 +++ b/jdk/src/share/classes/sun/security/validator/PKIXValidator.java Tue Nov 02 10:15:06 2010 +0000 @@ -31,20 +31,35 @@ import java.security.cert.*; import javax.security.auth.x500.X500Principal; +import sun.security.action.GetBooleanAction; +import sun.security.provider.certpath.AlgorithmChecker; /** * Validator implementation built on the PKIX CertPath API. This * implementation will be emphasized going forward.<p> - * + * <p> * Note that the validate() implementation tries to use a PKIX validator * if that appears possible and a PKIX builder otherwise. This increases * performance and currently also leads to better exception messages * in case of failures. + * <p> + * {@code PKIXValidator} objects are immutable once they have been created. + * Please DO NOT add methods that can change the state of an instance once + * it has been created. * * @author Andreas Sterbenz */ public final class PKIXValidator extends Validator { + /** + * Flag indicating whether to enable revocation check for the PKIX trust + * manager. Typically, this will only work if the PKIX implementation + * supports CRL distribution points as we do not manually setup CertStores. + */ + private final static boolean checkTLSRevocation = + AccessController.doPrivileged + (new GetBooleanAction("com.sun.net.ssl.checkRevocation")); + // enable use of the validator if possible private final static boolean TRY_VALIDATOR = true; @@ -53,10 +68,10 @@ private int certPathLength = -1; // needed only for the validator - private Map<X500Principal, List<PublicKey>> trustedSubjects; - private CertificateFactory factory; + private final Map<X500Principal, List<PublicKey>> trustedSubjects; + private final CertificateFactory factory; - private boolean plugin = false; + private final boolean plugin; PKIXValidator(String variant, Collection<X509Certificate> trustedCerts) { super(TYPE_PKIX, variant); @@ -75,7 +90,33 @@ throw new RuntimeException("Unexpected error: " + e.toString(), e); } setDefaultParameters(variant); - initCommon(); + + // initCommon(); + if (TRY_VALIDATOR) { + if (TRY_VALIDATOR == false) { + return; + } + trustedSubjects = new HashMap<X500Principal, List<PublicKey>>(); + for (X509Certificate cert : trustedCerts) { + X500Principal dn = cert.getSubjectX500Principal(); + List<PublicKey> keys; + if (trustedSubjects.containsKey(dn)) { + keys = trustedSubjects.get(dn); + } else { + keys = new ArrayList<PublicKey>(); + trustedSubjects.put(dn, keys); + } + keys.add(cert.getPublicKey()); + } + try { + factory = CertificateFactory.getInstance("X.509"); + } catch (CertificateException e) { + throw new RuntimeException("Internal error", e); + } + plugin = variant.equals(VAR_PLUGIN_CODE_SIGNING); + } else { + plugin = false; + } } PKIXValidator(String variant, PKIXBuilderParameters params) { @@ -88,31 +129,33 @@ } } parameterTemplate = params; - initCommon(); - } - private void initCommon() { - if (TRY_VALIDATOR == false) { - return; + // initCommon(); + if (TRY_VALIDATOR) { + if (TRY_VALIDATOR == false) { + return; + } + trustedSubjects = new HashMap<X500Principal, List<PublicKey>>(); + for (X509Certificate cert : trustedCerts) { + X500Principal dn = cert.getSubjectX500Principal(); + List<PublicKey> keys; + if (trustedSubjects.containsKey(dn)) { + keys = trustedSubjects.get(dn); + } else { + keys = new ArrayList<PublicKey>(); + trustedSubjects.put(dn, keys); + } + keys.add(cert.getPublicKey()); + } + try { + factory = CertificateFactory.getInstance("X.509"); + } catch (CertificateException e) { + throw new RuntimeException("Internal error", e); + } + plugin = variant.equals(VAR_PLUGIN_CODE_SIGNING); + } else { + plugin = false; } - trustedSubjects = new HashMap<X500Principal, List<PublicKey>>(); - for (X509Certificate cert : trustedCerts) { - X500Principal dn = cert.getSubjectX500Principal(); - List<PublicKey> keys; - if (trustedSubjects.containsKey(dn)) { - keys = tr