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
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.5
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;
32    /** The X.509 credential suffix */
33    private static final String fileSuffix = ".der";
34
35    /** Make sure BouncyCastle is loaded */
36    static { Context.loadBouncyCastle(); } 
37
38    /**
39     * Create an empty Credential.
40     */
41    public X509Credential() {
42        super();
43        ac = null;
44        setSuffix(fileSuffix);
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) {
55        super(head, tail);
56        ac = null; 
57        setSuffix(fileSuffix);
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 {
69        try { 
70            ac = new X509V2AttributeCertificate(stream);
71        }
72        catch (Exception e) {
73            throw new IOException(e.getMessage(), e);
74        }
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
83     * @throws CertInvalidException if the stream is unparsable
84     * @throws MissingIssuerException if none of the Identities can validate the
85     *                              certificate
86     * @throws BadSignatureException if the signature check fails
87     */
88    protected void init(Collection<Identity> ids) 
89            throws ABACException {
90        for (Identity i: ids) {
91            try {
92                ac.verify(i.getCertificate().getPublicKey(), "BC");
93                id = i;
94                break;
95            }
96            catch (GeneralSecurityException e) { }
97        }
98        if (id == null) throw new MissingIssuerException("Unknown identity");
99
100        m_expiration = ac.getNotAfter();
101        load_roles();
102
103        if (!id.getKeyID().equals(m_head.principal()))
104            throw new MissingIssuerException("Unknown identity");
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
114     * @throws CertInvalidException if the stream is unparsable
115     * @throws MissingIssuerException if none of the Identities can validate the
116     *                              certificate
117     * @throws BadSignatureException if the signature check fails
118     */
119    protected void init(InputStream stream, Collection<Identity> ids) 
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");
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
139     * @throws CertInvalidException if the stream is unparsable
140     * @throws MissingIssuerException if none of the Identities can validate the
141     *                              certificate
142     * @throws BadSignatureException if the signature check fails
143     */
144    X509Credential(String filename, Collection<Identity> ids) 
145        throws ABACException { 
146        super();
147        setSuffix(fileSuffix);
148        try {
149            init(new FileInputStream(filename), ids); 
150        }
151        catch (FileNotFoundException e) {
152            throw new CertInvalidException("Bad filename", e);
153        }
154    }
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
164     * @throws CertInvalidException if the stream is unparsable
165     * @throws MissingIssuerException if none of the Identities can validate the
166     *                              certificate
167     * @throws BadSignatureException if the signature check fails
168     */
169    X509Credential(File file, Collection<Identity> ids) 
170            throws ABACException {
171        super();
172        setSuffix(fileSuffix);
173        try {
174            init(new FileInputStream(file), ids);
175        }
176        catch (FileNotFoundException e) {
177            throw new CertInvalidException("Bad filename", e);
178        }
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
188     * @throws CertInvalidException if the stream is unparsable
189     * @throws MissingIssuerException if none of the Identities can validate the
190     *                              certificate
191     * @throws BadSignatureException if the signature check fails
192     */
193    X509Credential(InputStream s, Collection<Identity> ids) 
194            throws ABACException { 
195        super();
196        setSuffix(fileSuffix);
197        init(s, ids);
198    }
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
208     * @throws CertInvalidException if the stream is unparsable
209     * @throws MissingIssuerException if none of the Identities can validate the
210     *                              certificate
211     * @throws BadSignatureException if the signature check fails
212     */
213    X509Credential(X509V2AttributeCertificate c, 
214            Collection<Identity> ids) 
215            throws ABACException {
216        super();
217        setSuffix(fileSuffix);
218        ac = c;
219        init(ids);
220    }
221
222
223    /**
224     * Create a certificate from this credential issued by the given identity
225     * valid for the given time.
226     * @param i the Identity that will issue the certificate
227     * @param validity a long holding the number of seconds that the credential
228     * is valid for.
229     * @throws ABACException if xml creation fails
230     * @throws MissingIssuerException if the issuer is bad
231     * @throws BadSignatureException if the signature creation fails
232     */
233    public void make_cert(Identity i, long validity) 
234            throws ABACException {
235        PrivateKey key = i.getKeyPair().getPrivate();
236        SubjectPublicKeyInfo pki = Context.extractSubjectPublicKeyInfo(
237                i.getKeyPair().getPublic());
238        X509V2AttributeCertificateGenerator gen = 
239            new X509V2AttributeCertificateGenerator();
240
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())));
246            m_expiration = new Date(System.currentTimeMillis() 
247                    + (1000L * validity));
248            gen.setNotAfter(m_expiration);
249            gen.setNotBefore(new Date(System.currentTimeMillis()));
250            gen.setSerialNumber(BigInteger.valueOf(System.currentTimeMillis()));
251            gen.addAttribute(new X509Attribute(attrOID, 
252                        new DERSequence(
253                            new DERSequence(
254                                new DERUTF8String(toString())))));
255            gen.setSignatureAlgorithm("SHA256WithRSAEncryption");
256
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        }
265        // Create the cert.
266        id = i;
267    }
268
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
281    /**
282     * Load the roles off the attribute cert.
283     * @throws CertInvalidException if the certificate is badly formatted
284     */
285    private void load_roles() throws CertInvalidException {
286        String roles = null;
287        try {
288            X509Attribute attr = ac.getAttributes()[0];
289
290            DERSequence    t1     = (DERSequence)attr.getValues()[0];
291            DERSequence    t2  = (DERSequence)t1.getObjectAt(0);
292            DERUTF8String  t3    = (DERUTF8String)t2.getObjectAt(0);
293
294            roles = t3.getString();
295        }
296        catch (Exception e) {
297            throw new CertInvalidException("Badly formatted certificate");
298        }
299
300        String[] parts = roles.split("\\s*<--?\\s*");
301        if (parts.length != 2)
302            throw new CertInvalidException("Invalid attribute: " + roles);
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
337    /**
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.
341     * @return a CredentialParser for this kind of credential.
342     */
343    static public CredentialFactorySpecialization
344            getCredentialFactorySpecialization() {
345        return new CredentialFactorySpecialization() {
346            public Credential[] parseCredential(InputStream is, 
347                    Collection<Identity> ids) throws ABACException {
348                return new Credential[] { new X509Credential(is, ids) }; 
349            }
350            public Credential generateCredential(Role head, Role tail,
351                    KeyIDMap aliases) {
352                return new X509Credential(head, tail);
353            }
354        };
355    }
356
357
358}
Note: See TracBrowser for help on using the repository browser.