source: java/net/deterlab/abac/GENICredential.java @ 3797bbe

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

checkpoint

  • Property mode set to 100644
File size: 19.3 KB
Line 
1package net.deterlab.abac;
2
3import java.io.*;
4import java.math.*;
5import java.text.*;
6
7import java.util.*;
8import java.security.*;
9import java.security.cert.*;
10
11import javax.security.auth.x500.*;
12
13import javax.xml.parsers.*;
14import javax.xml.transform.*;
15import javax.xml.transform.dom.*;
16import javax.xml.transform.stream.*;
17
18import javax.xml.crypto.*;
19import javax.xml.crypto.dsig.*;
20import javax.xml.crypto.dsig.dom.*;
21import javax.xml.crypto.dsig.keyinfo.*;
22import javax.xml.crypto.dsig.spec.*;
23
24import org.xml.sax.*;
25import org.w3c.dom.*;
26
27import org.bouncycastle.asn1.*;
28import org.bouncycastle.asn1.x509.*;
29import org.bouncycastle.x509.*;
30import org.bouncycastle.x509.util.*;
31import org.bouncycastle.openssl.*;
32
33/**
34 * An ABAC credential, with or without an underlying certificate that
35 * represents it.  These are edges in proof garphs and can be constructed from
36 * their constituent Roles.
37 * @author <a href="http://abac.deterlab.net">ISI ABAC team</a>
38 * @version 1.3
39 */
40public class GENICredential extends Credential implements Comparable {
41    /** The role at the head */
42    protected Role m_head
43    /** The role at the tail */;
44    protected Role m_tail;
45    /** The signed XML representing this credential */
46    protected Document doc;
47    /** The identity that issued the certificate */
48    protected Identity id;
49
50    /**
51     * Create an empty Credential.
52     */
53    public GENICredential() {
54        m_head = m_tail = null;
55        doc = null;
56        id = null;
57    }
58    /**
59     * Create a credential from a head and tail role.  This credential has no
60     * underlying certificate, and cannot be exported or used in real proofs.
61     * make_cert can create a certificate for a credential initialized this
62     * way.
63     * @param head the Role at the head of the credential
64     * @param tail the Role at the tail of the credential
65     */
66    public GENICredential(Role head, Role tail) {
67        m_head = head;
68        m_tail = tail;
69        doc = null; 
70        id = null;
71    }
72
73    /**
74     * Do the credential extraction from an input stream.  This parses the
75     * certificate from the input stream and saves it. The contents must be
76     * validated and parsed later.
77     * @param stream the InputStream to read the certificate from.
78     * @throws IOException if the stream is unparsable
79     */
80    protected void read_certificate(InputStream stream) 
81            throws IOException {
82        DocumentBuilderFactory dbf = DocumentBuilderFactory.newInstance();
83        DocumentBuilder db = null;
84
85        if ( dbf == null ) 
86            throw new IOException("Cannot get DocumentBuilderFactory!?");
87        try {
88            if ( (db = dbf.newDocumentBuilder()) == null ) 
89                throw new IOException("Cannot get DocumentBuilder!?");
90            doc = db.parse(stream);
91        }
92        catch (IllegalArgumentException ie) {
93            throw new IOException("null stream");
94        }
95        catch (SAXException se) {
96            throw new IOException(se.getMessage());
97        }
98        catch (Exception e) {
99            throw new IOException(e.getMessage());
100        }
101
102    }
103
104    /**
105     * Initialize a credential from parsed certificate.  Validiate it against
106     * the given identities and parse out the roles.  Note that catching
107     * java.security.GeneralSecurityException catches most of the exceptions
108     * this throws.
109     * @param ids a Collection of Identities to use in validating the cert
110     * @throws CertificateException if the certificate is badly formatted
111     * @throws InvalidKeyException if none of the Identities can validate the
112     *                              certificate
113     * @throws NoSuchAlgorithmException if the credential uses an unknown
114     *                              signature algorithm
115     * @throws NoSuchProviderException if the provider of the signature
116     *                              algorithm is unavailable
117     * @throws SignatureException if the signature check fails
118     */
119    protected void init(Collection<Identity> ids) 
120            throws CertificateException, InvalidKeyException, 
121                NoSuchAlgorithmException, NoSuchProviderException,
122                SignatureException {
123
124        XMLSignatureFactory fac = XMLSignatureFactory.getInstance("DOM");
125        NodeList nl = doc.getElementsByTagNameNS(XMLSignature.XMLNS, 
126                "Signature");
127        DOMValidateContext valContext = null;
128        XMLSignature signature = null;
129        boolean isValid = false;
130
131        try {
132
133            if (nl.getLength() == 0) 
134                throw new SignatureException("Cannot find Signature element");
135
136            // XXX: need a javax.xml.crypto.KeySelector  we'll probably have to
137            // parse the certificate in teh sig and use singletonKeySelector
138            valContext = new DOMValidateContext((KeySelector) null, nl.item(0));
139            if ( valContext == null )
140                throw new SignatureException("No context");
141
142            signature = fac.unmarshalXMLSignature(valContext);
143            if (signature == null) 
144                throw new SignatureException("No sig");
145
146            if (!signature.validate(valContext))
147                throw new SignatureException("bag signature");
148
149            load_roles();
150
151            if (!id.getKeyID().equals(m_head.principal()))
152                throw new InvalidKeyException("Unknown identity");
153        }
154        catch (Exception e) {
155            throw new SignatureException(e.getMessage());
156        }
157    }
158
159    /**
160     * Parse a credential from an InputStream and initialize the role from it.
161     * Combine read_credential(stream) and init(ids).  Note that catching
162     * java.security.GeneralSecurityException catches most of the exceptions
163     * this throws.
164     * @param stream the InputStream to read the certificate from.
165     * @param ids a Collection of Identities to use in validating the cert
166     * @throws CertificateException if the certificate is badly formatted
167     * @throws InvalidKeyException if none of the Identities can validate the
168     *                              certificate
169     * @throws NoSuchAlgorithmException if the credential uses an unknown
170     *                              signature algorithm
171     * @throws NoSuchProviderException if the provider of the signature
172     *                              algorithm is unavailable
173     * @throws SignatureException if the signature check fails
174     * @throws IOException if the certificate is unparsable.
175     */
176    protected void init(InputStream stream, Collection<Identity> ids) 
177            throws CertificateException, InvalidKeyException, 
178                NoSuchAlgorithmException, NoSuchProviderException,
179                SignatureException, IOException {
180        read_certificate(stream);
181        if (doc == null) throw new IOException("Unknown Format");
182        init(ids);
183    }
184
185    /**
186     * Create a credential from an attribute cert in a file. Throws an
187     * exception if the cert file can't be opened or if there's a format
188     * problem with the cert.  Note that catching
189     * java.security.GeneralSecurityException catches most of the exceptions
190     * this throws.
191     * @param filename a String containing the filename to read
192     * @param ids a Collection of Identities to use in validating the cert
193     * @throws StreamParsingException if the stream is unparsable
194     * @throws CertificateException if the certificate is badly formatted
195     * @throws InvalidKeyException if none of the Identities can validate the
196     *                              certificate
197     * @throws NoSuchAlgorithmException if the credential uses an unknown
198     *                              signature algorithm
199     * @throws NoSuchProviderException if the provider of the signature
200     *                              algorithm is unavailable
201     * @throws SignatureException if the signature check fails
202     * @throws IOException if the certificate is unparsable.
203     */
204    public GENICredential(String filename, Collection<Identity> ids) 
205        throws Exception { init(new FileInputStream(filename), ids); }
206
207    /**
208     * Create a credential from an attribute cert in a file. Throws an
209     * exception if the cert file can't be opened or if there's a format
210     * problem with the cert.  Note that catching
211     * java.security.GeneralSecurityException catches most of the exceptions
212     * this throws.
213     * @param file the File to read
214     * @param ids a Collection of Identities to use in validating the cert
215     * @throws StreamParsingException if the stream is unparsable
216     * @throws CertificateException if the certificate is badly formatted
217     * @throws InvalidKeyException if none of the Identities can validate the
218     *                              certificate
219     * @throws NoSuchAlgorithmException if the credential uses an unknown
220     *                              signature algorithm
221     * @throws NoSuchProviderException if the provider of the signature
222     *                              algorithm is unavailable
223     * @throws SignatureException if the signature check fails
224     * @throws IOException if the certificate is unparsable.
225     */
226    public GENICredential(File file, Collection<Identity> ids) 
227            throws CertificateException, InvalidKeyException, 
228                NoSuchAlgorithmException, NoSuchProviderException,
229                SignatureException, StreamParsingException, IOException {
230        init(new FileInputStream(file), ids);
231    }
232
233    /**
234     * Create a credential from an InputStream.  Throws an exception if the
235     * stream can't be parsed or if there's a format problem with the cert.
236     * Note that catching java.security.GeneralSecurityException catches most
237     * of the exceptions this throws.
238     * @param s the InputStream to read
239     * @param ids a Collection of Identities to use in validating the cert
240     * @throws StreamParsingException if the stream is unparsable
241     * @throws CertificateException if the certificate is badly formatted
242     * @throws InvalidKeyException if none of the Identities can validate the
243     *                              certificate
244     * @throws NoSuchAlgorithmException if the credential uses an unknown
245     *                              signature algorithm
246     * @throws NoSuchProviderException if the provider of the signature
247     *                              algorithm is unavailable
248     * @throws SignatureException if the signature check fails
249     * @throws IOException if the certificate is unparsable.
250     */
251    public GENICredential(InputStream s, Collection<Identity> ids) 
252            throws CertificateException, InvalidKeyException, 
253                NoSuchAlgorithmException, NoSuchProviderException,
254                SignatureException, StreamParsingException, IOException {
255        init(s, ids);
256    }
257
258    /**
259     * Create a certificate from this credential issued by the given identity.
260     * Note that catching java.security.GeneralSecurityException catches most
261     * of the exceptions this throws.
262     * @param i the Identity that will issue the certificate
263     * @throws IOException reading or writing problems
264     * @throws CertificateEncodingException Problem creating certificate
265     * @throws InvalidKeyException if none of the Identities can sign the
266     *                              certificate
267     * @throws NoSuchAlgorithmException if the credential uses an unknown
268     *                              signature algorithm
269     * @throws NoSuchProviderException if the provider of the signature
270     *                              algorithm is unavailable
271     * @throws SignatureException if the signature creation fails
272     */
273    public void make_cert(Identity i) 
274            throws IOException, CertificateEncodingException,
275               NoSuchProviderException, NoSuchAlgorithmException,
276               SignatureException, InvalidKeyException {
277        try {
278            SimpleDateFormat df = new SimpleDateFormat("yyy-MM-dd'T'HH:mm:ss'Z'");
279            StringBuffer expBuf = new StringBuffer();
280            X509Certificate cert = i.getCertificate();
281            KeyPair kp = i.getKeyPair();
282            if ( cert == null ) 
283                throw new IOException("No credential in identity?");
284            if ( kp == null ) 
285                throw new IOException("No keypair in identity?");
286
287            XMLSignatureFactory fac = XMLSignatureFactory.getInstance("DOM");
288            Reference ref = fac.newReference("#ref0", 
289                    fac.newDigestMethod(DigestMethod.SHA1, null),
290                    Collections.singletonList(
291                        fac.newTransform(Transform.ENVELOPED, 
292                            (TransformParameterSpec) null)),
293                    null, null);
294
295            SignedInfo si = fac.newSignedInfo(
296                    fac.newCanonicalizationMethod(CanonicalizationMethod.INCLUSIVE,
297                        (C14NMethodParameterSpec) null),
298                    fac.newSignatureMethod(SignatureMethod.RSA_SHA1, null),
299                    Collections.singletonList(ref));
300            KeyInfoFactory kif = fac.getKeyInfoFactory();
301            List x509Content = new ArrayList();
302            x509Content.add(cert.getSubjectX500Principal().getName());
303            x509Content.add(cert);
304            X509Data xd = kif.newX509Data(x509Content);
305            KeyInfo ki = kif.newKeyInfo(Collections.singletonList(xd));
306
307
308            DocumentBuilderFactory dbf = DocumentBuilderFactory.newInstance();
309            DocumentBuilder db = null;
310
311            if ( dbf == null ) 
312                throw new IOException("Cannot get DocumentBuilderFactory!?");
313            if ( (db = dbf.newDocumentBuilder()) == null ) 
314                throw new IOException("Cannot get DocumentBuilder!?");
315            doc = db.newDocument();
316            if ( doc == null ) 
317                throw new IOException("No Document");
318            Element root = doc.createElement("signed-credential"); 
319            doc.appendChild(root);
320            Element cred = doc.createElement("credential");
321            Element sig = doc.createElement("credential");
322            cred.setAttributeNS("xml", "id", "ref0");
323            root.appendChild(cred);
324            cred.appendChild(doc.createElement("type").appendChild(
325                        doc.createTextNode("abac")));
326            df.format(new Date(System.currentTimeMillis() 
327                        + (3600L * 1000L * 24L * 365L)), expBuf, 
328                    new FieldPosition(0));
329            cred.appendChild(doc.createElement("expires").appendChild(
330                        doc.createTextNode(expBuf.toString())));
331            cred.appendChild(doc.createElement("version").appendChild(
332                        doc.createTextNode("1.0")));
333            cred.appendChild(doc.createElement("rt0").appendChild(
334                        doc.createTextNode(m_head + "<-" + m_tail)));
335            root.appendChild(sig);
336
337            DOMSignContext dsc = new DOMSignContext(kp.getPrivate(), sig);
338            XMLSignature signature = fac.newXMLSignature(si, ki);
339            signature.sign(dsc);
340        }
341        catch (Exception e) {
342            throw new SignatureException(e.getMessage());
343        }
344
345        /*
346        PrivateKey key = i.getKeyPair().getPrivate();
347        SubjectPublicKeyInfo pki = Context.extractSubjectPublicKeyInfo(
348                i.getKeyPair().getPublic());
349        X509V2AttributeCertificateGenerator gen =
350            new X509V2AttributeCertificateGenerator();
351
352        gen.setIssuer(new AttributeCertificateIssuer(
353                    new X500Principal("CN="+m_head.principal())));
354        gen.setHolder(new AttributeCertificateHolder(
355                    new X500Principal("CN="+m_head.principal())));
356        gen.setNotAfter(new Date(System.currentTimeMillis()
357                    + (3600L * 1000L * 24L * 365L)));
358        gen.setNotBefore(new Date(System.currentTimeMillis()));
359        gen.setSerialNumber(BigInteger.valueOf(System.currentTimeMillis()));
360        gen.addAttribute(new X509Attribute(attrOID,
361                    new DERSequence(
362                        new DERSequence(
363                            new DERUTF8String(toString())))));
364        gen.setSignatureAlgorithm("SHA256WithRSAEncryption");
365
366        // Creddy expects an authority key identifier.
367        gen.addExtension(authKeyOID, false,
368                new AuthorityKeyIdentifier(pki));
369        // Create the cert.
370        ac = (X509V2AttributeCertificate) gen.generate(key, "BC");
371        id = i;
372        */
373    }
374
375    /**
376     * Load the roles off the attribute cert. Throws a RuntimeException if
377     * there's something wrong with the cert.
378     */
379    private void load_roles() {
380        if ( doc == null ) 
381            throw new RuntimeException("No credential");
382
383        String roles = null;
384        NodeList nodes = doc.getElementsByTagName("rt0");
385
386        if (nodes == null || nodes.getLength() != 1) 
387            throw new RuntimeException("More than one rt0 element?");
388
389        Node node = nodes.item(0);
390
391        if ( node == null ) 
392            throw new RuntimeException("bad rt0 element?");
393
394        if ( (roles = node.getTextContent()) == null ) 
395            throw new RuntimeException("bad rt0 element (no text)?");
396
397        String[] parts = roles.split("\\s*<--?\\s*");
398        if (parts.length != 2)
399            throw new RuntimeException("Invalid attribute: " + roles);
400
401        m_head = new Role(parts[0]);
402        m_tail = new Role(parts[1]);
403    }
404
405    /**
406     * Two credentials are the same if their roles are the same.
407     * @param o an Object to compare
408     * @return true if the Credentials have the Roles
409     */
410    public boolean equals(Object o) {
411        if ( o instanceof Credential ) {
412            Credential c = (Credential) o;
413
414            if (m_head == null || m_tail == null ) return false;
415            else return (m_head.equals(c.head()) && m_tail.equals(c.tail()));
416        }
417        else return false;
418    }
419
420    /**
421     * Allow credentials to be compared.  They are ordered by their Roles, head
422     * then tail.
423     * @param o an Object to compare
424     * @return -1 if this Credential is before, 0 if they are the same, and 1
425     *              if this Credential is after the given object.
426     */
427    public int compareTo(Object o) {
428        if (o instanceof Credential) {
429            Credential c = (Credential) o;
430
431            if (head().equals(c.head())) return tail().compareTo(c.tail());
432            else return head().compareTo(c.head());
433        }
434        else return 1;
435    }
436
437
438    /**
439     * Get the head role from the credential.
440     * @return the Role in the head
441     */
442    public Role head() { return m_head; }
443
444    /**
445     * Get the tail role from the credential
446     * @return the Role in the tail
447     */
448    public Role tail() { return m_tail; }
449
450    /**
451     * Gets the cert associated with this credential (if any).
452     * @return the attribute cert associated with this credential (if any).
453     */
454    public Document GENIcert() { return doc; }
455
456    /**
457     * Turn the credential into string form. The format is head &lt;- tail. For
458     * example: A.r1 &lt;- B.r2.r3.  Principal names are key identifiers.
459     * @return the string form
460     */
461    public String toString() {
462        return m_head + " <- " + m_tail;
463    }
464
465    /**
466     * Turn the credential into string form. The format is head &lt;- tail. For
467     * example: A.r1 &lt;- B.r2.r3.  Principal names are shortened to menmonics
468     * if the Context knows the identity.
469     * @param c the Context to translate names in
470     * @return the string form
471     */
472    public String simpleString(Context c) {
473        return m_head.simpleString(c) + " <- " + m_tail.simpleString(c);
474    }
475
476    /**
477     * Output the signed GENI ABAC certificate associated with this
478     * Credential to the OutputStream.
479     * @param s the OutputStream on which to write
480     * @throws IOException if there is an error writing.
481     */
482    public void write(OutputStream s) throws IOException {
483        if ( doc == null ) 
484            return;
485        try {
486            TransformerFactory tf = TransformerFactory.newInstance();
487            Transformer t = tf.newTransformer();
488            DOMSource source = new DOMSource(doc);
489            StreamResult result = new StreamResult(s);
490
491            t.transform(source, result);
492            s.flush();
493        } 
494        catch (Exception e) {
495            throw new IOException(e.getMessage());
496        }
497    }
498
499    /**
500     * Output the signed GENI ABAC certificate associated with this
501     * Credential to the filename given.
502     * @param fn a String containing the output filename
503     * @throws IOException if there is an error writing.
504     */
505    public void write(String fn) throws IOException, FileNotFoundException {
506        write(new FileOutputStream(fn));
507    }
508
509    /**
510     * Return true if this Credential has a certificate associated.  A jabac
511     * extension.
512     * @return true if this Credential has a certificate associated.
513     */
514    public boolean hasCertificate() { return doc != null; }
515
516    /**
517     * Return the Identity that issued the underlying certificate.  A jabac
518     * extension.
519     * @return the Identity that issued the underlying certificate.
520     */
521    public Identity issuer() { return id; }
522    /**
523     * Return the X509Certificate that issued the underlying certificate.
524     * @return the X509Certificate that issued the underlying certificate.
525     */
526    public X509Certificate issuerCert() { return id.getCertificate(); }
527    /**
528     * Return the X509V2AttributeCertificate that underlys the Credential
529     * @return the X509V2AttributeCertificate that underlys the Credential.
530    public X509V2AttributeCertificate attributeCert() { return ac; }
531     */
532
533
534}
Note: See TracBrowser for help on using the repository browser.