finished last of the compile errors
[org.ibex.crypto.git] / src / org / ibex / crypto / X509.java
1 /*
2  * com.brian_web.x509.* - By Brian Alliet
3  * Copyright (C) 2004 Brian Alliet
4  * 
5  * Based on Bouncy Castle by The Legion Of The Bouncy Castle
6  * Copyright (c) 2000 The Legion Of The Bouncy Castle 
7  * (http://www.bouncycastle.org)
8  * 
9  * Permission is hereby granted, free of charge, to any person obtaining a 
10  * copy of this software and associated documentation files (the "Software"),
11  * to deal in the Software without restriction, including without limitation 
12  * the rights to use, copy, modify, merge, publish, distribute, sublicense, 
13  * and/or sell copies of the Software, and to permit persons to whom the 
14  * Software is furnished to do so, subject to the following conditions:
15  * 
16  * The above copyright notice and this permission notice shall be included in
17  * all copies or substantial portions of the Software.
18  * 
19  * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
20  * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
21  * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
22  * AUTHORS OR COPYRIGHT HOLDER.S BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 
23  * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
24  * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER 
25  * DEALINGS IN THE SOFTWARE.
26  */
27
28 package org.ibex.crypto;
29
30 import java.io.*;
31 import java.util.*;
32
33 public class X509 {
34     public static class Certificate {
35         public static final String RSA_ENCRYPTION           = "1.2.840.113549.1.1.1";
36         public static final String MD2_WITH_RSA_ENCRYPTION  = "1.2.840.113549.1.1.2";
37         public static final String MD4_WITH_RSA_ENCRYPTION  = "1.2.840.113549.1.1.3";
38         public static final String MD5_WITH_RSA_ENCRYPTION  = "1.2.840.113549.1.1.4";
39         public static final String SHA1_WITH_RSA_ENCRYPTION = "1.2.840.113549.1.1.5";
40     
41         public static final String BASIC_CONSTRAINTS = "2.5.29.19";
42     
43         private final byte[] certBytes;
44         private final byte[] tbsCertBytes;
45     
46         public final Number version;    
47         public final Number serialNo;
48         public final X509.Name issuer;
49         public final Date startDate;
50         public final Date endDate;
51         public final X509.Name subject;
52     
53         public final AlgorithmIdentifier publicKeyAlgorithm;
54         public final DER.BitString publicKey;
55     
56         public final Object issuerUniqueID;
57         public final Object subjectUniqueID;
58     
59         public final Vector extensions;
60     
61         public final DER.BitString signature;
62         public final AlgorithmIdentifier signatureAlgorithm;
63     
64         public final BC basicContraints;
65
66     
67         public Certificate(InputStream is) throws IOException {
68             int i;
69             RecordingInputStream certIS = new RecordingInputStream(is);
70             DER.InputStream certSequence = new DER.InputStream(certIS).getSequenceStream();
71             RecordingInputStream tbsCertIS = new RecordingInputStream(certSequence);
72         
73             try {
74                 Vector tbsSequence = (Vector) new DER.InputStream(tbsCertIS).readObject();
75                 tbsCertBytes = tbsCertIS.getBytes();
76                 signatureAlgorithm = new AlgorithmIdentifier(certSequence.readObject());
77                 signature = (DER.BitString) certSequence.readObject();
78             
79                 i=0;
80                 if(tbsSequence.elementAt(i) instanceof DER.TaggedObject)
81                     version = (Number)((DER.TaggedObject)tbsSequence.elementAt(i++)).object;
82                 else
83                     version = new Integer(0);
84             
85                 serialNo = (Number) tbsSequence.elementAt(i++);
86                 AlgorithmIdentifier signatureAlgorithm2 = new AlgorithmIdentifier(tbsSequence.elementAt(i++));
87                 if(!signatureAlgorithm2.equals(signatureAlgorithm))
88                     throw new DER.Exception("AlgoritmIdentifier mismatch " + signatureAlgorithm + " vs " + signatureAlgorithm2);
89                 issuer = new X509.Name(tbsSequence.elementAt(i++));
90             
91                 Vector validity = (Vector) tbsSequence.elementAt(i++);
92                 startDate = (Date) validity.elementAt(0);
93                 endDate = (Date) validity.elementAt(1);
94             
95                 subject = new X509.Name(tbsSequence.elementAt(i++));
96             
97                 Vector publicKeyInfo = (Vector) tbsSequence.elementAt(i++);
98                 publicKeyAlgorithm = new AlgorithmIdentifier(publicKeyInfo.elementAt(0));
99                 publicKey = (DER.BitString) publicKeyInfo.elementAt(1);
100           
101                 Object issuerUniqueID_=null,subjectUniqueID_=null;
102                 Vector extensions_=null;
103                 for(;i < tbsSequence.size();i++) {
104                     DER.TaggedObject to = (DER.TaggedObject) tbsSequence.elementAt(i);
105                     switch(to.tag) {
106                         case 1: issuerUniqueID_ = to.object; break;
107                         case 2: subjectUniqueID_ = to.object; break;
108                         case 3: extensions_ = (Vector) to.object; break;
109                     }
110                 }
111                 issuerUniqueID = issuerUniqueID_;
112                 subjectUniqueID = subjectUniqueID_;
113                 extensions = extensions_;
114             
115                 BC bc = null;
116             
117                 if(extensions != null) {
118                     for(Enumeration e = extensions.elements(); e.hasMoreElements(); ) {
119                         Vector extension = (Vector) e.nextElement();
120                         String oid = (String) extension.elementAt(0);
121                         byte[] data = (byte[]) extension.elementAt(extension.size()-1);
122                         if(oid.equals(BASIC_CONSTRAINTS))
123                             bc = new BC(new DER.InputStream(new ByteArrayInputStream(data)).readObject());
124                     }
125                 }
126                 basicContraints = bc;
127             } catch(RuntimeException e) {
128                 e.printStackTrace();
129                 throw new DER.Exception("Invalid x509 Certificate");
130             }
131             certBytes = certIS.getBytes();
132         }
133     
134     
135         public String getSubjectField(String fieldID) { return subject.get(fieldID); }
136         public String getCN() { return getSubjectField(X509.Name.CN); }
137     
138         public boolean isValid() {
139             Date now = new Date();
140             return !now.after(endDate) && !now.before(startDate);
141         }
142     
143         public RSA.PublicKey getRSAPublicKey() throws DER.Exception {
144             if(!RSA_ENCRYPTION.equals(publicKeyAlgorithm.id)) throw new DER.Exception("This isn't an RSA public key");
145             try {
146                 return new RSA.PublicKey(new DER.InputStream(new ByteArrayInputStream(publicKey.data)).readObject());
147             } catch(IOException e) {
148                 throw new DER.Exception(e.getMessage());
149             } catch(RuntimeException e) {
150                 throw new DER.Exception("Invalid RSA Public Key " + e.getMessage());
151             }
152         }
153     
154         public boolean isSignedBy(Certificate signer) throws DER.Exception {
155             return isSignedWith(signer.getRSAPublicKey());
156         }
157         public boolean isSignedWith(RSA.PublicKey rsapk) throws DER.Exception {
158             try {
159                 Digest digest;
160                 if(signatureAlgorithm.id.equals(MD5_WITH_RSA_ENCRYPTION)) digest = new MD5();
161                 else if(signatureAlgorithm.id.equals(SHA1_WITH_RSA_ENCRYPTION)) digest = new SHA1();
162                 else if(signatureAlgorithm.id.equals(MD2_WITH_RSA_ENCRYPTION)) digest = new MD2();
163                 else throw new DER.Exception("Unknown signing algorithm: " + signatureAlgorithm.id);
164                         
165                 PKCS1 pkcs1 = new PKCS1(new RSA(rsapk.modulus,rsapk.exponent,true));
166                 byte[] d = pkcs1.decode(signature.data);
167             
168                 Vector v = (Vector) new DER.InputStream(new ByteArrayInputStream(d)).readObject();
169                 byte[] signedDigest = (byte[]) v.elementAt(1);
170                             
171                 if(signedDigest.length != digest.getDigestSize()) return false;
172             
173                 digest.update(tbsCertBytes,0,tbsCertBytes.length);
174                 byte[] ourDigest = new byte[digest.getDigestSize()];
175                 digest.doFinal(ourDigest,0);
176             
177                 for(int i=0;i<digest.getDigestSize();i++) if(ourDigest[i] != signedDigest[i]) return false;
178                 return true;
179             }
180             catch(RuntimeException e) { e.printStackTrace(); return false; }
181             catch(PKCS1.Exn e) { e.printStackTrace(); return false; }
182             catch(IOException e) { e.printStackTrace(); return false; }
183         }
184     
185         public byte[] getMD5Fingerprint() { return getFingerprint(new MD5()); }
186         public byte[] getSHA1Fingerprint() { return getFingerprint(new SHA1()); }
187         public byte[] getFingerprint(Digest h) {
188             h.update(certBytes,0,certBytes.length);
189             byte[] digest = new byte[h.getDigestSize()];
190             h.doFinal(digest,0);
191             return digest;
192         }
193     
194         private class RecordingInputStream extends FilterInputStream {
195             public ByteArrayOutputStream baos = new ByteArrayOutputStream();
196             private boolean on = true;
197             public void on() { on = true; }
198             public void off() { on = false; }
199             public RecordingInputStream(InputStream is) { super(is); }
200             public int read() throws IOException {
201                 int n = super.read();
202                 if(n != -1 && on) baos.write(n);
203                 return n;
204             }
205             public int read(byte[] buf, int off, int len) throws IOException {
206                 int n = super.read(buf,off,len);
207                 if(n != -1 && on) baos.write(buf,off,n);
208                 return n;
209             }
210             public byte[] getBytes() { return baos.toByteArray(); }
211         }
212     
213         public static class BC {
214             public final boolean isCA;
215             public final Number pathLenConstraint;
216             BC(Object o) {
217                 Vector seq = (Vector) o;
218                 isCA = seq.size() > 0 ? ((Boolean) seq.elementAt(0)).booleanValue() : false;
219                 pathLenConstraint = seq.size() > 1 ? (Number) seq.elementAt(1) : null;
220             }
221         }
222     
223         public static class AlgorithmIdentifier {
224             public final String id;
225             public final Object parameters;
226         
227             AlgorithmIdentifier(Object o) {
228                 Vector seq = (Vector) o;
229                 id = (String) seq.elementAt(0);
230                 parameters = seq.elementAt(1);
231             }
232             public boolean equals(Object o_) {
233                 if(o_ == this) return true;
234                 if(!(o_ instanceof AlgorithmIdentifier)) return false;
235                 AlgorithmIdentifier o = (AlgorithmIdentifier) o_;
236                 return o.id.equals(id) && o.parameters.equals(parameters);
237             }
238             public int hashCode() { return id.hashCode() ^ parameters.hashCode(); }
239         }
240     
241         /*public static void main(String[] args) throws Exception {
242           Certificate cert = new Certificate(new FileInputStream(args[0]));
243           System.err.println("CN: " + cert.getCN());
244           System.err.println("Subject: " + cert.subject);
245           System.err.println("Issuer: " + cert.issuer);
246           System.err.println("Start Date: " + cert.startDate);
247           System.err.println("End Date: " + cert.endDate);
248           System.err.println("SHA1 Fingerprint: " + prettyBytes(cert.getSHA1Fingerprint()));
249           RSA.PublicKey key = cert.getRSA.PublicKey();
250           System.err.println("Modulus: " + prettyBytes(key.modulus.toByteArray()));
251           System.err.println("Exponent: " + key.exponent);
252           System.err.println("Signature: " + prettyBytes(cert.signature.data));
253           }
254     
255           public static String prettyBytes(byte[] fp) {
256           StringBuffer sb = new StringBuffer(fp.length*3);
257           for(int i=0;i<fp.length;i++) {
258           if(i>0) sb.append(":");
259           sb.append("0123456789abcdef".charAt((fp[i] & 0xf0) >>> 4));
260           sb.append("0123456789abcdef".charAt((fp[i] & 0x0f) >>> 0));
261           }
262           return sb.toString();
263           }*/
264     }
265
266     public static class Name {
267         // Some common OIDs
268         public static final String C = "2.5.4.6";
269         public static final String O = "2.5.4.10";
270         public static final String T = "2.5.4.12";
271         public static final String SN = "2.5.4.5";
272         public static final String L = "2.5.4.7";
273         public static final String ST = "2.5.4.8";
274         public static final String OU = "2.5.4.11";
275         public static final String CN = "2.5.4.3";
276         public static final String E = "1.2.840.113549.1.9.1";
277     
278         private final Vector keys = new Vector();
279         private final Vector values = new Vector();
280     
281         public Name(Object seq_) throws DER.Exception {
282             try {
283                 Vector seq = (Vector) seq_;
284                 for(Enumeration e = seq.elements();e.hasMoreElements();) {
285                     Vector component = (Vector) ((Vector)e.nextElement()).elementAt(0);
286                     keys.add(component.elementAt(0));
287                     values.add(component.elementAt(1));
288                 }
289             } catch(RuntimeException e) {
290                 e.printStackTrace();
291                 throw new DER.Exception("Invalid Name " + e.toString());
292             }
293         }
294     
295         public boolean equals(Object o_) {
296             if(o_ instanceof String) return toString().equals(o_);
297             if(!(o_ instanceof Name)) return false;
298             Name o = (Name) o_;
299             if(keys.size() != o.keys.size()) return false;
300             int size = keys.size();
301             for(int i=0;i<size;i++) {
302                 String oid = (String) keys.elementAt(i);
303                 String oid2 = (String) o.keys.elementAt(i);
304                 if(!oid.equals(oid2)) return false;
305             
306                 String val1 = (String) values.elementAt(i);
307                 String val2 = (String) o.values.elementAt(i);
308                 if(val1.equals(val2)) continue;
309             
310                 val1 = val1.trim().toLowerCase();
311                 val2 = val2.trim().toLowerCase();
312                 if(val1.equals(val2)) continue;
313             
314                 val1 = removeExtraSpaces(val1);
315                 val2 = removeExtraSpaces(val2);
316                 if(val1.equals(val2)) continue;
317             
318                 return false;
319             }
320             return true;
321         }
322     
323         public int hashCode() { return keys.hashCode() ^ values.hashCode(); }
324     
325         public String get(String fieldID) {
326             int i = keys.indexOf(fieldID);
327             return i == -1 ? null : (String)values.elementAt(i);
328         }
329     
330         public String[] getOIDs() {
331             String[] ret = new String[keys.size()];
332             keys.copyInto(ret);
333             return ret;
334         }
335     
336         public String[] getValues() {
337             String[] ret = new String[values.size()];
338             values.copyInto(ret);
339             return ret;
340         }
341     
342         private static String removeExtraSpaces(String s) {
343             if(s.indexOf(' ') == -1) return s;
344             StringBuffer sb = new StringBuffer(s.length());
345             int l = s.length();
346             boolean inWhitespace = false;
347             for(int i=0;i<l;i++) {
348                 if(s.charAt(i) == ' ') {
349                     if(inWhitespace) continue;
350                     inWhitespace = true;
351                 } else if(inWhitespace) {
352                     inWhitespace = false;
353                 }
354                 sb.append(s.charAt(i));
355             }
356             return sb.toString();
357         }
358     
359         private final static Hashtable oidMap = new Hashtable();
360         static {
361             oidMap.put(Name.C,"C");
362             oidMap.put(Name.O,"O");
363             oidMap.put(Name.T,"T");
364             oidMap.put(Name.SN,"SN");
365             oidMap.put(Name.L,"L");
366             oidMap.put(Name.ST,"ST");
367             oidMap.put(Name.OU,"OU");
368             oidMap.put(Name.CN,"CN");
369             oidMap.put(Name.E,"E");
370         }
371     
372         public String toString() {
373             StringBuffer sb = new StringBuffer();
374             int size = keys.size();
375             for(int i=0;i<size;i++) {
376                 if(sb.length() > 0) sb.append(",");
377                 String fieldID = (String) keys.elementAt(i);
378                 String fieldName = (String) oidMap.get(fieldID);
379                 sb.append(fieldName != null ? fieldName : fieldID).append("=").append(values.elementAt(i));
380             }
381             return sb.toString();
382         }
383     }
384 }