-// Copyright (C) 2001 Adam Megacz <adam@xwt.org> all rights reserved.
+// Copyright (C) 2002 Adam Megacz <adam@xwt.org> 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
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.*;
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 {
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");
while(true) {
String s2 = br.readLine();
if (s2 == null) return;
- System.out.println(s2);
+ Log.log(TinySSL.class, s2);
}
} catch (Exception e) {
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[] { };
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();
}
}
if (!good) throw new SSLException("server certificate does not seem to have a CN: " + CN);
- if (!CN.equals(hostname))
+ if (!ignoreUntrustedCert && !CN.equals(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");
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("the server sent a broken chain of certificates");
+ } 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((DERConstructedSequence)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;
}
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
"1HP9SFIIThbbP4pO0M8RcPO/mn+SXXwc+EY/J8Y8+iR/LGWzOOZEAEaMGAuWQcRXfH2G71" +
"lSk8UOg013gfqLptQ5GVj0VXXn7F+8qkBOvqlzdUMG+7AUcyM83cV5tkaWH4mx0ciU9cZw" +
"IDAQAB"
-
};
+ public static boolean alwaysFalse = false;
+
static class entropySpinner extends Thread {
volatile boolean stop = false;
byte counter = 0;
entropySpinner() { start(); }
- public void run() { while (!stop) counter++; }
+ public void run() {
+ while (true) {
+ counter++;
+
+ // without this line, GCJ will over-optimize this loop into an infinite loop. Argh.
+ if (alwaysFalse) stop = true;
+
+ if (stop) return;
+ }
+ }
}
static {
if (Log.on) Log.log(TinySSL.class, e);
}
+ if (Log.on) Log.log(TinySSL.class, "generating entropy...");
randpool = new byte[10];
try { Thread.sleep(100); } catch (Exception e) { }
for(int i=0; i<spinners.length; i++) {
randpool = new byte[md5.getDigestSize()];
md5.doFinal(randpool, 0);
+ if (Log.on) Log.log(TinySSL.class, "TinySSL is initialized.");
}