This is the mail archive of the java-patches@gcc.gnu.org mailing list for the Java project.


Index Nav: [Date Index] [Subject Index] [Author Index] [Thread Index]
Message Nav: [Date Prev] [Date Next] [Thread Prev] [Thread Next]
Other format: [Raw text]

[patch] merge signed jar file support from classpath


Hi all,

testing the following patch right now.

Please review carefully since I had some conflicts in URLClassLoader.java.

Would it be ok for main?


Andreas


2004-11-19 Andreas Tobler <a.tobler@schweiz.ch>

Merge signed jar file support from Classpath:

	* Makefile.am: Add new files.
	* Makefile.in: Regenerate.
	
	2004-11-07  Casey Marshall  <csm@gnu.org>

  	Signed JAR file support.
	* java/net/URLClassLoader.java
	(JarURLResource.getCertificates): Re-read jar entry to ensure
	certificates are picked up.
	(findClass): Fill in class `signers' field, too.
	* java/util/jar/JarFile.java (META_INF): New constant.
	(PKCS7_DSA_SUFFIX): New constant.
	(PKCS7_RSA_SUFFIX): New constant.
	(DIGEST_KEY_SUFFIX): New constant.
	(SF_SUFFIX): New constant.
	(MD2_OID): New constant.
	(MD4_OID): New constant.
	(MD5_OID): New constant.
	(SHA1_OID): New constant.
	(DSA_ENCRYPTION_OID): New constant.
	(RSA_ENCRYPTION_OID): New constant.
	(signaturesRead): New field.
	(verified): New field.
	(entryCerts): New field.
	(DEBUG): New constant.
	(debug): New method.
	(JarEnumeration.nextElement): Fill in entry certificates, read
	signatures if they haven't been read.
	(getEntry): Likewise.
	(getInputStream): Verify stream if it hasn't been verified yet.
	(readSignatures): New method.
	(verify): New method.
	(verifyHashes): New method.
	(readManifestEntry): New method.
	(EntryInputStream): New class.
	* java/util/zip/InflaterInputStream.java
	Don't defer to underlying stream for mark/reset.
	(markSupported): New method; return `false'.
	(mark): New method.
	(reset): New method.
	* gnu/java/io/Base64InputStream.java (decode): New class method.
	* gnu/java/security/der/DERReader.java: Don't make class final.
	(in): Made protected.
	(encBuf): Likewise.
	(readLength): Likewise.
	* gnu/java/security/ber/BER.java,
	* gnu/java/security/ber/BEREncodingException.java,
	* gnu/java/security/ber/BERReader.java,
	* gnu/java/security/ber/BERValue.java,
	* gnu/java/security/pkcs/PKCS7SignedData.java,
	* gnu/java/security/pkcs/SignerInfo.java: New files.

2004-11-07 Casey Marshall <csm@gnu.org>

	* gnu/java/security/provider/GnuDSAPrivateKey.java
	(encodedKey): New field.
	(getFormat): Return "PKCS#8".
	(getEncoded): Implemented.
	(toString): Check for 'null' values.
	* gnu/java/security/provider/GnuDSAPublicKey.java
	(encodedKey): New field.
	(getFormat): Return "X.509".
	(getEncoded): Implemented.
	(toString): Check for 'null' values.

2004-11-07 Mark Wielaard <mark@klomp.org>

	* java/util/jar/JarFile.java (EntryInputStream): Add actual
	InputStream as argument.
	(getInputStream): Construct a new EntryInputStream with the result of
	super.getInputStream(entry).
Index: Makefile.am
===================================================================
RCS file: /cvs/gcc/gcc/libjava/Makefile.am,v
retrieving revision 1.427
diff -u -r1.427 Makefile.am
--- Makefile.am	18 Nov 2004 05:37:09 -0000	1.427
+++ Makefile.am	18 Nov 2004 21:46:22 -0000
@@ -2766,12 +2766,18 @@
 gnu/java/security/action/GetPropertyAction.java \
 gnu/java/security/action/GetSecurityPropertyAction.java \
 gnu/java/security/action/SetAccessibleAction.java \
+gnu/java/security/ber/BER.java \
+gnu/java/security/ber/BEREncodingException.java \
+gnu/java/security/ber/BERReader.java \
+gnu/java/security/ber/BERValue.java \
 gnu/java/security/der/BitString.java \
 gnu/java/security/der/DER.java \
 gnu/java/security/der/DEREncodingException.java \
 gnu/java/security/der/DERReader.java \
 gnu/java/security/der/DERValue.java \
 gnu/java/security/der/DERWriter.java \
+gnu/java/security/pkcs/PKCS7SignedData.java \
+gnu/java/security/pkcs/SignerInfo.java \
 gnu/java/security/provider/CollectionCertStoreImpl.java \
 gnu/java/security/provider/DSAKeyFactory.java \
 gnu/java/security/provider/DSAKeyPairGenerator.java \
Index: gnu/java/io/Base64InputStream.java
===================================================================
RCS file: /cvs/gcc/gcc/libjava/gnu/java/io/Base64InputStream.java,v
retrieving revision 1.2
diff -u -r1.2 Base64InputStream.java
--- gnu/java/io/Base64InputStream.java	3 May 2004 19:52:27 -0000	1.2
+++ gnu/java/io/Base64InputStream.java	18 Nov 2004 21:46:22 -0000
@@ -1,5 +1,5 @@
 /* Base64InputStream.java -- base-64 input stream.
-   Copyright (C) 2003 Free Software Foundation, Inc.
+   Copyright (C) 2003, 2004 Free Software Foundation, Inc.
 
 This file is part of GNU Classpath.
 
@@ -38,6 +38,8 @@
 
 package gnu.java.io;
 
+import java.io.ByteArrayInputStream;
+import java.io.ByteArrayOutputStream;
 import java.io.FilterInputStream;
 import java.io.IOException;
 import java.io.InputStream;
@@ -90,6 +92,30 @@
     eof = false;
   }
 
+  // Class method.
+  // ------------------------------------------------------------------------
+
+  /**
+   * Decode a single Base-64 string to a byte array.
+   *
+   * @param base64 The Base-64 encoded data.
+   * @return The decoded bytes.
+   * @throws IOException If the given data do not compose a valid Base-64
+   *  sequence.
+   */
+  public static byte[] decode(String base64) throws IOException
+  {
+    Base64InputStream in =
+      new Base64InputStream(new ByteArrayInputStream(base64.getBytes()));
+    ByteArrayOutputStream out =
+      new ByteArrayOutputStream((int) (base64.length() / 0.666));
+    byte[] buf = new byte[1024];
+    int len;
+    while ((len = in.read(buf)) != -1)
+      out.write(buf, 0, len);
+    return out.toByteArray();
+  }
+
   // Instance methods.
   // ------------------------------------------------------------------------
 
Index: gnu/java/security/der/DERReader.java
===================================================================
RCS file: /cvs/gcc/gcc/libjava/gnu/java/security/der/DERReader.java,v
retrieving revision 1.3
diff -u -r1.3 DERReader.java
--- gnu/java/security/der/DERReader.java	30 Aug 2004 13:06:48 -0000	1.3
+++ gnu/java/security/der/DERReader.java	18 Nov 2004 21:46:23 -0000
@@ -62,15 +62,15 @@
  *
  * @author Casey Marshall (csm@gnu.org)
  */
-public final class DERReader implements DER
+public class DERReader implements DER
 {
 
   // Fields.
   // ------------------------------------------------------------------------
 
-  private InputStream in;
+  protected InputStream in;
 
-  private final ByteArrayOutputStream encBuf;
+  protected final ByteArrayOutputStream encBuf;
 
   // Constructor.
   // ------------------------------------------------------------------------
@@ -185,6 +185,26 @@
     return value;
   }
 
+  protected int readLength() throws IOException
+  {
+    int i = in.read();
+    if (i == -1)
+      throw new EOFException();
+    encBuf.write(i);
+    if ((i & ~0x7F) == 0)
+      {
+        return i;
+      }
+    else if (i < 0xFF)
+      {
+        byte[] octets = new byte[i & 0x7F];
+        in.read(octets);
+        encBuf.write(octets);
+        return new BigInteger(1, octets).intValue();
+      }
+    throw new DEREncodingException();
+  }
+
   // Own methods.
   // ------------------------------------------------------------------------
 
@@ -236,26 +256,6 @@
       }
   }
 
-  private int readLength() throws IOException
-  {
-    int i = in.read();
-    if (i == -1)
-      throw new EOFException();
-    encBuf.write(i);
-    if ((i & ~0x7F) == 0)
-      {
-        return i;
-      }
-    else if (i < 0xFF)
-      {
-        byte[] octets = new byte[i & 0x7F];
-        in.read(octets);
-        encBuf.write(octets);
-        return new BigInteger(1, octets).intValue();
-      }
-    throw new DEREncodingException();
-  }
-
   private static String makeString(int tag, byte[] value)
     throws IOException
   {
Index: java/util/zip/InflaterInputStream.java
===================================================================
RCS file: /cvs/gcc/gcc/libjava/java/util/zip/InflaterInputStream.java,v
retrieving revision 1.24
diff -u -r1.24 InflaterInputStream.java
--- java/util/zip/InflaterInputStream.java	7 Nov 2004 01:25:47 -0000	1.24
+++ java/util/zip/InflaterInputStream.java	18 Nov 2004 21:46:23 -0000
@@ -245,4 +245,18 @@
 
     return skipped;
  }
+
+  public boolean markSupported()
+  {
+    return false;
+  }
+
+  public void mark(int readLimit)
+  {
+  }
+
+  public void reset() throws IOException
+  {
+    throw new IOException("reset not supported");
+  }
 }
Index: java/util/jar/JarFile.java
===================================================================
RCS file: /cvs/gcc/gcc/libjava/java/util/jar/JarFile.java,v
retrieving revision 1.12
diff -u -r1.12 JarFile.java
--- java/util/jar/JarFile.java	23 Apr 2004 06:36:05 -0000	1.12
+++ java/util/jar/JarFile.java	18 Nov 2004 21:46:24 -0000
@@ -37,11 +37,38 @@
 
 package java.util.jar;
 
