Mercurial > hg > openjdk > icedtea > jdk7 > jdk
changeset 2485:8834c3633f0b
6890876: jarsigner can add CRL info into signed jar
Reviewed-by: mullan
line wrap: on
line diff
--- a/src/share/classes/com/sun/jarsigner/ContentSignerParameters.java Thu May 06 11:26:16 2010 +0800 +++ b/src/share/classes/com/sun/jarsigner/ContentSignerParameters.java Thu May 06 13:42:52 2010 +0800 @@ -1,5 +1,5 @@ /* - * Copyright 2003 Sun Microsystems, Inc. All Rights Reserved. + * Copyright 2003-2010 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 @@ -26,7 +26,9 @@ package com.sun.jarsigner; import java.net.URI; +import java.security.cert.X509CRL; import java.security.cert.X509Certificate; +import java.util.Set; import java.util.zip.ZipFile; /** @@ -81,6 +83,13 @@ public X509Certificate[] getSignerCertificateChain(); /** + * Retrieves the signer's X.509 CRLs. + * + * @return An unmodifiable set of X.509 CRLs (never <code>null</code>) + */ + public Set<X509CRL> getCRLs(); + + /** * Retrieves the content that was signed. * The content is the JAR file's signature file. *
--- a/src/share/classes/java/security/CodeSigner.java Thu May 06 11:26:16 2010 +0800 +++ b/src/share/classes/java/security/CodeSigner.java Thu May 06 13:42:52 2010 +0800 @@ -1,5 +1,5 @@ /* - * Copyright 2003-2004 Sun Microsystems, Inc. All Rights Reserved. + * Copyright 2003-2010 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 @@ -26,7 +26,10 @@ package java.security; import java.io.Serializable; +import java.security.cert.CRL; import java.security.cert.CertPath; +import sun.misc.JavaSecurityCodeSignerAccess; +import sun.misc.SharedSecrets; /** * This class encapsulates information about a code signer. @@ -163,4 +166,43 @@ sb.append(")"); return sb.toString(); } + + // A private attribute attached to this CodeSigner object. Can be accessed + // through SharedSecrets.getJavaSecurityCodeSignerAccess().[g|s]etCRLs + // + // Currently called in SignatureFileVerifier.getSigners + private transient CRL[] crls; + + /** + * Sets the CRLs attached + * @param crls, null to clear + */ + void setCRLs(CRL[] crls) { + this.crls = crls; + } + + /** + * Returns the CRLs attached + * @return the crls, initially null + */ + CRL[] getCRLs() { + return crls; + } + + // Set up JavaSecurityCodeSignerAccess in SharedSecrets + static { + SharedSecrets.setJavaSecurityCodeSignerAccess( + new JavaSecurityCodeSignerAccess() { + @Override + public void setCRLs(CodeSigner signer, CRL[] crls) { + signer.setCRLs(crls); + } + + @Override + public CRL[] getCRLs(CodeSigner signer) { + return signer.getCRLs(); + } + }); + } + }
--- a/src/share/classes/java/util/jar/JarVerifier.java Thu May 06 11:26:16 2010 +0800 +++ b/src/share/classes/java/util/jar/JarVerifier.java Thu May 06 13:42:52 2010 +0800 @@ -1,5 +1,5 @@ /* - * Copyright 1997-2009 Sun Microsystems, Inc. All Rights Reserved. + * Copyright 1997-2010 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 @@ -27,7 +27,6 @@ import java.io.*; import java.util.*; -import java.util.zip.*; import java.security.*; import java.security.cert.CertificateException;
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/src/share/classes/sun/misc/JavaSecurityCodeSignerAccess.java Thu May 06 13:42:52 2010 +0800 @@ -0,0 +1,33 @@ +/* + * Copyright 2010 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. Sun designates this + * particular file as subject to the "Classpath" exception as provided + * by Sun 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 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. + */ +package sun.misc; + +import java.security.CodeSigner; +import java.security.cert.CRL; + +public interface JavaSecurityCodeSignerAccess { + void setCRLs(CodeSigner signer, CRL[] crls); + CRL[] getCRLs(CodeSigner signer); +}
--- a/src/share/classes/sun/misc/SharedSecrets.java Thu May 06 11:26:16 2010 +0800 +++ b/src/share/classes/sun/misc/SharedSecrets.java Thu May 06 13:42:52 2010 +0800 @@ -27,8 +27,8 @@ import java.util.jar.JarFile; import java.io.Console; -import java.io.File; import java.io.FileDescriptor; +import java.security.CodeSigner; import java.security.ProtectionDomain; /** A repository of "shared secrets", which are a mechanism for @@ -49,6 +49,7 @@ private static JavaNioAccess javaNioAccess; private static JavaIOFileDescriptorAccess javaIOFileDescriptorAccess; private static JavaSecurityProtectionDomainAccess javaSecurityProtectionDomainAccess; + private static JavaSecurityCodeSignerAccess javaSecurityCodeSignerAccess; public static JavaUtilJarAccess javaUtilJarAccess() { if (javaUtilJarAccess == null) { @@ -126,4 +127,16 @@ unsafe.ensureClassInitialized(ProtectionDomain.class); return javaSecurityProtectionDomainAccess; } + + public static void setJavaSecurityCodeSignerAccess + (JavaSecurityCodeSignerAccess jscsa) { + javaSecurityCodeSignerAccess = jscsa; + } + + public static JavaSecurityCodeSignerAccess + getJavaSecurityCodeSignerAccess() { + if (javaSecurityCodeSignerAccess == null) + unsafe.ensureClassInitialized(CodeSigner.class); + return javaSecurityCodeSignerAccess; + } }
--- a/src/share/classes/sun/security/pkcs/PKCS7.java Thu May 06 11:26:16 2010 +0800 +++ b/src/share/classes/sun/security/pkcs/PKCS7.java Thu May 06 13:42:52 2010 +0800 @@ -1,5 +1,5 @@ /* - * Copyright 1996-2006 Sun Microsystems, Inc. All Rights Reserved. + * Copyright 1996-2010 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 @@ -28,7 +28,6 @@ import java.io.*; import java.math.BigInteger; import java.util.*; -import java.security.cert.Certificate; import java.security.cert.X509Certificate; import java.security.cert.CertificateException; import java.security.cert.X509CRL; @@ -173,20 +172,30 @@ * @param digestAlgorithmIds the message digest algorithm identifiers. * @param contentInfo the content information. * @param certificates an array of X.509 certificates. + * @param crls an array of CRLs * @param signerInfos an array of signer information. */ public PKCS7(AlgorithmId[] digestAlgorithmIds, ContentInfo contentInfo, X509Certificate[] certificates, + X509CRL[] crls, SignerInfo[] signerInfos) { version = BigInteger.ONE; this.digestAlgorithmIds = digestAlgorithmIds; this.contentInfo = contentInfo; this.certificates = certificates; + this.crls = crls; this.signerInfos = signerInfos; } + public PKCS7(AlgorithmId[] digestAlgorithmIds, + ContentInfo contentInfo, + X509Certificate[] certificates, + SignerInfo[] signerInfos) { + this(digestAlgorithmIds, contentInfo, certificates, null, signerInfos); + } + private void parseNetscapeCertChain(DerValue val) throws ParsingException, IOException { DerInputStream dis = new DerInputStream(val.toByteArray()); @@ -312,7 +321,7 @@ ByteArrayInputStream bais = null; try { if (certfac == null) - crls[i] = (X509CRL) new X509CRLImpl(crlVals[i]); + crls[i] = new X509CRLImpl(crlVals[i]); else { byte[] encoded = crlVals[i].toByteArray(); bais = new ByteArrayInputStream(encoded); @@ -480,7 +489,30 @@ signedData.putOrderedSetOf((byte)0xA0, implCerts); } - // no crls (OPTIONAL field) + // CRLs (optional) + if (crls != null && crls.length != 0) { + // cast to X509CRLImpl[] since X509CRLImpl implements DerEncoder + Set<X509CRLImpl> implCRLs = new HashSet<X509CRLImpl>(crls.length); + for (X509CRL crl: crls) { + if (crl instanceof X509CRLImpl) + implCRLs.add((X509CRLImpl) crl); + else { + try { + byte[] encoded = crl.getEncoded(); + implCRLs.add(new X509CRLImpl(encoded)); + } catch (CRLException ce) { + IOException ie = new IOException(ce.getMessage()); + ie.initCause(ce); + throw ie; + } + } + } + + // Add the CRL set (tagged with [1] IMPLICIT) + // to the signed data + signedData.putOrderedSetOf((byte)0xA1, + implCRLs.toArray(new X509CRLImpl[implCRLs.size()])); + } // signerInfos signedData.putOrderedSetOf(DerValue.tag_Set, signerInfos);
--- a/src/share/classes/sun/security/tools/JarSigner.java Thu May 06 11:26:16 2010 +0800 +++ b/src/share/classes/sun/security/tools/JarSigner.java Thu May 06 13:42:52 2010 +0800 @@ -26,6 +26,7 @@ package sun.security.tools; import java.io.*; +import java.security.cert.X509CRL; import java.util.*; import java.util.zip.*; import java.util.jar.*; @@ -35,6 +36,7 @@ import java.text.Collator; import java.text.MessageFormat; import java.security.cert.Certificate; +import java.security.cert.CRL; import java.security.cert.X509Certificate; import java.security.cert.CertificateException; import java.security.*; @@ -56,6 +58,7 @@ import sun.security.x509.*; import sun.security.util.*; import sun.misc.BASE64Encoder; +import sun.misc.SharedSecrets; /** @@ -114,14 +117,16 @@ static final int SIGNED_BY_ALIAS = 0x08; // signer is in alias list X509Certificate[] certChain; // signer's cert chain (when composing) + Set<X509CRL> crls; // signer provided CRLs PrivateKey privateKey; // private key KeyStore store; // the keystore specified by -keystore // or the default keystore, never null String keystore; // key store file + List<String> crlfiles = new ArrayList<String>(); // CRL files to add boolean nullStream = false; // null keystore input stream (NONE) boolean token = false; // token-based keystore - String jarfile; // jar file to sign or verify + String jarfile; // jar files to sign or verify String alias; // alias to sign jar with List<String> ckaliases = new ArrayList<String>(); // aliases in -verify char[] storepass; // keystore password @@ -146,6 +151,7 @@ boolean signManifest = true; // "sign" the whole manifest boolean externalSF = true; // leave the .SF out of the PKCS7 block boolean strict = false; // treat warnings as error + boolean autoCRL = false; // Automatcially add CRL defined in cert // read zip entry raw bytes private ByteArrayOutputStream baos = new ByteArrayOutputStream(2048); @@ -226,6 +232,29 @@ } else { loadKeyStore(keystore, true); getAliasInfo(alias); + crls = new HashSet<X509CRL>(); + if (crlfiles.size() > 0 || autoCRL) { + CertificateFactory fac = + CertificateFactory.getInstance("X509"); + List<CRL> list = new ArrayList<CRL>(); + for (String file: crlfiles) { + Collection<? extends CRL> tmp = KeyTool.loadCRLs(file); + for (CRL crl: tmp) { + if (crl instanceof X509CRL) { + crls.add((X509CRL)crl); + } + } + } + if (autoCRL) { + List<CRL> crlsFromCert = + KeyTool.readCRLsFromCert(certChain[0]); + for (CRL crl: crlsFromCert) { + if (crl instanceof X509CRL) { + crls.add((X509CRL)crl); + } + } + } + } // load the alternative signing mechanism if (altSignerClass != null) { @@ -367,6 +396,13 @@ } else if (collator.compare(flags, "-digestalg") ==0) { if (++n == args.length) usageNoArg(); digestalg = args[n]; + } else if (collator.compare(flags, "-crl") ==0) { + if ("auto".equals(modifier)) { + autoCRL = true; + } else { + if (++n == args.length) usageNoArg(); + crlfiles.add(args[n]); + } } else if (collator.compare(flags, "-certs") ==0) { showcerts = true; } else if (collator.compare(flags, "-strict") ==0) { @@ -516,6 +552,9 @@ ("[-sigalg <algorithm>] name of signature algorithm")); System.out.println(); System.out.println(rb.getString + ("[-crl[:auto| <file>] include CRL in signed jar")); + System.out.println(); + System.out.println(rb.getString ("[-verify] verify a signed JAR file")); System.out.println(); System.out.println(rb.getString @@ -654,6 +693,20 @@ if (showcerts) { sb.append(si); sb.append('\n'); + CRL[] crls = SharedSecrets + .getJavaSecurityCodeSignerAccess() + .getCRLs(signer); + if (crls != null) { + for (CRL crl: crls) { + if (crl instanceof X509CRLImpl) { + sb.append(tab).append("["); + sb.append(String.format( + rb.getString("with a CRL including %d entries"), + ((X509CRLImpl)crl).getRevokedCertificates().size())) + .append("]\n"); + } + } + } } } } else if (showcerts && !verbose.equals("all")) { @@ -1233,7 +1286,7 @@ try { block = - sf.generateBlock(privateKey, sigalg, certChain, + sf.generateBlock(privateKey, sigalg, certChain, crls, externalSF, tsaUrl, tsaCert, signingMechanism, args, zipFile); } catch (SocketTimeoutException e) { @@ -2197,6 +2250,7 @@ public Block generateBlock(PrivateKey privateKey, String sigalg, X509Certificate[] certChain, + Set<X509CRL> crls, boolean externalSF, String tsaUrl, X509Certificate tsaCert, ContentSigner signingMechanism, @@ -2204,7 +2258,7 @@ throws NoSuchAlgorithmException, InvalidKeyException, IOException, SignatureException, CertificateException { - return new Block(this, privateKey, sigalg, certChain, externalSF, + return new Block(this, privateKey, sigalg, certChain, crls, externalSF, tsaUrl, tsaCert, signingMechanism, args, zipFile); } @@ -2218,7 +2272,8 @@ * Construct a new signature block. */ Block(SignatureFile sfg, PrivateKey privateKey, String sigalg, - X509Certificate[] certChain, boolean externalSF, String tsaUrl, + X509Certificate[] certChain, Set<X509CRL> crls, + boolean externalSF, String tsaUrl, X509Certificate tsaCert, ContentSigner signingMechanism, String[] args, ZipFile zipFile) throws NoSuchAlgorithmException, InvalidKeyException, IOException, @@ -2305,7 +2360,7 @@ // Assemble parameters for the signing mechanism ContentSignerParameters params = new JarSignerParameters(args, tsaUri, tsaCert, signature, - signatureAlgorithm, certChain, content, zipFile); + signatureAlgorithm, certChain, crls, content, zipFile); // Generate the signature block block = signingMechanism.generateSignedData( @@ -2346,6 +2401,7 @@ private byte[] signature; private String signatureAlgorithm; private X509Certificate[] signerCertificateChain; + private Set<X509CRL> crls; private byte[] content; private ZipFile source; @@ -2354,7 +2410,8 @@ */ JarSignerParameters(String[] args, URI tsa, X509Certificate tsaCertificate, byte[] signature, String signatureAlgorithm, - X509Certificate[] signerCertificateChain, byte[] content, + X509Certificate[] signerCertificateChain, Set<X509CRL> crls, + byte[] content, ZipFile source) { if (signature == null || signatureAlgorithm == null || @@ -2367,6 +2424,7 @@ this.signature = signature; this.signatureAlgorithm = signatureAlgorithm; this.signerCertificateChain = signerCertificateChain; + this.crls = crls; this.content = content; this.source = source; } @@ -2442,4 +2500,13 @@ public ZipFile getSource() { return source; } + + @Override + public Set<X509CRL> getCRLs() { + if (crls == null) { + return Collections.emptySet(); + } else { + return Collections.unmodifiableSet(crls); + } + } }
--- a/src/share/classes/sun/security/tools/JarSignerResources.java Thu May 06 11:26:16 2010 +0800 +++ b/src/share/classes/sun/security/tools/JarSignerResources.java Thu May 06 13:42:52 2010 +0800 @@ -1,5 +1,5 @@ /* - * Copyright 2000-2009 Sun Microsystems, Inc. All Rights Reserved. + * Copyright 2000-2010 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 @@ -74,6 +74,8 @@ "[-digestalg <algorithm>] name of digest algorithm"}, {"[-sigalg <algorithm>] name of signature algorithm", "[-sigalg <algorithm>] name of signature algorithm"}, + {"[-crl[:auto| <file>] include CRL in signed jar", + "[-crl[:auto| <file>] include CRL in signed jar"}, {"[-verify] verify a signed JAR file", "[-verify] verify a signed JAR file"}, {"[-verbose[:suboptions]] verbose output when signing/verifying.", @@ -191,6 +193,7 @@ {"using an alternative signing mechanism", "using an alternative signing mechanism"}, {"entry was signed on", "entry was signed on {0}"}, + {"with a CRL including %d entries", "with a CRL including %d entries"}, {"Warning: ", "Warning: "}, {"This jar contains unsigned entries which have not been integrity-checked. ", "This jar contains unsigned entries which have not been integrity-checked. "},
--- a/src/share/classes/sun/security/tools/KeyTool.java Thu May 06 11:26:16 2010 +0800 +++ b/src/share/classes/sun/security/tools/KeyTool.java Thu May 06 13:42:52 2010 +0800 @@ -25,6 +25,7 @@ package sun.security.tools; +import sun.misc.SharedSecrets; import java.io.*; import java.security.CodeSigner; import java.security.KeyStore; @@ -42,6 +43,7 @@ import java.security.Provider; import java.security.cert.Certificate; import java.security.cert.CertificateFactory; +import java.security.cert.CRL; import java.security.cert.X509Certificate; import java.security.cert.CertificateException; import java.text.Collator; @@ -50,14 +52,20 @@ import java.util.jar.JarEntry; import java.util.jar.JarFile; import java.lang.reflect.Constructor; +import java.math.BigInteger; +import java.net.URI; import java.net.URL; import java.net.URLClassLoader; - +import java.security.cert.CertStore; + +import java.security.cert.X509CRL; +import java.security.cert.X509CRLEntry; +import java.security.cert.X509CRLSelector; +import javax.security.auth.x500.X500Principal; import sun.misc.BASE64Encoder; import sun.security.util.ObjectIdentifier; import sun.security.pkcs.PKCS10; import sun.security.provider.X509Factory; -import sun.security.util.DerOutputStream; import sun.security.util.Password; import sun.security.util.PathList; import javax.crypto.KeyGenerator; @@ -72,6 +80,7 @@ import sun.misc.BASE64Decoder; import sun.security.pkcs.PKCS10Attribute; import sun.security.pkcs.PKCS9Attribute; +import sun.security.provider.certpath.ldap.LDAPCertStoreHelper; import sun.security.util.DerValue; import sun.security.x509.*; @@ -147,6 +156,7 @@ private Set<char[]> passwords = new HashSet<char[]> (); private String startDate = null; + private List <String> ids = new ArrayList <String> (); // used in GENCRL private List <String> v3ext = new ArrayList <String> (); enum Command { @@ -180,9 +190,6 @@ STARTDATE, EXT, VALIDITY, KEYPASS, KEYSTORE, STOREPASS, STORETYPE, PROVIDERNAME, PROVIDERCLASS, PROVIDERARG, PROVIDERPATH, V, PROTECTED), - IDENTITYDB("Imports entries from a JDK 1.1.x-style identity database", - FILEIN, STORETYPE, KEYSTORE, STOREPASS, PROVIDERNAME, - PROVIDERCLASS, PROVIDERARG, PROVIDERPATH, V), IMPORTCERT("Imports a certificate or a certificate chain", NOPROMPT, TRUSTCACERTS, PROTECTED, ALIAS, FILEIN, KEYPASS, KEYSTORE, STOREPASS, STORETYPE, @@ -195,10 +202,6 @@ SRCALIAS, DESTALIAS, SRCKEYPASS, DESTKEYPASS, NOPROMPT, PROVIDERCLASS, PROVIDERARG, PROVIDERPATH, V), - KEYCLONE("Clones a key entry", - ALIAS, DESTALIAS, KEYPASS, NEW, STORETYPE, - KEYSTORE, STOREPASS, PROVIDERNAME, PROVIDERCLASS, - PROVIDERARG, PROVIDERPATH, V), KEYPASSWD("Changes the key password of an entry", ALIAS, KEYPASS, NEW, KEYSTORE, STOREPASS, STORETYPE, PROVIDERNAME, PROVIDERCLASS, PROVIDERARG, @@ -211,12 +214,29 @@ RFC, FILEIN, SSLSERVER, JARFILE, V), PRINTCERTREQ("Prints the content of a certificate request", FILEIN, V), + PRINTCRL("Prints the content of a CRL file", + FILEIN, V), + STOREPASSWD("Changes the store password of a keystore", + NEW, KEYSTORE, STOREPASS, STORETYPE, PROVIDERNAME, + PROVIDERCLASS, PROVIDERARG, PROVIDERPATH, V), + + // Undocumented start here, KEYCLONE is used a marker in -help; + + KEYCLONE("Clones a key entry", + ALIAS, DESTALIAS, KEYPASS, NEW, STORETYPE, + KEYSTORE, STOREPASS, PROVIDERNAME, PROVIDERCLASS, + PROVIDERARG, PROVIDERPATH, V), SELFCERT("Generates a self-signed certificate", ALIAS, SIGALG, DNAME, STARTDATE, VALIDITY, KEYPASS, STORETYPE, KEYSTORE, STOREPASS, PROVIDERNAME, PROVIDERCLASS, PROVIDERARG, PROVIDERPATH, V), - STOREPASSWD("Changes the store password of a keystore", - NEW, KEYSTORE, STOREPASS, STORETYPE, PROVIDERNAME, + GENCRL("Generates CRL", + RFC, FILEOUT, ID, + ALIAS, SIGALG, EXT, KEYPASS, KEYSTORE, + STOREPASS, STORETYPE, PROVIDERNAME, PROVIDERCLASS, + PROVIDERARG, PROVIDERPATH, V, PROTECTED), + IDENTITYDB("Imports entries from a JDK 1.1.x-style identity database", + FILEIN, STORETYPE, KEYSTORE, STOREPASS, PROVIDERNAME, PROVIDERCLASS, PROVIDERARG, PROVIDERPATH, V); final String description; @@ -244,6 +264,7 @@ EXT("ext", "<value>", "X.509 extension"), FILEOUT("file", "<filename>", "output file name"), FILEIN("file", "<filename>", "input file name"), + ID("id", "<id:reason>", "Serial ID of cert to revoke"), INFILE("infile", "<filename>", "input file name"), KEYALG("keyalg", "<keyalg>", "key algorithm name"), KEYPASS("keypass", "<arg>", "key password"), @@ -458,6 +479,8 @@ validity = Long.parseLong(args[++i]); } else if (collator.compare(flags, "-ext") == 0) { v3ext.add(args[++i]); + } else if (collator.compare(flags, "-id") == 0) { + ids.add(args[++i]); } else if (collator.compare(flags, "-file") == 0) { filename = args[++i]; } else if (collator.compare(flags, "-infile") == 0) { @@ -720,7 +743,8 @@ command != GENSECKEY && command != IDENTITYDB && command != IMPORTCERT && - command != IMPORTKEYSTORE) { + command != IMPORTKEYSTORE && + command != PRINTCRL) { throw new Exception(rb.getString ("Keystore file does not exist: ") + ksfname); } @@ -855,10 +879,12 @@ && !KeyStoreUtil.isWindowsKeyStore(storetype) && isKeyStoreRelated(command)) { // here we have EXPORTCERT and LIST (info valid until STOREPASSWD) - System.err.print(rb.getString("Enter keystore password: ")); - System.err.flush(); - storePass = Password.readPassword(System.in); - passwords.add(storePass); + if (command != PRINTCRL) { + System.err.print(rb.getString("Enter keystore password: ")); + System.err.flush(); + storePass = Password.readPassword(System.in); + passwords.add(storePass); + } } // Now load a nullStream-based keystore, @@ -895,7 +921,7 @@ // Create a certificate factory if (command == PRINTCERT || command == IMPORTCERT - || command == IDENTITYDB) { + || command == IDENTITYDB || command == PRINTCRL) { cf = CertificateFactory.getInstance("X509"); } @@ -1086,6 +1112,22 @@ ps.close(); } } + } else if (command == GENCRL) { + if (alias == null) { + alias = keyAlias; + } + PrintStream ps = null; + if (filename != null) { + ps = new PrintStream(new FileOutputStream(filename)); + out = ps; + } + try { + doGenCRL(out); + } finally { + if (ps != null) { + ps.close(); + } + } } else if (command == PRINTCERTREQ) { InputStream inStream = System.in; if (filename != null) { @@ -1098,6 +1140,8 @@ inStream.close(); } } + } else if (command == PRINTCRL) { + doPrintCRL(filename, out); } // If we need to save the keystore, do so. @@ -1152,7 +1196,8 @@ CertificateValidity interval = new CertificateValidity(firstDate, lastDate); - PrivateKey privateKey = (PrivateKey)recoverKey(alias, storePass, keyPass).fst; + PrivateKey privateKey = + (PrivateKey)recoverKey(alias, storePass, keyPass).fst; if (sigAlgName == null) { sigAlgName = getCompatibleSigAlgName(privateKey.getAlgorithm()); } @@ -1221,6 +1266,56 @@ } } + private void doGenCRL(PrintStream out) + throws Exception { + if (ids == null) { + throw new Exception("Must provide -id when -gencrl"); + } + Certificate signerCert = keyStore.getCertificate(alias); + byte[] encoded = signerCert.getEncoded(); + X509CertImpl signerCertImpl = new X509CertImpl(encoded); + X509CertInfo signerCertInfo = (X509CertInfo)signerCertImpl.get( + X509CertImpl.NAME + "." + X509CertImpl.INFO); + X500Name owner = (X500Name)signerCertInfo.get(X509CertInfo.SUBJECT + "." + + CertificateSubjectName.DN_NAME); + + Date firstDate = getStartDate(startDate); + Date lastDate = (Date) firstDate.clone(); + lastDate.setTime(lastDate.getTime() + (long)validity*1000*24*60*60); + CertificateValidity interval = new CertificateValidity(firstDate, + lastDate); + + + PrivateKey privateKey = + (PrivateKey)recoverKey(alias, storePass, keyPass).fst; + if (sigAlgName == null) { + sigAlgName = getCompatibleSigAlgName(privateKey.getAlgorithm()); + } + + X509CRLEntry[] badCerts = new X509CRLEntry[ids.size()]; + for (int i=0; i<ids.size(); i++) { + String id = ids.get(i); + int d = id.indexOf(':'); + if (d >= 0) { + CRLExtensions ext = new CRLExtensions(); + ext.set("Reason", new CRLReasonCodeExtension(Integer.parseInt(id.substring(d+1)))); + badCerts[i] = new X509CRLEntryImpl(new BigInteger(id.substring(0, d)), + firstDate, ext); + } else { + badCerts[i] = new X509CRLEntryImpl(new BigInteger(ids.get(i)), firstDate); + } + } + X509CRLImpl crl = new X509CRLImpl(owner, firstDate, lastDate, badCerts); + crl.sign(privateKey, sigAlgName); + if (rfc) { + out.println("-----BEGIN X509 CRL-----"); + new BASE64Encoder().encodeBuffer(crl.getEncodedInternal(), out); + out.println("-----END X509 CRL-----"); + } else { + out.write(crl.getEncodedInternal()); + } + } + /** * Creates a PKCS#10 cert signing request, corresponding to the * keys (and name) associated with a given alias. @@ -1925,6 +2020,177 @@ } } + private static <T> Iterable<T> e2i(final Enumeration<T> e) { + return new Iterable<T>() { + @Override + public Iterator<T> iterator() { + return new Iterator<T>() { + @Override + public boolean hasNext() { + return e.hasMoreElements(); + } + @Override + public T next() { + return e.nextElement(); + } + public void remove() { + throw new UnsupportedOperationException("Not supported yet."); + } + }; + } + }; + } + + /** + * Loads CRLs from a source. This method is also called in JarSigner. + * @param src the source, which means System.in if null, or a URI, + * or a bare file path name + */ + public static Collection<? extends CRL> loadCRLs(String src) throws Exception { + InputStream in = null; + URI uri = null; + if (src == null) { + in = System.in; + } else { + try { + uri = new URI(src); + if (uri.getScheme().equals("ldap")) { + // No input stream for LDAP + } else { + in = uri.toURL().openStream(); + } + } catch (Exception e) { + try { + in = new FileInputStream(src); + } catch (Exception e2) { + if (uri == null || uri.getScheme() == null) { + throw e2; // More likely a bare file path + } else { + throw e; // More likely a protocol or network problem + } + } + } + } + if (in != null) { + try { + // Read the full stream before feeding to X509Factory, + // otherwise, keytool -gencrl | keytool -printcrl + // might not work properly, since -gencrl is slow + // and there's no data in the pipe at the beginning. + ByteArrayOutputStream bout = new ByteArrayOutputStream(); + byte[] b = new byte[4096]; + while (true) { + int len = in.read(b); + if (len < 0) break; + bout.write(b, 0, len); + } + return CertificateFactory.getInstance("X509").generateCRLs( + new ByteArrayInputStream(bout.toByteArray())); + } finally { + if (in != System.in) { + in.close(); + } + } + } else { // must be LDAP, and uri is not null + String path = uri.getPath(); + if (path.charAt(0) == '/') path = path.substring(1); + LDAPCertStoreHelper h = new LDAPCertStoreHelper(); + CertStore s = h.getCertStore(uri); + X509CRLSelector sel = + h.wrap(new X509CRLSelector(), null, path); + return s.getCRLs(sel); + } + } + + /** + * Returns CRLs described in a X509Certificate's CRLDistributionPoints + * Extension. Only those containing a general name of type URI are read. + */ + public static List<CRL> readCRLsFromCert(X509Certificate cert) + throws Exception { + List<CRL> crls = new ArrayList<CRL>(); + CRLDistributionPointsExtension ext = + X509CertImpl.toImpl(cert).getCRLDistributionPointsExtension(); + if (ext == null) return crls; + for (DistributionPoint o: (List<DistributionPoint>) + ext.get(CRLDistributionPointsExtension.POINTS)) { + GeneralNames names = o.getFullName(); + if (names != null) { + for (GeneralName name: names.names()) { + if (name.getType() == GeneralNameInterface.NAME_URI) { + URIName uriName = (URIName)name.getName(); + for (CRL crl: KeyTool.loadCRLs(uriName.getName())) { + if (crl instanceof X509CRL) { + crls.add((X509CRL)crl); + } + } + break; // Different name should point to same CRL + } + } + } + } + return crls; + } + + private static String verifyCRL(KeyStore ks, CRL crl) + throws Exception { + X509CRLImpl xcrl = (X509CRLImpl)crl; + X500Principal issuer = xcrl.getIssuerX500Principal(); + for (String s: e2i(ks.aliases())) { + Certificate cert = ks.getCertificate(s); + if (cert instanceof X509Certificate) { + X509Certificate xcert = (X509Certificate)cert; + if (xcert.getSubjectX500Principal().equals(issuer)) { + try { + ((X509CRLImpl)crl).verify(cert.getPublicKey()); + return s; + } catch (Exception e) { + } + } + } + } + return null; + } + + private void doPrintCRL(String src, PrintStream out) + throws Exception { + for (CRL crl: loadCRLs(src)) { + printCRL(crl, out); + String issuer = null; + if (caks != null) { + issuer = verifyCRL(caks, crl); + if (issuer != null) { + System.out.println("Verified by " + issuer + " in cacerts"); + } + } + if (issuer == null && keyStore != null) { + issuer = verifyCRL(keyStore, crl); + if (issuer != null) { + System.out.println("Verified by " + issuer + " in keystore"); + } + } + if (issuer == null) { + out.println(rb.getString + ("*******************************************")); + out.println("WARNING: not verified. Make sure -keystore and -alias are correct."); + out.println(rb.getString + ("*******************************************\n\n")); + } + } + } + + private void printCRL(CRL crl, PrintStream out) + throws Exception { + if (rfc) { + X509CRL xcrl = (X509CRL)crl; + out.println("-----BEGIN X509 CRL-----"); + new BASE64Encoder().encodeBuffer(xcrl.getEncoded(), out); + out.println("-----END X509 CRL-----"); + } else { + out.println(crl.toString()); + } + } + private void doPrintCertReq(InputStream in, PrintStream out) throws Exception { @@ -2063,6 +2329,16 @@ out.println(); } } + CRL[] crls = SharedSecrets + .getJavaSecurityCodeSignerAccess() + .getCRLs(signer); + if (crls != null) { + out.println(rb.getString("CRLs:")); + out.println(); + for (CRL crl: crls) { + printCRL(crl, out); + } + } } } } @@ -3330,15 +3606,22 @@ /** * Match a command (may be abbreviated) with a command set. * @param s the command provided - * @param list the legal command set + * @param list the legal command set. If there is a null, commands after it + * are regarded experimental, which means they are supported but their + * existence should not be revealed to user. * @return the position of a single match, or -1 if none matched * @throws Exception if s is ambiguous */ private static int oneOf(String s, String... list) throws Exception { int[] match = new int[list.length]; int nmatch = 0; + int experiment = Integer.MAX_VALUE; for (int i = 0; i<list.length; i++) { String one = list[i]; + if (one == null) { + experiment = i; + continue; + } if (one.toLowerCase(Locale.ENGLISH) .startsWith(s.toLowerCase(Locale.ENGLISH))) { match[nmatch++] = i; @@ -3360,17 +3643,27 @@ } } } - if (nmatch == 0) return -1; - if (nmatch == 1) return match[0]; - StringBuffer sb = new StringBuffer(); - MessageFormat form = new MessageFormat(rb.getString - ("command {0} is ambiguous:")); - Object[] source = {s}; - sb.append(form.format(source) +"\n "); - for (int i=0; i<nmatch; i++) { - sb.append(" " + list[match[i]]); + if (nmatch == 0) { + return -1; + } else if (nmatch == 1) { + return match[0]; + } else { + // If multiple matches is in experimental commands, ignore them + if (match[1] > experiment) { + return match[0]; + } + StringBuffer sb = new StringBuffer(); + MessageFormat form = new MessageFormat(rb.getString + ("command {0} is ambiguous:")); + Object[] source = {s}; + sb.append(form.format(source)); + sb.append("\n "); + for (int i=0; i<nmatch && match[i]<experiment; i++) { + sb.append(' '); + sb.append(list[match[i]]); + } + throw new Exception(sb.toString()); } - throw new Exception(sb.toString()); } /** @@ -3405,6 +3698,8 @@ "IssuerAlternativeName", "SubjectInfoAccess", "AuthorityInfoAccess", + null, + "CRLDistributionPoints", }; private ObjectIdentifier findOidForExtName(String type) @@ -3417,6 +3712,7 @@ case 4: return PKIXExtensions.IssuerAlternativeName_Id; case 5: return PKIXExtensions.SubjectInfoAccess_Id; case 6: return PKIXExtensions.AuthInfoAccess_Id; + case 8: return PKIXExtensions.CRLDistributionPoints_Id; default: return new ObjectIdentifier(type); } } @@ -3712,6 +4008,28 @@ ("Illegal value: ") + extstr); } break; + case 8: // CRL, experimental, only support 1 distributionpoint + if(value != null) { + String[] ps = value.split(","); + GeneralNames gnames = new GeneralNames(); + for(String item: ps) { + colonpos = item.indexOf(':'); + if (colonpos < 0) { + throw new Exception("Illegal item " + item + " in " + extstr); + } + String t = item.substring(0, colonpos); + String v = item.substring(colonpos+1); + gnames.add(createGeneralName(t, v)); + } + ext.set(CRLDistributionPointsExtension.NAME, + new CRLDistributionPointsExtension( + isCritical, Collections.singletonList( + new DistributionPoint(gnames, null, null)))); + } else { + throw new Exception(rb.getString + ("Illegal value: ") + extstr); + } + break; case -1: ObjectIdentifier oid = new ObjectIdentifier(name); byte[] data = null; @@ -3748,6 +4066,9 @@ new DerValue(DerValue.tag_OctetString, data) .toByteArray())); break; + default: + throw new Exception(rb.getString( + "Unknown extension type: ") + extstr); } } // always non-critical @@ -3810,11 +4131,8 @@ System.err.println(rb.getString("Commands:")); System.err.println(); for (Command c: Command.values()) { - if (c != IDENTITYDB - && c != KEYCLONE - && c != SELFCERT) { // Deprecated commands - System.err.printf(" %-20s%s\n", c, rb.getString(c.description)); - } + if (c == KEYCLONE) break; + System.err.printf(" %-20s%s\n", c, rb.getString(c.description)); } System.err.println(); System.err.println(rb.getString(
--- a/src/share/classes/sun/security/tools/TimestampedSigner.java Thu May 06 11:26:16 2010 +0800 +++ b/src/share/classes/sun/security/tools/TimestampedSigner.java Thu May 06 13:42:52 2010 +0800 @@ -38,6 +38,7 @@ import java.util.List; import com.sun.jarsigner.*; +import java.security.cert.X509CRL; import java.util.Arrays; import sun.security.pkcs.*; import sun.security.timestamp.*; @@ -239,7 +240,7 @@ // Create the PKCS #7 signed data message PKCS7 p7 = new PKCS7(algorithms, contentInfo, signerCertificateChain, - signerInfos); + parameters.getCRLs().toArray(new X509CRL[parameters.getCRLs().size()]), signerInfos); ByteArrayOutputStream p7out = new ByteArrayOutputStream(); p7.encodeSignedData(p7out);
--- a/src/share/classes/sun/security/util/Resources.java Thu May 06 11:26:16 2010 +0800 +++ b/src/share/classes/sun/security/util/Resources.java Thu May 06 13:42:52 2010 +0800 @@ -1,5 +1,5 @@ /* - * Copyright 2000-2009 Sun Microsystems, Inc. All Rights Reserved. + * Copyright 2000-2010 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 @@ -71,6 +71,7 @@ "Generates a secret key"}, //-genseckey {"Generates certificate from a certificate request", "Generates certificate from a certificate request"}, //-gencert + {"Generates CRL", "Generates CRL"}, //-gencrl {"Imports entries from a JDK 1.1.x-style identity database", "Imports entries from a JDK 1.1.x-style identity database"}, //-identitydb {"Imports a certificate or a certificate chain", @@ -87,6 +88,8 @@ "Prints the content of a certificate"}, //-printcert {"Prints the content of a certificate request", "Prints the content of a certificate request"}, //-printcertreq + {"Prints the content of a CRL file", + "Prints the content of a CRL file"}, //-printcrl {"Generates a self-signed certificate", "Generates a self-signed certificate"}, //-selfcert {"Changes the store password of a keystore", @@ -176,6 +179,8 @@ "verbose output"}, //-v {"validity number of days", "validity number of days"}, //-validity + {"Serial ID of cert to revoke", + "Serial ID of cert to revoke"}, //-id // keytool: Running part {"keytool error: ", "keytool error: "}, {"Illegal option: ", "Illegal option: "}, @@ -375,6 +380,7 @@ {"Signer #%d:", "Signer #%d:"}, {"Timestamp:", "Timestamp:"}, {"Signature:", "Signature:"}, + {"CRLs:", "CRLs:"}, {"Certificate owner: ", "Certificate owner: "}, {"Not a signed jar file", "Not a signed jar file"}, {"No certificate from the SSL server", @@ -433,6 +439,7 @@ {"This extension cannot be marked as critical. ", "This extension cannot be marked as critical. "}, {"Odd number of hex digits found: ", "Odd number of hex digits found: "}, + {"Unknown extension type: ", "Unknown extension type: "}, {"command {0} is ambiguous:", "command {0} is ambiguous:"}, // policytool
--- a/src/share/classes/sun/security/util/SignatureFileVerifier.java Thu May 06 11:26:16 2010 +0800 +++ b/src/share/classes/sun/security/util/SignatureFileVerifier.java Thu May 06 13:42:52 2010 +0800 @@ -1,5 +1,5 @@ /* - * Copyright 1997-2009 Sun Microsystems, Inc. All Rights Reserved. + * Copyright 1997-2010 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 @@ -25,7 +25,6 @@ package sun.security.util; -import java.security.CodeSigner; import java.security.cert.CertPath; import java.security.cert.X509Certificate; import java.security.cert.CertificateException; @@ -34,11 +33,11 @@ import java.io.*; import java.util.*; import java.util.jar.*; -import java.io.ByteArrayOutputStream; import sun.security.pkcs.*; import sun.security.timestamp.TimestampToken; import sun.misc.BASE64Decoder; +import sun.misc.SharedSecrets; import sun.security.jca.Providers; @@ -479,7 +478,12 @@ signers = new ArrayList<CodeSigner>(); } // Append the new code signer - signers.add(new CodeSigner(certChain, getTimestamp(info))); + CodeSigner signer = new CodeSigner(certChain, getTimestamp(info)); + if (block.getCRLs() != null) { + SharedSecrets.getJavaSecurityCodeSignerAccess().setCRLs( + signer, block.getCRLs()); + } + signers.add(signer); if (debug != null) { debug.println("Signature Block Certificate: " +
--- a/src/share/classes/sun/security/x509/X509CRLImpl.java Thu May 06 11:26:16 2010 +0800 +++ b/src/share/classes/sun/security/x509/X509CRLImpl.java Thu May 06 13:42:52 2010 +0800 @@ -1,5 +1,5 @@ /* - * Copyright 1997-2007 Sun Microsystems, Inc. All Rights Reserved. + * Copyright 1997-2010 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 @@ -89,7 +89,7 @@ * @author Hemma Prafullchandra * @see X509CRL */ -public class X509CRLImpl extends X509CRL { +public class X509CRLImpl extends X509CRL implements DerEncoder { // CRL data, and its envelope private byte[] signedCRL = null; // DER encoded crl @@ -1189,6 +1189,13 @@ } } + @Override + public void derEncode(OutputStream out) throws IOException { + if (signedCRL == null) + throw new IOException("Null CRL to encode"); + out.write(signedCRL.clone()); + } + /** * Immutable X.509 Certificate Issuer DN and serial number pair */
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/test/sun/security/tools/jarsigner/crl.sh Thu May 06 13:42:52 2010 +0800 @@ -0,0 +1,91 @@ +# +# Copyright 2010 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 6890876 +# @summary jarsigner can add CRL info into signed jar +# + +if [ "${TESTJAVA}" = "" ] ; then + JAVAC_CMD=`which javac` + TESTJAVA=`dirname $JAVAC_CMD`/.. +fi + +# set platform-dependent variables +# PF: platform name, say, solaris-sparc + +PF="" + +OS=`uname -s` +case "$OS" in + Windows* ) + FS="\\" + ;; + * ) + FS="/" + ;; +esac + +KS=crl.jks +JFILE=crl.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 + +# Generates some crl files, each containing two entries + +$KT -alias a -dname CN=a -keyalg rsa -genkey -validity 300 +$KT -alias a -gencrl -id 1:1 -id 2:2 -file crl1 +$KT -alias a -gencrl -id 3:3 -id 4:4 -file crl2 +$KT -alias b -dname CN=b -keyalg rsa -genkey -validity 300 +$KT -alias b -gencrl -id 5:1 -id 6:2 -file crl3 + +$KT -alias c -dname CN=c -keyalg rsa -genkey -validity 300 \ + -ext crl=uri:file://`pwd`/crl1 + +echo A > A + +# Test -crl:auto, cRLDistributionPoints is a local file + +$JAR cvf $JFILE A +$JARSIGNER -keystore $KS -storepass changeit $JFILE c \ + -crl:auto || exit 1 +$JARSIGNER -keystore $KS -verify -debug -strict $JFILE || exit 6 +$KT -printcert -jarfile $JFILE | grep CRLs || exit 7 + +# Test -crl <file> + +$JAR cvf $JFILE A +$JARSIGNER -keystore $KS -storepass changeit $JFILE a \ + -crl crl1 -crl crl2 || exit 1 +$JARSIGNER -keystore $KS -storepass changeit $JFILE b \ + -crl crl3 -crl crl2 || exit 1 +$JARSIGNER -keystore $KS -verify -debug -strict $JFILE || exit 3 +$KT -printcert -jarfile $JFILE | grep CRLs || exit 4 +CRLCOUNT=`$KT -printcert -jarfile $JFILE | grep SerialNumber | wc -l` +if [ $CRLCOUNT != 8 ]; then exit 5; fi + +exit 0