From 5e986ae42496bf905e67e70ca536cc884cb2d5bf Mon Sep 17 00:00:00 2001 From: Weijun Wang Date: Wed, 21 Oct 2009 08:17:35 +0800 Subject: [PATCH] 6870812: enhance security tools to use ECC algorithms Reviewed-by: vinnie, mullan --- .../share/classes/java/util/jar/JarFile.java | 3 +- .../classes/java/util/jar/JarVerifier.java | 8 +- .../classes/sun/security/jca/Providers.java | 5 +- .../classes/sun/security/pkcs/SignerInfo.java | 13 ++-- .../classes/sun/security/tools/JarSigner.java | 28 +++---- .../classes/sun/security/tools/KeyTool.java | 2 +- .../sun/security/tools/TimestampedSigner.java | 18 ++--- .../security/util/SignatureFileVerifier.java | 11 +-- .../sun/security/x509/AlgorithmId.java | 49 +++++++++++++ .../classes/sun/tools/jar/SignatureFile.java | 8 +- jdk/test/sun/security/tools/jarsigner/ec.sh | 73 +++++++++++++++++++ 11 files changed, 171 insertions(+), 47 deletions(-) create mode 100644 jdk/test/sun/security/tools/jarsigner/ec.sh diff --git a/jdk/src/share/classes/java/util/jar/JarFile.java b/jdk/src/share/classes/java/util/jar/JarFile.java index 309f4ff48a7..79b1be9e6ce 100644 --- a/jdk/src/share/classes/java/util/jar/JarFile.java +++ b/jdk/src/share/classes/java/util/jar/JarFile.java @@ -1,5 +1,5 @@ /* - * Copyright 1997-2006 Sun Microsystems, Inc. All Rights Reserved. + * Copyright 1997-2009 Sun Microsystems, Inc. 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 @@ -297,6 +297,7 @@ class JarFile extends ZipFile { String name = names[i].toUpperCase(Locale.ENGLISH); if (name.endsWith(".DSA") || name.endsWith(".RSA") || + name.endsWith(".EC") || name.endsWith(".SF")) { // Assume since we found a signature-related file // that the jar is signed and that we therefore diff --git a/jdk/src/share/classes/java/util/jar/JarVerifier.java b/jdk/src/share/classes/java/util/jar/JarVerifier.java index 099cf7abbb4..a4ceaa790fd 100644 --- a/jdk/src/share/classes/java/util/jar/JarVerifier.java +++ b/jdk/src/share/classes/java/util/jar/JarVerifier.java @@ -1,5 +1,5 @@ /* - * Copyright 1997-2005 Sun Microsystems, Inc. All Rights Reserved. + * Copyright 1997-2009 Sun Microsystems, Inc. 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 @@ -50,7 +50,7 @@ class JarVerifier { private Hashtable verifiedSigners; /* a table mapping names to code signers, for jar entries that have - passed the .SF/.DSA -> MANIFEST check */ + passed the .SF/.DSA/.EC -> MANIFEST check */ private Hashtable sigFileSigners; /* a hash table to hold .SF bytes */ @@ -111,7 +111,7 @@ class JarVerifier { /* * Assumptions: * 1. The manifest should be the first entry in the META-INF directory. - * 2. The .SF/.DSA files follow the manifest, before any normal entries + * 2. The .SF/.DSA/.EC files follow the manifest, before any normal entries * 3. Any of the following will throw a SecurityException: * a. digest mismatch between a manifest section and * the SF section. @@ -129,7 +129,7 @@ class JarVerifier { } if (SignatureFileVerifier.isBlockOrSF(uname)) { - /* We parse only DSA or RSA PKCS7 blocks. */ + /* We parse only DSA, RSA or EC PKCS7 blocks. */ parsingBlockOrSF = true; baos.reset(); mev.setEntry(null, je); diff --git a/jdk/src/share/classes/sun/security/jca/Providers.java b/jdk/src/share/classes/sun/security/jca/Providers.java index 9a7cd8c72f4..54f865d9972 100644 --- a/jdk/src/share/classes/sun/security/jca/Providers.java +++ b/jdk/src/share/classes/sun/security/jca/Providers.java @@ -1,5 +1,5 @@ /* - * Copyright 2003-2005 Sun Microsystems, Inc. All Rights Reserved. + * Copyright 2003-2009 Sun Microsystems, Inc. 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 @@ -86,6 +86,9 @@ public class Providers { private static final String[] jarVerificationProviders = { "sun.security.provider.Sun", "sun.security.rsa.SunRsaSign", + // Note: SunEC *is* in a signed JAR file, but it's not signed + // by EC itself. So it's still safe to be listed here. + "sun.security.ec.SunEC", BACKUP_PROVIDER_CLASSNAME, }; diff --git a/jdk/src/share/classes/sun/security/pkcs/SignerInfo.java b/jdk/src/share/classes/sun/security/pkcs/SignerInfo.java index 75dd0c26bdb..c0a9db2fc4a 100644 --- a/jdk/src/share/classes/sun/security/pkcs/SignerInfo.java +++ b/jdk/src/share/classes/sun/security/pkcs/SignerInfo.java @@ -1,5 +1,5 @@ /* - * Copyright 1996-2006 Sun Microsystems, Inc. All Rights Reserved. + * Copyright 1996-2009 Sun Microsystems, Inc. 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 @@ -286,8 +286,6 @@ public class SignerInfo implements DerEncoder { } String digestAlgname = getDigestAlgorithmId().getName(); - if (digestAlgname.equalsIgnoreCase("SHA")) - digestAlgname = "SHA1"; byte[] dataSigned; @@ -337,9 +335,12 @@ public class SignerInfo implements DerEncoder { String encryptionAlgname = getDigestEncryptionAlgorithmId().getName(); - if (encryptionAlgname.equalsIgnoreCase("SHA1withDSA")) - encryptionAlgname = "DSA"; - String algname = digestAlgname + "with" + encryptionAlgname; + // Workaround: sometimes the encryptionAlgname is actually + // a signature name + String tmp = AlgorithmId.getEncAlgFromSigAlg(encryptionAlgname); + if (tmp != null) encryptionAlgname = tmp; + String algname = AlgorithmId.makeSigAlg( + digestAlgname, encryptionAlgname); Signature sig = Signature.getInstance(algname); X509Certificate cert = getCertificate(block); diff --git a/jdk/src/share/classes/sun/security/tools/JarSigner.java b/jdk/src/share/classes/sun/security/tools/JarSigner.java index 3e1d0929104..b373ebc23ef 100644 --- a/jdk/src/share/classes/sun/security/tools/JarSigner.java +++ b/jdk/src/share/classes/sun/security/tools/JarSigner.java @@ -1031,9 +1031,9 @@ public class JarSigner { } if (sigfile.length() > 8) { - sigfile = sigfile.substring(0, 8).toUpperCase(); + sigfile = sigfile.substring(0, 8).toUpperCase(Locale.ENGLISH); } else { - sigfile = sigfile.toUpperCase(); + sigfile = sigfile.toUpperCase(Locale.ENGLISH); } StringBuilder tmpSigFile = new StringBuilder(sigfile.length()); @@ -1083,8 +1083,8 @@ public class JarSigner { ZipOutputStream zos = new ZipOutputStream(ps); /* First guess at what they might be - we don't xclude RSA ones. */ - String sfFilename = (META_INF + sigfile + ".SF").toUpperCase(); - String bkFilename = (META_INF + sigfile + ".DSA").toUpperCase(); + String sfFilename = (META_INF + sigfile + ".SF").toUpperCase(Locale.ENGLISH); + String bkFilename = (META_INF + sigfile + ".DSA").toUpperCase(Locale.ENGLISH); Manifest manifest = new Manifest(); Map mfEntries = manifest.getEntries(); @@ -1447,9 +1447,10 @@ public class JarSigner { * . META-INF/*.SF * . META-INF/*.DSA * . META-INF/*.RSA + * . META-INF/*.EC */ private boolean signatureRelated(String name) { - String ucName = name.toUpperCase(); + String ucName = name.toUpperCase(Locale.ENGLISH); if (ucName.equals(JarFile.MANIFEST_NAME) || ucName.equals(META_INF) || (ucName.startsWith(SIG_PREFIX) && @@ -1459,7 +1460,7 @@ public class JarSigner { if (ucName.startsWith(META_INF) && SignatureFileVerifier.isBlockOrSF(ucName)) { - // .SF/.DSA/.RSA files in META-INF subdirs + // .SF/.DSA/.RSA/.EC files in META-INF subdirs // are not considered signature-related return (ucName.indexOf("/") == ucName.lastIndexOf("/")); } @@ -2227,7 +2228,6 @@ class SignatureFile { } BigInteger serial = certChain[0].getSerialNumber(); - String digestAlgorithm; String signatureAlgorithm; String keyAlgorithm = privateKey.getAlgorithm(); /* @@ -2237,22 +2237,24 @@ class SignatureFile { if (sigalg == null) { if (keyAlgorithm.equalsIgnoreCase("DSA")) - digestAlgorithm = "SHA1"; + signatureAlgorithm = "SHA1withDSA"; else if (keyAlgorithm.equalsIgnoreCase("RSA")) - digestAlgorithm = "SHA256"; - else { + signatureAlgorithm = "SHA256withRSA"; + else if (keyAlgorithm.equalsIgnoreCase("EC")) + signatureAlgorithm = "SHA256withECDSA"; + else throw new RuntimeException("private key is not a DSA or " + "RSA key"); - } - signatureAlgorithm = digestAlgorithm + "with" + keyAlgorithm; } else { signatureAlgorithm = sigalg; } // check common invalid key/signature algorithm combinations - String sigAlgUpperCase = signatureAlgorithm.toUpperCase(); + String sigAlgUpperCase = signatureAlgorithm.toUpperCase(Locale.ENGLISH); if ((sigAlgUpperCase.endsWith("WITHRSA") && !keyAlgorithm.equalsIgnoreCase("RSA")) || + (sigAlgUpperCase.endsWith("WITHECDSA") && + !keyAlgorithm.equalsIgnoreCase("EC")) || (sigAlgUpperCase.endsWith("WITHDSA") && !keyAlgorithm.equalsIgnoreCase("DSA"))) { throw new SignatureException diff --git a/jdk/src/share/classes/sun/security/tools/KeyTool.java b/jdk/src/share/classes/sun/security/tools/KeyTool.java index d34d0bcdc48..3ce402a65d0 100644 --- a/jdk/src/share/classes/sun/security/tools/KeyTool.java +++ b/jdk/src/share/classes/sun/security/tools/KeyTool.java @@ -1407,7 +1407,7 @@ public final class KeyTool { } else if ("RSA".equalsIgnoreCase(keyAlgName)) { return "SHA256WithRSA"; } else if ("EC".equalsIgnoreCase(keyAlgName)) { - return "SHA1withECDSA"; + return "SHA256withECDSA"; } else { throw new Exception(rb.getString ("Cannot derive signature algorithm")); diff --git a/jdk/src/share/classes/sun/security/tools/TimestampedSigner.java b/jdk/src/share/classes/sun/security/tools/TimestampedSigner.java index 2b9edc8e5d4..efdc3c0c83b 100644 --- a/jdk/src/share/classes/sun/security/tools/TimestampedSigner.java +++ b/jdk/src/share/classes/sun/security/tools/TimestampedSigner.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2007 Sun Microsystems, Inc. All Rights Reserved. + * Copyright (c) 2007-2009 Sun Microsystems, Inc. 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 @@ -159,18 +159,10 @@ public final class TimestampedSigner extends ContentSigner { // "with" // or "withand" String signatureAlgorithm = parameters.getSignatureAlgorithm(); - String digestAlgorithm = null; - String keyAlgorithm = null; - int with = signatureAlgorithm.indexOf("with"); - if (with > 0) { - digestAlgorithm = signatureAlgorithm.substring(0, with); - int and = signatureAlgorithm.indexOf("and", with + 4); - if (and > 0) { - keyAlgorithm = signatureAlgorithm.substring(with + 4, and); - } else { - keyAlgorithm = signatureAlgorithm.substring(with + 4); - } - } + String keyAlgorithm = + AlgorithmId.getEncAlgFromSigAlg(signatureAlgorithm); + String digestAlgorithm = + AlgorithmId.getDigAlgFromSigAlg(signatureAlgorithm); AlgorithmId digestAlgorithmId = AlgorithmId.get(digestAlgorithm); // Examine signer's certificate diff --git a/jdk/src/share/classes/sun/security/util/SignatureFileVerifier.java b/jdk/src/share/classes/sun/security/util/SignatureFileVerifier.java index aa054d881a4..bf2646f7dcf 100644 --- a/jdk/src/share/classes/sun/security/util/SignatureFileVerifier.java +++ b/jdk/src/share/classes/sun/security/util/SignatureFileVerifier.java @@ -1,5 +1,5 @@ /* - * Copyright 1997-2006 Sun Microsystems, Inc. All Rights Reserved. + * Copyright 1997-2009 Sun Microsystems, Inc. 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 @@ -54,14 +54,14 @@ public class SignatureFileVerifier { ("-DIGEST-" + ManifestDigester.MF_MAIN_ATTRS).toUpperCase (Locale.ENGLISH); - /** the PKCS7 block for this .DSA/.RSA file */ + /** the PKCS7 block for this .DSA/.RSA/.EC file */ private PKCS7 block; /** the raw bytes of the .SF file */ private byte sfBytes[]; /** the name of the signature block file, uppercased and without - * the extension (.DSA/.RSA) + * the extension (.DSA/.RSA/.EC) */ private String name; @@ -80,7 +80,7 @@ public class SignatureFileVerifier { /** * Create the named SignatureFileVerifier. * - * @param name the name of the signature block file (.DSA/.RSA) + * @param name the name of the signature block file (.DSA/.RSA/.EC) * * @param rawBytes the raw bytes of the signature block file */ @@ -148,7 +148,8 @@ public class SignatureFileVerifier { */ public static boolean isBlockOrSF(String s) { // we currently only support DSA and RSA PKCS7 blocks - if (s.endsWith(".SF") || s.endsWith(".DSA") || s.endsWith(".RSA")) { + if (s.endsWith(".SF") || s.endsWith(".DSA") || + s.endsWith(".RSA") || s.endsWith(".EC")) { return true; } return false; diff --git a/jdk/src/share/classes/sun/security/x509/AlgorithmId.java b/jdk/src/share/classes/sun/security/x509/AlgorithmId.java index fb6104dbce9..e7312777806 100644 --- a/jdk/src/share/classes/sun/security/x509/AlgorithmId.java +++ b/jdk/src/share/classes/sun/security/x509/AlgorithmId.java @@ -883,4 +883,53 @@ public class AlgorithmId implements Serializable, DerEncoder { nameTable.put(pbeWithSHA1AndDESede_oid, "PBEWithSHA1AndDESede"); nameTable.put(pbeWithSHA1AndRC2_40_oid, "PBEWithSHA1AndRC2_40"); } + + /** + * Creates a signature algorithm name from a digest algorithm + * name and a encryption algorithm name. + */ + public static String makeSigAlg(String digAlg, String encAlg) { + digAlg = digAlg.replace("-", "").toUpperCase(Locale.ENGLISH); + if (digAlg.equalsIgnoreCase("SHA")) digAlg = "SHA1"; + + encAlg = encAlg.toUpperCase(Locale.ENGLISH); + if (encAlg.equals("EC")) encAlg = "ECDSA"; + + return digAlg + "with" + encAlg; + } + + /** + * Extracts the encryption algorithm name from a signature + * algorithm name. + */ + public static String getEncAlgFromSigAlg(String signatureAlgorithm) { + signatureAlgorithm = signatureAlgorithm.toUpperCase(Locale.ENGLISH); + int with = signatureAlgorithm.indexOf("WITH"); + String keyAlgorithm = null; + if (with > 0) { + int and = signatureAlgorithm.indexOf("AND", with + 4); + if (and > 0) { + keyAlgorithm = signatureAlgorithm.substring(with + 4, and); + } else { + keyAlgorithm = signatureAlgorithm.substring(with + 4); + } + if (keyAlgorithm.equalsIgnoreCase("ECDSA")) { + keyAlgorithm = "EC"; + } + } + return keyAlgorithm; + } + + /** + * Extracts the digest algorithm name from a signature + * algorithm name. + */ + public static String getDigAlgFromSigAlg(String signatureAlgorithm) { + signatureAlgorithm = signatureAlgorithm.toUpperCase(Locale.ENGLISH); + int with = signatureAlgorithm.indexOf("WITH"); + if (with > 0) { + return signatureAlgorithm.substring(0, with); + } + return null; + } } diff --git a/jdk/src/share/classes/sun/tools/jar/SignatureFile.java b/jdk/src/share/classes/sun/tools/jar/SignatureFile.java index d4afe4f8bff..e777e9271ec 100644 --- a/jdk/src/share/classes/sun/tools/jar/SignatureFile.java +++ b/jdk/src/share/classes/sun/tools/jar/SignatureFile.java @@ -1,5 +1,5 @@ /* - * Copyright 1996-2003 Sun Microsystems, Inc. All Rights Reserved. + * Copyright 1996-2009 Sun Microsystems, Inc. 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 @@ -34,6 +34,7 @@ import sun.misc.BASE64Encoder; import sun.misc.BASE64Decoder; import sun.security.pkcs.*; +import sun.security.x509.AlgorithmId; /** *

A signature file as defined in the 8 || name.indexOf('.') != -1) { throw new JarException("invalid file name"); } - rawName = name.toUpperCase(); + rawName = name.toUpperCase(Locale.ENGLISH); } } @@ -217,7 +218,8 @@ public class SignatureFile { if (signatureBlock != null) { SignerInfo info = signatureBlock.getSignerInfos()[0]; suffix = info.getDigestEncryptionAlgorithmId().getName(); - suffix = suffix.substring(suffix.length() - 3); + String temp = AlgorithmId.getEncAlgFromSigAlg(suffix); + if (temp != null) suffix = temp; } return "META-INF/" + rawName + "." + suffix; } diff --git a/jdk/test/sun/security/tools/jarsigner/ec.sh b/jdk/test/sun/security/tools/jarsigner/ec.sh new file mode 100644 index 00000000000..452851e0551 --- /dev/null +++ b/jdk/test/sun/security/tools/jarsigner/ec.sh @@ -0,0 +1,73 @@ +# +# Copyright 2009 Sun Microsystems, Inc. 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 Sun Microsystems, Inc., 4150 Network Circle, Santa Clara, +# CA 95054 USA or visit www.sun.com if you need additional information or +# have any questions. +# + +# @test +# @bug 6870812 +# @summary enhance security tools to use ECC algorithm +# + +if [ "${TESTJAVA}" = "" ] ; then + JAVAC_CMD=`which javac` + TESTJAVA=`dirname $JAVAC_CMD`/.. +fi + +# set platform-dependent variables +OS=`uname -s` +case "$OS" in + Windows_* ) + FS="\\" + ;; + * ) + FS="/" + ;; +esac + +KS=ec.jks +JFILE=ec.jar + +KT="$TESTJAVA${FS}bin${FS}keytool -storepass changeit -keypass changeit -keystore $KS" +JAR=$TESTJAVA${FS}bin${FS}jar +JARSIGNER=$TESTJAVA${FS}bin${FS}jarsigner + +rm $KS $JFILE +echo A > A +$JAR cvf $JFILE A + +$KT -alias a -dname CN=a -keyalg ec -genkey -validity 300 || exit 11 +$KT -alias b -dname CN=b -keyalg ec -genkey -validity 300 || exit 12 +$KT -alias c -dname CN=c -keyalg ec -genkey -validity 300 || exit 13 +$KT -alias x -dname CN=x -keyalg ec -genkey -validity 300 || exit 14 + +$JARSIGNER -keystore $KS -storepass changeit $JFILE a -debug -strict || exit 21 +$JARSIGNER -keystore $KS -storepass changeit $JFILE b -debug -strict -sigalg SHA1withECDSA || exit 22 +$JARSIGNER -keystore $KS -storepass changeit $JFILE c -debug -strict -sigalg SHA512withECDSA || exit 23 + +$JARSIGNER -keystore $KS -storepass changeit -verify $JFILE a -debug -strict || exit 31 +$JARSIGNER -keystore $KS -storepass changeit -verify $JFILE b -debug -strict || exit 32 +$JARSIGNER -keystore $KS -storepass changeit -verify $JFILE c -debug -strict || exit 33 + +# Not signed by x, should exit with non-zero +$JARSIGNER -keystore $KS -storepass changeit -verify $JFILE x -debug -strict && exit 34 + +exit 0 +