source: java/net/deterlab/abac/X509Credential.java @ 2034d0d

Last change on this file since 2034d0d was a1a9a47, checked in by Ted Faber <faber@…>, 11 years ago

Bump version

  • Property mode set to 100644
File size: 12.5 KB
RevLine 
[3797bbe]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>
[a1a9a47]23 * @version 1.5
[3797bbe]24 */
25public class X509Credential extends 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    /** The certificate representing this credential */
31    protected X509V2AttributeCertificate ac;
[d31242c]32    /** The X.509 credential suffix */
33    private static final String fileSuffix = ".der";
[3797bbe]34
35    /** Make sure BouncyCastle is loaded */
36    static { Context.loadBouncyCastle(); } 
37
38    /**
39     * Create an empty Credential.
40     */
41    public X509Credential() {
[7f614c1]42        super();
[3797bbe]43        ac = null;
[d31242c]44        setSuffix(fileSuffix);
[3797bbe]45    }
46    /**
47     * Create a credential from a head and tail role.  This credential has no
48     * underlying certificate, and cannot be exported or used in real proofs.
49     * make_cert can create a certificate for a credential initialized this
50     * way.
51     * @param head the Role at the head of the credential
52     * @param tail the Role at the tail of the credential
53     */
54    public X509Credential(Role head, Role tail) {
[7f614c1]55        super(head, tail);
[3797bbe]56        ac = null; 
[d31242c]57        setSuffix(fileSuffix);
[3797bbe]58    }
59
60    /**
61     * Do the credential extraction from an input stream.  This parses the
62     * certificate from the input stream and saves it. The contents must be
63     * validated and parsed later.
64     * @param stream the InputStream to read the certificate from.
65     * @throws IOException if the stream is unparsable
66     */
67    protected void read_certificate(InputStream stream) 
68            throws IOException {
[3612811]69        try { 
70            ac = new X509V2AttributeCertificate(stream);
71        }
72        catch (Exception e) {
73            throw new IOException(e.getMessage(), e);
74        }
[3797bbe]75    }
76
77    /**
78     * Initialize a credential from parsed certificate.  Validiate it against
79     * the given identities and parse out the roles.  Note that catching
80     * java.security.GeneralSecurityException catches most of the exceptions
81     * this throws.
82     * @param ids a Collection of Identities to use in validating the cert
[44896b5]83     * @throws CertInvalidException if the stream is unparsable
84     * @throws MissingIssuerException if none of the Identities can validate the
[3797bbe]85     *                              certificate
[44896b5]86     * @throws BadSignatureException if the signature check fails
[3797bbe]87     */
88    protected void init(Collection<Identity> ids) 
[44896b5]89            throws ABACException {
[3797bbe]90        for (Identity i: ids) {
91            try {
92                ac.verify(i.getCertificate().getPublicKey(), "BC");
93                id = i;
94                break;
95            }
[44896b5]96            catch (GeneralSecurityException e) { }
[3797bbe]97        }
[44896b5]98        if (id == null) throw new MissingIssuerException("Unknown identity");
[3797bbe]99
[7f614c1]100        m_expiration = ac.getNotAfter();
[3797bbe]101        load_roles();
102
103        if (!id.getKeyID().equals(m_head.principal()))
[44896b5]104            throw new MissingIssuerException("Unknown identity");
[3797bbe]105    }
106
107    /**
108     * Parse a credential from an InputStream and initialize the role from it.
109     * Combine read_credential(stream) and init(ids).  Note that catching
110     * java.security.GeneralSecurityException catches most of the exceptions
111     * this throws.
112     * @param stream the InputStream to read the certificate from.
113     * @param ids a Collection of Identities to use in validating the cert
[44896b5]114     * @throws CertInvalidException if the stream is unparsable
115     * @throws MissingIssuerException if none of the Identities can validate the
[3797bbe]116     *                              certificate
[44896b5]117     * @throws BadSignatureException if the signature check fails
[3797bbe]118     */
119    protected void init(InputStream stream, Collection<Identity> ids) 
[44896b5]120            throws ABACException {
121        try {
122            read_certificate(stream);
123        }
124        catch (IOException e) {
125            throw new CertInvalidException(e.getMessage(), e);
126        }
127        if (ac == null) throw new CertInvalidException("Unknown Format");
[3797bbe]128        init(ids);
129    }
130
131    /**
132     * Create a credential from an attribute cert in a file. Throws an
133     * exception if the cert file can't be opened or if there's a format
134     * problem with the cert.  Note that catching
135     * java.security.GeneralSecurityException catches most of the exceptions
136     * this throws.
137     * @param filename a String containing the filename to read
138     * @param ids a Collection of Identities to use in validating the cert
[44896b5]139     * @throws CertInvalidException if the stream is unparsable
140     * @throws MissingIssuerException if none of the Identities can validate the
[3797bbe]141     *                              certificate
[44896b5]142     * @throws BadSignatureException if the signature check fails
[3797bbe]143     */
[4d5f56d]144    X509Credential(String filename, Collection<Identity> ids) 
[44896b5]145        throws ABACException { 
[d31242c]146        super();
147        setSuffix(fileSuffix);
[44896b5]148        try {
149            init(new FileInputStream(filename), ids); 
150        }
151        catch (FileNotFoundException e) {
152            throw new CertInvalidException("Bad filename", e);
153        }
154    }
[3797bbe]155
156    /**
157     * Create a credential from an attribute cert in a file. Throws an
158     * exception if the cert file can't be opened or if there's a format
159     * problem with the cert.  Note that catching
160     * java.security.GeneralSecurityException catches most of the exceptions
161     * this throws.
162     * @param file the File to read
163     * @param ids a Collection of Identities to use in validating the cert
[44896b5]164     * @throws CertInvalidException if the stream is unparsable
165     * @throws MissingIssuerException if none of the Identities can validate the
[3797bbe]166     *                              certificate
[44896b5]167     * @throws BadSignatureException if the signature check fails
[3797bbe]168     */
[4d5f56d]169    X509Credential(File file, Collection<Identity> ids) 
[44896b5]170            throws ABACException {
[d31242c]171        super();
172        setSuffix(fileSuffix);
[44896b5]173        try {
174            init(new FileInputStream(file), ids);
175        }
176        catch (FileNotFoundException e) {
177            throw new CertInvalidException("Bad filename", e);
178        }
[3797bbe]179    }
180
181    /**
182     * Create a credential from an InputStream.  Throws an exception if the
183     * stream can't be parsed or if there's a format problem with the cert.
184     * Note that catching java.security.GeneralSecurityException catches most
185     * of the exceptions this throws.
186     * @param s the InputStream to read
187     * @param ids a Collection of Identities to use in validating the cert
[44896b5]188     * @throws CertInvalidException if the stream is unparsable
189     * @throws MissingIssuerException if none of the Identities can validate the
[3797bbe]190     *                              certificate
[44896b5]191     * @throws BadSignatureException if the signature check fails
[3797bbe]192     */
[4d5f56d]193    X509Credential(InputStream s, Collection<Identity> ids) 
[d31242c]194            throws ABACException { 
195        super();
196        setSuffix(fileSuffix);
197        init(s, ids);
198    }
[3797bbe]199
200    /**
201     * Create a credential from an X509V2AttributeCertificate object.  Throws
202     * an exception if the certificate doesn't parse into an ABAC credential,
203     * or cannot be validated.  Note that catching
204     * java.security.GeneralSecurityException catches most of the exceptions
205     * this throws.
206     * @param c the X509V2AttributeCertificate to create from
207     * @param ids a Collection of Identities to use in validating the cert
[44896b5]208     * @throws CertInvalidException if the stream is unparsable
209     * @throws MissingIssuerException if none of the Identities can validate the
[3797bbe]210     *                              certificate
[44896b5]211     * @throws BadSignatureException if the signature check fails
[3797bbe]212     */
[4d5f56d]213    X509Credential(X509V2AttributeCertificate c, 
[44896b5]214            Collection<Identity> ids) 
215            throws ABACException {
[d31242c]216        super();
217        setSuffix(fileSuffix);
[3797bbe]218        ac = c;
219        init(ids);
220    }
221
222
223    /**
[675770e]224     * Create a certificate from this credential issued by the given identity
225     * valid for the given time.
[3797bbe]226     * @param i the Identity that will issue the certificate
[675770e]227     * @param validity a long holding the number of seconds that the credential
228     * is valid for.
[44896b5]229     * @throws ABACException if xml creation fails
230     * @throws MissingIssuerException if the issuer is bad
231     * @throws BadSignatureException if the signature creation fails
[3797bbe]232     */
[675770e]233    public void make_cert(Identity i, long validity) 
[44896b5]234            throws ABACException {
[3797bbe]235        PrivateKey key = i.getKeyPair().getPrivate();
236        SubjectPublicKeyInfo pki = Context.extractSubjectPublicKeyInfo(
237                i.getKeyPair().getPublic());
238        X509V2AttributeCertificateGenerator gen = 
239            new X509V2AttributeCertificateGenerator();
240
[44896b5]241        try {
242            gen.setIssuer(new AttributeCertificateIssuer(
243                        new X500Principal("CN="+m_head.principal())));
244            gen.setHolder(new AttributeCertificateHolder(
245                        new X500Principal("CN="+m_head.principal())));
[675770e]246            m_expiration = new Date(System.currentTimeMillis() 
247                    + (1000L * validity));
248            gen.setNotAfter(m_expiration);
[44896b5]249            gen.setNotBefore(new Date(System.currentTimeMillis()));
250            gen.setSerialNumber(BigInteger.valueOf(System.currentTimeMillis()));
251            gen.addAttribute(new X509Attribute(attrOID, 
[3797bbe]252                        new DERSequence(
[44896b5]253                            new DERSequence(
254                                new DERUTF8String(toString())))));
255            gen.setSignatureAlgorithm("SHA256WithRSAEncryption");
[3797bbe]256
[44896b5]257            // Creddy expects an authority key identifier.
258            gen.addExtension(authKeyOID, false, 
259                    new AuthorityKeyIdentifier(pki));
260            ac = (X509V2AttributeCertificate) gen.generate(key, "BC");
261        }
262        catch (Exception e) {
263            throw new ABACException("Cannot encode cert", e);
264        }
[3797bbe]265        // Create the cert.
266        id = i;
267    }
268
[675770e]269    /**
270     * Create a certificate from this credential issued by the given identity
271     * valid for the default validity.
272     * @param i the Identity that will issue the certificate
273     * @throws ABACException if xml creation fails
274     * @throws MissingIssuerException if the issuer is bad
275     * @throws BadSignatureException if the signature creation fails
276     */
277    public void make_cert(Identity i) throws ABACException {
278        make_cert(i, defaultValidity);
279    }
280
[3797bbe]281    /**
[44896b5]282     * Load the roles off the attribute cert.
283     * @throws CertInvalidException if the certificate is badly formatted
[3797bbe]284     */
[44896b5]285    private void load_roles() throws CertInvalidException {
[3797bbe]286        String roles = null;
287        try {
288            X509Attribute attr = ac.getAttributes()[0];
289
[7b33c9b]290            DERSequence    t1     = (DERSequence)attr.getValues()[0];
291            DERSequence    t2  = (DERSequence)t1.getObjectAt(0);
292            DERUTF8String  t3    = (DERUTF8String)t2.getObjectAt(0);
[3797bbe]293
[7b33c9b]294            roles = t3.getString();
[3797bbe]295        }
296        catch (Exception e) {
[44896b5]297            throw new CertInvalidException("Badly formatted certificate");
[3797bbe]298        }
299
300        String[] parts = roles.split("\\s*<--?\\s*");
301        if (parts.length != 2)
[44896b5]302            throw new CertInvalidException("Invalid attribute: " + roles);
[3797bbe]303
304        m_head = new Role(parts[0]);
305        m_tail = new Role(parts[1]);
306    }
307
308    /**
309     * Output the DER formatted attribute certificate associated with this
310     * Credential to the OutputStream.
311     * @param s the OutputStream on which to write
312     * @throws IOException if there is an error writing.
313     */
314    public void write(OutputStream s) throws IOException {
315        if (ac != null ) 
316            s.write(ac.getEncoded());
317        s.flush();
318    }
319
320    /**
321     * Output the DER formatted attribute certificate associated with this
322     * Credential to the filename given.
323     * @param fn a String containing the output filename
324     * @throws IOException if there is an error writing.
325     */
326    public void write(String fn) throws IOException, FileNotFoundException {
327        write(new FileOutputStream(fn));
328    }
329
330    /**
331     * Return true if this Credential has a certificate associated.  A jabac
332     * extension.
333     * @return true if this Credential has a certificate associated.
334     */
335    public boolean hasCertificate() { return ac != null; }
336
[f84d71e]337    /**
[aaadefd]338     * Return a CredentialFactorySpecialization for X509Credentials.  Used by
339     * the CredentialFactory to parse and generate these kind of credentials.
340     * It is basically a wrapper around constuctor calls.
[f84d71e]341     * @return a CredentialParser for this kind of credential.
342     */
[aaadefd]343    static public CredentialFactorySpecialization
344            getCredentialFactorySpecialization() {
345        return new CredentialFactorySpecialization() {
[f84d71e]346            public Credential[] parseCredential(InputStream is, 
347                    Collection<Identity> ids) throws ABACException {
348                return new Credential[] { new X509Credential(is, ids) }; 
349            }
[79f516b]350            public Credential generateCredential(Role head, Role tail,
351                    KeyIDMap aliases) {
[aaadefd]352                return new X509Credential(head, tail);
353            }
[f84d71e]354        };
355    }
356
[3797bbe]357
358}
Note: See TracBrowser for help on using the repository browser.