changeset 2346:8834c3633f0b

6890876: jarsigner can add CRL info into signed jar Reviewed-by: mullan
author weijun
date Thu, 06 May 2010 13:42:52 +0800
parents 3d51799b65a9
children 0bda20126372
files src/share/classes/com/sun/jarsigner/ContentSignerParameters.java src/share/classes/java/security/CodeSigner.java src/share/classes/java/util/jar/JarVerifier.java src/share/classes/sun/misc/JavaSecurityCodeSignerAccess.java src/share/classes/sun/misc/SharedSecrets.java src/share/classes/sun/security/pkcs/PKCS7.java src/share/classes/sun/security/tools/JarSigner.java src/share/classes/sun/security/tools/JarSignerResources.java src/share/classes/sun/security/tools/KeyTool.java src/share/classes/sun/security/tools/TimestampedSigner.java src/share/classes/sun/security/util/Resources.java src/share/classes/sun/security/util/SignatureFileVerifier.java src/share/classes/sun/security/x509/X509CRLImpl.java test/sun/security/tools/jarsigner/crl.sh
diffstat 14 files changed, 684 insertions(+), 58 deletions(-) [+]
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