source: java/net/deterlab/abac/Credential.java @ e1c49ce

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

Make zipfile take a file, disambiguate same name/different keys

  • Property mode set to 100644
File size: 11.4 KB
Line 
1package net.deterlab.abac;
2
3import java.io.*;
4import java.math.*;
5
6import java.util.*;
7import java.util.zip.*;
8import java.security.*;
9import java.security.cert.*;
10
11import net.deterlab.abac.Identity;
12
13import org.bouncycastle.asn1.*;
14import org.bouncycastle.x509.*;
15import org.bouncycastle.jce.X509Principal;
16import org.bouncycastle.jce.provider.X509AttrCertParser;
17import org.bouncycastle.jce.provider.X509CertificateObject;
18import org.bouncycastle.openssl.PEMReader;
19
20import org.bouncycastle.asn1.util.ASN1Dump;
21
22import java.security.PrivateKey;
23
24public class Credential {
25    protected static Vector<Identity> s_ids = new Vector<Identity>();
26    protected static String attrOID = "1.3.6.1.5.5.7.10.4";
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    }
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;
44        m_ac = null; 
45        m_id = null;
46    }
47
48    /**
49     * Do the credential initialization from a filename.
50     */
51    protected void init(InputStream stream) throws Exception {
52        X509AttrCertParser parser = new X509AttrCertParser();
53        parser.engineInit(stream);
54        m_ac = (X509V2AttributeCertificate)parser.engineRead();
55        m_id = null;
56
57        if ( m_ac == null ) throw new IOException("Invalid Credential Format");
58
59        for (Identity id: s_ids) {
60            try {
61                m_ac.verify(id.getCertificate().getPublicKey(), "BC");
62                m_id = id;
63                break;
64            }
65            catch (InvalidKeyException e) { }
66        }
67        if (m_id == null) throw new InvalidKeyException("Unknown identity");
68
69        load_roles();
70
71        if (!m_id.getKeyID().equals(m_head.issuer_part()))
72            throw new InvalidKeyException("Unknown identity");
73    }
74
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 {
80        init(new FileInputStream(filename));
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 {
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);
96    }
97
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    }
123
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            //System.err.println(ASN1Dump.dumpAsString(attr));
134
135            DERSequence    java     = (DERSequence)attr.getValues()[0];
136            DERSequence    fucking  = (DERSequence)java.getObjectAt(0);
137            DERUTF8String  sucks    = (DERUTF8String)fucking.getObjectAt(0);
138
139            roles = sucks.getString();
140        }
141        catch (Exception e) {
142            throw new RuntimeException("Your attribute certificate is funky and I'm not gonna debug it", e);
143        }
144
145        String[] parts = roles.split("\\s*<--?\\s*");
146        if (parts.length != 2)
147            throw new RuntimeException("Invalid attribute: " + roles);
148
149        m_head = new Role(parts[0]);
150        m_tail = new Role(parts[1]);
151    }
152
153    /**
154     * Two credentials are the same if their roles are the same.
155     */
156    public boolean equals(Object o) {
157        if ( o instanceof Credential ) {
158            Credential c = (Credential) o;
159
160            if (m_head == null || m_tail == null ) return false;
161            else return (m_head.equals(c.head()) && m_tail.equals(c.tail()));
162        }
163        else return false;
164    }
165
166    /**
167     * Get the head role from the credential.
168     */
169    public Role head() {
170        return m_head;
171    }
172
173    /**
174     * Get the tail role from the credential
175     */
176    public Role tail() {
177        return m_tail;
178    }
179
180    /**
181     * Gets the cert associated with this credential (if any).
182     */
183    public X509V2AttributeCertificate cert() {
184        return m_ac;
185    }
186
187    /**
188     * Turn the credential into string form. The format is head &lt;- tail. For
189     * example: A.r1 &lt;- B.r2.r3.
190     */
191    public String toString() {
192        return m_head + " <- " + m_tail;
193    }
194
195    public String simpleString() {
196        return m_head.simpleString() + " <- " + m_tail.simpleString();
197    }
198
199    public void write(OutputStream s) throws IOException {
200        s.write(m_ac.getEncoded());
201    }
202
203    public void write(String fn) throws IOException, FileNotFoundException {
204        write(new FileOutputStream(fn));
205    }
206
207    public boolean hasCertificate() { return m_ac != null; }
208
209    public Identity getID() { return m_id; }
210
211    /**
212     * Import a zip file.  First import all the identities
213     * (pem), then the credentials (der) into the credential graph then any
214     * alias files into the two maps.  If keys is not null, any key pairs in
215     * PEM files are put in there.  If errors is not null, errors reading files
216     * are added indexed by filename.
217     */
218    static public Collection<Credential> readZipFile(File zf, 
219            Collection<KeyPair> keys, Map<String, Exception> errors) 
220                throws IOException {
221        Vector<Credential> creds = new Vector<Credential>();
222        Vector<ZipEntry> derFiles = new Vector<ZipEntry>();
223
224        ZipFile z = new ZipFile(zf);
225
226        for (Enumeration<? extends ZipEntry> ze = z.entries(); 
227                ze.hasMoreElements();) {
228            ZipEntry  f = ze.nextElement();
229            Object o = null;
230            try {
231                o = new PEMReader(new InputStreamReader(
232                            z.getInputStream(f))).readObject();
233            }
234            catch (IOException e) { 
235                // PEMReader couldn't deal, so we assume it's a DER
236                derFiles.add(f);
237                continue;
238            }
239            try {
240                if ( o == null ) {
241                    // This shouldn't be, but assume it's a DER
242                    derFiles.add(f);
243                }
244                else if (o instanceof X509CertificateObject) {
245                    Credential.addIdentity(
246                            new Identity((X509CertificateObject)o));
247                }
248                else if (o instanceof KeyPair ) {
249                    if ( keys != null ) keys.add((KeyPair) o);
250                }
251                else {
252                    throw new IOException("Unexpected PEM object: " + 
253                            o.getClass().getName());
254                }
255            }
256            catch (Exception e ) {
257                if (errors != null ) errors.put(f.getName(), e);
258            }
259        }
260
261        for ( ZipEntry f : derFiles ) {
262            try {
263                creds.add(new Credential(z.getInputStream(f)));
264            }
265            catch (Exception e ) {
266                if (errors != null ) errors.put(f.getName(), e);
267            }
268        }
269        return creds;
270    }
271
272    static public Collection<Credential> readZipFile(File d) 
273            throws IOException {
274        return readZipFile(d, null, null);
275    }
276    static public Collection<Credential> readZipFile(File d, 
277            Map<String, Exception> errors) throws IOException {
278        return readZipFile(d, null, errors);
279    }
280    static public Collection<Credential> readZipFile(File d, 
281            Collection<KeyPair> keys) throws IOException {
282        return readZipFile(d, keys, null);
283    }
284
285
286    /**
287     * Import a directory full of files.  First import all the identities
288     * (pem), then the credentials (der) into the credential graph then any
289     * alias files into the two maps.  If keys is not null, any key pairs in
290     * PEM files are put in there.  If errors is not null, errors reading files
291     * are added indexed by filename.
292     */
293    static public Collection<Credential> readDirectory(File d, 
294            Collection<KeyPair> keys, Map<String, Exception> errors) {
295        Vector<Credential> creds = new Vector<Credential>();
296        Vector<File> derFiles = new Vector<File>();
297        Collection<File> files = new Vector<File>();
298
299        if (d.isDirectory() ) 
300            for (File f : d.listFiles()) 
301                files.add(f);
302        else files.add(d);
303
304        System.err.println(files);
305        for (File f: files ) {
306            Object o = null;
307            try {
308                o = new PEMReader(new FileReader(f)).readObject();
309            }
310            catch (IOException e) { 
311                // PEMReader couldn't deal, so we assume it's a DER
312                derFiles.add(f);
313                continue;
314            }
315            try {
316                if ( o == null ) {
317                    // This shouldn't be, but assume it's a DER
318                    derFiles.add(f);
319                }
320                else if (o instanceof X509CertificateObject) {
321                    Credential.addIdentity(
322                            new Identity((X509CertificateObject)o));
323                }
324                else if (o instanceof KeyPair ) {
325                    if ( keys != null ) keys.add((KeyPair) o);
326                }
327                else {
328                    throw new IOException("Unexpected PEM object: " + 
329                            o.getClass().getName());
330                }
331            }
332            catch (Exception e ) {
333                if (errors != null ) errors.put(f.getName(), e);
334            }
335        }
336
337        for ( File f : derFiles ) {
338            try {
339                creds.add(new Credential(f));
340            }
341            catch (Exception e ) {
342                if (errors != null ) errors.put(f.getName(), e);
343            }
344        }
345        return creds;
346    }
347
348    static public Collection<Credential> readDirectory(File d) {
349        return readDirectory(d, null, null);
350    }
351    static public Collection<Credential> readDirectory(File d, 
352            Map<String, Exception> errors) {
353        return readDirectory(d, null, errors);
354    }
355    static public Collection<Credential> readDirectory(File d, 
356            Collection<KeyPair> keys) {
357        return readDirectory(d, keys, null);
358    }
359
360    static public void writeZipFile(Collection<Credential> creds, File f,
361            boolean allIDs) 
362            throws IOException {
363        ZipOutputStream z = new ZipOutputStream(new FileOutputStream(f));
364        Set<Identity> ids = allIDs ? 
365            new TreeSet(s_ids) : new TreeSet<Identity>();
366
367        int n = 0;
368        for (Credential c: creds) {
369            z.putNextEntry(new ZipEntry("attr" + n++  + ".der"));
370            c.write(z);
371            z.closeEntry();
372            if ( c.getID() != null && !allIDs) ids.add(c.getID());
373        }
374        for (Identity i: ids) {
375            z.putNextEntry(new ZipEntry(i.getName() + ".pem"));
376            i.write(z);
377            z.closeEntry();
378        }
379        z.close();
380    }
381
382
383private Role m_head, m_tail;
384
385private X509V2AttributeCertificate m_ac;
386private Identity m_id;
387
388    /**
389     * Put the Identity into the set of ids used to validate certificates.
390     * Also put the keyID and name into the translation mappings used by Roles
391     * to pretty print.  In the role mapping, if multiple ids use the same
392     * common name they are disambiguated.  Only one entry for keyid is
393     * allowed.
394     */
395    public static void addIdentity(Identity id) { 
396        s_ids.add(id);
397        if (id.getName() != null && id.getKeyID() != null) {
398            if ( !Role.key_in_mapping(id.getKeyID()) ) {
399                String name = id.getName();
400                int n= 1;
401
402                while (Role.name_in_mapping(name)) {
403                    name = id.getName() + n++;
404                }
405                Role.add_mapping(name, id.getKeyID());
406            }
407        }
408    }
409    public static Collection<Identity> identities() { return s_ids; }
410    public static void clearIdentities() {
411        s_ids.clear(); Role.clear_mapping();
412    }
413}
Note: See TracBrowser for help on using the repository browser.