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;
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";
41 public static final String BASIC_CONSTRAINTS = "2.5.29.19";
43 private final byte[] certBytes;
44 private final byte[] tbsCertBytes;
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;
53 public final AlgorithmIdentifier publicKeyAlgorithm;
54 public final DER.BitString publicKey;
56 public final Object issuerUniqueID;
57 public final Object subjectUniqueID;
59 public final Vector extensions;
61 public final DER.BitString signature;
62 public final AlgorithmIdentifier signatureAlgorithm;
64 public final BC basicContraints;
67 public Certificate(InputStream is) throws IOException {
69 RecordingInputStream certIS = new RecordingInputStream(is);
70 DER.InputStream certSequence = new DER.InputStream(certIS).getSequenceStream();
71 RecordingInputStream tbsCertIS = new RecordingInputStream(certSequence);
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();
80 if(tbsSequence.elementAt(i) instanceof DER.TaggedObject)
81 version = (Number)((DER.TaggedObject)tbsSequence.elementAt(i++)).object;
83 version = new Integer(0);
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++));
91 Vector validity = (Vector) tbsSequence.elementAt(i++);
92 startDate = (Date) validity.elementAt(0);
93 endDate = (Date) validity.elementAt(1);
95 subject = new X509.Name(tbsSequence.elementAt(i++));
97 Vector publicKeyInfo = (Vector) tbsSequence.elementAt(i++);
98 publicKeyAlgorithm = new AlgorithmIdentifier(publicKeyInfo.elementAt(0));
99 publicKey = (DER.BitString) publicKeyInfo.elementAt(1);
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);
106 case 1: issuerUniqueID_ = to.object; break;
107 case 2: subjectUniqueID_ = to.object; break;
108 case 3: extensions_ = (Vector) to.object; break;
111 issuerUniqueID = issuerUniqueID_;
112 subjectUniqueID = subjectUniqueID_;
113 extensions = extensions_;
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());
126 basicContraints = bc;
127 } catch(RuntimeException e) {
129 throw new DER.Exception("Invalid x509 Certificate");
131 certBytes = certIS.getBytes();
135 public String getSubjectField(String fieldID) { return subject.get(fieldID); }
136 public String getCN() { return getSubjectField(X509.Name.CN); }
138 public boolean isValid() {
139 Date now = new Date();
140 return !now.after(endDate) && !now.before(startDate);
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");
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());
154 public boolean isSignedBy(Certificate signer) throws DER.Exception {
155 return isSignedWith(signer.getRSAPublicKey());
157 public boolean isSignedWith(RSA.PublicKey rsapk) throws DER.Exception {
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);
165 PKCS1 pkcs1 = new PKCS1(new RSA(rsapk.modulus,rsapk.exponent,true));
166 byte[] d = pkcs1.decode(signature.data);
168 Vector v = (Vector) new DER.InputStream(new ByteArrayInputStream(d)).readObject();
169 byte[] signedDigest = (byte[]) v.elementAt(1);
171 if(signedDigest.length != digest.getDigestSize()) return false;
173 digest.update(tbsCertBytes,0,tbsCertBytes.length);
174 byte[] ourDigest = new byte[digest.getDigestSize()];
175 digest.doFinal(ourDigest,0);
177 for(int i=0;i<digest.getDigestSize();i++) if(ourDigest[i] != signedDigest[i]) return false;
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; }
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()];
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);
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);
210 public byte[] getBytes() { return baos.toByteArray(); }
213 public static class BC {
214 public final boolean isCA;
215 public final Number pathLenConstraint;
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;
223 public static class AlgorithmIdentifier {
224 public final String id;
225 public final Object parameters;
227 AlgorithmIdentifier(Object o) {
228 Vector seq = (Vector) o;
229 id = (String) seq.elementAt(0);
230 parameters = seq.elementAt(1);
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);
238 public int hashCode() { return id.hashCode() ^ parameters.hashCode(); }
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));
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));
262 return sb.toString();
266 public static class Name {
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";
278 private final Vector keys = new Vector();
279 private final Vector values = new Vector();
281 public Name(Object seq_) throws DER.Exception {
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));
289 } catch(RuntimeException e) {
291 throw new DER.Exception("Invalid Name " + e.toString());
295 public boolean equals(Object o_) {
296 if(o_ instanceof String) return toString().equals(o_);
297 if(!(o_ instanceof Name)) return false;
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;
306 String val1 = (String) values.elementAt(i);
307 String val2 = (String) o.values.elementAt(i);
308 if(val1.equals(val2)) continue;
310 val1 = val1.trim().toLowerCase();
311 val2 = val2.trim().toLowerCase();
312 if(val1.equals(val2)) continue;
314 val1 = removeExtraSpaces(val1);
315 val2 = removeExtraSpaces(val2);
316 if(val1.equals(val2)) continue;
323 public int hashCode() { return keys.hashCode() ^ values.hashCode(); }
325 public String get(String fieldID) {
326 int i = keys.indexOf(fieldID);
327 return i == -1 ? null : (String)values.elementAt(i);
330 public String[] getOIDs() {
331 String[] ret = new String[keys.size()];
336 public String[] getValues() {
337 String[] ret = new String[values.size()];
338 values.copyInto(ret);
342 private static String removeExtraSpaces(String s) {
343 if(s.indexOf(' ') == -1) return s;
344 StringBuffer sb = new StringBuffer(s.length());
346 boolean inWhitespace = false;
347 for(int i=0;i<l;i++) {
348 if(s.charAt(i) == ' ') {
349 if(inWhitespace) continue;
351 } else if(inWhitespace) {
352 inWhitespace = false;
354 sb.append(s.charAt(i));
356 return sb.toString();
359 private final static Hashtable oidMap = new Hashtable();
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");
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));
381 return sb.toString();