source: java/net/deterlab/abac/Identity.java @ 98a9afb

abac0-leakabac0-meimei-idmei-rt0-nmei_rt0tvf-new-xml
Last change on this file since 98a9afb was 0100d7b, checked in by Ted Faber <faber@…>, 12 years ago

Make sure hashCodes work right

  • Property mode set to 100644
File size: 12.9 KB
Line 
1package net.deterlab.abac;
2
3import java.io.*;
4
5import java.util.*;
6import java.security.*;
7import java.security.cert.*;
8import javax.security.auth.x500.*;
9
10import java.math.BigInteger;
11
12import org.bouncycastle.asn1.*;
13import org.bouncycastle.asn1.util.*;
14import org.bouncycastle.asn1.x509.*;
15import org.bouncycastle.x509.*;
16import org.bouncycastle.openssl.*;
17
18
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>
24 * @version 1.4
25 */
26public class Identity implements Comparable {
27    /** Default validity period (in seconds) */
28    static public long defaultValidity = 3600L * 24L * 365L;
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;
37    /** The expiration for this Identity */
38    protected Date expiration;
39
40    /** Make sure BouncyCastle is loaded */
41    static { Context.loadBouncyCastle(); } 
42
43    /**
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
47     * @throws CertInvalidException if the stream is unparsable
48     * @throws MissingIssuerException if none of the Identities can validate the
49     *                              certificate
50     * @throws BadSignatureException if the signature check fails
51     * @throws ABACException if an uncategorized error occurs
52     */
53    protected void init(Reader r) throws ABACException {
54        PEMReader pr = new PEMReader(r);
55        Object c = null;
56
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");
70            }
71        }
72        catch (IOException e) {
73            throw new CertInvalidException(e.getMessage(), e);
74        }
75        // If there's nothing for the PEM reader to parse, the cert is invalid.
76        if (cn == null) 
77            throw new CertInvalidException("Not an identity certificate");
78    }
79
80    /**
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
86     * @throws CertInvalidException if the stream is unparsable
87     * @throws MissingIssuerException if none of the Identities can validate the
88     *                              certificate
89     * @throws BadSignatureException if the signature check fails
90     * @throws ABACException if an uncategorized error occurs
91     */
92    protected void init(X509Certificate c) throws ABACException {
93        cert = (X509Certificate) c;
94        try {
95            cert.verify(cert.getPublicKey());
96        }
97        catch (SignatureException e) {
98            throw new BadSignatureException(e.getMessage(), e);
99        }
100        catch (CertificateException e) {
101            throw new CertInvalidException(e.getMessage(), e);
102        }
103        catch (GeneralSecurityException e) {
104            throw new ABACException(e.getMessage(), e);
105        }
106        // Cert is valid, fill in the CN and keyid
107        keyid = Context.extractKeyID(cert.getPublicKey());
108        cn = cert.getSubjectDN().getName();
109        expiration = cert.getNotAfter();
110        /// XXX: better parse
111        if (cn.startsWith("CN=")) cn = cn.substring(3);
112    }
113
114    /**
115     * Construct from a string, used as a CN.  Keys are generated.
116     * @param cn a String containing the menomnic name
117     * @param validity a long containing the validity period (in seconds)
118     * @throws CertInvalidException if the stream is unparsable
119     * @throws MissingIssuerException if none of the Identities can validate the
120     *                              certificate
121     * @throws BadSignatureException if the signature check fails
122     * @throws ABACException if an uncategorized error occurs
123     */
124    public Identity(String cn, long validity) throws ABACException {
125        X509V1CertificateGenerator gen = new X509V1CertificateGenerator();
126        try {
127            kp = KeyPairGenerator.getInstance("RSA").genKeyPair();
128        }
129        catch (NoSuchAlgorithmException e) {
130            throw new ABACException(e.getMessage(), e);
131        }
132        X509Certificate a = null;
133
134        gen.setIssuerDN(new X500Principal("CN=" + cn));
135        gen.setSubjectDN(new X500Principal("CN=" + cn));
136        gen.setNotAfter(new Date(System.currentTimeMillis() +
137                1000L * validity));
138        gen.setNotBefore(new Date(System.currentTimeMillis()));
139        gen.setSerialNumber(BigInteger.valueOf(System.currentTimeMillis()));
140        gen.setPublicKey(kp.getPublic());
141        gen.setSignatureAlgorithm("SHA256WithRSAEncryption");
142        try {
143            a = (X509Certificate) gen.generate(kp.getPrivate(), "BC");
144        }
145        catch (CertificateEncodingException e) {
146            throw new CertInvalidException(e.getMessage(), e);
147        }
148        catch (GeneralSecurityException e) {
149            throw new ABACException(e.getMessage(), e);
150        }
151
152        init(a);
153    }
154
155    /**
156     * Construct from a string, used as a CN.  Keys are generated.
157     * @param cn a String containing the menomnic name
158     * @throws CertInvalidException if the stream is unparsable
159     * @throws MissingIssuerException if none of the Identities can validate the
160     *                              certificate
161     * @throws BadSignatureException if the signature check fails
162     * @throws ABACException if an uncategorized error occurs
163     */
164    public Identity(String cn) throws ABACException { 
165        this(cn, defaultValidity);
166    }
167
168    /**
169     * Construct from a file containing a self-signed PEM certificate.
170     * @param file the File to read
171     * @throws CertInvalidException if the stream is unparsable
172     * @throws MissingIssuerException if none of the Identities can validate the
173     *                              certificate
174     * @throws BadSignatureException if the signature check fails
175     * @throws ABACException if an uncategorized error occurs
176     * @throws FileNotFoundException if the file is invalid
177     */
178    public Identity(File file) throws ABACException, FileNotFoundException { 
179        kp = null;
180        init(new FileReader(file));
181    }
182
183    /**
184     * Construct from a reader containing a self-signed PEM certificate.
185     * @param r the Reader containing the certificate
186     * @throws CertInvalidException if the stream is unparsable
187     * @throws MissingIssuerException if none of the Identities can validate the
188     *                              certificate
189     * @throws BadSignatureException if the signature check fails
190     * @throws ABACException if an uncategorized error occurs
191     */
192    public Identity(Reader r) throws ABACException {
193        kp = null;
194        init(r);
195    }
196
197    /**
198     * Construct from an InputStream containing a self-signed PEM certificate.
199     * @param s the InputStream containing the certificate
200     * @throws CertInvalidException if the stream is unparsable
201     * @throws MissingIssuerException if none of the Identities can validate the
202     *                              certificate
203     * @throws BadSignatureException if the signature check fails
204     * @throws ABACException if an uncategorized error occurs
205     */
206    public Identity(InputStream s) throws ABACException {
207        kp = null;
208        init(new InputStreamReader(s));
209    }
210
211    /**
212     * Construct from an X509Certificate
213     * @param cert an X509Certificate to init from
214     * @throws CertInvalidException if the stream is unparsable
215     * @throws MissingIssuerException if none of the Identities can validate the
216     *                              certificate
217     * @throws BadSignatureException if the signature check fails
218     * @throws ABACException if an uncategorized error occurs
219     */
220    Identity(X509Certificate cert) throws ABACException {
221        kp = null;
222        init(cert);
223    }
224
225    /**
226     * Write the PEM key to the given writer.
227     * @param w the Writer
228     * @return true if the Identity had a keypair and wrote the key
229     * @throws IOException if writing fails
230     */
231    public boolean writePrivateKey(Writer w) throws IOException {
232        if (kp != null ) {
233            PEMWriter pw = new PEMWriter(w);
234
235            pw.writeObject(kp.getPrivate());
236            pw.flush();
237            return true;
238        }
239        else return false;
240    }
241
242    /**
243     * Write the PEM key to a file with the given name.
244     */
245    public boolean writePrivateKey(String fn) 
246            throws IOException, FileNotFoundException {
247        return writePrivateKey(new FileWriter(fn));
248    }
249
250    /**
251     * Write the PEM key to the given file.
252     * @param fn a String with the output file name
253     * @return true if the Identity had a keypair and wrote the key
254     * @throws IOException if writing fails
255     */
256    public boolean writePrivateKey(File fn) 
257            throws IOException, FileNotFoundException {
258        return writePrivateKey(new FileWriter(fn));
259    }
260
261    /**
262     * Write the PEM key to the given OutputStream.
263     * @param s an OutputStream to write on
264     * @return true if the Identity had a keypair and wrote the key
265     * @throws IOException if writing fails
266     */
267    public boolean writePrivateKey(OutputStream s) 
268            throws IOException, FileNotFoundException {
269        return writePrivateKey(new OutputStreamWriter(s));
270    }
271
272
273    /**
274     * Write the PEM cert to the given writer.
275     * @param w a Writer to write on
276     * @throws IOException if writing fails
277     */
278    public void write(Writer w) throws IOException {
279        PEMWriter pw = new PEMWriter(w);
280
281        pw.writeObject(cert);
282        pw.flush();
283    }
284
285    /**
286     * Write the PEM cert to a file with the given name.
287     */
288    public void write(String fn) throws IOException, FileNotFoundException {
289        write(new FileWriter(fn));
290    }
291
292    /**
293     * Write the PEM cert to the given file.
294     * @param fn a String with the output file name
295     * @throws IOException if writing fails
296     */
297    public void write(File fn) throws IOException, FileNotFoundException {
298        write(new FileWriter(fn));
299    }
300
301    /**
302     * Write the PEM cert to the given OutputStream.
303     * @param s an OutputStream to write on
304     * @throws IOException if writing fails
305     */
306    public void write(OutputStream s) 
307        throws IOException, FileNotFoundException {
308        write(new OutputStreamWriter(s));
309    }
310
311
312    /**
313     * Return the Identity's KeyID
314     * @return the Identity's KeyID
315     */
316    public String getKeyID() { return keyid; }
317    /**
318     * Return the Identity's mnemonic name
319     * @return the Identity's mnemonic name
320     */
321    public String getName() { return cn; }
322    /**
323     * Return the Identity's X509 Certificate
324     * @return the Identity's X509 Certificate
325     */
326    public X509Certificate getCertificate() { return cert; }
327
328    /**
329     * Return the expiration time of the Identity
330     * @return a Date the expiration time of the Identity
331     */
332    public Date getExpiration(){ return expiration; }
333
334    /**
335     * Return a simple string rep of the Identity.
336     * @return a simple string rep of the Identity.
337     */
338    public String toString() { 
339        String s = keyid + " (" + cn ;
340
341        if (getKeyPair() != null ) s += " [keyed]";
342        s += ")";
343        return s;
344    }
345    /**
346     * Associate a keypair with this Identity.  If the ID has a certificate,
347     * make sure that the keypair matches it.  If not throw an
348     * IllegalArgumentException.
349     * @param k the KeyPair to connect
350     * @throws IllegalArgumentException if the keypair does not
351     *                              match the pubkey in the X509 certificate
352     */
353    public void setKeyPair(KeyPair k) {
354        if (keyid != null) {
355            String kid = Context.extractKeyID(k.getPublic());
356
357            if ( kid != null && kid.equals(keyid)) kp = k;
358            else 
359                throw new IllegalArgumentException(
360                        "Keypair does not match certificate");
361        }
362        else kp = k;
363    }
364
365    /**
366     * Return the keypair associated with this Identity (if any)
367     * @return the keypair associated with this Identity (if any)
368     */
369    public KeyPair getKeyPair() { return kp; }
370
371    /**
372     * Return true if the two identites refer to teh same key.  Two Identities
373     * are equal if their key ID's match.
374     * @return true if the two key ID's are equal.
375     */
376    public boolean equals(Object o) { 
377        if ( o == null ) return false;
378        else if ( ! (o instanceof Identity) ) return false;
379        else return getKeyID().equals(((Identity)o).getKeyID());
380    }
381
382    /**
383     * Return a hash code for the Identity - the hash of its KeyID()
384     * @return an int, the hashCode
385     */
386    public int hashCode() {
387        if (keyid == null) return super.hashCode();
388        return keyid.hashCode();
389    }
390
391
392    /**
393     * Order 2 identities for sorting.  They are ordered by their key ID's.
394     * @param o an Object to compare
395     * @return -1 if this Identity is before, 0 if they are the same, and 1
396     *              if this Identity is after the given object.
397     */
398    public int compareTo(Object o) { 
399        if ( ! (o instanceof Identity) ) return 1;
400        else return getKeyID().compareTo(((Identity)o).getKeyID());
401    }
402
403};
Note: See TracBrowser for help on using the repository browser.