source: java/net/deterlab/abac/GENICredentialv1_1.java @ 8200a9c

Last change on this file since 8200a9c was 461edba, checked in by Ted Faber <faber@…>, 10 years ago

Clean up javadoc for v8.

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