source: java/net/deterlab/abac/Credential.java @ 42ca4b8

abac0-leakabac0-meicompt_changesgec13mei-idmei-rt0-nmei_rt0mei_rt2mei_rt2_fix_1meiyap-rt1meiyap1rt2tvf-new-xml
Last change on this file since 42ca4b8 was 42ca4b8, checked in by Ted Faber <faber@…>, 13 years ago

Allow identities to carry keys, and adjust reading routines to handle keys well.

  • Property mode set to 100644
File size: 13.0 KB
RevLine 
[31b67d5]1package net.deterlab.abac;
2
[7ef13e3]3import java.io.*;
[281158a]4import java.math.*;
[7ef13e3]5
6import java.util.*;
[5cf72cc]7import java.util.zip.*;
[7ef13e3]8import java.security.*;
9import java.security.cert.*;
10
[9725efb]11import net.deterlab.abac.Identity;
[90f939f]12
13import org.bouncycastle.asn1.*;
14import org.bouncycastle.x509.*;
[281158a]15import org.bouncycastle.jce.X509Principal;
[90f939f]16import org.bouncycastle.jce.provider.X509AttrCertParser;
[7ef13e3]17import org.bouncycastle.jce.provider.X509CertificateObject;
18import org.bouncycastle.openssl.PEMReader;
[90f939f]19
[281158a]20import org.bouncycastle.asn1.util.ASN1Dump;
21
22import java.security.PrivateKey;
23
[88e139a]24public class Credential implements Comparable {
[9725efb]25    protected static Vector<Identity> s_ids = new Vector<Identity>();
[281158a]26    protected static String attrOID = "1.3.6.1.5.5.7.10.4";
[9394f1f]27
28    /**
29     * A dummy credential.
30     */
31    public Credential() {
32        m_head = m_tail = null;
33        m_ac = null;
34        m_id = null;
35    }
[31b67d5]36    /**
37     * Create a credential from a head and tail role. This is only for testing.
38     * In a real implementation the Credential must be loaded from an X.509
39     * attribute cert.
40     */
41    public Credential(Role head, Role tail) {
42        m_head = head;
43        m_tail = tail;
[9394f1f]44        m_ac = null; 
45        m_id = null;
[31b67d5]46    }
47
[90f939f]48    /**
[7ef13e3]49     * Do the credential initialization from a filename.
[90f939f]50     */
[1a7e6d3]51    protected void init(InputStream stream) throws Exception {
[90f939f]52        X509AttrCertParser parser = new X509AttrCertParser();
[1a7e6d3]53        parser.engineInit(stream);
[90f939f]54        m_ac = (X509V2AttributeCertificate)parser.engineRead();
[7ef13e3]55        m_id = null;
56
[be05757]57        if ( m_ac == null ) throw new IOException("Invalid Credential Format");
58
[9725efb]59        for (Identity id: s_ids) {
[7ef13e3]60            try {
[9725efb]61                m_ac.verify(id.getCertificate().getPublicKey(), "BC");
[7ef13e3]62                m_id = id;
63                break;
64            }
[9725efb]65            catch (InvalidKeyException e) { }
[7ef13e3]66        }
67        if (m_id == null) throw new InvalidKeyException("Unknown identity");
[90f939f]68
69        load_roles();
[281158a]70
71        if (!m_id.getKeyID().equals(m_head.issuer_part()))
72            throw new InvalidKeyException("Unknown identity");
[90f939f]73    }
74
[7ef13e3]75    /**
76     * Create a credential from an attribute cert. Throws an exception if the
77     * cert file can't be opened or if there's a format problem with the cert.
78     */
79    public Credential(String filename) throws Exception {
[1a7e6d3]80        init(new FileInputStream(filename));
[7ef13e3]81    }
82
83    /**
84     * Create a credential from an attribute cert. Throws an exception if the
85     * cert file can't be opened or if there's a format problem with the cert.
86     */
87    public Credential(File file) throws Exception {
[1a7e6d3]88        init(new FileInputStream(file));
89    }
90
91    /**
92     * Create a credential from an InputStream.
93     */
94    public Credential(InputStream s) throws Exception { 
95        init(s);
[7ef13e3]96    }
97
[281158a]98    public void make_cert(PrivateKey key) {
99        X509V2AttributeCertificateGenerator gen = 
100            new X509V2AttributeCertificateGenerator();
101
102        gen.setIssuer(new AttributeCertificateIssuer(
103                    new X509Principal("CN="+m_head.issuer_part())));
104        gen.setHolder(new AttributeCertificateHolder(
105                    new X509Principal("CN="+m_head.issuer_part())));
106        gen.setNotAfter(new Date(System.currentTimeMillis() 
107                    + 3600 * 1000 * 24 * 365));
108        gen.setNotBefore(new Date(System.currentTimeMillis()));
109        gen.setSerialNumber(BigInteger.valueOf(System.currentTimeMillis()));
110        gen.addAttribute(new X509Attribute(attrOID, 
111                    new DERSequence(
112                        new DERSequence(
113                            new DERUTF8String(toString())))));
114        gen.setSignatureAlgorithm("SHA256WithRSAEncryption");
115
116        try { 
117            m_ac = (X509V2AttributeCertificate) gen.generate(key, "BC");
118        }
119        catch (Exception e) { 
120            System.err.println(e);
121        }
122    }
[7ef13e3]123
[90f939f]124    /**
125     * Load the roles off the attribute cert. Throws a RuntimeException if
126     * there's something wrong with the cert.
127     */
128    private void load_roles() throws RuntimeException {
129        String roles = null;
130        try {
131            X509Attribute attr = m_ac.getAttributes()[0];
132
133            DERSequence    java     = (DERSequence)attr.getValues()[0];
134            DERSequence    fucking  = (DERSequence)java.getObjectAt(0);
135            DERUTF8String  sucks    = (DERUTF8String)fucking.getObjectAt(0);
136
137            roles = sucks.getString();
138        }
139        catch (Exception e) {
140            throw new RuntimeException("Your attribute certificate is funky and I'm not gonna debug it", e);
141        }
142
143        String[] parts = roles.split("\\s*<--?\\s*");
144        if (parts.length != 2)
145            throw new RuntimeException("Invalid attribute: " + roles);
146
147        m_head = new Role(parts[0]);
148        m_tail = new Role(parts[1]);
149    }
150
[cfcdcb4b]151    /**
152     * Two credentials are the same if their roles are the same.
153     */
154    public boolean equals(Object o) {
155        if ( o instanceof Credential ) {
156            Credential c = (Credential) o;
157
158            if (m_head == null || m_tail == null ) return false;
159            else return (m_head.equals(c.head()) && m_tail.equals(c.tail()));
160        }
161        else return false;
162    }
163
[88e139a]164    public int compareTo(Object o) {
165        if (o instanceof Credential) {
166            Credential c = (Credential) o;
167
168            if (head().equals(c.head())) return tail().compareTo(c.tail());
169            else return head().compareTo(c.head());
170        }
171        else return 1;
172    }
173
174
[31b67d5]175    /**
176     * Get the head role from the credential.
177     */
178    public Role head() {
179        return m_head;
180    }
181
182    /**
183     * Get the tail role from the credential
184     */
185    public Role tail() {
186        return m_tail;
187    }
188
[90f939f]189    /**
190     * Gets the cert associated with this credential (if any).
191     */
192    public X509V2AttributeCertificate cert() {
193        return m_ac;
194    }
195
[31b67d5]196    /**
197     * Turn the credential into string form. The format is head &lt;- tail. For
198     * example: A.r1 &lt;- B.r2.r3.
199     */
200    public String toString() {
201        return m_head + " <- " + m_tail;
202    }
203
[de63a31]204    public String simpleString() {
205        return m_head.simpleString() + " <- " + m_tail.simpleString();
206    }
207
[1a7e6d3]208    public void write(OutputStream s) throws IOException {
209        s.write(m_ac.getEncoded());
210    }
211
212    public void write(String fn) throws IOException, FileNotFoundException {
213        write(new FileOutputStream(fn));
214    }
215
[f6789db]216    public boolean hasCertificate() { return m_ac != null; }
217
[5cf72cc]218    public Identity getID() { return m_id; }
219
220    /**
221     * Import a zip file.  First import all the identities
222     * (pem), then the credentials (der) into the credential graph then any
223     * alias files into the two maps.  If keys is not null, any key pairs in
224     * PEM files are put in there.  If errors is not null, errors reading files
225     * are added indexed by filename.
226     */
[e1c49ce]227    static public Collection<Credential> readZipFile(File zf, 
228            Collection<KeyPair> keys, Map<String, Exception> errors) 
229                throws IOException {
[5cf72cc]230        Vector<Credential> creds = new Vector<Credential>();
[42ca4b8]231        Vector<ZipEntry> derEntries = new Vector<ZipEntry>();
232        Map<String, Identity> ids = new TreeMap<String, Identity>();
233        Map<String, KeyPair> kps = new TreeMap<String, KeyPair>();
[5cf72cc]234
[e1c49ce]235        ZipFile z = new ZipFile(zf);
236
[5cf72cc]237        for (Enumeration<? extends ZipEntry> ze = z.entries(); 
238                ze.hasMoreElements();) {
239            ZipEntry  f = ze.nextElement();
240            try {
[42ca4b8]241                PEMReader r = new PEMReader(
242                        new InputStreamReader(z.getInputStream(f)));
243                Object o = readPEM(r);
244
245                if ( o != null ) {
246                    if (o instanceof Identity) {
247                        Identity i = (Identity) o;
248                        String kid = i.getKeyID();
249
250                        if (kps.containsKey(kid) ) {
251                            i.setKeyPair(kps.get(kid));
252                            kps.remove(kid);
253                        }
254                        else if (i.getKeyPair() == null ) 
255                            ids.put(i.getKeyID(), i);
256
257                        Credential.addIdentity(i);
258                    }
259                    else if (o instanceof KeyPair ) {
260                        KeyPair kp = (KeyPair) o;
261                        String kid = Identity.extractKeyID(kp.getPublic());
262
263                        if (ids.containsKey(kid)) {
264                            Identity i = ids.get(kid);
265
266                            i.setKeyPair(kp);
267                            ids.remove(kid);
268                        }
269                        else {
270                            kps.put(kid, kp);
271                        }
272                    }
[5cf72cc]273                }
274                else {
[42ca4b8]275                    // Not a PEM file
276                    derEntries.add(f);
277                    continue;
[5cf72cc]278                }
279            }
280            catch (Exception e ) {
281                if (errors != null ) errors.put(f.getName(), e);
282            }
283        }
284
[42ca4b8]285        for ( ZipEntry f : derEntries ) {
[5cf72cc]286            try {
287                creds.add(new Credential(z.getInputStream(f)));
288            }
289            catch (Exception e ) {
290                if (errors != null ) errors.put(f.getName(), e);
291            }
292        }
293        return creds;
294    }
295
[e1c49ce]296    static public Collection<Credential> readZipFile(File d) 
297            throws IOException {
[5cf72cc]298        return readZipFile(d, null, null);
299    }
[e1c49ce]300    static public Collection<Credential> readZipFile(File d, 
301            Map<String, Exception> errors) throws IOException {
[5cf72cc]302        return readZipFile(d, null, errors);
303    }
[e1c49ce]304    static public Collection<Credential> readZipFile(File d, 
305            Collection<KeyPair> keys) throws IOException {
[5cf72cc]306        return readZipFile(d, keys, null);
307    }
308
[42ca4b8]309    protected static Object readPEM(PEMReader r) throws IOException {
310        Identity i = null;
311        KeyPair keys = null;
312        Object o = null;
313
314        while ( (o = r.readObject()) != null ) {
315            if (o instanceof X509CertificateObject) {
316                if ( i == null ) {
317                    try {
318                        i = new Identity((X509CertificateObject)o);
319                    }
320                    catch (Exception e) {
321                        // Translate Idenitiy exceptions to IOException
322                        throw new IOException(e);
323                    }
324                    if (keys != null ) {
325                        i.setKeyPair(keys);
326                        keys = null;
327                    }
328                }
329                else throw new IOException("Two certificates");
330            }
331            else if (o instanceof KeyPair ) {
332                if ( i != null ) i.setKeyPair((KeyPair) o);
333                else keys = (KeyPair) o;
334            }
335            else {
336                throw new IOException("Unexpected PEM object: " + 
337                        o.getClass().getName());
338            }
339        }
340
341        if ( i != null ) return i;
342        else if ( keys != null) return keys;
343        else return null;
344    }
[5cf72cc]345
[be05757]346    /**
347     * Import a directory full of files.  First import all the identities
348     * (pem), then the credentials (der) into the credential graph then any
349     * alias files into the two maps.  If keys is not null, any key pairs in
350     * PEM files are put in there.  If errors is not null, errors reading files
351     * are added indexed by filename.
352     */
353    static public Collection<Credential> readDirectory(File d, 
[5cf72cc]354            Collection<KeyPair> keys, Map<String, Exception> errors) {
[be05757]355        Vector<Credential> creds = new Vector<Credential>();
356        Vector<File> derFiles = new Vector<File>();
[e1c49ce]357        Collection<File> files = new Vector<File>();
[42ca4b8]358        Map<String, Identity> ids = new TreeMap<String, Identity>();
359        Map<String, KeyPair> kps = new TreeMap<String, KeyPair>();
[e1c49ce]360
361        if (d.isDirectory() ) 
362            for (File f : d.listFiles()) 
363                files.add(f);
364        else files.add(d);
[be05757]365
[e1c49ce]366        for (File f: files ) {
[be05757]367            try {
[42ca4b8]368                PEMReader r = new PEMReader(new FileReader(f));
369                Object o = readPEM(r);
370
371                if ( o != null ) {
372                    if (o instanceof Identity) {
373                        Identity i = (Identity) o;
374                        String kid = i.getKeyID();
375
376                        if (kps.containsKey(kid) ) {
377                            i.setKeyPair(kps.get(kid));
378                            kps.remove(kid);
379                        }
380                        else if (i.getKeyPair() == null ) 
381                            ids.put(i.getKeyID(), i);
382
383                        Credential.addIdentity(i);
384                    }
385                    else if (o instanceof KeyPair ) {
386                        KeyPair kp = (KeyPair) o;
387                        String kid = Identity.extractKeyID(kp.getPublic());
388
389                        if (ids.containsKey(kid)) {
390                            Identity i = ids.get(kid);
391
392                            i.setKeyPair(kp);
393                            ids.remove(kid);
394                        }
395                        else {
396                            kps.put(kid, kp);
397                        }
398                    }
[be05757]399                }
400                else {
[42ca4b8]401                    // Not a PEM file
402                    derFiles.add(f);
403                    continue;
[be05757]404                }
405            }
406            catch (Exception e ) {
[5cf72cc]407                if (errors != null ) errors.put(f.getName(), e);
[be05757]408            }
409        }
410
411        for ( File f : derFiles ) {
412            try {
413                creds.add(new Credential(f));
414            }
415            catch (Exception e ) {
[5cf72cc]416                if (errors != null ) errors.put(f.getName(), e);
[be05757]417            }
418        }
419        return creds;
420    }
421
422    static public Collection<Credential> readDirectory(File d) {
423        return readDirectory(d, null, null);
424    }
425    static public Collection<Credential> readDirectory(File d, 
[5cf72cc]426            Map<String, Exception> errors) {
[be05757]427        return readDirectory(d, null, errors);
428    }
429    static public Collection<Credential> readDirectory(File d, 
430            Collection<KeyPair> keys) {
431        return readDirectory(d, keys, null);
432    }
433
[5cf72cc]434    static public void writeZipFile(Collection<Credential> creds, File f,
435            boolean allIDs) 
436            throws IOException {
437        ZipOutputStream z = new ZipOutputStream(new FileOutputStream(f));
438        Set<Identity> ids = allIDs ? 
[8a6181b]439            new TreeSet<Identity>(s_ids) : new TreeSet<Identity>();
[5cf72cc]440
441        int n = 0;
442        for (Credential c: creds) {
443            z.putNextEntry(new ZipEntry("attr" + n++  + ".der"));
444            c.write(z);
445            z.closeEntry();
446            if ( c.getID() != null && !allIDs) ids.add(c.getID());
447        }
448        for (Identity i: ids) {
449            z.putNextEntry(new ZipEntry(i.getName() + ".pem"));
450            i.write(z);
451            z.closeEntry();
452        }
453        z.close();
454    }
455
456
[be05757]457private Role m_head, m_tail;
[90f939f]458
[be05757]459private X509V2AttributeCertificate m_ac;
460private Identity m_id;
[7ef13e3]461
[e1c49ce]462    /**
463     * Put the Identity into the set of ids used to validate certificates.
464     * Also put the keyID and name into the translation mappings used by Roles
465     * to pretty print.  In the role mapping, if multiple ids use the same
466     * common name they are disambiguated.  Only one entry for keyid is
467     * allowed.
468     */
[de63a31]469    public static void addIdentity(Identity id) { 
470        s_ids.add(id);
[e1c49ce]471        if (id.getName() != null && id.getKeyID() != null) {
472            if ( !Role.key_in_mapping(id.getKeyID()) ) {
473                String name = id.getName();
474                int n= 1;
475
476                while (Role.name_in_mapping(name)) {
477                    name = id.getName() + n++;
478                }
479                Role.add_mapping(name, id.getKeyID());
480            }
481        }
[de63a31]482    }
[9725efb]483    public static Collection<Identity> identities() { return s_ids; }
[e1c49ce]484    public static void clearIdentities() {
485        s_ids.clear(); Role.clear_mapping();
486    }
[31b67d5]487}
Note: See TracBrowser for help on using the repository browser.