+import gnu.java.io.Base64InputStream;
+import gnu.java.security.OID;
+import gnu.java.security.pkcs.PKCS7SignedData;
+import gnu.java.security.pkcs.SignerInfo;
+
+import java.io.ByteArrayOutputStream;
 import java.io.File;
 import java.io.FileNotFoundException;
+import java.io.FilterInputStream;
 import java.io.IOException;
 import java.io.InputStream;
+
+import java.security.InvalidKeyException;
+import java.security.MessageDigest;
+import java.security.NoSuchAlgorithmException;
+import java.security.Signature;
+import java.security.SignatureException;
+import java.security.cert.CRLException;
+import java.security.cert.Certificate;
+import java.security.cert.CertificateException;
+import java.security.cert.X509Certificate;
+
+import java.util.Arrays;
+import java.util.HashMap;
+import java.util.HashSet;
+import java.util.Iterator;
 import java.util.Enumeration;
+import java.util.LinkedList;
+import java.util.List;
+import java.util.Map;
+import java.util.Set;
+
 import java.util.zip.ZipEntry;
 import java.util.zip.ZipException;
 import java.util.zip.ZipFile;
@@ -52,11 +79,11 @@
  * Note that this class is not a subclass of java.io.File but a subclass of
  * java.util.zip.ZipFile and you can only read JarFiles with it (although
  * there are constructors that take a File object).
- * <p>
- * XXX - verification of Manifest signatures is not yet implemented.
  *
  * @since 1.2
  * @author Mark Wielaard (mark@klomp.org)
+ * @author Casey Marshall (csm@gnu.org) wrote the certificate and entry
+ *  verification code.
  */
 public class JarFile extends ZipFile
 {
@@ -65,6 +92,29 @@
   /** The name of the manifest entry: META-INF/MANIFEST.MF */
   public static final String MANIFEST_NAME = "META-INF/MANIFEST.MF";
 
+  /** The META-INF directory entry. */
+  private static final String META_INF = "META-INF/";
+
+  /** The suffix for PKCS7 DSA signature entries. */
+  private static final String PKCS7_DSA_SUFFIX = ".DSA";
+
+  /** The suffix for PKCS7 RSA signature entries. */
+  private static final String PKCS7_RSA_SUFFIX = ".RSA";
+
+  /** The suffix for digest attributes. */
+  private static final String DIGEST_KEY_SUFFIX = "-Digest";
+
+  /** The suffix for signature files. */
+  private static final String SF_SUFFIX = ".SF";
+
+  // Signature OIDs.
+  private static final OID MD2_OID = new OID("1.2.840.113549.2.2");
+  private static final OID MD4_OID = new OID("1.2.840.113549.2.4");
+  private static final OID MD5_OID = new OID("1.2.840.113549.2.5");
+  private static final OID SHA1_OID = new OID("1.3.14.3.2.26");
+  private static final OID DSA_ENCRYPTION_OID = new OID("1.2.840.10040.4.1");
+  private static final OID RSA_ENCRYPTION_OID = new OID("1.2.840.113549.1.1.1");
+
   /**
    * The manifest of this file, if any, otherwise null.
    * Read when first needed.
@@ -77,6 +127,24 @@
   /** Whether the has already been loaded. */
   private boolean manifestRead = false;
 
+  /** Whether the signature files have been loaded. */
+  private boolean signaturesRead = false;
+
+  /** A map between entry names and booleans, signaling whether or
+      not that entry has been verified. */
+  private HashMap verified = new HashMap();
+
+  /** A mapping from entry name to certificates, if any. */
+  private HashMap entryCerts;
+
+  private static boolean DEBUG = false;
+  private static void debug(Object msg)
+  {
+    System.err.print(JarFile.class.getName());
+    System.err.print(" >>> ");
+    System.err.println(msg);
+  }
+
   // Constructors
 
   /**
@@ -241,7 +309,6 @@
   /**
    * Wraps a given Zip Entries Enumeration. For every zip entry a
    * JarEntry is created and the corresponding Attributes are looked up.
-   * XXX - Should also look up the certificates.
    */
   private class JarEnumeration implements Enumeration
   {
@@ -276,7 +343,33 @@
 	{
 	  jar.attr = manifest.getAttributes(jar.getName());
 	}
-      // XXX jar.certs
+
+      if (!signaturesRead)
+        try
+          {
+            readSignatures();
+          }
+        catch (IOException ioe)
+          {
+            if (DEBUG)
+              {
+                debug(ioe);
+                ioe.printStackTrace();
+              }
+            signaturesRead = true; // fudge it.
+          }
+
+      // Include the certificates only if we have asserted that the
+      // signatures are valid. This means the certificates will not be
+      // available if the entry hasn't been read yet.
+      if (entryCerts != null && verified.containsKey(zip.getName())
+          && ((Boolean) verified.get(zip.getName())).booleanValue())
+        {
+          Set certs = (Set) entryCerts.get(jar.getName());
+          if (certs != null)
+            jar.certs = (Certificate[])
+              certs.toArray(new Certificate[certs.size()]);
+        }
       return jar;
     }
   }
@@ -305,7 +398,34 @@
 	if (manifest != null)
 	  {
 	    jarEntry.attr = manifest.getAttributes(name);
-	    // XXX jarEntry.certs
+          }
+
+        if (!signaturesRead)
+          try
+            {
+              readSignatures();
+            }
+          catch (IOException ioe)
+            {
+              if (DEBUG)
+                {
+                  debug(ioe);
+                  ioe.printStackTrace();
+                }
+              signaturesRead = true;
+            }
+        // See the comments in the JarEnumeration for why we do this
+        // check.
+        if (DEBUG)
+          debug("entryCerts=" + entryCerts + " verified " + name
+                + " ? " + verified.get(name));
+        if (entryCerts != null && verified.containsKey(name)
+            && ((Boolean) verified.get(name)).booleanValue())
+          {
+            Set certs = (Set) entryCerts.get(name);
+            if (certs != null)
+              jarEntry.certs = (Certificate[])
+                certs.toArray(new Certificate[certs.size()]);
 	  }
 	return jarEntry;
       }
