X-Git-Url: http://git.megacz.com/?a=blobdiff_plain;f=src%2Forg%2Fxwt%2FTinySSL.java;h=cf9f90133a1a02909635759643485e5f869b1dc2;hb=0f94de16d032aa04fcfa50056f7964de2fcd2d58;hp=44b998c6dc41bbeb0c0873b461a9fe9b30dd6956;hpb=ce478ac0d941574e9165a09a00c95a5930fa7838;p=org.ibex.core.git diff --git a/src/org/xwt/TinySSL.java b/src/org/xwt/TinySSL.java index 44b998c..cf9f901 100644 --- a/src/org/xwt/TinySSL.java +++ b/src/org/xwt/TinySSL.java @@ -1,4 +1,4 @@ -// Copyright (C) 2001 Adam Megacz all rights reserved. +// Copyright (C) 2002 Adam Megacz all rights reserved. // // You may modify, copy, and redistribute this code under the terms of // the GNU Library Public License version 2.1, with the exception of @@ -22,7 +22,7 @@ import org.bouncycastle.crypto.engines.RC4Engine; import org.bouncycastle.util.encoders.Base64; import org.bouncycastle.asn1.DERInputStream; import org.bouncycastle.asn1.DEROutputStream; -import org.bouncycastle.asn1.DERConstructedSequence; +import org.bouncycastle.asn1.DERSequence; import org.bouncycastle.asn1.DERObject; import org.bouncycastle.asn1.DEROctetString; import org.bouncycastle.asn1.BERInputStream; @@ -31,6 +31,9 @@ import org.bouncycastle.asn1.x509.RSAPublicKeyStructure; import org.bouncycastle.asn1.x509.SubjectPublicKeyInfo; import org.bouncycastle.asn1.x509.TBSCertificateStructure; import org.bouncycastle.asn1.x509.X509Name; +import org.bouncycastle.asn1.x509.X509Extensions; +import org.bouncycastle.asn1.x509.X509Extension; +import org.bouncycastle.asn1.x509.BasicConstraints; import org.xwt.util.Log; import java.net.*; import java.io.*; @@ -81,6 +84,9 @@ import java.text.*; 1.02 27-Mar-02 Fixed a bug which would hang the connection when more than one Handshake message appeared in the same TLS Record + 1.03 10-Aug-02 Fixed a vulnerability outlined at + http://online.securityfocus.com/archive/1/286290 + */ public class TinySSL extends Socket { @@ -90,7 +96,7 @@ public class TinySSL extends Socket { public static void main(String[] args) { Log.on = true; try { - Socket s = new TinySSL("www.verisign.com", 443); + Socket s = new TinySSL("www.paypal.com", 443); PrintWriter pw = new PrintWriter(s.getOutputStream()); BufferedReader br = new BufferedReader(new InputStreamReader(s.getInputStream())); pw.println("GET / HTTP/1.0"); @@ -100,7 +106,7 @@ public class TinySSL extends Socket { while(true) { String s2 = br.readLine(); if (s2 == null) return; - System.out.println(s2); + Log.log(TinySSL.class, s2); } } catch (Exception e) { @@ -112,6 +118,7 @@ public class TinySSL extends Socket { public static class SSLException extends IOException { public SSLException(String s) { super(s); } } static SubjectPublicKeyInfo[] trusted_CA_public_keys; + static String[] trusted_CA_public_key_identifiers; public static byte[] pad1 = new byte[48]; public static byte[] pad2 = new byte[48]; public static byte[] pad1_sha = new byte[40]; @@ -142,6 +149,9 @@ public class TinySSL extends Socket { String hostname; + /** if true, we don't mind if the server's cert isn't signed by a CA. USE WITH CAUTION! */ + boolean ignoreUntrustedCert = false; + /** the concatenation of all the bytes of all handshake messages sent or recieved */ public byte[] handshakes = new byte[] { }; @@ -151,10 +161,12 @@ public class TinySSL extends Socket { public InputStream getInputStream() throws IOException { return is != null ? is : super.getInputStream(); } public OutputStream getOutputStream() throws IOException { return os != null ? os : super.getOutputStream(); } - public TinySSL(String host, int port) throws IOException { this(host, port, true); } - public TinySSL(String host, int port, boolean negotiateImmediately) throws IOException { + public TinySSL(String host, int port) throws IOException { this(host, port, true, false); } + public TinySSL(String host, int port, boolean negotiateImmediately) throws IOException { this(host, port, negotiateImmediately, false); } + public TinySSL(String host, int port, boolean negotiateImmediately, boolean ignoreUntrustedCert) throws IOException { super(host, port); hostname = host; + this.ignoreUntrustedCert = ignoreUntrustedCert; if (negotiateImmediately) negotiate(); } @@ -340,7 +352,7 @@ public class TinySSL extends Socket { int certlen = ((rec[7 + i] & 0xff) << 16) | ((rec[7 + i + 1] & 0xff) << 8) | (rec[7 + i + 2] & 0xff); try { DERInputStream dIn = new DERInputStream(new ByteArrayInputStream(rec, 7 + i + 3, certlen)); - this_cert = new X509CertificateStructure((DERConstructedSequence)dIn.readObject()); + this_cert = new X509CertificateStructure((DERSequence)dIn.readObject()); } catch (Exception e) { SSLException t = new SSLException("error decoding server certificate: " + e); t.fillInStackTrace(); @@ -363,15 +375,17 @@ public class TinySSL extends Socket { } if (!good) throw new SSLException("server certificate does not seem to have a CN: " + CN); - if (!CN.equals(hostname)) + if (!ignoreUntrustedCert && !CN.equalsIgnoreCase(hostname)) throw new SSLException("connecting to host " + hostname + " but server certificate was issued for " + CN); - SimpleDateFormat dateF = new SimpleDateFormat("MM-dd-yy-HH-mm-ss-z"); + SimpleDateFormat dateF = new SimpleDateFormat("MM-dd-yyyy-HH-mm-ss-z"); // the following idiocy is a result of the brokenness of the GNU Classpath's SimpleDateFormat String s = tbs.getStartDate().getTime(); - s = s.substring(2, 4) + "-" + s.substring(4, 6) + "-" + s.substring(0, 2) + "-" + s.substring(6, 8) + "-" + - s.substring(8, 10) + "-" + s.substring(10, 12) + "-" + s.substring(12); + s = s.substring(0, 4) + "-" + s.substring(4, 6) + "-" + s.substring(6, 8) + "-" + + s.substring(8, 10) + "-" + s.substring(10, 12) + "-" + + s.substring(12, 14) + "-" + s.substring(14); + Date startDate = dateF.parse(s, new ParsePosition(0)); s = tbs.getEndDate().getTime(); @@ -380,13 +394,28 @@ public class TinySSL extends Socket { Date endDate = dateF.parse(s, new ParsePosition(0)); Date now = new Date(); - if (now.after(endDate)) throw new SSLException("server certificate expired on " + endDate); - if (now.before(startDate)) throw new SSLException("server certificate will not be valid until " + startDate); + if (!ignoreUntrustedCert && now.after(endDate)) + throw new SSLException("server certificate expired on " + endDate); + if (!ignoreUntrustedCert && now.before(startDate)) + throw new SSLException("server certificate will not be valid until " + startDate); Log.log(this, "server cert (name, validity dates) checks out okay"); - } else if (!isSignedBy(last_cert, this_cert.getSubjectPublicKeyInfo())) - throw new SSLException("certificate chain discontinuity"); + } else { + + // don't check the top cert since some very old root certs lack a BasicConstraints field. + if (certlen + 3 + i < numcertbytes) { + // defend against Mike Benham's attack + X509Extension basicConstraints = this_cert.getTBSCertificate().getExtensions().getExtension(X509Extensions.BasicConstraints); + if (basicConstraints == null) throw new SSLException("certificate did not contain a basic constraints block"); + DERInputStream dis = new DERInputStream(new ByteArrayInputStream(basicConstraints.getValue().getOctets())); + BasicConstraints bc = new BasicConstraints((DERSequence)dis.readObject()); + if (!bc.isCA()) throw new SSLException("non-CA certificate used for signing"); + } + + if (!isSignedBy(last_cert, this_cert.getSubjectPublicKeyInfo())) + throw new SSLException("the server sent a broken chain of certificates"); + } last_cert = this_cert; i += certlen + 3; @@ -394,14 +423,30 @@ public class TinySSL extends Socket { } if (Log.on) Log.log(this, " Certificate (" + numcerts + " certificates)"); + if (ignoreUntrustedCert) break; + boolean good = false; + + // pass 1 -- only check CA's whose subject is a partial match + String subject = this_cert.getSubject().toString(); for(int i=0; i