source: java/net/deterlab/abac/Credential.java @ 238717d

abac0-leakabac0-meicompt_changesgec13mei-idmei-rt0-nmei_rt0mei_rt2mei_rt2_fix_1meiyap-rt1meiyap1rt2tvf-new-xml
Last change on this file since 238717d was 238717d, checked in by Ted Faber <faber@…>, 13 years ago

Auto-load the BouncyCastle? provider. (Makes jnlp work)

  • Property mode set to 100644
File size: 16.2 KB
Line 
1package net.deterlab.abac;
2
3import java.io.*;
4import java.math.*;
5
6import java.util.*;
7import java.security.*;
8import java.security.cert.*;
9
10import javax.security.auth.x500.*;
11
12import org.bouncycastle.asn1.*;
13import org.bouncycastle.asn1.x509.*;
14import org.bouncycastle.x509.*;
15import org.bouncycastle.x509.util.*;
16import org.bouncycastle.openssl.*;
17
18/**
19 * An ABAC credential, with or without an underlying certificate that
20 * represents it.  These are edges in proof garphs and can be constructed from
21 * their constituent Roles.
22 * @author <a href="http://abac.deterlab.net">ISI ABAC team</a>
23 * @version 1.3
24 */
25public class Credential implements Comparable {
26    /** The ASN1 OID for an IETF attribute. */
27    protected static String attrOID = "1.3.6.1.5.5.7.10.4";
28    /** The ASN1 OID for AuthorityKeyIdentifier. */
29    protected static String authKeyOID = "2.5.29.35";
30
31    /** The role at the head */
32    protected Role m_head
33    /** The role at the tail */;
34    protected Role m_tail;
35    /** The certificate representing this credential */
36    protected X509V2AttributeCertificate ac;
37    /** The identity that issued the certificate */
38    protected Identity id;
39
40    /** Make sure BouncyCastle is loaded */
41    static { Context.loadBouncyCastle(); } 
42
43    /**
44     * Create an empty Credential.
45     */
46    public Credential() {
47        m_head = m_tail = null;
48        ac = null;
49        id = null;
50    }
51    /**
52     * Create a credential from a head and tail role.  This credential has no
53     * underlying certificate, and cannot be exported or used in real proofs.
54     * make_cert can create a certificate for a credential initialized this
55     * way.
56     * @param head the Role at the head of the credential
57     * @param tail the Role at the tail of the credential
58     */
59    public Credential(Role head, Role tail) {
60        m_head = head;
61        m_tail = tail;
62        ac = null; 
63        id = null;
64    }
65
66    /**
67     * Do the credential extraction from an input stream.  This parses the
68     * certificate from the input stream and saves it. The contents must be
69     * validated and parsed later.
70     * @param stream the InputStream to read the certificate from.
71     * @throws IOException if the stream is unparsable
72     */
73    protected void read_certificate(InputStream stream) 
74            throws IOException {
75        ac = new X509V2AttributeCertificate(stream);
76    }
77
78    /**
79     * Initialize a credential from parsed certificate.  Validiate it against
80     * the given identities and parse out the roles.  Note that catching
81     * java.security.GeneralSecurityException catches most of the exceptions
82     * this throws.
83     * @param ids a Collection of Identities to use in validating the cert
84     * @throws CertificateException if the certificate is badly formatted
85     * @throws InvalidKeyException if none of the Identities can validate the
86     *                              certificate
87     * @throws NoSuchAlgorithmException if the credential uses an unknown
88     *                              signature algorithm
89     * @throws NoSuchProviderException if the provider of the signature
90     *                              algorithm is unavailable
91     * @throws SignatureException if the signature check fails
92     */
93    protected void init(Collection<Identity> ids) 
94            throws CertificateException, InvalidKeyException, 
95                NoSuchAlgorithmException, NoSuchProviderException,
96                SignatureException {
97        for (Identity i: ids) {
98            try {
99                ac.verify(i.getCertificate().getPublicKey(), "BC");
100                id = i;
101                break;
102            }
103            catch (InvalidKeyException e) { }
104        }
105        if (id == null) throw new InvalidKeyException("Unknown identity");
106
107        load_roles();
108
109        if (!id.getKeyID().equals(m_head.principal()))
110            throw new InvalidKeyException("Unknown identity");
111    }
112
113    /**
114     * Parse a credential from an InputStream and initialize the role from it.
115     * Combine read_credential(stream) and init(ids).  Note that catching
116     * java.security.GeneralSecurityException catches most of the exceptions
117     * this throws.
118     * @param stream the InputStream to read the certificate from.
119     * @param ids a Collection of Identities to use in validating the cert
120     * @throws CertificateException if the certificate is badly formatted
121     * @throws InvalidKeyException if none of the Identities can validate the
122     *                              certificate
123     * @throws NoSuchAlgorithmException if the credential uses an unknown
124     *                              signature algorithm
125     * @throws NoSuchProviderException if the provider of the signature
126     *                              algorithm is unavailable
127     * @throws SignatureException if the signature check fails
128     * @throws IOException if the certificate is unparsable.
129     */
130    protected void init(InputStream stream, Collection<Identity> ids) 
131            throws CertificateException, InvalidKeyException, 
132                NoSuchAlgorithmException, NoSuchProviderException,
133                SignatureException, IOException {
134        read_certificate(stream);
135        if (ac == null) throw new IOException("Unknown Format");
136        init(ids);
137    }
138
139    /**
140     * Create a credential from an attribute cert in a file. Throws an
141     * exception if the cert file can't be opened or if there's a format
142     * problem with the cert.  Note that catching
143     * java.security.GeneralSecurityException catches most of the exceptions
144     * this throws.
145     * @param filename a String containing the filename to read
146     * @param ids a Collection of Identities to use in validating the cert
147     * @throws StreamParsingException if the stream is unparsable
148     * @throws CertificateException if the certificate is badly formatted
149     * @throws InvalidKeyException if none of the Identities can validate the
150     *                              certificate
151     * @throws NoSuchAlgorithmException if the credential uses an unknown
152     *                              signature algorithm
153     * @throws NoSuchProviderException if the provider of the signature
154     *                              algorithm is unavailable
155     * @throws SignatureException if the signature check fails
156     * @throws IOException if the certificate is unparsable.
157     */
158    public Credential(String filename, Collection<Identity> ids) 
159        throws Exception { init(new FileInputStream(filename), ids); }
160
161    /**
162     * Create a credential from an attribute cert in a file. Throws an
163     * exception if the cert file can't be opened or if there's a format
164     * problem with the cert.  Note that catching
165     * java.security.GeneralSecurityException catches most of the exceptions
166     * this throws.
167     * @param file the File to read
168     * @param ids a Collection of Identities to use in validating the cert
169     * @throws StreamParsingException if the stream is unparsable
170     * @throws CertificateException if the certificate is badly formatted
171     * @throws InvalidKeyException if none of the Identities can validate the
172     *                              certificate
173     * @throws NoSuchAlgorithmException if the credential uses an unknown
174     *                              signature algorithm
175     * @throws NoSuchProviderException if the provider of the signature
176     *                              algorithm is unavailable
177     * @throws SignatureException if the signature check fails
178     * @throws IOException if the certificate is unparsable.
179     */
180    public Credential(File file, Collection<Identity> ids) 
181            throws CertificateException, InvalidKeyException, 
182                NoSuchAlgorithmException, NoSuchProviderException,
183                SignatureException, StreamParsingException, IOException {
184        init(new FileInputStream(file), ids);
185    }
186
187    /**
188     * Create a credential from an InputStream.  Throws an exception if the
189     * stream can't be parsed or if there's a format problem with the cert.
190     * Note that catching java.security.GeneralSecurityException catches most
191     * of the exceptions this throws.
192     * @param s the InputStream to read
193     * @param ids a Collection of Identities to use in validating the cert
194     * @throws StreamParsingException if the stream is unparsable
195     * @throws CertificateException if the certificate is badly formatted
196     * @throws InvalidKeyException if none of the Identities can validate the
197     *                              certificate
198     * @throws NoSuchAlgorithmException if the credential uses an unknown
199     *                              signature algorithm
200     * @throws NoSuchProviderException if the provider of the signature
201     *                              algorithm is unavailable
202     * @throws SignatureException if the signature check fails
203     * @throws IOException if the certificate is unparsable.
204     */
205    public Credential(InputStream s, Collection<Identity> ids) 
206            throws CertificateException, InvalidKeyException, 
207                NoSuchAlgorithmException, NoSuchProviderException,
208                SignatureException, StreamParsingException, IOException {
209        init(s, ids);
210    }
211
212    /**
213     * Create a credential from an X509V2AttributeCertificate object.  Throws
214     * an exception if the certificate doesn't parse into an ABAC credential,
215     * or cannot be validated.  Note that catching
216     * java.security.GeneralSecurityException catches most of the exceptions
217     * this throws.
218     * @param c the X509V2AttributeCertificate to create from
219     * @param ids a Collection of Identities to use in validating the cert
220     * @throws CertificateException if the certificate is badly formatted
221     * @throws InvalidKeyException if none of the Identities can validate the
222     *                              certificate
223     * @throws NoSuchAlgorithmException if the credential uses an unknown
224     *                              signature algorithm
225     * @throws NoSuchProviderException if the provider of the signature
226     *                              algorithm is unavailable
227     * @throws SignatureException if the signature check fails
228     * @throws IOException if the certificate is unparsable.
229     */
230    public Credential(X509V2AttributeCertificate c, Collection<Identity> ids) 
231            throws CertificateException, InvalidKeyException, 
232                NoSuchAlgorithmException, NoSuchProviderException,
233                SignatureException, IOException {
234        ac = c;
235        init(ids);
236    }
237
238
239    /**
240     * Create a certificate from this credential issued by the given identity.
241     * Note that catching java.security.GeneralSecurityException catches most
242     * of the exceptions this throws.
243     * @param i the Identity that will issue the certificate
244     * @throws IOException reading or writing problems
245     * @throws CertificateEncodingException Problem creating certificate
246     * @throws InvalidKeyException if none of the Identities can sign the
247     *                              certificate
248     * @throws NoSuchAlgorithmException if the credential uses an unknown
249     *                              signature algorithm
250     * @throws NoSuchProviderException if the provider of the signature
251     *                              algorithm is unavailable
252     * @throws SignatureException if the signature creation fails
253     */
254    public void make_cert(Identity i) 
255            throws IOException, CertificateEncodingException,
256               NoSuchProviderException, NoSuchAlgorithmException,
257               SignatureException, InvalidKeyException {
258        PrivateKey key = i.getKeyPair().getPrivate();
259        SubjectPublicKeyInfo pki = Context.extractSubjectPublicKeyInfo(
260                i.getKeyPair().getPublic());
261        X509V2AttributeCertificateGenerator gen = 
262            new X509V2AttributeCertificateGenerator();
263
264        gen.setIssuer(new AttributeCertificateIssuer(
265                    new X500Principal("CN="+m_head.principal())));
266        gen.setHolder(new AttributeCertificateHolder(
267                    new X500Principal("CN="+m_head.principal())));
268        gen.setNotAfter(new Date(System.currentTimeMillis() 
269                    + 3600 * 1000 * 24 * 365));
270        gen.setNotBefore(new Date(System.currentTimeMillis()));
271        gen.setSerialNumber(BigInteger.valueOf(System.currentTimeMillis()));
272        gen.addAttribute(new X509Attribute(attrOID, 
273                    new DERSequence(
274                        new DERSequence(
275                            new DERUTF8String(toString())))));
276        gen.setSignatureAlgorithm("SHA256WithRSAEncryption");
277
278        // Creddy expects an authority key identifier.
279        gen.addExtension(authKeyOID, false, 
280                new AuthorityKeyIdentifier(pki));
281        // Create the cert.
282        ac = (X509V2AttributeCertificate) gen.generate(key, "BC");
283    }
284
285    /**
286     * Load the roles off the attribute cert. Throws a RuntimeException if
287     * there's something wrong with the cert.
288     */
289    private void load_roles() {
290        String roles = null;
291        try {
292            X509Attribute attr = ac.getAttributes()[0];
293
294            DERSequence    java     = (DERSequence)attr.getValues()[0];
295            DERSequence    fucking  = (DERSequence)java.getObjectAt(0);
296            DERUTF8String  sucks    = (DERUTF8String)fucking.getObjectAt(0);
297
298            roles = sucks.getString();
299        }
300        catch (Exception e) {
301            throw new RuntimeException("Badly formatted certificate");
302        }
303
304        String[] parts = roles.split("\\s*<--?\\s*");
305        if (parts.length != 2)
306            throw new RuntimeException("Invalid attribute: " + roles);
307
308        m_head = new Role(parts[0]);
309        m_tail = new Role(parts[1]);
310    }
311
312    /**
313     * Two credentials are the same if their roles are the same.
314     * @param o an Object to compare
315     * @return true if the Credentials have the Roles
316     */
317    public boolean equals(Object o) {
318        if ( o instanceof Credential ) {
319            Credential c = (Credential) o;
320
321            if (m_head == null || m_tail == null ) return false;
322            else return (m_head.equals(c.head()) && m_tail.equals(c.tail()));
323        }
324        else return false;
325    }
326
327    /**
328     * Allow credentials to be compared.  They are ordered by their Roles, head
329     * then tail.
330     * @param o an Object to compare
331     * @return -1 if this Credential is before, 0 if they are the same, and 1
332     *              if this Credential is after the given object.
333     */
334    public int compareTo(Object o) {
335        if (o instanceof Credential) {
336            Credential c = (Credential) o;
337
338            if (head().equals(c.head())) return tail().compareTo(c.tail());
339            else return head().compareTo(c.head());
340        }
341        else return 1;
342    }
343
344
345    /**
346     * Get the head role from the credential.
347     * @return the Role in the head
348     */
349    public Role head() { return m_head; }
350
351    /**
352     * Get the tail role from the credential
353     * @return the Role in the tail
354     */
355    public Role tail() { return m_tail; }
356
357    /**
358     * Gets the cert associated with this credential (if any).
359     * @return the attribute cert associated with this credential (if any).
360     */
361    public X509V2AttributeCertificate cert() { return ac; }
362
363    /**
364     * Turn the credential into string form. The format is head &lt;- tail. For
365     * example: A.r1 &lt;- B.r2.r3.  Principal names are key identifiers.
366     * @return the string form
367     */
368    public String toString() {
369        return m_head + " <- " + m_tail;
370    }
371
372    /**
373     * Turn the credential into string form. The format is head &lt;- tail. For
374     * example: A.r1 &lt;- B.r2.r3.  Principal names are shortened to menmonics
375     * if the Context knows the identity.
376     * @param c the Context to translate names in
377     * @return the string form
378     */
379    public String simpleString(Context c) {
380        return m_head.simpleString(c) + " <- " + m_tail.simpleString(c);
381    }
382
383    /**
384     * Output the DER formatted attribute certificate associated with this
385     * Credential to the OutputStream.
386     * @param s the OutputStream on which to write
387     * @throws IOException if there is an error writing.
388     */
389    public void write(OutputStream s) throws IOException {
390        if (ac != null ) 
391            s.write(ac.getEncoded());
392        s.flush();
393    }
394
395    /**
396     * Output the DER formatted attribute certificate associated with this
397     * Credential to the filename given.
398     * @param fn a String containing the output filename
399     * @throws IOException if there is an error writing.
400     */
401    public void write(String fn) throws IOException, FileNotFoundException {
402        write(new FileOutputStream(fn));
403    }
404
405    /**
406     * Return true if this Credential has a certificate associated.  A jabac
407     * extension.
408     * @return true if this Credential has a certificate associated.
409     */
410    public boolean hasCertificate() { return ac != null; }
411
412    /**
413     * Return the Identity that issued the underlying certificate.  A jabac
414     * extension.
415     * @return the Identity that issued the underlying certificate.
416     */
417    public Identity issuer() { return id; }
418    /**
419     * Return the X509Certificate that issued the underlying certificate.
420     * @return the X509Certificate that issued the underlying certificate.
421     */
422    public X509Certificate issuerCert() { return id.getCertificate(); }
423    /**
424     * Return the X509V2AttributeCertificate that underlys the Credential
425     * @return the X509V2AttributeCertificate that underlys the Credential.
426     */
427    public X509V2AttributeCertificate attributeCert() { return ac; }
428
429
430}
Note: See TracBrowser for help on using the repository browser.