@@ -313,15 +433,32 @@
   }
 
   /**
-   * XXX should verify the inputstream
-   * @param entry XXX
+   * Returns an input stream for the given entry. If configured to
+   * verify entries, the input stream returned will verify them while
+   * the stream is read, but only on the first time.
+   *
+   * @param entry The entry to get the input stream for.
    * @exception ZipException XXX
    * @exception IOException XXX
    */
   public synchronized InputStream getInputStream(ZipEntry entry) throws
     ZipException, IOException
   {
-    return super.getInputStream(entry);	// XXX verify
+    // If we haven't verified the hash, do it now.
+    if (!verified.containsKey(entry.getName()) && verify)
+      {
+        if (DEBUG)
+          debug("reading and verifying " + entry);
+        return new EntryInputStream(entry, super.getInputStream(entry));
+      }
+    else
+      {
+        if (DEBUG)
+          debug("reading already verified entry " + entry);
+        if (!((Boolean) verified.get(entry.getName())).booleanValue())
+          throw new ZipException("digest for " + entry + " is invalid");
+        return super.getInputStream(entry);
+      }
   }
 
   /**
@@ -349,4 +486,536 @@
 
     return manifest;
   }
+
+  private void readSignatures() throws IOException
+  {
+    Map pkcs7Dsa = new HashMap();
+    Map pkcs7Rsa = new HashMap();
+    Map sigFiles = new HashMap();
+
+    // Phase 1: Read all signature files. These contain the user
+    // certificates as well as the signatures themselves.
+    for (Enumeration e = super.entries(); e.hasMoreElements(); )
+      {
+        ZipEntry ze = (ZipEntry) e.nextElement();
+        String name = ze.getName();
+        if (name.startsWith(META_INF))
+          {
+            String alias = name.substring(META_INF.length());
+            if (alias.lastIndexOf('.') >= 0)
+              alias = alias.substring(0, alias.lastIndexOf('.'));
+
+            if (name.endsWith(PKCS7_DSA_SUFFIX) || name.endsWith(PKCS7_RSA_SUFFIX))
+              {
+                if (DEBUG)
+                  debug("reading PKCS7 info from " + name + ", alias=" + alias);
+                PKCS7SignedData sig = null;
+                try
+                  {
+                    sig = new PKCS7SignedData(super.getInputStream(ze));
+                  }
+                catch (CertificateException ce)
+                  {
+                    IOException ioe = new IOException("certificate parsing error");
+                    ioe.initCause(ce);
+                    throw ioe;
+                  }
+                catch (CRLException crle)
+                  {
+                    IOException ioe = new IOException("CRL parsing error");
+                    ioe.initCause(crle);
+                    throw ioe;
+                  }
+                if (name.endsWith(PKCS7_DSA_SUFFIX))
+                  pkcs7Dsa.put(alias, sig);
+                else if (name.endsWith(PKCS7_RSA_SUFFIX))
+                  pkcs7Rsa.put(alias, sig);
+              }
+            else if (name.endsWith(SF_SUFFIX))
+              {
+                if (DEBUG)
+                  debug("reading signature file for " + alias + ": " + name);
+                Manifest sf = new Manifest(super.getInputStream(ze));
+                sigFiles.put(alias, sf);
+                if (DEBUG)
+                  debug("result: " + sf);
+              }
+          }
+      }
+
+    // Phase 2: verify the signatures on any signature files.
+    Set validCerts = new HashSet();
+    Map entryCerts = new HashMap();
+    for (Iterator it = sigFiles.entrySet().iterator(); it.hasNext(); )
+      {
+        int valid = 0;
+        Map.Entry e = (Map.Entry) it.next();
+        String alias = (String) e.getKey();
+
+        PKCS7SignedData sig = (PKCS7SignedData) pkcs7Dsa.get(alias);
+        if (sig != null)
+          {
+            Certificate[] certs = sig.getCertificates();
+            Set signerInfos = sig.getSignerInfos();
+            for (Iterator it2 = signerInfos.iterator(); it2.hasNext(); )
+              verify(certs, (SignerInfo) it2.next(), alias, validCerts);
+          }
+
+        sig = (PKCS7SignedData) pkcs7Rsa.get(alias);
+        if (sig != null)
+          {
+            Certificate[] certs = sig.getCertificates();
+            Set signerInfos = sig.getSignerInfos();
+            for (Iterator it2 = signerInfos.iterator(); it2.hasNext(); )
+              verify(certs, (SignerInfo) it2.next(), alias, validCerts);
+          }
+
+        // It isn't a signature for anything. Punt it.
+        if (validCerts.isEmpty())
+          {
+            it.remove();
+            continue;
+          }
+
+        entryCerts.put(e.getValue(), new HashSet(validCerts));
+        validCerts.clear();
+      }
+
+    // Phase 3: verify the signature file signatures against the manifest,
+    // mapping the entry name to the target certificates.
+    this.entryCerts = new HashMap();
+    for (Iterator it = entryCerts.entrySet().iterator(); it.hasNext(); )
+      {
+        Map.Entry e = (Map.Entry) it.next();
+        Manifest sigfile = (Manifest) e.getKey();
+        Map entries = sigfile.getEntries();
+        Set certificates = (Set) e.getValue();
+
+        for (Iterator it2 = entries.entrySet().iterator(); it2.hasNext(); )
+          {
+            Map.Entry e2 = (Map.Entry) it2.next();
+            String entryname = String.valueOf(e2.getKey());
+            Attributes attr = (Attributes) e2.getValue();
+            if (verifyHashes(entryname, attr))
+              {
+                if (DEBUG)
+                  debug("entry " + entryname + " has certificates " + certificates);
+                Set s = (Set) this.entryCerts.get(entryname);
+                if (s != null)
+                  s.addAll(certificates);
+                else
+                  this.entryCerts.put(entryname, new HashSet(certificates));
+              }
+          }
+      }
+
+    signaturesRead = true;
+  }
+
+  /**
+   * Tell if the given signer info is over the given alias's signature file,
+   * given one of the certificates specified.
+   */
+  private void verify(Certificate[] certs, SignerInfo signerInfo,
+                      String alias, Set validCerts)
+  {
+    Signature sig = null;
+    try
+      {
+        OID alg = signerInfo.getDigestEncryptionAlgorithmId();
+        if (alg.equals(DSA_ENCRYPTION_OID))
+          {
+            if (!signerInfo.getDigestAlgorithmId().equals(SHA1_OID))
+              return;
+            sig = Signature.getInstance("SHA1withDSA");
+          }
+        else if (alg.equals(RSA_ENCRYPTION_OID))
+          {
+            OID hash = signerInfo.getDigestAlgorithmId();
+            if (hash.equals(MD2_OID))
+              sig = Signature.getInstance("md2WithRsaEncryption");
+            else if (hash.equals(MD4_OID))
+              sig = Signature.getInstance("md4WithRsaEncryption");
+            else if (hash.equals(MD5_OID))
+              sig = Signature.getInstance("md5WithRsaEncryption");
+            else if (hash.equals(SHA1_OID))
+              sig = Signature.getInstance("sha1WithRsaEncryption");
+            else
+              return;
+          }
+      }
+    catch (NoSuchAlgorithmException nsae)
+      {
+        if (DEBUG)
+          {
+            debug(nsae);
+            nsae.printStackTrace();
+          }
+        return;
+      }
+    ZipEntry sigFileEntry = super.getEntry(META_INF + alias + SF_SUFFIX);
+    if (sigFileEntry == null)
+      return;
+    for (int i = 0; i < certs.length; i++)
+      {
+        if (!(certs[i] instanceof X509Certificate))
+          continue;
+        X509Certificate cert = (X509Certificate) certs[i];
+        if (!cert.getIssuerX500Principal().equals(signerInfo.getIssuer()) ||
+            !cert.getSerialNumber().equals(signerInfo.getSerialNumber()))
+          continue;
+        try
+          {
+            sig.initVerify(cert.getPublicKey());
+            InputStream in = super.getInputStream(sigFileEntry);
+            if (in == null)
+              continue;
+            byte[] buf = new byte[1024];
+            int len = 0;
+            while ((len = in.read(buf)) != -1)
+              sig.update(buf, 0, len);
+            if (sig.verify(signerInfo.getEncryptedDigest()))
+              {
+                if (DEBUG)
+                  debug("signature for " + cert.getSubjectDN() + " is good");
+                validCerts.add(cert);
+              }
+          }
+        catch (IOException ioe)
+          {
+            continue;
+          }
+        catch (InvalidKeyException ike)
+          {
+            continue;
+          }
+        catch (SignatureException se)
+          {
+            continue;
+          }
+      }
+  }
+
+  /**
+   * Verifies that the digest(s) in a signature file were, in fact, made
+   * over the manifest entry for ENTRY.
+   *
+   * @param entry The entry name.
+   * @param attr The attributes from the signature file to verify.
+   */
+  private boolean verifyHashes(String entry, Attributes attr)
+  {
+    int verified = 0;
+
+    // The bytes for ENTRY's manifest entry, which are signed in the
+    // signature file.
+    byte[] entryBytes = null;
+    try
+      {
+        entryBytes = readManifestEntry(super.getEntry(entry));
+      }
+    catch (IOException ioe)
+      {
+        if (DEBUG)
+          {
+            debug(ioe);
+            ioe.printStackTrace();
+          }
+        return false;
+      }
+
+    for (Iterator it = attr.entrySet().iterator(); it.hasNext(); )
+      {
+        Map.Entry e = (Map.Entry) it.next();
+        String key = String.valueOf(e.getKey());
+        if (!key.endsWith(DIGEST_KEY_SUFFIX))
+          continue;
+        String alg = key.substring(0, key.length() - DIGEST_KEY_SUFFIX.length());
+        try
+          {
+            byte[] hash = Base64InputStream.decode((String) e.getValue());
+            MessageDigest md = MessageDigest.getInstance(alg);
+            md.update(entryBytes);
+            byte[] hash2 = md.digest();
+            if (DEBUG)
+              debug("verifying SF entry " + entry + " alg: " + md.getAlgorithm()
+                    + " expect=" + new java.math.BigInteger(hash).toString(16)
+                    + " comp=" + new java.math.BigInteger(hash2).toString(16));
+            if (!Arrays.equals(hash, hash2))
+              return false;
+            verified++;
+          }
+        catch (IOException ioe)
+          {
+            if (DEBUG)
+              {
+                debug(ioe);
+                ioe.printStackTrace();
+              }
+            return false;
+          }
+        catch (NoSuchAlgorithmException nsae)
+          {
+            if (DEBUG)
+              {
+                debug(nsae);
+                nsae.printStackTrace();
+              }
+            return false;
+          }
+      }
+
+    // We have to find at least one valid digest.
+    return verified > 0;
+  }
+
+  /**
+   * Read the raw bytes that comprise a manifest entry. We can't use the
+   * Manifest object itself, because that loses information (such as line
+   * endings, and order of entries).
+   */
+  private byte[] readManifestEntry(ZipEntry entry) throws IOException
+  {
+    InputStream in = super.getInputStream(super.getEntry(MANIFEST_NAME));
+    ByteArrayOutputStream out = new ByteArrayOutputStream();
+    byte[] target = ("Name: " + entry.getName()).getBytes();
+    int t = 0, c, prev = -1, state = 0, l = -1;
+
+    while ((c = in.read()) != -1)
+      {
+//         if (DEBUG)
+//           debug("read "
+//                 + (c == '\n' ? "\\n" : (c == '\r' ? "\\r" : String.valueOf((char) c)))
+//                 + " state=" + state + " prev="
+//                 + (prev == '\n' ? "\\n" : (prev == '\r' ? "\\r" : String.valueOf((char) prev)))
+//                 + " t=" + t + (t < target.length ? (" target[t]=" + (char) target[t]) : "")
+//                 + " l=" + l);
+        switch (state)
+          {
+
+          // Step 1: read until we find the "target" bytes: the start
+          // of the entry we need to read.
+          case 0:
+            if (((byte) c) != target[t])
+              t = 0;
+            else
+              {
+                t++;
+                if (t == target.length)
+                  {
+                    out.write(target);
+                    state = 1;
+                  }
+              }
+            break;
+
+          // Step 2: assert that there is a newline character after
+          // the "target" bytes.
+          case 1:
+            if (c != '\n' && c != '\r')
+              {
+                out.reset();
+                t = 0;
+                state = 0;
+              }
+            else
+              {
+                out.write(c);
+                state = 2;
+              }
+            break;
+
+          // Step 3: read this whole entry, until we reach an empty
+          // line.
+          case 2:
+            if (c == '\n')
+              {
+                out.write(c);
+                // NL always terminates a line.
+                if (l == 0 || (l == 1 && prev == '\r'))
+                  return out.toByteArray();
+                l = 0;
+              }
+            else
+              {
+                // Here we see a blank line terminated by a CR,
+                // followed by the next entry. Technically, `c' should
+                // always be 'N' at this point.
+                if (l == 1 && prev == '\r')
+                  return out.toByteArray();
+                out.write(c);
+                l++;
+              }
+            prev = c;
+            break;
+
+          default:
+            throw new RuntimeException("this statement should be unreachable");
+          }
+      }
+
+    // The last entry, with a single CR terminating the line.
+    if (state == 2 && prev == '\r' && l == 0)
+      return out.toByteArray();
+
+    // We should not reach this point, we didn't find the entry (or, possibly,
+    // it is the last entry and is malformed).
+    throw new IOException("could not find " + entry + " in manifest");
+  }
+
+  /**
+   * A utility class that verifies jar entries as they are read.
+   */
+  private class EntryInputStream extends FilterInputStream
+  {
+    private final long length;
+    private long pos;
+    private final ZipEntry entry;
+    private final byte[][] hashes;
+    private final MessageDigest[] md;
+    private boolean checked;
+
+    EntryInputStream(final ZipEntry entry, final InputStream in)
+      throws IOException
+    {
+      super(in);
+      this.entry = entry;
+
+      length = entry.getSize();
+      pos = 0;
+      checked = false;
+
+      Attributes attr = manifest.getAttributes(entry.getName());
+      if (DEBUG)
+        debug("verifying entry " + entry + " attr=" + attr);
+      if (attr == null)
+        {
+          hashes = new byte[0][];
+          md = new MessageDigest[0];
+        }
+      else
+        {
+          List hashes = new LinkedList();
+          List md = new LinkedList();
+          for (Iterator it = attr.entrySet().iterator(); it.hasNext(); )
+            {
+              Map.Entry e = (Map.Entry) it.next();
+              String key = String.valueOf(e.getKey());
+              if (key == null)
+                continue;
+              if (!key.endsWith(DIGEST_KEY_SUFFIX))
+                continue;
+              hashes.add(Base64InputStream.decode((String) e.getValue()));
+              try
+                {
+                  md.add(MessageDigest.getInstance
+                         (key.substring(0, key.length() - DIGEST_KEY_SUFFIX.length())));
+                }
+              catch (NoSuchAlgorithmException nsae)
+                {
+                  IOException ioe = new IOException("no such message digest: " + key);
+                  ioe.initCause(nsae);
+                  throw ioe;
+                }
+            }
+          if (DEBUG)
+            debug("digests=" + md);
+          this.hashes = (byte[][]) hashes.toArray(new byte[hashes.size()][]);
+          this.md = (MessageDigest[]) md.toArray(new MessageDigest[md.size()]);
+        }
+    }
+
+    public boolean markSupported()
+    {
+      return false;
+    }
+
+    public void mark(int readLimit)
+    {
+    }
+
+    public void reset()
+    {
+    }
+
+    public int read() throws IOException
+    {
+      int b = super.read();
+      if (b == -1)
+        {
+          eof();
+          return -1;
+        }
+      for (int i = 0; i < md.length; i++)
+        md[i].update((byte) b);
+      pos++;
+      if (length > 0 && pos >= length)
+        eof();
+      return b;
+    }
+
+    public int read(byte[] buf, int off, int len) throws IOException
+    {
+      int count = super.read(buf, off, (int) Math.min(len, (length != 0
+                                                            ? length - pos
+                                                            : Integer.MAX_VALUE)));
+      if (count == -1 || (length > 0 && pos >= length))
+        {
+          eof();
+          return -1;
+        }
+      for (int i = 0; i < md.length; i++)
+        md[i].update(buf, off, count);
+      pos += count;
+      if (length != 0 && pos >= length)
+        eof();
+      return count;
+    }
+
+    public int read(byte[] buf) throws IOException
+    {
+      return read(buf, 0, buf.length);
+    }
+
+    public long skip(long bytes) throws IOException
+    {
+      byte[] b = new byte[1024];
+      long amount = 0;
+      while (amount < bytes)
+        {
+          int l = read(b, 0, (int) Math.min(b.length, bytes - amount));
+          if (l == -1)
+            break;
+          amount += l;
+        }
+      return amount;
+    }
+
+    private void eof() throws IOException
+    {
+      if (checked)
+        return;
+      checked = true;
+      for (int i = 0; i < md.length; i++)
+        {
+          byte[] hash = md[i].digest();
+          if (DEBUG)
+            debug("verifying " + md[i].getAlgorithm() + " expect="
+                  + new java.math.BigInteger(hashes[i]).toString(16)
+                  + " comp=" + new java.math.BigInteger(hash).toString(16));
+          if (!Arrays.equals(hash, hashes[i]))
+            {
+              if (DEBUG)
+                debug(entry + " could NOT be verified");
+              verified.put(entry.getName(), Boolean.FALSE);
+              return;
+              // XXX ??? what do we do here?
+              // throw new ZipException("message digest mismatch");
+            }
+        }
+      if (DEBUG)
+        debug(entry + " has been VERIFIED");
+      verified.put(entry.getName(), Boolean.TRUE);
+    }
+  }
 }
