Index: java/GraphTest.java
===================================================================
--- java/GraphTest.java	(revision 2750153bf75d4485203c2ca772cf869e8c8b928a)
+++ java/GraphTest.java	(revision 1a7e6d35b6743a808b934ce72d45b1853308d1dc)
@@ -137,9 +137,19 @@
         Query q = graph.querier();
         Graph<Role, Credential> ret = q.run(role, prin);
-        for (Credential c : ret.getEdges())
+	String fn = "attr";
+	int n = 0;
+	String suf = ".der";
+        for (Credential c : ret.getEdges()) {
             System.out.println(replace(c.toString(), keys));
+	    c.write(fn + n++ + suf);
+	}
 
-	for (Identity i: Credential.identities())
+	fn = "id";
+	n = 0;
+	suf = ".pem";
+	for (Identity i: Credential.identities()) {
 	    System.out.println("ID: " + i);
+	    i.write(fn + n++ + suf);
+	}
     }
 }
Index: java/build.xml
===================================================================
--- java/build.xml	(revision 2750153bf75d4485203c2ca772cf869e8c8b928a)
+++ java/build.xml	(revision 1a7e6d35b6743a808b934ce72d45b1853308d1dc)
@@ -9,4 +9,8 @@
   <target name="clean">
     <delete dir="${build.dir}"/>
+    <delete>
+      <fileset dir="${basedir}" includes="**/*.der"/>
+      <fileset dir="${basedir}" includes="**/*.pem"/>
+    </delete>
   </target>
 
@@ -23,12 +27,28 @@
   </target>
 
-    <target name="jar" depends="compile" description="Make a jar">
-      <mkdir dir="${jar.dir}"/>
-      <jar destfile="${jar.dir}/abac.jar" basedir="${classes.dir}"
-	excludes="**/GraphTest.class">
-	<manifest>
-	    <attribute name="Class-Path" value="${classpath}" />
-	</manifest>
-        </jar>
-    </target>
+  <target name="jar" depends="compile" description="Make a jar">
+    <mkdir dir="${jar.dir}"/>
+    <jar destfile="${jar.dir}/abac.jar" basedir="${classes.dir}"
+      excludes="**/GraphTest.class">
+      <manifest>
+	<attribute name="Class-Path" value="${classpath}" />
+      </manifest>
+    </jar>
+  </target>
+
+  <target name="run" depends="compile" description="run visualizer">
+    <!-- ant doesn't seem to understand symlinks right.  If you don't delete
+    <symlink action="delete" link="${classes.dir}/resources"/>
+    <symlink link="${classes.dir}/resources" resource="${basedir}/resources/"/>
+	 this before you create it, you wind up with extras. -->
+    <java classname="${main.class}" fork="true" >
+      <classpath>
+	<path refid="compile.classpath"/>
+	<pathelement path="${classes.dir}"/>
+      </classpath>
+      <arg file="../examples/acme_rockets"/>
+      <arg value="Acme.buy_rockets"/>
+      <arg value="Coyote"/>
+    </java>
+  </target>
 </project>
Index: java/net/deterlab/abac/Credential.java
===================================================================
--- java/net/deterlab/abac/Credential.java	(revision 2750153bf75d4485203c2ca772cf869e8c8b928a)
+++ java/net/deterlab/abac/Credential.java	(revision 1a7e6d35b6743a808b934ce72d45b1853308d1dc)
@@ -30,8 +30,7 @@
      * Do the credential initialization from a filename.
      */
