# HG changeset patch # User xuelei # Date 1425456623 0 # Node ID 5705356edc61d143faac30025006579527da5355 # Parent 4e0df0a960819b9b29f86257ecdc4481a9eace7c 8050371: More MessageDigest tests Reviewed-by: xuelei Contributed-by: Zaiyao Liu diff -r 4e0df0a96081 -r 5705356edc61 jdk/src/java.base/share/classes/sun/security/ssl/ClientHandshaker.java --- a/jdk/src/java.base/share/classes/sun/security/ssl/ClientHandshaker.java Wed Mar 04 10:12:25 2015 +0800 +++ b/jdk/src/java.base/share/classes/sun/security/ssl/ClientHandshaker.java Wed Mar 04 08:10:23 2015 +0000 @@ -59,6 +59,10 @@ */ final class ClientHandshaker extends Handshaker { + // constants for subject alt names of type DNS and IP + private final static int ALTNAME_DNS = 2; + private final static int ALTNAME_IP = 7; + // the server's public key from its certificate. private PublicKey serverKey; @@ -1502,20 +1506,49 @@ return true; } - // check the iPAddress field in subjectAltName extension - Object thisIPAddress = getSubjectAltName(thisCert, 7); // 7: iPAddress - Object prevIPAddress = getSubjectAltName(prevCert, 7); - if (thisIPAddress != null && prevIPAddress!= null) { - // only allow the exactly match - return Objects.equals(thisIPAddress, prevIPAddress); + // check subject alternative names + Collection> thisSubjectAltNames = null; + try { + thisSubjectAltNames = thisCert.getSubjectAlternativeNames(); + } catch (CertificateParsingException cpe) { + if (debug != null && Debug.isOn("handshake")) { + System.out.println( + "Attempt to obtain subjectAltNames extension failed!"); + } + } + + Collection> prevSubjectAltNames = null; + try { + prevSubjectAltNames = prevCert.getSubjectAlternativeNames(); + } catch (CertificateParsingException cpe) { + if (debug != null && Debug.isOn("handshake")) { + System.out.println( + "Attempt to obtain subjectAltNames extension failed!"); + } } - // check the dNSName field in subjectAltName extension - Object thisDNSName = getSubjectAltName(thisCert, 2); // 2: dNSName - Object prevDNSName = getSubjectAltName(prevCert, 2); - if (thisDNSName != null && prevDNSName!= null) { - // only allow the exactly match - return Objects.equals(thisDNSName, prevDNSName); + if ((thisSubjectAltNames != null) && (prevSubjectAltNames != null)) { + // check the iPAddress field in subjectAltName extension + Collection thisSubAltIPAddrs = + getSubjectAltNames(thisSubjectAltNames, ALTNAME_IP); + Collection prevSubAltIPAddrs = + getSubjectAltNames(prevSubjectAltNames, ALTNAME_IP); + if ((thisSubAltIPAddrs != null) && (prevSubAltIPAddrs != null) && + (isEquivalent(thisSubAltIPAddrs, prevSubAltIPAddrs))) { + + return true; + } + + // check the dNSName field in subjectAltName extension + Collection thisSubAltDnsNames = + getSubjectAltNames(thisSubjectAltNames, ALTNAME_DNS); + Collection prevSubAltDnsNames = + getSubjectAltNames(prevSubjectAltNames, ALTNAME_DNS); + if ((thisSubAltDnsNames != null) && (prevSubAltDnsNames != null) && + (isEquivalent(thisSubAltDnsNames, prevSubAltDnsNames))) { + + return true; + } } // check the certificate subject and issuer @@ -1537,28 +1570,39 @@ * Returns the subject alternative name of the specified type in the * subjectAltNames extension of a certificate. */ - private static Object getSubjectAltName(X509Certificate cert, int type) { - Collection> subjectAltNames; + private static Collection getSubjectAltNames( + Collection> subjectAltNames, int type) { - try { - subjectAltNames = cert.getSubjectAlternativeNames(); - } catch (CertificateParsingException cpe) { - if (debug != null && Debug.isOn("handshake")) { - System.out.println( - "Attempt to obtain subjectAltNames extension failed!"); - } - return null; - } - - if (subjectAltNames != null) { - for (List subjectAltName : subjectAltNames) { - int subjectAltNameType = (Integer)subjectAltName.get(0); - if (subjectAltNameType == type) { - return subjectAltName.get(1); + HashSet subAltDnsNames = null; + for (List subjectAltName : subjectAltNames) { + int subjectAltNameType = (Integer)subjectAltName.get(0); + if (subjectAltNameType == type) { + String subAltDnsName = (String)subjectAltName.get(1); + if ((subAltDnsName != null) && !subAltDnsName.isEmpty()) { + if (subAltDnsNames == null) { + subAltDnsNames = + new HashSet<>(subjectAltNames.size()); + } + subAltDnsNames.add(subAltDnsName); } } } - return null; + return subAltDnsNames; + } + + private static boolean isEquivalent(Collection thisSubAltNames, + Collection prevSubAltNames) { + + for (String thisSubAltName : thisSubAltNames) { + for (String prevSubAltName : prevSubAltNames) { + // Only allow the exactly match. Check no wildcard character. + if (thisSubAltName.equalsIgnoreCase(prevSubAltName)) { + return true; + } + } + } + + return false; } } diff -r 4e0df0a96081 -r 5705356edc61 jdk/test/java/security/MessageDigest/TestSameLength.java --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/jdk/test/java/security/MessageDigest/TestSameLength.java Wed Mar 04 08:10:23 2015 +0000 @@ -0,0 +1,143 @@ +/* + * Copyright (c) 2015, 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. + * + * 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. + */ + +import static java.lang.System.out; + +import java.nio.ByteBuffer; +import java.security.MessageDigest; +import java.util.Random; + +/** + * @test + * @bug 8050371 + * @summary Check md.getDigestLength() equal digest output length with various + * algorithm/dataLen/(update,digest methods). + * @author Kevin Liu + */ + +public class TestSameLength { + + public static void main(String[] args) throws Exception { + TestSameLength test = new TestSameLength(); + test.run(); + } + + private void run() throws Exception { + String[] algorithmArr = { + "SHA", "Sha", "SHA-1", "sha-1", "SHA1", "sha1", "MD5", "md5", + "SHA-224", "SHA-256", "SHA-384", "SHA-512" + }; + int[] nUpdatesArr = { + 0, 1, 2, 3 + }; + int[] dataLenArr = { + 1, 50, 2500, 125000, 6250000 + }; + + for (String algorithm: algorithmArr) { + for (UpdateMethod update: UpdateMethod.values()) { + for (int dataLen: dataLenArr) { + if (!runTest(algorithm, dataLen, update)) { + throw new RuntimeException( + "Test failed at algorithm/dataLen/numUpdate:" + + algorithm + "/" + dataLen + "/" + + update.toString()); + } + } + } + } + + out.println("All " + algorithmArr.length * nUpdatesArr.length + * dataLenArr.length + " tests Passed"); + } + + private boolean runTest(String algo, long dataLen, + UpdateMethod whichUpdate) throws Exception { + try { + // Do initialization + byte[] data = new byte[(int) dataLen]; + new Random().nextBytes(data); + MessageDigest md = MessageDigest.getInstance(algo); + int outputLen = md.getDigestLength(); + + // Perform the update using all available/possible update methods + whichUpdate.updateDigest(data, md, dataLen); + // Get the output + byte[] output = md.digest(); + + // Compare input and output + return outputLen == output.length; + } catch (Exception ex) { + System.err.println("Testing: " + algo + "/" + dataLen + "/" + + whichUpdate.toString() + + " failed with unexpected exception"); + ex.printStackTrace(); + throw ex; + } + } + + private static enum UpdateMethod { + UPDATE_BYTE { + @Override + public void updateDigest(byte[] data, + MessageDigest md, long dataLen) { + + for (int i = 0; i < dataLen; i++) { + md.update(data[i]); + } + } + }, + + UPDATE_BUFFER { + @Override + public void updateDigest(byte[] data, + MessageDigest md, long dataLen) { + + md.update(data); + } + }, + + UPDATE_BUFFER_LEN { + @Override + public void updateDigest(byte[] data, + MessageDigest md, long dataLen) { + + for (int i = 0; i < dataLen; i++) { + md.update(data, i, 1); + } + } + }, + + UPDATE_BYTE_BUFFER { + @Override + public void updateDigest(byte[] data, + MessageDigest md, long dataLen) { + + md.update(ByteBuffer.wrap(data)); + } + }; + + public abstract void updateDigest(byte[] data, + MessageDigest md, long dataLen); + } +} diff -r 4e0df0a96081 -r 5705356edc61 jdk/test/java/security/MessageDigest/TestSameValue.java --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/jdk/test/java/security/MessageDigest/TestSameValue.java Wed Mar 04 08:10:23 2015 +0000 @@ -0,0 +1,310 @@ +/* + * Copyright (c) 2015, 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. + * + * 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. + */ + +import static java.lang.System.out; + +import java.nio.ByteBuffer; +import java.security.DigestException; +import java.security.MessageDigest; +import java.util.Random; + +/** + * @test + * @bug 8050371 + * @summary Check md.digest(data) value whether same with digest output value + * with various update/digest methods. + * @author Kevin Liu + */ + +public class TestSameValue { + + public static void main(String[] args) throws Exception { + TestSameValue test1 = new TestSameValue(); + test1.run(); + } + + private void run() throws Exception { + + byte[] data = new byte[6706]; + MessageDigest md = null; + // Initialize input data + new Random().nextBytes(data); + + String[] providers = { + null, "SUN" + }; + String[] algorithmArr = { + "SHA", "Sha", "MD5", "md5", "SHA-224", "SHA-256", "SHA-384", + "SHA-512" + }; + + for (String algorithm: algorithmArr) { + for (String provider: providers) { + if (provider != null) { + md = MessageDigest.getInstance(algorithm, provider); + } else { + md = MessageDigest.getInstance(algorithm); + } + for (UpdateDigestMethod updateMethod: UpdateDigestMethod + .values()) { + byte[] output = updateMethod.updateDigest(data, md); + // Get the output and the "correct" one + byte[] standard = md.digest(data); + // Compare input and output + if (!MessageDigest.isEqual(output, standard)) { + throw new RuntimeException( + "Test failed at algorithm/provider/numUpdate:" + + algorithm + "/" + provider + "/" + + updateMethod); + } + } + } + } + + out.println("All " + algorithmArr.length + * UpdateDigestMethod.values().length * providers.length + + " tests Passed"); + } + + private static enum UpdateDigestMethod { + + /* + * update the data one by one using method update(byte input) then + * do digest (giving the output buffer, offset, and the number of + * bytes to put in the output buffer) + */ + UPDATE_DIGEST_BUFFER { + @Override + public byte[] updateDigest(byte[] data, MessageDigest md) + throws DigestException { + for (byte element: data) { + md.update(element); + } + byte[] output = new byte[md.getDigestLength()]; + int len = md.digest(output, 0, output.length); + if (len != output.length) { + throw new RuntimeException( + "ERROR" + ": digest length differs!"); + } + return output; + } + }, + + /* + * update the data one by one using method update(byte input) + * then do digest + */ + UPDATE_DIGEST { + @Override + public byte[] updateDigest(byte[] data, MessageDigest md) { + for (byte element: data) { + md.update(element); + } + return md.digest(); + } + }, + + /* + * update all the data at once as a block, then do digest ( giving the + * output buffer, offset, and the number of bytes to put in the output + * buffer) + */ + UPDATE_BLOCK_DIGEST_BUFFER { + @Override + public byte[] updateDigest(byte[] data, MessageDigest md) + throws DigestException { + md.update(data); + byte[] output = new byte[md.getDigestLength()]; + int len = md.digest(output, 0, output.length); + if (len != output.length) { + throw new RuntimeException( + "ERROR" + ": digest length differs!"); + } + return output; + } + }, + + // update all the data at once as a block, then do digest + UPDATE_BLOCK_DIGEST { + @Override + public byte[] updateDigest(byte[] data, MessageDigest md) { + md.update(data); + return md.digest(); + } + }, + + /* + * update the leading bytes (length is "data.length-LASTNBYTES") + * at once as a block, then do digest (do a final update using + * the left LASTNBYTES bytes which is passed as a parameter for + * the digest method, then complete the digest) + */ + UPDATE_LEADING_BLOCK_DIGEST_REMAIN { + @Override + public byte[] updateDigest(byte[] data, MessageDigest md) { + byte[] mainPart = new byte[data.length - LASTNBYTES]; + for (int i = 0; i < mainPart.length; i++) { + mainPart[i] = data[i]; + } + for (int j = 0; j < LASTNBYTES; j++) { + REMAIN[j] = data[data.length - LASTNBYTES + j]; + } + md.update(mainPart); + return md.digest(REMAIN); + } + }, + + /* + * update the data 2 bytes each time, after finishing updating, + * do digest (giving the output buffer, offset, and the number + * of bytes to put in the output buffer) + */ + UPDATE_BYTES_DIGEST_BUFFER { + @Override + public byte[] updateDigest(byte[] data, MessageDigest md) + throws DigestException { + + for (int i = 0; i < data.length / 2; i++) { + md.update(data, i * 2, 2); + } + byte[] output = new byte[md.getDigestLength()]; + int len = md.digest(output, 0, output.length); + if (len != output.length) { + throw new RuntimeException( + "ERROR" + ": digest length differs!"); + } + return output; + } + }, + + /* + * update the data 2 bytes each time, after finishing updating, + * do digest + */ + UPDATE_BYTES_DIGEST { + @Override + public byte[] updateDigest(byte[] data, MessageDigest md) { + for (int i=0;i