Index: java/net/URLClassLoader.java
===================================================================
RCS file: /cvs/gcc/gcc/libjava/java/net/URLClassLoader.java,v
retrieving revision 1.21
diff -u -r1.21 URLClassLoader.java
--- java/net/URLClassLoader.java	18 Nov 2004 19:00:32 -0000	1.21
+++ java/net/URLClassLoader.java	18 Nov 2004 21:46:25 -0000
@@ -372,7 +372,11 @@
 
     Certificate[] getCertificates()
     {
-      return entry.getCertificates();
+      // We have to get the entry from the jar file again, because the
+      // certificates will not be available until the entire entry has
+      // been read.
+      return ((JarEntry) ((JarURLLoader) loader).jarfile.getEntry(name))
+        .getCertificates();
     }
 
     URL getURL()
@@ -851,85 +855,96 @@
     // construct the class (and watch out for those nasty IOExceptions)
     try
       {
-        byte[] data;
-        InputStream in = resource.getInputStream();
-	int length = resource.getLength();
-	if (length != -1)
+	byte[] data;
+	InputStream in = resource.getInputStream();
+	try
 	  {
-	    // We know the length of the data.
-	    // Just try to read it in all at once
-	    data = new byte[length];
-	    int pos = 0;
-	    while (length - pos > 0)
+	    int length = resource.getLength();
+	    if (length != -1)
 	      {
-		int len = in.read(data, pos, length - pos);
-		if (len == -1)
-		  throw new EOFException("Not enough data reading from: "
-					 + in);
-		pos += len;
+		// We know the length of the data.
+		// Just try to read it in all at once
+		data = new byte[length];
+		int pos = 0;
+		 while (length - pos > 0)
+		   {
+		     int len = in.read(data, pos, length - pos);
+		     if (len == -1)
+		       throw new EOFException("Not enough data reading from: "
+					      + in);
+		     pos += len;
+		   }
+	      }
+	    else
+	      {
+		// We don't know the data length.
+		// Have to read it in chunks.
+		ByteArrayOutputStream out = new ByteArrayOutputStream(4096);
+		byte[] b = new byte[4096];
+		int l = 0;
+		while (l != -1)
+		  {
+		    l = in.read(b);
+		    if (l != -1)
+		      out.write(b, 0, l);
+		  }
+		data = out.toByteArray();
 	      }
 	  }
-	else
+	finally
+	  {
+	    in.close();
+	  }
+	final byte[] classData = data;
+	
+	// Now get the CodeSource
+	final CodeSource source = resource.getCodeSource();
+	
+	// Find out package name
+	String packageName = null;
+	int lastDot = className.lastIndexOf('.');
+	if (lastDot != -1)
+	  packageName = className.substring(0, lastDot);
+	
+	if (packageName != null && getPackage(packageName) == null)
+	  {
+	    // define the package
+	    Manifest manifest = resource.loader.getManifest();
+	    if (manifest == null)
+	      definePackage(packageName, null, null, null, null,
+			    null, null, null);
+	    else
+	      definePackage(packageName, manifest,
+			    resource.loader.baseURL);
+	  }
+	
+	// And finally construct the class!
+	SecurityManager sm = System.getSecurityManager();
+	Class result = null;
+	if (sm != null && securityContext != null)
 	  {
-	    // We don't know the data length.
-	    // Have to read it in chunks.
-	    ByteArrayOutputStream out = new ByteArrayOutputStream(4096);
-	    byte[] b = new byte[4096];
-	    int l = 0;
-	    while (l != -1)
+	    result = (Class)AccessController.doPrivileged(new PrivilegedAction()
 	      {
-		l = in.read(b);
-		if (l != -1)
-		  out.write(b, 0, l);
-	      }
-	    data = out.toByteArray();
-          }
-        final byte[] classData = data;
-
-        // Now get the CodeSource
-        final CodeSource source = resource.getCodeSource();
-
-        // Find out package name
-        String packageName = null;
-        int lastDot = className.lastIndexOf('.');
-        if (lastDot != -1)
-          packageName = className.substring(0, lastDot);
-
-        if (packageName != null && getPackage(packageName) == null)
-          {
-            // define the package
-            Manifest manifest = resource.loader.getManifest();
-            if (manifest == null)
-              definePackage(packageName, null, null, null, null, null, null,
-                            null);
-            else
-              definePackage(packageName, manifest, resource.loader.baseURL);
-          }
-
-        // And finally construct the class!
-        SecurityManager sm = System.getSecurityManager();
-        if (sm != null && securityContext != null)
-          {
-	    return (Class)AccessController.doPrivileged
-              (new PrivilegedAction()
-                {
-                  public Object run()
-                  {
-                    return defineClass(className, classData,
-                                       0, classData.length,
-                                       source);
-                  }
-                }, securityContext);
-          }
-        else
-	  return defineClass(className, classData, 0, classData.length, source);
+		public Object run()
+		{
+		  return defineClass(className, classData, 0,
+				     classData.length, source);
+		}
+	      }, securityContext);
+	  }
+	else
+	  result = defineClass(className, classData, 0, classData.length,
+			       source);
+	
+	super.setSigners(result, resource.getCertificates());
+	return result;
       }
     catch (IOException ioe)
       {
-        throw new ClassNotFoundException(className, ioe);
+	throw new ClassNotFoundException(className, ioe);
       }
   }