-    protected void init(String filename) throws Exception {
-        FileInputStream fis = new FileInputStream(filename);
+    protected void init(InputStream stream) throws Exception {
         X509AttrCertParser parser = new X509AttrCertParser();
-        parser.engineInit(fis);
+        parser.engineInit(stream);
         m_ac = (X509V2AttributeCertificate)parser.engineRead();
 	m_id = null;
@@ -55,5 +54,5 @@
      */
     public Credential(String filename) throws Exception {
-	init(filename);
+	init(new FileInputStream(filename));
     }
 
@@ -63,5 +62,12 @@
      */
     public Credential(File file) throws Exception {
-	init(file.getPath());
+	init(new FileInputStream(file));
+    }
+
+    /**
+     * Create a credential from an InputStream.
+     */
+    public Credential(InputStream s) throws Exception { 
+	init(s);
     }
 
@@ -123,4 +129,13 @@
     }
 
+    public void write(OutputStream s) throws IOException {
+	s.write(m_ac.getEncoded());
+    }
+
+    public void write(String fn) throws IOException, FileNotFoundException {
+	write(new FileOutputStream(fn));
+    }
+
+
     private Role m_head, m_tail;
 
Index: java/net/deterlab/abac/Identity.java
===================================================================
--- java/net/deterlab/abac/Identity.java	(revision 2750153bf75d4485203c2ca772cf869e8c8b928a)
+++ java/net/deterlab/abac/Identity.java	(revision 1a7e6d35b6743a808b934ce72d45b1853308d1dc)
@@ -15,4 +15,5 @@
 import org.bouncycastle.jce.provider.X509CertificateObject;
 import org.bouncycastle.openssl.PEMReader;
+import org.bouncycastle.openssl.PEMWriter;
 
 public class Identity {
@@ -21,9 +22,15 @@
     private String m_cn;
 
-    public void init(String filename) throws 
+    /**
+     *  Initialize internals from PEM cert in a reader.  Use a PEMReader to get
+     *  the certificate, confirm it is self signed,  and then the keyid and
+     *  common name.  There's some work to get this stuff, but it's all an
+     *  incantation of getting the right classes to get the right data.  Looks
+     *  more complex than it is.
+     */
+    public void init(Reader r) throws 
 	CertificateException, NoSuchAlgorithmException,InvalidKeyException,
-	NoSuchProviderException, SignatureException, FileNotFoundException,
-	IOException {
-	    PEMReader pr = new PEMReader(new FileReader(filename));
+	NoSuchProviderException, SignatureException, IOException {
+	    PEMReader pr = new PEMReader(r);
 	    Object c = pr.readObject();
 
@@ -56,18 +63,76 @@
     }
 
+    /**
+     * Construct from a string, which is a filename, containing a PEM format
+     * self signed certificate.
+     */
     public Identity(String filename) throws 
 	CertificateException, NoSuchAlgorithmException,InvalidKeyException,
 	NoSuchProviderException, SignatureException, FileNotFoundException,
-	IOException { init(filename); }
+	IOException { init(new FileReader(filename)); }
 
 
+    /**
+     * Construct from a file, containing a self-signed PEM certificate.
+     */
     public Identity(File file) throws 
 	CertificateException, NoSuchAlgorithmException,InvalidKeyException,
 	NoSuchProviderException, SignatureException, FileNotFoundException,
-	IOException { init(file.getPath()); }
+	IOException { init(new FileReader(file)); }
 
+    /**
+     * Construct from a reader, containing a self-signed PEM certificate.
+     */
+    public Identity(Reader r) throws 
+	CertificateException, NoSuchAlgorithmException,InvalidKeyException,
+	NoSuchProviderException, SignatureException, IOException { init(r); }
+
+    /**
+     * Construct from an InputStream, containing a self-signed PEM certificate.
+     */
+    public Identity(InputStream s) throws 
+	CertificateException, NoSuchAlgorithmException,InvalidKeyException,
+	NoSuchProviderException, SignatureException, IOException { 
+	    init(new InputStreamReader(s));
+	}
+
+
+    /**
+     * Write the PEM cert to the given writer.
+     */
+    public void write(Writer w) throws IOException {
+	PEMWriter pw = new PEMWriter(w);
+
+	pw.writeObject(m_cert);
+	pw.close();
+    }
+
+    /**
+     * Write the PEM cert to a file with the given name.
+     */
+    public void write(String fn) throws IOException, FileNotFoundException {
+	write(new FileWriter(fn));
+    }
+
+    /**
+     * Write the PEM cert to the given file.
+     */
+    public void write(File fn) throws IOException, FileNotFoundException {
+	write(new FileWriter(fn));
+    }
+
+    /**
+     * Write the PEM cert to the given OutputStream.
+     */
+    public void write(OutputStream s) 
+	throws IOException, FileNotFoundException {
+	write(new OutputStreamWriter(s));
+    }
+
+    // Accessors
     public String getKeyID() { return m_keyid; }
     public String getName() { return m_cn; }
     public String toString() { return m_keyid + " (" + m_cn + ")"; }
     public X509CertificateObject getCertificate() { return m_cert; }
+
 };
