source: java/net/deterlab/abac/GENICredentialv1_1.java @ c1736fe

abac0-leakabac0-meitvf-new-xml
Last change on this file since c1736fe was c1736fe, checked in by Ted Faber <faber@…>, 11 years ago

Nicer class structure for GENI credentials

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