-
+  
   /**
    * Finds the first occurrence of a resource that can be found. The locations
    * are searched in the order they were added to the URLClassLoader.
Index: gnu/java/security/provider/GnuDSAPrivateKey.java
===================================================================
RCS file: /cvs/gcc/gcc/libjava/gnu/java/security/provider/GnuDSAPrivateKey.java,v
retrieving revision 1.4
diff -u -r1.4 GnuDSAPrivateKey.java
--- gnu/java/security/provider/GnuDSAPrivateKey.java	30 Apr 2003 07:23:40 -0000	1.4
+++ gnu/java/security/provider/GnuDSAPrivateKey.java	18 Nov 2004 21:46:26 -0000
@@ -1,5 +1,5 @@
 /* GnuDSAPrivateKey.java --- Gnu DSA Private Key
-   Copyright (C) 1999 Free Software Foundation, Inc.
+   Copyright (C) 1999,2003,2004  Free Software Foundation, Inc.
 
 This file is part of GNU Classpath.
 
@@ -7,7 +7,7 @@
 it under the terms of the GNU General Public License as published by
 the Free Software Foundation; either version 2, or (at your option)
 any later version.
- 
+
 GNU Classpath 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
@@ -38,19 +38,31 @@
 
 package gnu.java.security.provider;
 
+import gnu.java.security.OID;
+import gnu.java.security.der.DER;
+import gnu.java.security.der.DERValue;
+import gnu.java.security.der.DERWriter;
+
+import java.io.ByteArrayOutputStream;
+import java.io.IOException;
+
 import java.math.BigInteger;
+
 import java.security.interfaces.DSAPrivateKey;
 import java.security.interfaces.DSAParams;
 import java.security.spec.DSAParameterSpec;
 
+import java.util.ArrayList;
+
 public class GnuDSAPrivateKey implements DSAPrivateKey
 {
+  private byte[] encodedKey;
   BigInteger x;
   BigInteger p;
   BigInteger q;
   BigInteger g;
 
-  public GnuDSAPrivateKey(BigInteger x, BigInteger p, BigInteger q, BigInteger g ) 
+  public GnuDSAPrivateKey(BigInteger x, BigInteger p, BigInteger q, BigInteger g )
   {
     this.x = x;
     this.p = p;
@@ -65,12 +77,56 @@
 
   public String getFormat()
   {
-    return null;
+    return "PKCS#8";
   }
 
+  /**
+   * Encodes this key as a <code>PrivateKeyInfo</code>, as described in
+   * PKCS #8. The ASN.1 specification for this structure is:
+   *
+   * <blockquote><pre>
+   * PrivateKeyInfo ::= SEQUENCE {
+   *   version Version,
+   *   privateKeyAlgorithm PrivateKeyAlgorithmIdentifier,
+   *   privateKey PrivateKey,
+   *   attributes [0] IMPLICIT Attributes OPTIONAL }
+   *
+   * Version ::= INTEGER
+   *
+   * PrivateKeyAlgorithmIdentifier ::= AlgorithmIdentifier
+   *
+   * PrivateKey ::= OCTET STRING
+   *
+   * Attributes ::= SET OF Attribute
+   * </pre></blockquote>
+   *
+   * <p>DSA private keys (in Classpath at least) have no attributes.
+   */
   public byte[] getEncoded()
   {
-    return null;
+    if (encodedKey != null)
+      return (byte[]) encodedKey.clone();
+    try
+      {
+        ByteArrayOutputStream out = new ByteArrayOutputStream();
+        ArrayList pki = new ArrayList(3);
+        pki.add(new DERValue(DER.INTEGER, BigInteger.ZERO));
+        ArrayList algId = new ArrayList(2);
+        algId.add(new DERValue(DER.OBJECT_IDENTIFIER,
+                  new OID("1.2.840.10040.4.1")));
+        ArrayList algParams = new ArrayList(3);
+        algParams.add(new DERValue(DER.INTEGER, p));
+        algParams.add(new DERValue(DER.INTEGER, q));
+        algParams.add(new DERValue(DER.INTEGER, g));
+        algId.add(new DERValue(DER.CONSTRUCTED|DER.SEQUENCE, algParams));
+        pki.add(new DERValue(DER.OCTET_STRING, x.toByteArray()));
+        DERWriter.write(out, new DERValue(DER.CONSTRUCTED|DER.SEQUENCE, pki));
+        return (byte[]) (encodedKey = out.toByteArray()).clone();
+      }
+    catch (IOException ioe)
+      {
+        return null;
+      }
   }
 
   public DSAParams getParams()
@@ -85,7 +141,10 @@
 
   public String toString()
   {
-    return "GnuDSAPrivateKey: x=" + x.toString(16) + " p=" + p.toString(16)
-      + " q=" + q.toString(16) + " g=" + g.toString(16);
+    return "GnuDSAPrivateKey: x="
+      + (x != null ? x.toString(16) : "null") + " p="
+      + (p != null ? p.toString(16) : "null") + " q="
+      + (q != null ? q.toString(16) : "null") + " g="
+      + (g != null ? g.toString(16) : "null");
   }
 }
Index: gnu/java/security/provider/GnuDSAPublicKey.java
===================================================================
RCS file: /cvs/gcc/gcc/libjava/gnu/java/security/provider/GnuDSAPublicKey.java,v
retrieving revision 1.4
diff -u -r1.4 GnuDSAPublicKey.java
--- gnu/java/security/provider/GnuDSAPublicKey.java	30 Apr 2003 07:23:40 -0000	1.4
+++ gnu/java/security/provider/GnuDSAPublicKey.java	18 Nov 2004 21:46:26 -0000
@@ -1,5 +1,5 @@
 /* GnuDSAPublicKey.java --- Gnu DSA Public Key
-   Copyright (C) 1999,2003 Free Software Foundation, Inc.
+   Copyright (C) 1999,2003,2004 Free Software Foundation, Inc.
 
 This file is part of GNU Classpath.
 
@@ -7,7 +7,7 @@
 it under the terms of the GNU General Public License as published by
 the Free Software Foundation; either version 2, or (at your option)
 any later version.
- 
+
 GNU Classpath 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
@@ -38,19 +38,32 @@
 
 package gnu.java.security.provider;
 
+import gnu.java.security.OID;
+import gnu.java.security.der.BitString;
+import gnu.java.security.der.DER;
+import gnu.java.security.der.DERValue;
+import gnu.java.security.der.DERWriter;
+
+import java.io.ByteArrayOutputStream;
+import java.io.IOException;
+
 import java.math.BigInteger;
+
 import java.security.interfaces.DSAPublicKey;
 import java.security.interfaces.DSAParams;
 import java.security.spec.DSAParameterSpec;
 
+import java.util.ArrayList;
+
 public class GnuDSAPublicKey implements DSAPublicKey
 {
+  private byte[] encodedKey;
   BigInteger y;
   BigInteger p;
   BigInteger q;
   BigInteger g;
 
-  public GnuDSAPublicKey(BigInteger y, BigInteger p, BigInteger q, BigInteger g ) 
+  public GnuDSAPublicKey(BigInteger y, BigInteger p, BigInteger q, BigInteger g )
   {
     this.y = y;
     this.p = p;
@@ -65,16 +78,49 @@
 
   public String getFormat()
   {
-    return null;
+    return "X.509";
   }
 
+  /**
+   * The encoded form of DSA public keys is:
+   *
+   * <blockquote><pre>
+   * SubjectPublicKeyInfo ::= SEQUENCE {
+   *   algorithm AlgorithmIdentifier,
+   *   subjectPublicKey BIT STRING }
+   * </pre></blockquote>
+   */
   public byte[] getEncoded()
   {
-    return null;
+    if (encodedKey != null)
+      return (byte[]) encodedKey.clone();
+    try
+      {
+        ByteArrayOutputStream out = new ByteArrayOutputStream();
+        ArrayList spki = new ArrayList(2);
+        ArrayList alg = new ArrayList(2);
+        alg.add(new DERValue(DER.OBJECT_IDENTIFIER,
+                new OID("1.2.840.113549.1.1.1")));
+        ArrayList params = new ArrayList(3);
+        params.add(new DERValue(DER.INTEGER, p));
+        params.add(new DERValue(DER.INTEGER, q));
+        params.add(new DERValue(DER.INTEGER, g));
+        alg.add(new DERValue(DER.CONSTRUCTED|DER.SEQUENCE, params));
+        spki.add(new DERValue(DER.CONSTRUCTED|DER.SEQUENCE, alg));
+        spki.add(new DERValue(DER.BIT_STRING, new BitString(y.toByteArray())));
+        DERWriter.write(out, new DERValue(DER.CONSTRUCTED|DER.SEQUENCE, spki));
+        return (byte[]) (encodedKey = out.toByteArray()).clone();
+      }
+    catch (IOException ioe)
+      {
+        return null;
+      }
   }
 
   public DSAParams getParams()
   {
+    if (p == null || q == null || g == null)
+      return null;
     return (DSAParams)(new DSAParameterSpec(p,q,g));
   }
 
@@ -85,7 +131,10 @@
 
   public String toString()
   {
-    return "GnuDSAPublicKey: y=" + y.toString(16) + " p=" + p.toString(16)
-      + " q=" + q.toString(16) + " g=" + g.toString(16);
+    return
+      "GnuDSAPublicKey: y=" + (y != null ? y.toString(16) : "(null)") +
+      " p=" + (p != null ? p.toString(16) : "(null)") +
+      " q=" + (q != null ? q.toString(16) : "(null)") +
+      " g=" + (g != null ? g.toString(16) : "(null)");
   }
 }
