package net.deterlab.abac; import java.io.*; import java.util.*; import java.security.*; import java.security.cert.*; import javax.security.auth.x500.*; import java.math.BigInteger; import org.bouncycastle.asn1.*; import org.bouncycastle.asn1.util.*; import org.bouncycastle.asn1.x509.*; import org.bouncycastle.x509.*; import org.bouncycastle.jce.provider.X509AttrCertParser; import org.bouncycastle.jce.provider.X509CertificateObject; import org.bouncycastle.openssl.PEMReader; import org.bouncycastle.openssl.PEMWriter; public class Identity implements Comparable { private X509CertificateObject m_cert; private String m_keyid; private String m_cn; private KeyPair kp; /** * Initialize internals from PEM cert in a reader. Use a PEMReader to get * the certificate, and call init(cert) on it. */ protected void init(Reader r) throws CertificateException, NoSuchAlgorithmException,InvalidKeyException, NoSuchProviderException, SignatureException, IOException { PEMReader pr = new PEMReader(r); Object c = null; while ( ( c= pr.readObject()) != null ){ if (c instanceof X509CertificateObject) { if ( m_cn == null ) init((X509CertificateObject)c); else throw new CertificateException("Two certs in one file"); } else if (c instanceof KeyPair) setKeyPair((KeyPair)c); else throw new CertificateException( "Not an identity certificate"); } } /** * Initialize internals from cert. 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. */ protected void init(X509CertificateObject c) throws CertificateException, NoSuchAlgorithmException,InvalidKeyException, NoSuchProviderException, SignatureException, IOException { m_cert = (X509CertificateObject) c; m_cert.verify(m_cert.getPublicKey()); // Cert is valid, fill in the CN and keyid m_keyid = extractKeyID(m_cert.getPublicKey()); m_cn = m_cert.getSubjectDN().getName(); /// XXX: better parse if (m_cn.startsWith("CN=")) m_cn = m_cn.substring(3); } /** * Construct from a string, used as a CN */ public Identity(String cn) throws CertificateException, NoSuchAlgorithmException,InvalidKeyException, NoSuchProviderException, SignatureException, IOException { X509V1CertificateGenerator gen = new X509V1CertificateGenerator(); kp = KeyPairGenerator.getInstance("RSA").genKeyPair(); gen.setIssuerDN(new X500Principal("CN=" + cn)); gen.setSubjectDN(new X500Principal("CN=" + cn)); gen.setNotAfter(new Date(System.currentTimeMillis() + 3600 * 1000 * 24 * 365)); gen.setNotBefore(new Date(System.currentTimeMillis())); gen.setSerialNumber(BigInteger.valueOf(System.currentTimeMillis())); gen.setPublicKey(kp.getPublic()); gen.setSignatureAlgorithm("SHA256WithRSAEncryption"); X509CertificateObject a = (X509CertificateObject) gen.generate(kp.getPrivate()); init(a); } /** * Construct from a file, containing a self-signed PEM certificate. */ public Identity(File file) throws CertificateException, NoSuchAlgorithmException,InvalidKeyException, NoSuchProviderException, SignatureException, FileNotFoundException, IOException { kp = null; 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 { kp = null; init(r); } /** * Construct from an InputStream, containing a self-signed PEM certificate. */ public Identity(InputStream s) throws CertificateException, NoSuchAlgorithmException,InvalidKeyException, NoSuchProviderException, SignatureException, IOException { kp = null; init(new InputStreamReader(s)); } /** * Construct from an X509CertificateObject, if you parsed one somewhere * else. */ public Identity(X509CertificateObject cert) throws CertificateException, NoSuchAlgorithmException,InvalidKeyException, NoSuchProviderException, SignatureException, FileNotFoundException, IOException { kp = null; init(cert); } /** * Write the PEM key to the given writer. */ public boolean writePrivateKey(Writer w) throws IOException { if (kp != null ) { PEMWriter pw = new PEMWriter(w); pw.writeObject(kp.getPrivate()); pw.flush(); return true; } else return false; } /** * Write the PEM key to a file with the given name. */ public boolean writePrivateKey(String fn) throws IOException, FileNotFoundException { return writePrivateKey(new FileWriter(fn)); } /** * Write the PEM key to the given file. */ public boolean writePrivateKey(File fn) throws IOException, FileNotFoundException { return writePrivateKey(new FileWriter(fn)); } /** * Write the PEM key to the given OutputStream. */ public boolean writePrivateKey(OutputStream s) throws IOException, FileNotFoundException { return writePrivateKey(new OutputStreamWriter(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.flush(); } /** * 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)); } /** * Get to the SHA1 hash of the key. */ public static String extractKeyID(PublicKey k) { ASN1Sequence seq = null; try { seq = (ASN1Sequence) new ASN1InputStream( k.getEncoded()).readObject(); } catch (IOException ie) { // Badly formatted key?? return null; } SubjectPublicKeyInfo ki = new SubjectPublicKeyInfo(seq); SubjectKeyIdentifier id = SubjectKeyIdentifier.createSHA1KeyIdentifier(ki); // Now format it into a string for keeps Formatter fmt = new Formatter(new StringWriter()); for (byte b: id.getKeyIdentifier()) fmt.format("%02x", b); return fmt.out().toString(); } // Accessors public String getKeyID() { return m_keyid; } public String getName() { return m_cn; } public String toString() { String s = m_keyid + " (" + m_cn ; if (m_keyid != null ) s += " [keyed]"; s += ")"; return s; } /** * Associate a keypair with this Identity. If the ID has a certificate, * make sure that the keypair matches it. If not throw an * IllegalArgumentException. */ public void setKeyPair(KeyPair k) { if (m_keyid != null) { String kid = extractKeyID(k.getPublic()); if ( kid != null && kid.equals(m_keyid)) kp = k; else throw new IllegalArgumentException( "Keypair does not match certificate"); } else kp = k; } public KeyPair getKeyPair() { return kp; } public boolean equals(Object o) { if ( o == null ) return false; else if ( ! (o instanceof Identity) ) return false; else return getKeyID().equals(((Identity)o).getKeyID()); } public int compareTo(Object o) { if ( ! (o instanceof Identity) ) return 1; else return getKeyID().compareTo(((Identity)o).getKeyID()); } public X509CertificateObject getCertificate() { return m_cert; } };