source: java/net/deterlab/abac/Identity.java @ a1a9a47

abac0-leakabac0-mei
Last change on this file since a1a9a47 was a1a9a47, checked in by Ted Faber <faber@…>, 11 years ago

Bump version

  • Property mode set to 100644
File size: 14.7 KB
RevLine 
[9725efb]1package net.deterlab.abac;
2
3import java.io.*;
4
5import java.util.*;
6import java.security.*;
7import java.security.cert.*;
[3a52bed]8import javax.security.auth.x500.*;
[9725efb]9
[3a52bed]10import java.math.BigInteger;
[9725efb]11
12import org.bouncycastle.asn1.*;
13import org.bouncycastle.asn1.util.*;
14import org.bouncycastle.asn1.x509.*;
15import org.bouncycastle.x509.*;
[238717d]16import org.bouncycastle.openssl.*;
17
[9725efb]18
[e36ea1d]19/**
20 * An ABAC identity.  An X509 Certificate-encoded public key.  The key
21 * identifier is used as the name of the Identity.  This whole class is
22 * something of a jabac extension.
23 * @author <a href="http://abac.deterlab.net">ISI ABAC team</a>
[a1a9a47]24 * @version 1.5
[e36ea1d]25 */
[5cf72cc]26public class Identity implements Comparable {
[3f928b0]27    /** Default validity period (in seconds) */
28    static public long defaultValidity = 3600L * 24L * 365L;
[e36ea1d]29    /** The underlying X509 certificate. */
30    protected X509Certificate cert;
31    /** The public key id used as this principal's name */
32    protected String keyid;
33    /** The common name in the certificate, used as a mnemonic */
34    protected String cn;
35    /** The keypair, if any, used to sign for this Identity */
36    protected KeyPair kp;
[3f928b0]37    /** The expiration for this Identity */
38    protected Date expiration;
[9725efb]39
[238717d]40    /** Make sure BouncyCastle is loaded */
41    static { Context.loadBouncyCastle(); } 
42
[1a7e6d3]43    /**
[e36ea1d]44     * Initialize from PEM cert in a reader.  Use a PEMReader to get
45     * the certificate, and call init(cert) on it.
46     * @param r a Reader containing the certificate
[7ebf16d]47     * @throws CertInvalidException if the stream is unparsable
48     * @throws MissingIssuerException if none of the Identities can validate the
[e36ea1d]49     *                              certificate
[7ebf16d]50     * @throws BadSignatureException if the signature check fails
51     * @throws ABACException if an uncategorized error occurs
[1a7e6d3]52     */
[7ebf16d]53    protected void init(Reader r) throws ABACException {
[42ca4b8]54        PEMReader pr = new PEMReader(r);
55        Object c = null;
56
[7ebf16d]57        try {
58            while ( ( c= pr.readObject()) != null ){
59
60                if (c instanceof X509Certificate) {
61                    if ( cn == null ) 
62                        init((X509Certificate)c);
63                    else
64                        throw new CertInvalidException("Two certs in one");
65                }
66                else if (c instanceof KeyPair) setKeyPair((KeyPair)c);
67                else 
68                    throw new CertInvalidException(
69                            "Not an identity certificate");
[42ca4b8]70            }
[7ebf16d]71        }
72        catch (IOException e) {
73            throw new CertInvalidException(e.getMessage(), e);
[42ca4b8]74        }
[f31432f]75        // If there's nothing for the PEM reader to parse, the cert is invalid.
76        if (cn == null) 
[7ebf16d]77            throw new CertInvalidException("Not an identity certificate");
[8a14e37]78    }
79
80    /**
[e36ea1d]81     * Initialize internals from cert.  Confirm it is self signed,  and then
82     * the keyid and common name.  There's some work to get this stuff, but
83     * it's all an incantation of getting the right classes to get the right
84     * data.  Looks more complex than it is.
85     * @param c an X509Certificate to init from
[7ebf16d]86     * @throws CertInvalidException if the stream is unparsable
87     * @throws MissingIssuerException if none of the Identities can validate the
[e36ea1d]88     *                              certificate
[7ebf16d]89     * @throws BadSignatureException if the signature check fails
90     * @throws ABACException if an uncategorized error occurs
[8a14e37]91     */
[7ebf16d]92    protected void init(X509Certificate c) throws ABACException {
93        cert = (X509Certificate) c;
94        try {
[0595372]95            cert.verify(cert.getPublicKey());
[7ebf16d]96        }
97        catch (SignatureException e) {
[fbdd2d1]98            // XXX: the cert is not signed by the key we provided.  Right now
99            // we check each cert as if it were self-signed. Other signing
100            // strategies are allowed here by default.  We expect outside
101            // sources to validate ID certs if they expect different chains of
[7ebf16d]102        }
103        catch (CertificateException e) {
104            throw new CertInvalidException(e.getMessage(), e);
105        }
[4bd50f4]106        catch (InvalidKeyException e) {
107            // XXX: the cert is not signed by the key we provided.  Right now
108            // we check each cert as if it were self-signed. Other signing
109            // strategies are allowed here by default.  We expect outside
110            // sources to validate ID certs if they expect different chains of
111            // trust.
112        }
[7ebf16d]113        catch (GeneralSecurityException e) {
114            throw new ABACException(e.getMessage(), e);
115        }
116        // Cert is valid, fill in the CN and keyid
117        keyid = Context.extractKeyID(cert.getPublicKey());
118        cn = cert.getSubjectDN().getName();
119        expiration = cert.getNotAfter();
120        /// XXX: better parse
121        if (cn.startsWith("CN=")) cn = cn.substring(3);
[9725efb]122    }
123
[1a7e6d3]124    /**
[81c80b9]125     * Construct from a string, used as a CN.  Keys are generated.  If signer
126     * and signingKey are given, sign the certificate with them.  If neither is
127     * given, self sign it.  If one is given and not the other, throw an
128     * ABACException.
[e36ea1d]129     * @param cn a String containing the menomnic name
[3f928b0]130     * @param validity a long containing the validity period (in seconds)
[81c80b9]131     * @param signer an X509Certificate that is signing the Identity
132     * @param signingKey the key with which to sign
[7ebf16d]133     * @throws CertInvalidException if the stream is unparsable
134     * @throws MissingIssuerException if none of the Identities can validate the
[e36ea1d]135     *                              certificate
[7ebf16d]136     * @throws BadSignatureException if the signature check fails
137     * @throws ABACException if an uncategorized error occurs
[1a7e6d3]138     */
[81c80b9]139    public Identity(String cn, long validity, X509Certificate signer, 
140            PrivateKey signingKey) 
141            throws ABACException {
142
143        if ( (signer != null && signingKey == null) || 
144                (signer == null && signingKey != null) )
145            throw new ABACException("Both signer and signingKey must be "+ 
146                    "given or neither");
147
[3a52bed]148        X509V1CertificateGenerator gen = new X509V1CertificateGenerator();
[7ebf16d]149        try {
150            kp = KeyPairGenerator.getInstance("RSA").genKeyPair();
151        }
152        catch (NoSuchAlgorithmException e) {
153            throw new ABACException(e.getMessage(), e);
154        }
155        X509Certificate a = null;
[81c80b9]156        X500Principal sp = (signer != null ) ?
157            signer.getSubjectX500Principal() : new X500Principal("CN=" + cn);
158        PrivateKey sk = (signingKey != null ) ? signingKey : kp.getPrivate();
[3a52bed]159
[81c80b9]160        gen.setIssuerDN(sp);
[3a52bed]161        gen.setSubjectDN(new X500Principal("CN=" + cn));
[3f928b0]162        gen.setNotAfter(new Date(System.currentTimeMillis() +
163                1000L * validity));
[3a52bed]164        gen.setNotBefore(new Date(System.currentTimeMillis()));
165        gen.setSerialNumber(BigInteger.valueOf(System.currentTimeMillis()));
166        gen.setPublicKey(kp.getPublic());
167        gen.setSignatureAlgorithm("SHA256WithRSAEncryption");
[7ebf16d]168        try {
[81c80b9]169            a = (X509Certificate) gen.generate(sk, "BC");
[7ebf16d]170        }
171        catch (CertificateEncodingException e) {
172            throw new CertInvalidException(e.getMessage(), e);
173        }
174        catch (GeneralSecurityException e) {
175            throw new ABACException(e.getMessage(), e);
176        }
177
[3a52bed]178        init(a);
179    }
[81c80b9]180    /**
181     * Construct from a string, used as a CN.  Keys are generated.
182     * @param cn a String containing the menomnic name
183     * @param validity a long containing the validity period (in seconds)
184     * @throws CertInvalidException if the stream is unparsable
185     * @throws MissingIssuerException if none of the Identities can validate the
186     *                              certificate
187     * @throws BadSignatureException if the signature check fails
188     * @throws ABACException if an uncategorized error occurs
189     */
190    public Identity(String cn, long validity) throws ABACException {
191        this(cn, validity, null, null);
192    }
[3a52bed]193
[3f928b0]194    /**
195     * Construct from a string, used as a CN.  Keys are generated.
196     * @param cn a String containing the menomnic name
[7ebf16d]197     * @throws CertInvalidException if the stream is unparsable
198     * @throws MissingIssuerException if none of the Identities can validate the
[3f928b0]199     *                              certificate
[7ebf16d]200     * @throws BadSignatureException if the signature check fails
201     * @throws ABACException if an uncategorized error occurs
[3f928b0]202     */
[7ebf16d]203    public Identity(String cn) throws ABACException { 
[3f928b0]204        this(cn, defaultValidity);
205    }
[9725efb]206
[1a7e6d3]207    /**
[a7f73b5]208     * Construct from a file containing a self-signed PEM certificate.
[e36ea1d]209     * @param file the File to read
[7ebf16d]210     * @throws CertInvalidException if the stream is unparsable
211     * @throws MissingIssuerException if none of the Identities can validate the
[e36ea1d]212     *                              certificate
[7ebf16d]213     * @throws BadSignatureException if the signature check fails
214     * @throws ABACException if an uncategorized error occurs
215     * @throws FileNotFoundException if the file is invalid
[1a7e6d3]216     */
[7ebf16d]217    public Identity(File file) throws ABACException, FileNotFoundException { 
218        kp = null;
219        init(new FileReader(file));
220    }
[1a7e6d3]221
222    /**
[e36ea1d]223     * Construct from a reader containing a self-signed PEM certificate.
224     * @param r the Reader containing the certificate
[7ebf16d]225     * @throws CertInvalidException if the stream is unparsable
226     * @throws MissingIssuerException if none of the Identities can validate the
[e36ea1d]227     *                              certificate
[7ebf16d]228     * @throws BadSignatureException if the signature check fails
229     * @throws ABACException if an uncategorized error occurs
[1a7e6d3]230     */
[7ebf16d]231    public Identity(Reader r) throws ABACException {
232        kp = null;
233        init(r);
234    }
[1a7e6d3]235
236    /**
[e36ea1d]237     * Construct from an InputStream containing a self-signed PEM certificate.
238     * @param s the InputStream containing the certificate
[7ebf16d]239     * @throws CertInvalidException if the stream is unparsable
240     * @throws MissingIssuerException if none of the Identities can validate the
[e36ea1d]241     *                              certificate
[7ebf16d]242     * @throws BadSignatureException if the signature check fails
243     * @throws ABACException if an uncategorized error occurs
[1a7e6d3]244     */
[7ebf16d]245    public Identity(InputStream s) throws ABACException {
246        kp = null;
247        init(new InputStreamReader(s));
248    }
[1a7e6d3]249
[8a14e37]250    /**
[e36ea1d]251     * Construct from an X509Certificate
252     * @param cert an X509Certificate to init from
[7ebf16d]253     * @throws CertInvalidException if the stream is unparsable
254     * @throws MissingIssuerException if none of the Identities can validate the
[e36ea1d]255     *                              certificate
[7ebf16d]256     * @throws BadSignatureException if the signature check fails
257     * @throws ABACException if an uncategorized error occurs
[8a14e37]258     */
[4feb5be]259    public Identity(X509Certificate cert) throws ABACException {
[7ebf16d]260        kp = null;
261        init(cert);
262    }
[8a14e37]263
[8a93b41]264    /**
265     * Write the PEM key to the given writer.
[e36ea1d]266     * @param w the Writer
267     * @return true if the Identity had a keypair and wrote the key
268     * @throws IOException if writing fails
[8a93b41]269     */
270    public boolean writePrivateKey(Writer w) throws IOException {
271        if (kp != null ) {
272            PEMWriter pw = new PEMWriter(w);
273
274            pw.writeObject(kp.getPrivate());
275            pw.flush();
276            return true;
277        }
278        else return false;
279    }
280
281    /**
282     * Write the PEM key to a file with the given name.
283     */
284    public boolean writePrivateKey(String fn) 
285            throws IOException, FileNotFoundException {
286        return writePrivateKey(new FileWriter(fn));
287    }
288
289    /**
290     * Write the PEM key to the given file.
[e36ea1d]291     * @param fn a String with the output file name
292     * @return true if the Identity had a keypair and wrote the key
293     * @throws IOException if writing fails
[8a93b41]294     */
295    public boolean writePrivateKey(File fn) 
296            throws IOException, FileNotFoundException {
297        return writePrivateKey(new FileWriter(fn));
298    }
299
300    /**
301     * Write the PEM key to the given OutputStream.
[e36ea1d]302     * @param s an OutputStream to write on
303     * @return true if the Identity had a keypair and wrote the key
304     * @throws IOException if writing fails
[8a93b41]305     */
306    public boolean writePrivateKey(OutputStream s) 
307            throws IOException, FileNotFoundException {
308        return writePrivateKey(new OutputStreamWriter(s));
309    }
310
[1a7e6d3]311
312    /**
313     * Write the PEM cert to the given writer.
[e36ea1d]314     * @param w a Writer to write on
315     * @throws IOException if writing fails
[1a7e6d3]316     */
317    public void write(Writer w) throws IOException {
318        PEMWriter pw = new PEMWriter(w);
319
[0595372]320        pw.writeObject(cert);
[5cf72cc]321        pw.flush();
[1a7e6d3]322    }
323
324    /**
325     * Write the PEM cert to a file with the given name.
326     */
327    public void write(String fn) throws IOException, FileNotFoundException {
328        write(new FileWriter(fn));
329    }
330
331    /**
332     * Write the PEM cert to the given file.
[e36ea1d]333     * @param fn a String with the output file name
334     * @throws IOException if writing fails
[1a7e6d3]335     */
336    public void write(File fn) throws IOException, FileNotFoundException {
337        write(new FileWriter(fn));
338    }
339
340    /**
341     * Write the PEM cert to the given OutputStream.
[e36ea1d]342     * @param s an OutputStream to write on
343     * @throws IOException if writing fails
[1a7e6d3]344     */
345    public void write(OutputStream s) 
346        throws IOException, FileNotFoundException {
347        write(new OutputStreamWriter(s));
348    }
[9725efb]349
[e9360e2]350
[e36ea1d]351    /**
352     * Return the Identity's KeyID
353     * @return the Identity's KeyID
354     */
[0595372]355    public String getKeyID() { return keyid; }
[e36ea1d]356    /**
357     * Return the Identity's mnemonic name
358     * @return the Identity's mnemonic name
359     */
[0595372]360    public String getName() { return cn; }
[e36ea1d]361    /**
362     * Return the Identity's X509 Certificate
363     * @return the Identity's X509 Certificate
364     */
365    public X509Certificate getCertificate() { return cert; }
[3f928b0]366
367    /**
368     * Return the expiration time of the Identity
369     * @return a Date the expiration time of the Identity
370     */
371    public Date getExpiration(){ return expiration; }
372
[e36ea1d]373    /**
374     * Return a simple string rep of the Identity.
375     * @return a simple string rep of the Identity.
376     */
[42ca4b8]377    public String toString() { 
[0595372]378        String s = keyid + " (" + cn ;
[42ca4b8]379
[ac131a1]380        if (getKeyPair() != null ) s += " [keyed]";
[42ca4b8]381        s += ")";
382        return s;
383    }
384    /**
385     * Associate a keypair with this Identity.  If the ID has a certificate,
386     * make sure that the keypair matches it.  If not throw an
387     * IllegalArgumentException.
[e36ea1d]388     * @param k the KeyPair to connect
389     * @throws IllegalArgumentException if the keypair does not
390     *                              match the pubkey in the X509 certificate
[42ca4b8]391     */
392    public void setKeyPair(KeyPair k) {
[0595372]393        if (keyid != null) {
394            String kid = Context.extractKeyID(k.getPublic());
[42ca4b8]395
[0595372]396            if ( kid != null && kid.equals(keyid)) kp = k;
[42ca4b8]397            else 
398                throw new IllegalArgumentException(
399                        "Keypair does not match certificate");
400        }
401        else kp = k;
402    }
[e36ea1d]403
404    /**
405     * Return the keypair associated with this Identity (if any)
406     * @return the keypair associated with this Identity (if any)
407     */
[42ca4b8]408    public KeyPair getKeyPair() { return kp; }
[e36ea1d]409
410    /**
[a7f73b5]411     * Return true if the two identites refer to teh same key.  Two Identities
412     * are equal if their key ID's match.
[e36ea1d]413     * @return true if the two key ID's are equal.
414     */
[5cf72cc]415    public boolean equals(Object o) { 
416        if ( o == null ) return false;
417        else if ( ! (o instanceof Identity) ) return false;
418        else return getKeyID().equals(((Identity)o).getKeyID());
419    }
[0100d7b]420
421    /**
422     * Return a hash code for the Identity - the hash of its KeyID()
423     * @return an int, the hashCode
424     */
425    public int hashCode() {
426        if (keyid == null) return super.hashCode();
427        return keyid.hashCode();
428    }
429
430
[e36ea1d]431    /**
[a7f73b5]432     * Order 2 identities for sorting.  They are ordered by their key ID's.
[e36ea1d]433     * @param o an Object to compare
434     * @return -1 if this Identity is before, 0 if they are the same, and 1
435     *              if this Identity is after the given object.
436     */
[5cf72cc]437    public int compareTo(Object o) { 
438        if ( ! (o instanceof Identity) ) return 1;
439        else return getKeyID().compareTo(((Identity)o).getKeyID());
440    }
[1a7e6d3]441
[9725efb]442};
Note: See TracBrowser for help on using the repository browser.