Index: gnu/java/security/ber/BER.java
===================================================================
RCS file: gnu/java/security/ber/BER.java
diff -N gnu/java/security/ber/BER.java
--- /dev/null	1 Jan 1970 00:00:00 -0000
+++ gnu/java/security/ber/BER.java	18 Nov 2004 21:46:26 -0000
@@ -0,0 +1,46 @@
+/* BER.java -- basic encoding rules (BER) constants.
+   Copyright (C) 2004  Free Software Foundation, Inc.
+
+This file is part of GNU Classpath.
+
+GNU Classpath is free software; you can redistribute it and/or modify
+it under the terms of the GNU General Public License as published by
+the Free Software Foundation; either version 2, or (at your option)
+any later version.
+
+GNU Classpath 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 for more details.
+
+You should have received a copy of the GNU General Public License
+along with GNU Classpath; see the file COPYING.  If not, write to the
+Free Software Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA
+02111-1307 USA.
+
+Linking this library statically or dynamically with other modules is
+making a combined work based on this library.  Thus, the terms and
+conditions of the GNU General Public License cover the whole
+combination.
+
+As a special exception, the copyright holders of this library give you
+permission to link this library with independent modules to produce an
+executable, regardless of the license terms of these independent
+modules, and to copy and distribute the resulting executable under
+terms of your choice, provided that you also meet, for each linked
+independent module, the terms and conditions of the license of that
+module.  An independent module is a module which is not derived from
+or based on this library.  If you modify this library, you may extend
+this exception to your version of the library, but you are not
+obligated to do so.  If you do not wish to do so, delete this
+exception statement from your version. */
+
+
+package gnu.java.security.ber;
+
+import gnu.java.security.der.DER;
+
+public interface BER extends DER
+{
+  BERValue END_OF_SEQUENCE = new BERValue(0, null);
+}
Index: gnu/java/security/ber/BEREncodingException.java
===================================================================
RCS file: gnu/java/security/ber/BEREncodingException.java
diff -N gnu/java/security/ber/BEREncodingException.java
--- /dev/null	1 Jan 1970 00:00:00 -0000
+++ gnu/java/security/ber/BEREncodingException.java	18 Nov 2004 21:46:26 -0000
@@ -0,0 +1,54 @@
+/* BEREncodingException.java --- BER Encoding Exception
+   Copyright (C) 2004  Free Software Foundation, Inc.
+
+This file is part of GNU Classpath.
+
+GNU Classpath is free software; you can redistribute it and/or modify
+it under the terms of the GNU General Public License as published by
+the Free Software Foundation; either version 2, or (at your option)
+any later version.
+
+GNU Classpath 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 for more details.
+
+You should have received a copy of the GNU General Public License
+along with GNU Classpath; see the file COPYING.  If not, write to the
+Free Software Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA
+02111-1307 USA.
+
+Linking this library statically or dynamically with other modules is
+making a combined work based on this library.  Thus, the terms and
+conditions of the GNU General Public License cover the whole
+combination.
+
+As a special exception, the copyright holders of this library give you
+permission to link this library with independent modules to produce an
+executable, regardless of the license terms of these independent
+modules, and to copy and distribute the resulting executable under
+terms of your choice, provided that you also meet, for each linked
+independent module, the terms and conditions of the license of that
+module.  An independent module is a module which is not derived from
+or based on this library.  If you modify this library, you may extend
+this exception to your version of the library, but you are not
+obligated to do so.  If you do not wish to do so, delete this
+exception statement from your version. */
+
+
+package gnu.java.security.ber;
+
+import gnu.java.security.der.DEREncodingException;
+
+public class BEREncodingException extends DEREncodingException
+{
+  public BEREncodingException()
+  {
+    super ();
+  }
+
+  public BEREncodingException (String msg)
+  {
+    super (msg);
+  }
+}
Index: gnu/java/security/ber/BERReader.java
===================================================================
RCS file: gnu/java/security/ber/BERReader.java
diff -N gnu/java/security/ber/BERReader.java
--- /dev/null	1 Jan 1970 00:00:00 -0000
+++ gnu/java/security/ber/BERReader.java	18 Nov 2004 21:46:26 -0000
@@ -0,0 +1,103 @@
+/* BERReader.java -- basic encoding rules (BER) reader.
+   Copyright (C) 2004  Free Software Foundation, Inc.
+
+This file is part of GNU Classpath.
+
+GNU Classpath is free software; you can redistribute it and/or modify
+it under the terms of the GNU General Public License as published by
+the Free Software Foundation; either version 2, or (at your option)
+any later version.
+
+GNU Classpath 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 for more details.
+
+You should have received a copy of the GNU General Public License
+along with GNU Classpath; see the file COPYING.  If not, write to the
+Free Software Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA
+02111-1307 USA.
+
+Linking this library statically or dynamically with other modules is
+making a combined work based on this library.  Thus, the terms and
+conditions of the GNU General Public License cover the whole
+combination.
+
+As a special exception, the copyright holders of this library give you
+permission to link this library with independent modules to produce an
+executable, regardless of the license terms of these independent
+modules, and to copy and distribute the resulting executable under
+terms of your choice, provided that you also meet, for each linked
+independent module, the terms and conditions of the license of that
+module.  An independent module is a module which is not derived from
+or based on this library.  If you modify this library, you may extend
+this exception to your version of the library, but you are not
+obligated to do so.  If you do not wish to do so, delete this
+exception statement from your version. */
+
+
+package gnu.java.security.ber;
+
+import gnu.java.security.der.DERReader;
+import gnu.java.security.der.DERValue;
+
+import java.io.EOFException;
+import java.io.IOException;
+import java.io.InputStream;
+
+public class BERReader extends DERReader implements BER
+{
+
+  /**
+   * Create a new DER reader from a byte array.
+   *
+   * @param in The encoded bytes.
+   */
+  public BERReader(byte[] in)
+  {
+    super(in);
+  }
+
+  public BERReader (byte[] in, int off, int len)
+  {
+    super(in, off, len);
+  }
+
+  /**
+   * Create a new DER readed from an input stream.
+   *
+   * @param in The encoded bytes.
+   */
+  public BERReader(InputStream in)
+  {
+    super(in);
+  }
+
+  public DERValue read() throws IOException
+  {
+    in.mark(2);
+    int tag = in.read();
+    if (tag == -1)
+      throw new EOFException();
+    int length = in.read();
+    if (length == 0)
+      {
+        if (tag == 0)
+          return END_OF_SEQUENCE;
+        return new BERValue(tag, CONSTRUCTED_VALUE, new byte[] { (byte) tag, 0 });
+      }
+    else
+      {
+        in.reset();
+        return super.read();
+      }
+  }
+
+  public int peek() throws IOException
+  {
+    in.mark(1);
+    int ret = in.read();
+    in.reset();
+    return ret;
+  }
+}
Index: gnu/java/security/ber/BERValue.java
===================================================================
RCS file: gnu/java/security/ber/BERValue.java
diff -N gnu/java/security/ber/BERValue.java
--- /dev/null	1 Jan 1970 00:00:00 -0000
+++ gnu/java/security/ber/BERValue.java	18 Nov 2004 21:46:26 -0000
@@ -0,0 +1,82 @@
+/* BERReader.java -- basic encoding rules (BER) value.
+   Copyright (C) 2004  Free Software Foundation, Inc.
+
+This file is part of GNU Classpath.
+
+GNU Classpath is free software; you can redistribute it and/or modify
+it under the terms of the GNU General Public License as published by
+the Free Software Foundation; either version 2, or (at your option)
+any later version.
+
+GNU Classpath 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 for more details.
+
+You should have received a copy of the GNU General Public License
+along with GNU Classpath; see the file COPYING.  If not, write to the
+Free Software Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA
+02111-1307 USA.
+
+Linking this library statically or dynamically with other modules is
+making a combined work based on this library.  Thus, the terms and
+conditions of the GNU General Public License cover the whole
+combination.
+
+As a special exception, the copyright holders of this library give you
+permission to link this library with independent modules to produce an
+executable, regardless of the license terms of these independent
+modules, and to copy and distribute the resulting executable under
+terms of your choice, provided that you also meet, for each linked
+independent module, the terms and conditions of the license of that
+module.  An independent module is a module which is not derived from
+or based on this library.  If you modify this library, you may extend
+this exception to your version of the library, but you are not
+obligated to do so.  If you do not wish to do so, delete this
+exception statement from your version. */
+
+
+package gnu.java.security.ber;
+
+import gnu.java.security.der.DERValue;
+
+public class BERValue extends DERValue
+{
+
+  private boolean indefinite;
+
+  public BERValue(int tag, Object value, byte[] encoded)
+  {
+    super(tag, 0, value, encoded);
+    indefinite = true;
+  }
+
+  public BERValue(int tag, int length, Object value, byte[] encoded)
+  {
+    super(tag, length, value, encoded);
+  }
+
+  public BERValue(int tag, Object value)
+  {
+    super(tag, 0, value, null);
+  }
+
+  public static boolean isIndefinite(DERValue value)
+  {
+    if (value instanceof BERValue)
+      return ((BERValue) value).getIndefinite();
+    return false;
+  }
+
+  public boolean getIndefinite()
+  {
+    return indefinite;
+  }
+
+  public int getLength()
+  {
+    if (indefinite)
+      return 0;
+    return super.getLength();
+  }
+}
Index: gnu/java/security/pkcs/PKCS7SignedData.java
===================================================================
RCS file: gnu/java/security/pkcs/PKCS7SignedData.java
diff -N gnu/java/security/pkcs/PKCS7SignedData.java
--- /dev/null	1 Jan 1970 00:00:00 -0000
+++ gnu/java/security/pkcs/PKCS7SignedData.java	18 Nov 2004 21:46:27 -0000
@@ -0,0 +1,363 @@
+/* PKCS7SignedData.java -- reader for PKCS#7 signedData objects.
+   Copyright (C) 2004  Free Software Foundation, Inc.
+
+This program is free software; you can redistribute it and/or modify
+it under the terms of the GNU General Public License as published by
+the Free Software Foundation; either version 2, or (at your option)
+any later version.
+
+This program 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 for more details.
+
+You should have received a copy of the GNU General Public License
+along with this program; see the file COPYING.  If not, write to the
+Free Software Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA
+02111-1307 USA.
+
+Linking this library statically or dynamically with other modules is
+making a combined work based on this library.  Thus, the terms and
+conditions of the GNU General Public License cover the whole
+combination.
+
+As a special exception, the copyright holders of this library give you
+permission to link this library with independent modules to produce an
+executable, regardless of the license terms of these independent
+modules, and to copy and distribute the resulting executable under
+terms of your choice, provided that you also meet, for each linked
+independent module, the terms and conditions of the license of that
+module.  An independent module is a module which is not derived from
+or based on this library.  If you modify this library, you may extend
+this exception to your version of the library, but you are not
+obligated to do so.  If you do not wish to do so, delete this
+exception statement from your version. */
+
+
+package gnu.java.security.pkcs;
+
+import gnu.java.security.OID;
+import gnu.java.security.ber.BER;
+import gnu.java.security.ber.BEREncodingException;
+import gnu.java.security.ber.BERReader;
+import gnu.java.security.ber.BERValue;
+import gnu.java.security.der.DERValue;
+
+import java.io.ByteArrayInputStream;
+import java.io.IOException;
+import java.io.InputStream;
+
+import java.math.BigInteger;
+
+import java.security.cert.CRL;
+import java.security.cert.CRLException;
+import java.security.cert.Certificate;
+import java.security.cert.CertificateException;
+import java.security.cert.CertificateFactory;
+
+import java.util.ArrayList;
+import java.util.Collections;
+import java.util.HashSet;
+import java.util.Iterator;
+import java.util.LinkedList;
+import java.util.List;
+import java.util.Set;
+
+/**
+ * The SignedData object in PKCS #7. This is a read-only implementation of
+ * this format, and is used to provide signed Jar file support.
+ *
+ * @author Casey Marshall (csm@gnu.org)
+ */
+public class PKCS7SignedData
+{
+
+  public static final OID PKCS7_DATA = new OID("1.2.840.113549.1.7.1");
+  public static final OID PKCS7_SIGNED_DATA = new OID("1.2.840.113549.1.7.2");
+
+  private BigInteger version;
+  private Set digestAlgorithms;
+  private OID contentType;
+  private byte[] content;
+  private Certificate[] certificates;
+  private CRL[] crls;
+  private Set signerInfos;
+
+  private static final boolean DEBUG = false;
+  private static void debug(String msg)
+  {
+    System.err.print("PKCS7SignedData >> ");
+    System.err.println(msg);
+  }
+
+  public PKCS7SignedData(InputStream in)
+    throws CRLException, CertificateException, IOException
+  {
+    this(new BERReader(in));
+  }
+
+  /**
+   * Parse an encoded PKCS#7 SignedData object. The ASN.1 format of this
+   * object is:
+   *
+   * <pre>
+   * SignedData ::= SEQUENCE {
+   *   version Version,
+   *   digestAlgorithms DigestAlgorithmIdentifiers,
+   *   contentInfo ContentInfo,
+   *   certificates
+   *     [0] IMPLICIT ExtendedCertificatesAndCertificates OPTIONAL,
+   *   crls
+   *     [1] IMPLICIT CertificateRevocationLists OPTIONAL,
+   *   signerInfos SignerInfos }
+   *
+   * Version ::= INTEGER
+   *
+   * DigestAlgorithmIdentifiers ::= SET OF DigestAlgorithmIdentifier
+   *
+   * DigestAlgorithmIdentifier ::= AlgorithmIdentifier
+   *
+   * ContentInfo ::= SEQUENCE {
+   *   contentType ContentType,
+   *   content [0] EXPLICIT ANY DEFINED BY contentType OPTIONAL }
+   *
+   * ContentType ::= OBJECT IDENTIFIER
+   *
+   * ExtendedCertificatesAndCertificates ::=
+   *   SET OF ExtendedCertificatesAndCertificate
+   *
+   * ExtendedCertificatesAndCertificate ::= CHOICE {
+   *   certificate Certificate, -- from X.509
+   *   extendedCertificate [0] IMPLICIT ExtendedCertificate }
+   *
+   * CertificateRevocationLists ::= SET OF CertificateRevocationList
+   *   -- from X.509
+   *
+   * SignerInfos ::= SET OF SignerInfo
+   *
+   * SignerInfo ::= SEQUENCE {
+   *   version Version,
+   *   issuerAndSerialNumber IssuerAndSerialNumber,
+   *   digestAlgorithm DigestAlgorithmIdentifier,
+   *   authenticatedAttributes
+   *     [0] IMPLICIT Attributes OPTIONAL,
+   *   digestEncryptionAlgorithm DigestEncryptionAlgorithmIdentifier,
+   *   encryptedDigest EncryptedDigest,
+   *   unauthenticatedAttributes
+   *     [1] IMPLICIT Attributes OPTIONAL }
+   *
+   * EncryptedDigest ::= OCTET STRING
+   * </pre>
+   *
+   * <p>(Readers who are confused as to why it takes 40 levels of indirection
+   * to specify "data with a signature", rest assured that the present author
+   * is as confused as you are).</p>
+   */
+  public PKCS7SignedData(BERReader ber)
+    throws CRLException, CertificateException, IOException
+  {
+    CertificateFactory x509 = CertificateFactory.getInstance("X509");
+    DERValue val = ber.read();
+    if (!val.isConstructed())
+      throw new BEREncodingException("malformed ContentInfo");
+
+    val = ber.read();
+    if (val.getTag() != BER.OBJECT_IDENTIFIER)
+      throw new BEREncodingException("malformed ContentType");
+
+    if (!PKCS7_SIGNED_DATA.equals(val.getValue()))
+      throw new BEREncodingException("content is not SignedData");
+
+    val = ber.read();
+    if (val.getTag() != 0)
+      throw new BEREncodingException("malformed Content");
+
+    val = ber.read();
+    if (!val.isConstructed())
+      throw new BEREncodingException("malformed SignedData");
+
+    if (DEBUG)
+      debug("SignedData: " + val);
+
+    val = ber.read();
+    if (val.getTag() != BER.INTEGER)
+      throw new BEREncodingException("expecting Version");
+    version = (BigInteger) val.getValue();
+
+    if (DEBUG)
+      debug("  Version: " + version);
+
+    digestAlgorithms = new HashSet();
+    val = ber.read();
+    if (!val.isConstructed())
+      throw new BEREncodingException("malformed DigestAlgorithmIdentifiers");
+    if (DEBUG)
+      debug("  DigestAlgorithmIdentifiers: " + val);
+    int count = 0;
+    DERValue val2 = ber.read();
+    while (val2 != BER.END_OF_SEQUENCE &&
+           (val.getLength() > 0 && val.getLength() > count))
+      {
+        if (!val2.isConstructed())
+          throw new BEREncodingException("malformed AlgorithmIdentifier");
+        if (DEBUG)
+          debug("    AlgorithmIdentifier: " + val2);
+        count += val2.getEncodedLength();
+        val2 = ber.read();
+        if (val2.getTag() != BER.OBJECT_IDENTIFIER)
+          throw new BEREncodingException("malformed AlgorithmIdentifier");
+        if (DEBUG)
+          debug("      ID: " + val2.getValue());
+        List algId = new ArrayList(2);
+        algId.add(val2.getValue());
+        val2 = ber.read();
+        if (val2 != BER.END_OF_SEQUENCE)
+          {
+            count += val2.getEncodedLength();
+            if (val2.getTag() == BER.NULL)
+              algId.add(null);
+            else
+              algId.add(val2.getEncoded());
+            if (DEBUG)
+              debug("      params: " + new BigInteger(1, val2.getEncoded()).toString(16));
+            if (val2.isConstructed())
+              ber.skip(val2.getLength());
+            if (BERValue.isIndefinite(val))
+              val2 = ber.read();
+          }
+        else
+          algId.add(null);
+        digestAlgorithms.add(algId);
+      }
+
+    val = ber.read();
+    if (!val.isConstructed())
+      throw new BEREncodingException("malformed ContentInfo");
+    if (DEBUG)
+      debug("  ContentInfo: " + val);
+    val2 = ber.read();
+    if (val2.getTag() != BER.OBJECT_IDENTIFIER)
+      throw new BEREncodingException("malformed ContentType");
+    contentType = (OID) val2.getValue();
+    if (DEBUG)
+      debug("    ContentType: " + contentType);
+    if (BERValue.isIndefinite(val)
+        || (val.getLength() > 0 && val.getLength() > val2.getEncodedLength()))
+      {
+        val2 = ber.read();
+        if (val2 != BER.END_OF_SEQUENCE)
+          {
+            content = val2.getEncoded();
+            if (BERValue.isIndefinite(val))
+              val2 = ber.read();
+            if (DEBUG)
+              debug("    Content: " + new BigInteger(1, content).toString(16));
+          }
+      }
+
+    val = ber.read();
+    if (val.getTag() == 0)
+      {
+        if (!val.isConstructed())
+          throw new BEREncodingException("malformed ExtendedCertificatesAndCertificates");
+        if (DEBUG)
+          debug("  ExtendedCertificatesAndCertificates: " + val);
+        count = 0;
+        val2 = ber.read();
+        List certs = new LinkedList();
+        while (val2 != BER.END_OF_SEQUENCE &&
+               (val.getLength() > 0 && val.getLength() > count))
+          {
+            Certificate cert =
+              x509.generateCertificate(new ByteArrayInputStream(val2.getEncoded()));
+            if (DEBUG)
+              debug("    Certificate: " + cert);
+            certs.add(cert);
+            count += val2.getEncodedLength();
+            ber.skip(val2.getLength());
+            if (BERValue.isIndefinite(val) || val.getLength() > count)
+              val2 = ber.read();
+          }
+        certificates = (Certificate[]) certs.toArray(new Certificate[certs.size()]);
+        val = ber.read();
+      }
+
+    if (val.getTag() == 1)
+      {
+        if (!val.isConstructed())
+          throw new BEREncodingException("malformed CertificateRevocationLists");
+        if (DEBUG)
+          debug("  CertificateRevocationLists: " + val);
+        count = 0;
+        val2 = ber.read();
+        List crls = new LinkedList();
+        while (val2 != BER.END_OF_SEQUENCE &&
+               (val.getLength() > 0 && val.getLength() > count))
+          {
+            CRL crl = x509.generateCRL(new ByteArrayInputStream(val2.getEncoded()));
+            if (DEBUG)
+              debug ("    CRL: " + crl);
+            crls.add(crl);
+            count += val2.getEncodedLength();
+            ber.skip(val2.getLength());
+            if (BERValue.isIndefinite(val) || val.getLength() > count)
+              val2 = ber.read();
+          }
+        this.crls = (CRL[]) crls.toArray(new CRL[crls.size()]);
+        val = ber.read();
+      }
+
+    signerInfos = new HashSet();
+    if (!val.isConstructed())
+      throw new BEREncodingException("malformed SignerInfos");
+
+    if (DEBUG)
+      debug("  SignerInfos: " + val);
+
+    // FIXME read this more carefully.
+    // Since we are just reading a file (probably) we just read until we
+    // reach the end.
+    while (true)
+      {
+        int i = ber.peek();
+        if (i == 0 || i == -1)
+          break;
+        signerInfos.add(new SignerInfo(ber));
+      }
+  }
+
+  public BigInteger getVersion()
+  {
+    return version;
+  }
+
+  public Certificate[] getCertificates()
+  {
+    return (certificates != null ? (Certificate[]) certificates.clone()
+            : null);
+  }
+
+  public OID getContentType()
+  {
+    return contentType;
+  }
+
+  public byte[] getContent()
+  {
+    return (content != null ? (byte[]) content.clone() : null);
+  }
+
+  public Set getDigestAlgorithms()
+  {
+    // FIXME copy contents too, they are mutable!!!
+    return Collections.unmodifiableSet(digestAlgorithms);
+  }
+
+  public Set getSignerInfos()
+  {
+    Set copy = new HashSet();
+    for (Iterator it = signerInfos.iterator(); it.hasNext(); )
+      copy.add(it.next());
+    return Collections.unmodifiableSet(copy);
+  }
+}
Index: gnu/java/security/pkcs/SignerInfo.java
===================================================================
RCS file: gnu/java/security/pkcs/SignerInfo.java
diff -N gnu/java/security/pkcs/SignerInfo.java
--- /dev/null	1 Jan 1970 00:00:00 -0000
+++ gnu/java/security/pkcs/SignerInfo.java	18 Nov 2004 21:46:27 -0000
@@ -0,0 +1,280 @@
+/* SignerInfo.java -- a SignerInfo object, from PKCS #7.
+   Copyright (C) 2004  Free Software Foundation, Inc.
+
+This program is free software; you can redistribute it and/or modify
+it under the terms of the GNU General Public License as published by
+the Free Software Foundation; either version 2, or (at your option)
+any later version.
+
+This program 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 for more details.
+
+You should have received a copy of the GNU General Public License
+along with this program; see the file COPYING.  If not, write to the
+Free Software Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA
+02111-1307 USA.
+
+Linking this library statically or dynamically with other modules is
+making a combined work based on this library.  Thus, the terms and
+conditions of the GNU General Public License cover the whole
+combination.
+
+As a special exception, the copyright holders of this library give you
+permission to link this library with independent modules to produce an
+executable, regardless of the license terms of these independent
+modules, and to copy and distribute the resulting executable under
+terms of your choice, provided that you also meet, for each linked
+independent module, the terms and conditions of the license of that
+module.  An independent module is a module which is not derived from
+or based on this library.  If you modify this library, you may extend
+this exception to your version of the library, but you are not
+obligated to do so.  If you do not wish to do so, delete this
+exception statement from your version. */
+
+
+package gnu.java.security.pkcs;
+
+import gnu.java.security.OID;
+import gnu.java.security.ber.BER;
+import gnu.java.security.ber.BEREncodingException;
+import gnu.java.security.ber.BERReader;
+import gnu.java.security.ber.BERValue;
+import gnu.java.security.der.BitString;
+import gnu.java.security.der.DERValue;
+
+import java.io.IOException;
+import java.io.InputStream;
+
+import java.math.BigInteger;
+
+import javax.security.auth.x500.X500Principal;
+
+public class SignerInfo
+{
+  private final BigInteger version;
+  private final BigInteger serialNumber;
+  private final X500Principal issuer;
+  private final OID digestAlgorithmId;
+  private final byte[] digestAlgorithmParams;
+  private final byte[] authenticatedAttributes;
+  private final OID digestEncryptionAlgorithmId;
+  private final byte[] digestEncryptionAlgorithmParams;
+  private final byte[] encryptedDigest;
+  private final byte[] unauthenticatedAttributes;
+
+  private static final boolean DEBUG = false;
+  private static void debug(String msg)
+  {
+    System.err.print("SignerInfo >> ");
+    System.err.println(msg);
+  }
+
+  /**
+   * Parse a SignerInfo object.
+   */
+  public SignerInfo(BERReader ber) throws IOException
+  {
+    DERValue val = ber.read();
+    if (DEBUG)
+      debug("SignerInfo: " + val);
+    if (!val.isConstructed())
+      throw new BEREncodingException("malformed SignerInfo");
+
+    val = ber.read();
+    if (val.getTag() != BER.INTEGER)
+      throw new BEREncodingException("malformed Version");
+    version = (BigInteger) val.getValue();
+
+    if (DEBUG)
+      debug("  Version: " + version);
+
+    val = ber.read();
+    if (!val.isConstructed())
+      throw new BEREncodingException("malformed IssuerAndSerialNumber");
+
+    if (DEBUG)
+      debug("  IssuerAndSerialNumber: " + val);
+
+    val = ber.read();
+    if (!val.isConstructed())
+      throw new BEREncodingException("malformed Issuer");
+    issuer = new X500Principal(val.getEncoded());
+    ber.skip(val.getLength());
+    if (DEBUG)
+      debug("    Issuer: " + issuer);
+
+    val = ber.read();
+    if (val.getTag() != BER.INTEGER)
+      throw new BEREncodingException("malformed SerialNumber");
+    serialNumber = (BigInteger) val.getValue();
+    if (DEBUG)
+      debug("    SerialNumber: " + serialNumber);
+
+    val = ber.read();
+    if (!val.isConstructed())
+      throw new BEREncodingException("malformed DigestAlgorithmIdentifier");
+    if (DEBUG)
+      debug("  DigestAlgorithmIdentifier: " + val);
+
+    int count = 0;
+    DERValue val2 = ber.read();
+    if (val2.getTag() != BER.OBJECT_IDENTIFIER)
+      throw new BEREncodingException("malformed AlgorithmIdentifier");
+    digestAlgorithmId = (OID) val2.getValue();
+    if (DEBUG)
+      debug("    OID: " + digestAlgorithmId);
+
+    if (BERValue.isIndefinite(val))
+      {
+        val2 = ber.read();
+        if (val2 != BER.END_OF_SEQUENCE)
+          {
+            digestAlgorithmParams = val2.getEncoded();
+            val2 = ber.read();
+            if (val2 != BER.END_OF_SEQUENCE)
+              throw new BEREncodingException("expecting BER end-of-sequence");
+          }
+        else
+          digestAlgorithmParams = null;
+      }
+    else if (val2.getEncodedLength() < val.getLength())
+      {
+        val2 = ber.read();
+        digestAlgorithmParams = val2.getEncoded();
+        if (val2.isConstructed())
+          ber.skip(val2.getLength());
+      }
+    else
+      digestAlgorithmParams = null;
+    if(DEBUG)
+      debug("    params: " + (digestAlgorithmParams == null ? null
+                              : new BigInteger(digestAlgorithmParams).toString(16)));
+
+    val = ber.read();
+    if (val.getTag() == 0)
+      {
+        authenticatedAttributes = val.getEncoded();
+        val = ber.read();
+        if (val.isConstructed())
+          ber.skip(val.getLength());
+        if (DEBUG)
+          debug("  AuthenticatedAttributes: " + val);
+        val = ber.read();
+      }
+    else
+      authenticatedAttributes = null;
+
+    if (!val.isConstructed())
+      throw new BEREncodingException("malformed DigestEncryptionAlgorithmIdentifier");
+    if (DEBUG)
+      debug("  DigestEncryptionAlgorithmIdentifier: " + val);
+    count = 0;
+    val2 = ber.read();
+    if (val2.getTag() != BER.OBJECT_IDENTIFIER)
+      throw new BEREncodingException("malformed AlgorithmIdentifier");
+    digestEncryptionAlgorithmId = (OID) val2.getValue();
+    if (DEBUG)
+      debug("    OID: " + digestEncryptionAlgorithmId);
+
+    if (BERValue.isIndefinite(val))
+      {
+        val2 = ber.read();
+        if (val2 != BER.END_OF_SEQUENCE)
+          {
+            digestEncryptionAlgorithmParams = val2.getEncoded();
+            val2 = ber.read();
+            if (val2 != BER.END_OF_SEQUENCE)
+              throw new BEREncodingException("expecting BER end-of-sequence");
+          }
+        else
+          digestEncryptionAlgorithmParams = null;
+      }
+    else if (val2.getEncodedLength() < val.getLength())
+      {
+        val2 = ber.read();
+        digestEncryptionAlgorithmParams = val2.getEncoded();
+        if (val2.isConstructed())
+          ber.skip(val2.getLength());
+      }
+    else
+      digestEncryptionAlgorithmParams = null;
+    if(DEBUG)
+      debug("    params: " + (digestEncryptionAlgorithmParams == null ? null
+                              : new BigInteger(digestEncryptionAlgorithmParams).toString(16)));
+
+    val = ber.read();
+    if (val.getTag() != BER.OCTET_STRING)
+      throw new BEREncodingException("malformed EncryptedDigest");
+    encryptedDigest = (byte[]) val.getValue();
+    if (DEBUG)
+      debug("  EncryptedDigest: " + new BigInteger(1, encryptedDigest).toString(16));
+
+    if (ber.peek() == 1)
+      unauthenticatedAttributes = ber.read().getEncoded();
+    else
+      unauthenticatedAttributes = null;
+
+    if (ber.peek() == 0)
+      ber.read();
+  }
+
+  public BigInteger getVersion()
+  {
+    return version;
+  }
+
+  public BigInteger getSerialNumber()
+  {
+    return serialNumber;
+  }
+
+  public X500Principal getIssuer()
+  {
+    return issuer;
+  }
+
+  public OID getDigestAlgorithmId()
+  {
+    return digestAlgorithmId;
+  }
+
+  public byte[] getDigestAlgorithmParams()
+  {
+    return (digestAlgorithmParams != null
+            ? (byte[]) digestAlgorithmParams.clone()
+            : null);
+  }
+
+  public byte[] getAuthenticatedAttributes()
+  {
+    return (authenticatedAttributes != null
+            ? (byte[]) authenticatedAttributes.clone()
+            : null);
+  }
+
+  public OID getDigestEncryptionAlgorithmId()
+  {
+    return digestEncryptionAlgorithmId;
+  }
+
+  public byte[] getDigestEncryptionAlgorithmParams()
+  {
+    return (digestEncryptionAlgorithmParams != null
+            ? (byte[]) digestEncryptionAlgorithmParams.clone()
+            : null);
+  }
+
+  public byte[] getEncryptedDigest()
+  {
+    return (encryptedDigest != null ? (byte[]) encryptedDigest.clone() : null);
+  }
+
+  public byte[] getUnauthenticatedAttributes()
+  {
+    return (unauthenticatedAttributes != null
+            ? (byte[]) unauthenticatedAttributes.clone()
+            : null);
+  }
+}

Index Nav: [Date Index] [Subject Index] [Author Index] [Thread Index]
Message Nav: [Date Prev] [Date Next] [Thread Prev] [Thread Next]