source: java/net/deterlab/abac/GENICredentialv1_1.java @ 547bac4

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

Bump version

  • Property mode set to 100644
File size: 13.9 KB
RevLine 
[c1736fe]1package net.deterlab.abac;
2
3import java.io.*;
4import java.math.*;
5import java.text.*;
6
7import java.util.*;
8
9import java.security.*;
10import java.security.cert.*;
11
12import javax.xml.parsers.*;
13import javax.xml.transform.*;
14import javax.xml.transform.dom.*;
15import javax.xml.transform.stream.*;
16
17import javax.xml.crypto.*;
18import javax.xml.crypto.dsig.*;
19import javax.xml.crypto.dsig.dom.*;
20import javax.xml.crypto.dsig.keyinfo.*;
21import javax.xml.crypto.dsig.spec.*;
22
23import org.xml.sax.*;
24import org.w3c.dom.*;
25
26/**
27 * An ABAC credential formatted as an abac-type GENI credential, version 1.1.
28 * @author <a href="http://abac.deterlab.net">ISI ABAC team</a>
[a1a9a47]29 * @version 1.5
[c1736fe]30 */
[120b7d8]31public class GENICredentialv1_1 extends GENICredential
32    implements Comparable, MapsKeyIDs {
33
34    protected KeyIDMap keyMap;
[c1736fe]35    /**
36     * Create an empty Credential.
37     */
38    public GENICredentialv1_1() {
39        super();
[120b7d8]40        keyMap = new KeyIDMap();
[c1736fe]41    }
42    /**
43     * Create a credential from a head and tail role.  This credential has no
44     * underlying certificate, and cannot be exported or used in real proofs.
45     * make_cert can create a certificate for a credential initialized this
46     * way.
47     * @param head the Role at the head of the credential
48     * @param tail the Role at the tail of the credential
49     */
50    public GENICredentialv1_1(Role head, Role tail) {
51        super(head, tail);
[120b7d8]52        keyMap = new KeyIDMap();
[c1736fe]53    }
[79f516b]54
55    /**
56     * Create a credential from a head and tail role.  This credential has no
57     * underlying certificate, and cannot be exported or used in real proofs.
58     * make_cert can create a certificate for a credential initialized this
59     * way.  The keymap will be used to annotate the names.
60     * @param head the Role at the head of the credential
61     * @param tail the Role at the tail of the credential
62     */
63    public GENICredentialv1_1(Role head, Role tail, KeyIDMap km) {
64        super(head, tail);
65        keyMap = new KeyIDMap(km);
66    }
[c1736fe]67    /**
68     * Create a credential from an attribute cert in a file. Throws an
69     * exception if the cert file can't be opened or if there's a format
70     * problem with the cert.  Note that catching
71     * java.security.GeneralSecurityException catches most of the exceptions
72     * this throws.
73     * @param filename a String containing the filename to read
74     * @param ids a Collection of Identities to use in validating the cert
75     * @throws CertInvalidException if the stream is unparsable
76     * @throws MissingIssuerException if none of the Identities can validate the
77     *                              certificate
78     * @throws BadSignatureException if the signature check fails
79     */
80    GENICredentialv1_1(String filename, Collection<Identity> ids) 
81        throws ABACException { 
82        super(filename, ids);
[120b7d8]83        /* Parsers should create a keyMap in load_roles(), but create one here
84         * if not */
85        if ( keyMap == null ) keyMap = new KeyIDMap();
[c1736fe]86    }
87
88    /**
89     * Create a credential from an attribute cert in a file. Throws an
90     * exception if the cert file can't be opened or if there's a format
91     * problem with the cert.  Note that catching
92     * java.security.GeneralSecurityException catches most of the exceptions
93     * this throws.
94     * @param file the File to read
95     * @param ids a Collection of Identities to use in validating the cert
96     * @throws CertInvalidException if the stream is unparsable
97     * @throws MissingIssuerException if none of the Identities can validate the
98     *                              certificate
99     * @throws BadSignatureException if the signature check fails
100     */
101    GENICredentialv1_1(File file, Collection<Identity> ids) 
102            throws ABACException {
103        super(file, ids);
[120b7d8]104        /* Parsers should create a keyMap in load_roles(), but create one here
105         * if not */
106        if ( keyMap == null ) keyMap = new KeyIDMap();
[c1736fe]107    }
108
109    /**
110     * Create a credential from an InputStream.  Throws an exception if the
111     * stream can't be parsed or if there's a format problem with the cert.
112     * Note that catching java.security.GeneralSecurityException catches most
113     * of the exceptions this throws.
114     * @param s the InputStream to read
115     * @param ids a Collection of Identities to use in validating the cert
116     * @throws CertInvalidException if the stream is unparsable
117     * @throws MissingIssuerException if none of the Identities can validate the
118     *                              certificate
119     * @throws BadSignatureException if the signature check fails
120     */
121    GENICredentialv1_1(InputStream s, Collection<Identity> ids) 
122            throws ABACException { 
123        super(s, ids);
[120b7d8]124        /* Parsers should create a keyMap in load_roles(), but create one here
125         * if not */
126        if ( keyMap == null ) keyMap = new KeyIDMap();
127    }
128
[c1736fe]129    protected void add_structured_role(Role r, Node top) {
130        if ( r.is_principal()) {
131            Element p = doc.createElement("ABACprincipal");
132            Element k = doc.createElement("keyid");
[79f516b]133            String ms = keyMap.keyToNickname(r.principal());
134            Element m = (ms != null ) ? doc.createElement("mnemonic") : null;
[120b7d8]135
[c1736fe]136            k.appendChild(doc.createTextNode(r.principal()));
137            p.appendChild(k);
[79f516b]138
139            if (m != null ) {
140                m.appendChild(doc.createTextNode(ms));
141                p.appendChild(m);
142            }
[c1736fe]143            top.appendChild(p);
144        } else if ( r.is_role() ) {
145            Element p = doc.createElement("ABACprincipal");
146            Element k = doc.createElement("keyid");
[79f516b]147            String ms = keyMap.keyToNickname(r.principal());
148            Element m = (ms != null ) ? doc.createElement("mnemonic") : null;
[c1736fe]149            Element rr = doc.createElement("role");
[120b7d8]150
[c1736fe]151            k.appendChild(doc.createTextNode(r.principal()));
152            rr.appendChild(doc.createTextNode(r.role_name()));
153            p.appendChild(k);
[79f516b]154            if (m != null ) {
155                m.appendChild(doc.createTextNode(ms));
156                p.appendChild(m);
157            }
[c1736fe]158            top.appendChild(p);
159            top.appendChild(rr);
160        } else {
161            Element p = doc.createElement("ABACprincipal");
162            Element k = doc.createElement("keyid");
[79f516b]163            String ms = keyMap.keyToNickname(r.principal());
164            Element m = (ms != null ) ? doc.createElement("mnemonic") : null;
[c1736fe]165            Element rr = doc.createElement("role");
166            Element lr = doc.createElement("linking_role");
[120b7d8]167
[c1736fe]168            k.appendChild(doc.createTextNode(r.principal()));
169            rr.appendChild(doc.createTextNode(r.role_name()));
170            lr.appendChild(doc.createTextNode(r.linking_role()));
171            p.appendChild(k);
[79f516b]172            if (m != null ) {
173                m.appendChild(doc.createTextNode(ms));
174                p.appendChild(m);
175            }
[c1736fe]176            top.appendChild(p);
177            top.appendChild(rr);
178            top.appendChild(lr);
179        }
180    }
181
182    protected Node make_structured_rt0() {
183        Element a = doc.createElement("rt0");
184        Element v = doc.createElement("version");
185        Element n = doc.createElement("head");
186        Role[] tailRoles = null;
187
188        v.appendChild(doc.createTextNode("1.1"));
189        a.appendChild(v);
190        add_structured_role(head(), n);
191        a.appendChild(n);
192       
193        if (tail().is_intersection()) {
194            try { 
195                tailRoles = tail().prereqs();
196            } catch (ABACException ignored ) { }
197        } else {
198            tailRoles = new Role[] { tail() };
199        }
200
201        for ( Role tr: tailRoles ) {
202            n = doc.createElement("tail");
203            add_structured_role(tr, n);
204            a.appendChild(n);
205        }
206        return a;
207    }
208
209    /**
210     * Encode the abac credential's XML and set the validity.  This is straight
211     * line code that directly builds the credential.
212     * @param validity a long holding the number of seconds that the credential
213     * is valid for.
214     * @return a Node, the place to attach signatures
215     * @throws ABACException if any of the XML construction fails
216     */
217    protected Node make_rt0_content(long validity) throws ABACException {
218        DocumentBuilderFactory dbf = DocumentBuilderFactory.newInstance();
219        DocumentBuilder db = null;
220        SimpleDateFormat df = new SimpleDateFormat("yyyy-MM-dd'T'HH:mm:ss'Z'");
221        StringBuffer expBuf = new StringBuffer();
222
223        /* This is a weirdness to cope with the GENI format.  They have a naked
224         * xml:id specifier without any namespace declarations in the
225         * credential.  Notice that this is the opposite of the setting used in
226         * init to read a credential. */
227        dbf.setNamespaceAware(false);
228
229        if ( dbf == null ) 
230            throw new ABACException("Cannot get DocumentBuilderFactory!?");
231
232        try {
233            db = dbf.newDocumentBuilder();
234        }
235        catch (ParserConfigurationException pe) {
236            throw new ABACException("Cannot get DocumentBuilder!?" + 
237                    pe.getMessage(), pe);
238        }
239
240        doc = db.newDocument();
241        if ( doc == null ) 
242            throw new ABACException("No Document");
243
244        Element root = doc.createElement("signed-credential"); 
245        Element cred = doc.createElement("credential");
246        Element serial = doc.createElement("serial");
247        Element owner_gid = doc.createElement("owner_gid");
248        Element target_gid = doc.createElement("target_gid");
249        Element uuid = doc.createElement("uuid");
250        Element sig = doc.createElement("signatures");
251        Element e = doc.createElement("type");
252        Node text = doc.createTextNode("abac");
253
254        doc.appendChild(root);
255
256        cred.setAttribute("xml:id", "ref0");
257        /* So that the signing code can find the section to sign */
258        cred.setIdAttribute("xml:id", true);
259
260        root.appendChild(cred);
261        e.appendChild(text);
262        for (Element ele : new Element[] 
263                {serial, owner_gid, target_gid, uuid, sig, e })
264            cred.appendChild(ele);
265
266        m_expiration = new Date(System.currentTimeMillis() + 
267                (1000L * validity ));
268        df.setTimeZone(new SimpleTimeZone(0, "Z"));
269        df.format(m_expiration, expBuf, new FieldPosition(0));
270        e = doc.createElement("expires");
271        text = doc.createTextNode(expBuf.toString());
272        e.appendChild(text);
273        cred.appendChild(e);
274
275        e = doc.createElement("abac");
276        e.appendChild(make_structured_rt0());
277        cred.appendChild(e);
278
279        root.appendChild(sig);
280        return sig;
281    }
282
283    protected Role parse_structured_rt0(Node srt)  throws CertInvalidException {
284        Node p = getChildByName(srt, "ABACprincipal");
[120b7d8]285        Node k = null;
286        Node mn = null;
[c1736fe]287        Node r = getChildByName(srt, "role");
288        Node lr = getChildByName(srt, "linking_role");
[120b7d8]289        String ks = null;
290        String mns = null;
[c1736fe]291        String rs = ( r != null ) ? r.getTextContent() : null;
292        String lrs = ( lr!= null ) ? lr.getTextContent() : null;
[120b7d8]293        Role rv = null;
[c1736fe]294
295        if (p == null ) 
296            throw new CertInvalidException("No principal!?");
297
[120b7d8]298        if ( (k = getChildByName(p, "keyid")) == null ) 
[c1736fe]299            throw new CertInvalidException("Principal w/o keyid");
300
[120b7d8]301        if ( (ks = k.getTextContent()) == null ) 
[c1736fe]302            throw new CertInvalidException("Empty keyid");
303
[120b7d8]304        if ( (mn = getChildByName(p, "mnemonic")) != null )
305            mns = mn.getTextContent();
306
[c1736fe]307        if ( lrs != null ) {
308            if ( rs == null ) {
309                throw new CertInvalidException("Linking role without role");
310            }
[120b7d8]311            rv = new Role(ks + "." + lrs + "." + rs);
[c1736fe]312        } else if ( rs == null ) {
[120b7d8]313            rv = new Role(ks);
[c1736fe]314        } else {
[120b7d8]315            rv = new Role(ks + "." + rs);
[c1736fe]316        }
[120b7d8]317        if ( mns != null ) keyMap.addNickname(ks, mns);
318        return rv;
[c1736fe]319    }
320
321    /**
322     * Load the roles off the attribute cert.
323     * @throws CertInvalidException if the certificate is badly formatted
324     */
325    protected void load_roles() throws CertInvalidException {
326        if ( doc == null ) 
327            throw new CertInvalidException("No credential");
328
[120b7d8]329        if ( keyMap == null ) keyMap = new KeyIDMap();
330
[c1736fe]331        NodeList nodes = doc.getElementsByTagName("credential");
332        Node node = null;
333        Node type = null;
334        Node rt0 = null;
335        Node v = null;
336        String vs = null;
337
338        if (nodes == null || nodes.getLength() != 1) 
339            throw new CertInvalidException("More than one credential?");
340
341        node = nodes.item(0);
342        if ( node == null ) 
343            throw new CertInvalidException("bad credential element?");
344
345        if ( (type = getChildByName(node, "type")) == null ) {
346            throw new CertInvalidException("No Type field");
347        }
348
349        if ( !"abac".equals(type.getTextContent()) ) 
350            throw new CertInvalidException("Not an abac type credential");
351
352        // Walk down to the abac and rt0 field using the rt0 variable.
353        if ( (rt0 = getChildByName(node, "abac")) == null ) {
354            throw new CertInvalidException("No abac field");
355        }
356        if ( (rt0 = getChildByName(rt0, "rt0")) == null ) {
357            throw new CertInvalidException("No rt0 field");
358        }
359
360        if ( (v = getChildByName(rt0, "version")) == null ) {
361            throw new CertInvalidException("No version field");
362        }
363        if ( (vs = v.getTextContent()) == null) {
364            throw new CertInvalidException("empty version field");
365        }
366        if ( ! vs.trim().equals("1.1")) {
367            throw new CertInvalidException("bad version: expected 1.1 got "+vs);
368        }
369
370        m_head = null;
371        m_tail = null;
372
373        for (Node n = rt0.getFirstChild(); n != null; n = n.getNextSibling()) {
374            String nname = null;
375
376            if ( n.getNodeType() != Node.ELEMENT_NODE) continue;
377            nname = n.getNodeName();
378            if (nname == null ) continue;
379
380            if (nname.equals("head")) {
381                if ( m_head != null ) 
382                    throw new CertInvalidException("2 head elements");
383                try {
384                    m_head = parse_structured_rt0(n);
385                } catch (CertInvalidException ce) {
386                    throw new CertInvalidException("Error parsing head: " + 
387                            ce.getMessage());
388                }
389            } else if (nname.equals("tail")) {
390                Role t = null;
391
392                try {
393                    t = parse_structured_rt0(n);
394                } catch (CertInvalidException ce) {
395                    throw new CertInvalidException("Error parsing tail: " + 
396                            ce.getMessage());
397                }
398                // This is a little wasteful in processing terms, but simply
399                // appends the new tail entry to a new intersection role.
400                if ( m_tail != null ) 
401                    m_tail = new Role(m_tail.toString()  + " & " + 
402                            t.toString());
403                else m_tail = t;
404            }
405        }
406    }
[120b7d8]407    /**
408     * Return the keymap.
409     * @return a KeyIDMap, this class's keymap
410     */
411    public KeyIDMap getMapping() { return keyMap; }
[c1736fe]412    /**
413     * Return a CredentialCredentialFactorySpecialization for
414     * GENICredentialsv1_1.  Used by the CredentialFactory to parse and generate
415     * these kind of credentials.  It basically wraps constuctor calls.
416     * @return a CredentialFactorySpecialization for this kind of credential.
417     */
418    static public CredentialFactorySpecialization
419            getCredentialFactorySpecialization() {
420        return new CredentialFactorySpecialization() {
421            public Credential[] parseCredential(InputStream is, 
422                    Collection<Identity> ids) throws ABACException {
423                return new Credential[] { new GENICredentialv1_1(is, ids) }; 
424            }
[79f516b]425            public Credential generateCredential(Role head, Role tail,
426                    KeyIDMap aliases) {
427                return new GENICredentialv1_1(head, tail, aliases);
[c1736fe]428            }
429        };
430    }
431}
Note: See TracBrowser for help on using the repository browser.