source: java/net/deterlab/abac/X509Credential.java @ d06c453

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

More credential parsing

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