2 * com.brian_web.x509.* - By Brian Alliet
3 * Copyright (C) 2004 Brian Alliet
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)
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:
16 * The above copyright notice and this permission notice shall be included in
17 * all copies or substantial portions of the Software.
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.
28 package org.ibex.crypto;
36 public static class Certificate {
37 public static final String RSA_ENCRYPTION = "1.2.840.113549.1.1.1";
38 public static final String MD2_WITH_RSA_ENCRYPTION = "1.2.840.113549.1.1.2";
39 public static final String MD4_WITH_RSA_ENCRYPTION = "1.2.840.113549.1.1.3";
40 public static final String MD5_WITH_RSA_ENCRYPTION = "1.2.840.113549.1.1.4";
41 public static final String SHA1_WITH_RSA_ENCRYPTION = "1.2.840.113549.1.1.5";
43 public static final String BASIC_CONSTRAINTS = "2.5.29.19";
45 private final byte[] certBytes;
46 private final byte[] tbsCertBytes;
48 public final Number version;
49 public final Number serialNo;
50 public final X509.Name issuer;
51 public final Date startDate;
52 public final Date endDate;
53 public final X509.Name subject;
55 public final AlgorithmIdentifier publicKeyAlgorithm;
56 public final DER.BitString publicKey;
58 public final Object issuerUniqueID;
59 public final Object subjectUniqueID;
61 public final Vector extensions;
63 public final DER.BitString signature;
64 public final AlgorithmIdentifier signatureAlgorithm;
66 public final BC basicContraints;
69 public Certificate(InputStream is) throws IOException {
71 RecordingInputStream certIS = new RecordingInputStream(is);
72 DER.InputStream certSequence = DER.InputStream.New(certIS).getSequenceStream();
73 RecordingInputStream tbsCertIS = new RecordingInputStream(certSequence);
76 Vector tbsSequence = (Vector) DER.InputStream.New(tbsCertIS).readObject();
77 tbsCertBytes = tbsCertIS.getBytes();
78 signatureAlgorithm = new AlgorithmIdentifier(certSequence.readObject());
79 signature = (DER.BitString) certSequence.readObject();
82 if(tbsSequence.elementAt(i) instanceof DER.TaggedObject)
83 version = (Number)((DER.TaggedObject)tbsSequence.elementAt(i++)).object;
85 version = new Integer(0);
87 serialNo = (Number) tbsSequence.elementAt(i++);
88 AlgorithmIdentifier signatureAlgorithm2 = new AlgorithmIdentifier(tbsSequence.elementAt(i++));
89 if(!signatureAlgorithm2.equals(signatureAlgorithm))
90 throw new DER.Exception("AlgoritmIdentifier mismatch " + signatureAlgorithm + " vs " + signatureAlgorithm2);
91 issuer = new X509.Name(tbsSequence.elementAt(i++));
93 Vector validity = (Vector) tbsSequence.elementAt(i++);
94 startDate = (Date) validity.elementAt(0);
95 endDate = (Date) validity.elementAt(1);
97 subject = new X509.Name(tbsSequence.elementAt(i++));
99 Vector publicKeyInfo = (Vector) tbsSequence.elementAt(i++);
100 publicKeyAlgorithm = new AlgorithmIdentifier(publicKeyInfo.elementAt(0));
101 publicKey = (DER.BitString) publicKeyInfo.elementAt(1);
103 Object issuerUniqueID_=null,subjectUniqueID_=null;
104 Vector extensions_=null;
105 for(;i < tbsSequence.size();i++) {
106 DER.TaggedObject to = (DER.TaggedObject) tbsSequence.elementAt(i);
108 case 1: issuerUniqueID_ = to.object; break;
109 case 2: subjectUniqueID_ = to.object; break;
110 case 3: extensions_ = (Vector) to.object; break;
113 issuerUniqueID = issuerUniqueID_;
114 subjectUniqueID = subjectUniqueID_;
115 extensions = extensions_;
119 if(extensions != null) {
120 for(Enumeration e = extensions.elements(); e.hasMoreElements(); ) {
121 Vector extension = (Vector) e.nextElement();
122 String oid = (String) extension.elementAt(0);
123 byte[] data = (byte[]) extension.elementAt(extension.size()-1);
124 if(oid.equals(BASIC_CONSTRAINTS))
125 bc = new BC(DER.InputStream.New(new ByteArrayInputStream(data)).readObject());
128 basicContraints = bc;
129 } catch(RuntimeException e) {
131 throw new DER.Exception("Invalid x509 Certificate");
133 certBytes = certIS.getBytes();
137 public String getSubjectField(String fieldID) { return subject.get(fieldID); }
138 public String getCN() { return getSubjectField(X509.Name.CN); }
140 public boolean isValid() {
141 Date now = new Date();
142 return !now.after(endDate) && !now.before(startDate);
145 public RSA.PublicKey getRSAPublicKey() throws DER.Exception {
146 if(!RSA_ENCRYPTION.equals(publicKeyAlgorithm.id)) throw new DER.Exception("This isn't an RSA public key");
148 return new RSA.PublicKey(DER.InputStream.New(new ByteArrayInputStream(publicKey.data)).readObject());
149 } catch(IOException e) {
150 throw new DER.Exception(e.getMessage());
151 } catch(RuntimeException e) {
152 throw new DER.Exception("Invalid RSA Public Key " + e.getMessage());
156 public boolean isSignedBy(Certificate signer) throws DER.Exception {
157 return isSignedWith(signer.getRSAPublicKey());
159 public boolean isSignedWith(RSA.PublicKey rsapk) throws DER.Exception {
162 if(signatureAlgorithm.id.equals(MD5_WITH_RSA_ENCRYPTION)) digest = new MD5();
163 else if(signatureAlgorithm.id.equals(SHA1_WITH_RSA_ENCRYPTION)) digest = new SHA1();
164 else if(signatureAlgorithm.id.equals(MD2_WITH_RSA_ENCRYPTION)) digest = new MD2();
165 else throw new DER.Exception("Unknown signing algorithm: " + signatureAlgorithm.id);
167 PKCS1 pkcs1 = new PKCS1(new RSA(rsapk.modulus,rsapk.exponent,true));
168 byte[] d = pkcs1.decode(signature.data);
170 Vector v = (Vector) DER.InputStream.New(new ByteArrayInputStream(d)).readObject();
171 byte[] signedDigest = (byte[]) v.elementAt(1);
173 if(signedDigest.length != digest.getDigestSize()) return false;
175 digest.update(tbsCertBytes,0,tbsCertBytes.length);
176 byte[] ourDigest = new byte[digest.getDigestSize()];
177 digest.doFinal(ourDigest,0);
179 for(int i=0;i<digest.getDigestSize();i++) if(ourDigest[i] != signedDigest[i]) return false;
182 catch(RuntimeException e) { e.printStackTrace(); return false; }
183 catch(PKCS1.Exn e) { e.printStackTrace(); return false; }
184 catch(IOException e) { e.printStackTrace(); return false; }
187 public byte[] getMD5Fingerprint() { return getFingerprint(new MD5()); }
188 public byte[] getSHA1Fingerprint() { return getFingerprint(new SHA1()); }
189 public byte[] getFingerprint(Digest h) {
190 h.update(certBytes,0,certBytes.length);
191 byte[] digest = new byte[h.getDigestSize()];
196 private class RecordingInputStream extends FilterInputStream {
197 public ByteArrayOutputStream baos = new ByteArrayOutputStream();
198 private boolean on = true;
199 public void on() { on = true; }
200 public void off() { on = false; }
201 public RecordingInputStream(InputStream is) { super(is); }
202 public int read() throws IOException {
203 int n = super.read();
204 if(n != -1 && on) baos.write(n);
207 public int read(byte[] buf, int off, int len) throws IOException {
208 int n = super.read(buf,off,len);
209 if(n != -1 && on) baos.write(buf,off,n);
212 public byte[] getBytes() { return baos.toByteArray(); }
215 public static class BC {
216 public final boolean isCA;
217 public final Number pathLenConstraint;
219 Vector seq = (Vector) o;
220 isCA = seq.size() > 0 ? ((Boolean) seq.elementAt(0)).booleanValue() : false;
221 pathLenConstraint = seq.size() > 1 ? (Number) seq.elementAt(1) : null;
225 public static class AlgorithmIdentifier {
226 public final String id;
227 public final Object parameters;
229 AlgorithmIdentifier(Object o) {
230 Vector seq = (Vector) o;
231 id = (String) seq.elementAt(0);
232 parameters = seq.elementAt(1);
234 public boolean equals(Object o_) {
235 if(o_ == this) return true;
236 if(!(o_ instanceof AlgorithmIdentifier)) return false;
237 AlgorithmIdentifier o = (AlgorithmIdentifier) o_;
238 return o.id.equals(id) && o.parameters.equals(parameters);
240 public int hashCode() { return id.hashCode() ^ parameters.hashCode(); }
243 /*public static void main(String[] args) throws Exception {
244 Certificate cert = new Certificate(new FileInputStream(args[0]));
245 System.err.println("CN: " + cert.getCN());
246 System.err.println("Subject: " + cert.subject);
247 System.err.println("Issuer: " + cert.issuer);
248 System.err.println("Start Date: " + cert.startDate);
249 System.err.println("End Date: " + cert.endDate);
250 System.err.println("SHA1 Fingerprint: " + prettyBytes(cert.getSHA1Fingerprint()));
251 RSA.PublicKey key = cert.getRSA.PublicKey();
252 System.err.println("Modulus: " + prettyBytes(key.modulus.toByteArray()));
253 System.err.println("Exponent: " + key.exponent);
254 System.err.println("Signature: " + prettyBytes(cert.signature.data));
257 public static String prettyBytes(byte[] fp) {
258 StringBuffer sb = new StringBuffer(fp.length*3);
259 for(int i=0;i<fp.length;i++) {
260 if(i>0) sb.append(":");
261 sb.append("0123456789abcdef".charAt((fp[i] & 0xf0) >>> 4));
262 sb.append("0123456789abcdef".charAt((fp[i] & 0x0f) >>> 0));
264 return sb.toString();
268 public static class Name {
270 public static final String C = "2.5.4.6";
271 public static final String O = "2.5.4.10";
272 public static final String T = "2.5.4.12";
273 public static final String SN = "2.5.4.5";
274 public static final String L = "2.5.4.7";
275 public static final String ST = "2.5.4.8";
276 public static final String OU = "2.5.4.11";
277 public static final String CN = "2.5.4.3";
278 public static final String E = "1.2.840.113549.1.9.1";
280 private final Vector keys = new Vector();
281 private final Vector values = new Vector();
283 public Name(Object seq_) throws DER.Exception {
285 Vector seq = (Vector) seq_;
286 for(Enumeration e = seq.elements();e.hasMoreElements();) {
287 Vector component = (Vector) ((Vector)e.nextElement()).elementAt(0);
288 keys.addElement(component.elementAt(0));
289 values.addElement(component.elementAt(1));
291 } catch(RuntimeException e) {
293 throw new DER.Exception("Invalid Name " + e.toString());
297 public boolean equals(Object o_) {
298 if(o_ instanceof String) return toString().equals(o_);
299 if(!(o_ instanceof Name)) return false;
301 if(keys.size() != o.keys.size()) return false;
302 int size = keys.size();
303 for(int i=0;i<size;i++) {
304 String oid = (String) keys.elementAt(i);
305 String oid2 = (String) o.keys.elementAt(i);
306 if(!oid.equals(oid2)) return false;
308 String val1 = (String) values.elementAt(i);
309 String val2 = (String) o.values.elementAt(i);
310 if(val1.equals(val2)) continue;
312 val1 = val1.trim().toLowerCase();
313 val2 = val2.trim().toLowerCase();
314 if(val1.equals(val2)) continue;
316 val1 = removeExtraSpaces(val1);
317 val2 = removeExtraSpaces(val2);
318 if(val1.equals(val2)) continue;
325 public int hashCode() { return keys.hashCode() ^ values.hashCode(); }
327 public String get(String fieldID) {
328 int i = keys.indexOf(fieldID);
329 return i == -1 ? null : (String)values.elementAt(i);
332 public String[] getOIDs() {
333 String[] ret = new String[keys.size()];
338 public String[] getValues() {
339 String[] ret = new String[values.size()];
340 values.copyInto(ret);
344 private static String removeExtraSpaces(String s) {
345 if(s.indexOf(' ') == -1) return s;
346 StringBuffer sb = new StringBuffer(s.length());
348 boolean inWhitespace = false;
349 for(int i=0;i<l;i++) {
350 if(s.charAt(i) == ' ') {
351 if(inWhitespace) continue;
353 } else if(inWhitespace) {
354 inWhitespace = false;
356 sb.append(s.charAt(i));
358 return sb.toString();
361 private final static Hashtable oidMap = new Hashtable();
363 oidMap.put(Name.C,"C");
364 oidMap.put(Name.O,"O");
365 oidMap.put(Name.T,"T");
366 oidMap.put(Name.SN,"SN");
367 oidMap.put(Name.L,"L");
368 oidMap.put(Name.ST,"ST");
369 oidMap.put(Name.OU,"OU");
370 oidMap.put(Name.CN,"CN");
371 oidMap.put(Name.E,"E");
374 public String toString() {
375 StringBuffer sb = new StringBuffer();
376 int size = keys.size();
377 for(int i=0;i<size;i++) {
378 if(sb.length() > 0) sb.append(",");
379 String fieldID = (String) keys.elementAt(i);
380 String fieldName = (String) oidMap.get(fieldID);
381 sb.append(fieldName != null ? fieldName : fieldID).append("=").append(values.elementAt(i));
383 return sb.toString();