Index: java/net/deterlab/abac/Credential.java
===================================================================
--- java/net/deterlab/abac/Credential.java	(revision d69593ce0c5640e3248d4afcfaff92abbe6cea16)
+++ java/net/deterlab/abac/Credential.java	(revision b67a7ac65fd3b349b05ff0a3f584db5525074316)
@@ -5,32 +5,39 @@
 
 import java.util.*;
-import java.util.zip.*;
 import java.security.*;
 import java.security.cert.*;
 
-import net.deterlab.abac.Identity;
+import javax.security.auth.x500.*;
 
 import org.bouncycastle.asn1.*;
 import org.bouncycastle.asn1.x509.*;
 import org.bouncycastle.x509.*;
-import org.bouncycastle.jce.X509Principal;
-import org.bouncycastle.jce.provider.X509AttrCertParser;
-import org.bouncycastle.jce.provider.X509CertificateObject;
-import org.bouncycastle.openssl.PEMReader;
-
-import java.security.PrivateKey;
-
+import org.bouncycastle.x509.util.*;
+import org.bouncycastle.openssl.*;
+
+/**
+ * An ABAC credential, with or without an underlying certificate that
+ * represents it.  These are edges in proof garphs and can be constructed from
+ * their constituent Roles.
+ * @author <a href="http://abac.deterlab.net">ISI ABAC team</a>
+ * @version 1.3
+ */
 public class Credential implements Comparable {
-    protected static Vector<Identity> s_ids = new Vector<Identity>();
+    /** The ASN1 OID for an IETF attribute. */
     protected static String attrOID = "1.3.6.1.5.5.7.10.4";
+    /** The ASN1 OID for AuthorityKeyIdentifier. */
     protected static String authKeyOID = "2.5.29.35";
 
-    protected Role m_head;
+    /** The role at the head */
+    protected Role m_head
+    /** The role at the tail */;
     protected Role m_tail;
+    /** The certificate representing this credential */
     protected X509V2AttributeCertificate ac;
+    /** The identity that issued the certificate */
     protected Identity id;
 
     /**
-     * A dummy credential.
+     * Create an empty Credential.
      */
     public Credential() {
@@ -40,7 +47,10 @@
     }
     /**
-     * Create a credential from a head and tail role. This is only for testing.
-     * In a real implementation the Credential must be loaded from an X.509
-     * attribute cert.
+     * Create a credential from a head and tail role.  This credential has no
+     * underlying certificate, and cannot be exported or used in real proofs.
+     * make_cert can create a certificate for a credential initialized this
+     * way.
+     * @param head the Role at the head of the credential
+     * @param tail the Role at the tail of the credential
      */
     public Credential(Role head, Role tail) {
@@ -52,21 +62,30 @@
 
     /**
-     * Do the credential initialization from a filename.
+     * Do the credential extraction from an input stream.  This parses the
+     * certificate from the input stream and saves it. The contents must be
+     * validated and parsed later.
+     * @param stream the InputStream to read the certificate from.
+     * @throws IOException if the stream is unparsable
      */
     protected void read_certificate(InputStream stream) 
-	    throws Exception {
-        X509AttrCertParser parser = new X509AttrCertParser();
-        parser.engineInit(stream);
-        ac = (X509V2AttributeCertificate)parser.engineRead();
-    }
-
-
-    protected void init(InputStream stream, Collection<Identity> ids) 
-	    throws Exception {
-	read_certificate(stream);
-	if (ac == null) throw new IOException("Unknown Format");
-	init(ids);
-    }
-
+	    throws IOException {
+	ac = new X509V2AttributeCertificate(stream);
+    }
+
+    /**
+     * Initialize a credential from parsed certificate.  Validiate it against
+     * the given identities and parse out the roles.  Note that catching
+     * java.security.GeneralSecurityException catches most of the exceptions
+     * this throws.
+     * @param ids a Collection of Identities to use in validating the cert
+     * @throws CertificateException if the certificate is badly formatted
+     * @throws InvalidKeyException if none of the Identities can validate the
+     *				    certificate
+     * @throws NoSuchAlgorithmException if the credential uses an unknown
+     *				    signature algorithm
+     * @throws NoSuchProviderException if the provider of the signature
+     *				    algorithm is unavailable
+     * @throws SignatureException if the signature check fails
+     */
     protected void init(Collection<Identity> ids) 
 	    throws CertificateException, InvalidKeyException, 
@@ -88,7 +107,49 @@
 	    throw new InvalidKeyException("Unknown identity");
     }
-    /**
-     * Create a credential from an attribute cert. Throws an exception if the
-     * cert file can't be opened or if there's a format problem with the cert.
+
+    /**
+     * Parse a credential from an InputStream and initialize the role from it.
+     * Combine read_credential(stream) and init(ids).  Note that catching
+     * java.security.GeneralSecurityException catches most of the exceptions
+     * this throws.
+     * @param stream the InputStream to read the certificate from.
+     * @param ids a Collection of Identities to use in validating the cert
+     * @throws CertificateException if the certificate is badly formatted
+     * @throws InvalidKeyException if none of the Identities can validate the
+     *				    certificate
+     * @throws NoSuchAlgorithmException if the credential uses an unknown
+     *				    signature algorithm
+     * @throws NoSuchProviderException if the provider of the signature
+     *				    algorithm is unavailable
+     * @throws SignatureException if the signature check fails
+     * @throws IOException if the certificate is unparsable.
+     */
+    protected void init(InputStream stream, Collection<Identity> ids) 
+	    throws CertificateException, InvalidKeyException, 
+		NoSuchAlgorithmException, NoSuchProviderException,
+		SignatureException, IOException {
+	read_certificate(stream);
+	if (ac == null) throw new IOException("Unknown Format");
+	init(ids);
+    }
+
+    /**
+     * Create a credential from an attribute cert in a file. Throws an
+     * exception if the cert file can't be opened or if there's a format
+     * problem with the cert.  Note that catching
+     * java.security.GeneralSecurityException catches most of the exceptions
+     * this throws.
+     * @param filename a String containing the filename to read
+     * @param ids a Collection of Identities to use in validating the cert
+     * @throws StreamParsingException if the stream is unparsable
+     * @throws CertificateException if the certificate is badly formatted
+     * @throws InvalidKeyException if none of the Identities can validate the
+     *				    certificate
+     * @throws NoSuchAlgorithmException if the credential uses an unknown
+     *				    signature algorithm
+     * @throws NoSuchProviderException if the provider of the signature
+     *				    algorithm is unavailable
+     * @throws SignatureException if the signature check fails
+     * @throws IOException if the certificate is unparsable.
      */
     public Credential(String filename, Collection<Identity> ids) 
@@ -96,18 +157,76 @@
 
     /**
-     * Create a credential from an attribute cert. Throws an exception if the
-     * cert file can't be opened or if there's a format problem with the cert.
+     * Create a credential from an attribute cert in a file. Throws an
+     * exception if the cert file can't be opened or if there's a format
+     * problem with the cert.  Note that catching
+     * java.security.GeneralSecurityException catches most of the exceptions
+     * this throws.
+     * @param file the File to read
+     * @param ids a Collection of Identities to use in validating the cert
+     * @throws StreamParsingException if the stream is unparsable
+     * @throws CertificateException if the certificate is badly formatted
+     * @throws InvalidKeyException if none of the Identities can validate the
+     *				    certificate
+     * @throws NoSuchAlgorithmException if the credential uses an unknown
+     *				    signature algorithm
+     * @throws NoSuchProviderException if the provider of the signature
+     *				    algorithm is unavailable
+     * @throws SignatureException if the signature check fails
+     * @throws IOException if the certificate is unparsable.
      */
     public Credential(File file, Collection<Identity> ids) 
-	throws Exception { init(new FileInputStream(file), ids); }
-
-    /**
-     * Create a credential from an InputStream.
+	    throws CertificateException, InvalidKeyException, 
+		NoSuchAlgorithmException, NoSuchProviderException,
+		SignatureException, StreamParsingException, IOException {
+	init(new FileInputStream(file), ids);
+    }
+
+    /**
+     * Create a credential from an InputStream.  Throws an exception if the
+     * stream can't be parsed or if there's a format problem with the cert.
+     * Note that catching java.security.GeneralSecurityException catches most
+     * of the exceptions this throws.
+     * @param s the InputStream to read
+     * @param ids a Collection of Identities to use in validating the cert
+     * @throws StreamParsingException if the stream is unparsable
+     * @throws CertificateException if the certificate is badly formatted
+     * @throws InvalidKeyException if none of the Identities can validate the
+     *				    certificate
+     * @throws NoSuchAlgorithmException if the credential uses an unknown
+     *				    signature algorithm
+     * @throws NoSuchProviderException if the provider of the signature
+     *				    algorithm is unavailable
+     * @throws SignatureException if the signature check fails
+     * @throws IOException if the certificate is unparsable.
      */
     public Credential(InputStream s, Collection<Identity> ids) 
-	throws Exception { init(s, ids); }
-
+	    throws CertificateException, InvalidKeyException, 
+		NoSuchAlgorithmException, NoSuchProviderException,
+		SignatureException, StreamParsingException, IOException {
+	init(s, ids);
+    }
+
+    /**
+     * Create a credential from an X509V2AttributeCertificate object.  Throws
+     * an exception if the certificate doesn't parse into an ABAC credential,
+     * or cannot be validated.  Note that catching
+     * java.security.GeneralSecurityException catches most of the exceptions
+     * this throws.
+     * @param c the X509V2AttributeCertificate to create from
+     * @param ids a Collection of Identities to use in validating the cert
+     * @throws CertificateException if the certificate is badly formatted
+     * @throws InvalidKeyException if none of the Identities can validate the
+     *				    certificate
+     * @throws NoSuchAlgorithmException if the credential uses an unknown
+     *				    signature algorithm
+     * @throws NoSuchProviderException if the provider of the signature
+     *				    algorithm is unavailable
+     * @throws SignatureException if the signature check fails
+     * @throws IOException if the certificate is unparsable.
+     */
     public Credential(X509V2AttributeCertificate c, Collection<Identity> ids) 
-	throws Exception {
+	    throws CertificateException, InvalidKeyException, 
+		NoSuchAlgorithmException, NoSuchProviderException,
+		SignatureException, IOException {
 	ac = c;
 	init(ids);
@@ -117,7 +236,21 @@
     /**
      * Create a certificate from this credential issued by the given identity.
-     * This is just grungy credential generation work.
-     */
-    public void make_cert(Identity i) {
+     * Note that catching java.security.GeneralSecurityException catches most
+     * of the exceptions this throws.
+     * @param i the Identity that will issue the certificate
+     * @throws IOException reading or writing problems
+     * @throws CertificateEncodingException Problem creating certificate
+     * @throws InvalidKeyException if none of the Identities can sign the
+     *				    certificate
+     * @throws NoSuchAlgorithmException if the credential uses an unknown
+     *				    signature algorithm
+     * @throws NoSuchProviderException if the provider of the signature
+     *				    algorithm is unavailable
+     * @throws SignatureException if the signature creation fails
+     */
+    public void make_cert(Identity i) 
+	    throws IOException, CertificateEncodingException,
+	       NoSuchProviderException, NoSuchAlgorithmException,
+	       SignatureException, InvalidKeyException {
 	PrivateKey key = i.getKeyPair().getPrivate();
 	SubjectPublicKeyInfo pki = Context.extractSubjectPublicKeyInfo(
@@ -127,7 +260,7 @@
 
 	gen.setIssuer(new AttributeCertificateIssuer(
-		    new X509Principal("CN="+m_head.principal())));
+		    new X500Principal("CN="+m_head.principal())));
 	gen.setHolder(new AttributeCertificateHolder(
-		    new X509Principal("CN="+m_head.principal())));
+		    new X500Principal("CN="+m_head.principal())));
 	gen.setNotAfter(new Date(System.currentTimeMillis() 
 		    + 3600 * 1000 * 24 * 365));
@@ -140,14 +273,9 @@
 	gen.setSignatureAlgorithm("SHA256WithRSAEncryption");
 
-	try { 
-	    // Creddy expects an authority key identifier.
-	    gen.addExtension(authKeyOID, false, 
-		    new AuthorityKeyIdentifier(pki));
-	    // Create the cert.
-	    ac = (X509V2AttributeCertificate) gen.generate(key, "BC");
-	}
-	catch (Exception e) { 
-	    System.err.println(e);
-	}
+	// Creddy expects an authority key identifier.
+	gen.addExtension(authKeyOID, false, 
+		new AuthorityKeyIdentifier(pki));
+	// Create the cert.
+	ac = (X509V2AttributeCertificate) gen.generate(key, "BC");
     }
 
@@ -156,5 +284,5 @@
      * there's something wrong with the cert.
      */
-    private void load_roles() throws RuntimeException {
+    private void load_roles() {
         String roles = null;
         try {
@@ -181,4 +309,6 @@
     /**
      * Two credentials are the same if their roles are the same.
+     * @param o an Object to compare
+     * @return true if the Credentials have the Roles 
      */
     public boolean equals(Object o) {
@@ -192,4 +322,11 @@
     }
 
+    /**
+     * Allow credentials to be compared.  They are ordered by their Roles, head
+     * then tail.
+     * @param o an Object to compare
+     * @return -1 if this Credential is before, 0 if they are the same, and 1
+     *		    if this Credential is after the given object.
+     */
     public int compareTo(Object o) {
 	if (o instanceof Credential) {
@@ -205,4 +342,5 @@
     /**
      * Get the head role from the credential.
+     * @return the Role in the head
      */
     public Role head() { return m_head; }
@@ -210,4 +348,5 @@
     /**
      * Get the tail role from the credential
+     * @return the Role in the tail
      */
     public Role tail() { return m_tail; }
@@ -215,4 +354,5 @@
     /**
      * Gets the cert associated with this credential (if any).
+     * @return the attribute cert associated with this credential (if any).
      */
     public X509V2AttributeCertificate cert() { return ac; }
@@ -220,5 +360,6 @@
     /**
      * Turn the credential into string form. The format is head &lt;- tail. For
-     * example: A.r1 &lt;- B.r2.r3.
+     * example: A.r1 &lt;- B.r2.r3.  Principal names are key identifiers.
+     * @return the string form
      */
     public String toString() {
@@ -226,21 +367,59 @@
     }
 
+    /**
+     * Turn the credential into string form. The format is head &lt;- tail. For
+     * example: A.r1 &lt;- B.r2.r3.  Principal names are shortened to menmonics
+     * if the Context knows the identity.
+     * @param c the Context to translate names in
+     * @return the string form
+     */
     public String simpleString(Context c) {
 	return m_head.simpleString(c) + " <- " + m_tail.simpleString(c);
     }
 
+    /**
+     * Output the DER formatted attribute certificate associated with this
+     * Credential to the OutputStream.
+     * @param s the OutputStream on which to write
+     * @throws IOException if there is an error writing.
+     */
     public void write(OutputStream s) throws IOException {
-	s.write(ac.getEncoded());
+	if (ac != null ) 
+	    s.write(ac.getEncoded());
 	s.flush();
     }
 
+    /**
+     * Output the DER formatted attribute certificate associated with this
+     * Credential to the filename given.
+     * @param fn a String containing the output filename
+     * @throws IOException if there is an error writing.
+     */
     public void write(String fn) throws IOException, FileNotFoundException {
 	write(new FileOutputStream(fn));
     }
 
+    /**
+     * Return true if this Credential has a certificate associated.  A jabac
+     * extension.
+     * @return true if this Credential has a certificate associated.
+     */
     public boolean hasCertificate() { return ac != null; }
 
+    /**
+     * Return the Identity that issued the underlying certificate.  A jabac
+     * extension.
+     * @return the Identity that issued the underlying certificate.
+     */
     public Identity issuer() { return id; }
+    /**
+     * Return the X509Certificate that issued the underlying certificate.
+     * @return the X509Certificate that issued the underlying certificate.
+     */
     public X509Certificate issuerCert() { return id.getCertificate(); }
+    /**
+     * Return the X509V2AttributeCertificate that underlys the Credential
+     * @return the X509V2AttributeCertificate that underlys the Credential.
+     */
     public X509V2AttributeCertificate attributeCert() { return ac; }
 
