/** SSLConn.java Copyright (C) 1999, Claymore Systems, Inc. All Rights Reserved. ekr@rtfm.com Thu May 6 22:26:01 1999 This package is a SSLv3/TLS implementation written by Eric Rescorla <ekr@rtfm.com> and licensed by Claymore Systems, Inc. Redistribution and use in source and binary forms, with or without modification, are permitted provided that the following conditions are met: 1. Redistributions of source code must retain the above copyright notice, this list of conditions and the following disclaimer. 2. Redistributions in binary form must reproduce the above copyright notice, this list of conditions and the following disclaimer in the documentation and/or other materials provided with the distribution. 3. All advertising materials mentioning features or use of this software must display the following acknowledgement: This product includes software developed by Claymore Systems, Inc. 4. Neither the name of Claymore Systems, Inc. nor the name of Eric Rescorla may be used to endorse or promote products derived from this software without specific prior written permission. THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. $Id: SSLConn.java,v 1.4 2004/03/31 06:47:33 gawor Exp $ */ package COM.claymoresystems.ptls; import COM.claymoresystems.sslg.*; import java.util.Vector; import java.net.*; import java.io.*; public class SSLConn { public static final int SSL_CLIENT = 1; public static final int SSL_SERVER = 2; // static int debugVal=DEBUG_CRYPTO; static int debugVal=0; /* Connection Housekeeping*/ int ssl_version=0; int max_ssl_version=0; SSLContext ctx; SSLSocket s=null; SSLPolicyInt policy; // The policy we're following PushbackInputStream sock_in; // The stream to do our raw reading on InputStream sock_in_hp; // The stream for handshake messages InputStream sock_in_data; // The stream for app data; OutputStream _sock_out; // The stream to do our raw writing on BufferedOutputStream sock_out; OutputStream sock_out_external; // The stream to expose boolean sentClose=false; boolean recvdClose=false; Vector peerCertificateChain=null; String sessionLookupKey=null; int how; byte[] session_id; // Our cipher states SSLCipherState write_cipher_state=null; SSLCipherState read_cipher_state=null; SSLCipherState next_write_cipher_state; SSLCipherState next_read_cipher_state; long write_sequence_num; long read_sequence_num; boolean secureMode=false; // Have we seen at least one handshake? boolean invalid=false; // Set once we've sent or received a fatal alert SSLHandshake hs; // The handshake object SSLRecordReader reader; // The record reader public SSLConn(SSLSocket sock,InputStream in,OutputStream out,SSLContext c, int how) throws java.io.IOException { this.s=sock; this.how=how; ctx=c; policy=c.getPolicy(); sock_in=new PushbackInputStream(in); _sock_out=out; sock_out=new BufferedOutputStream(_sock_out); //Set up the streams for reading different record types reader=new SSLRecordReader(this); } void renegotiate(SSLPolicyInt p) throws IOException { policy=p; handshake(); } void handshake() throws IOException { init(); finishHandshake(); } public void init() { // You can't renegotiate to a different version so ignore // new policy information if we're already encrypting if(read_cipher_state==null){ max_ssl_version=policy.negotiateTLSP()?SSLHandshake.TLS_V1_VERSION:SSLHandshake.SSL_V3_VERSION; ssl_version=policy.negotiateTLSP()?SSLHandshake.TLS_V1_VERSION:SSLHandshake.SSL_V3_VERSION; } if(how == SSL_CLIENT){ hs=new SSLHandshakeClient(this); } else{ hs=new SSLHandshakeServer(this); } } public void finishHandshake() throws IOException { try { hs.handshake(); if(sock_in_hp.available()!=0) alert(SSLAlertX.TLS_ALERT_UNEXPECTED_MESSAGE); secureMode=true; } catch (IOException e){ if((SSLDebug.debugVal & SSLDebug.DEBUG_HANDSHAKE)>0){ e.printStackTrace(); } if(!(e instanceof SSLAlertException)) throw new SSLHandshakeFailedException(e.toString()); else throw e; } } public int getCipherSuite() throws IOException { if(!hs.finishedP()){ throw new SSLException("Handshake not finished"); } return write_cipher_state.cipher_suite.getValue(); } SSLPolicyInt getPolicy(){ return policy; } byte[] getSessionID() throws IOException { if(!hs.finishedP()){ throw new SSLException("Handshake not finished"); } return session_id; } int getVersion() throws IOException { if(!hs.finishedP()){ throw new SSLException("Handshake not finished"); } return ssl_version; } public SSLRecordReader getRecordReader() { return reader; } public SSLCipherState getReadCipherState() { return read_cipher_state; } public SSLCipherState getWriteCipherState() { return write_cipher_state; } public long getWriteSequence() { return write_sequence_num; } public long getReadSequence() { return read_sequence_num; } public void incrementReadSequence() { read_sequence_num++; } public void incrementWriteSequence() { write_sequence_num++; } public SSLHandshake getHandshake() { return hs; } public Vector getCertificateChain() throws IOException { if(!hs.finishedP()){ throw new SSLException("Handshake not finished"); } return peerCertificateChain; } void alert(int a) throws java.io.IOException { sendAlertNoException(a,true); throw new SSLThrewAlertException(new SSLAlertX(ssl_version,a, true)); } void alert(int a, String msg) throws java.io.IOException { sendAlertNoException(a,true); throw new SSLThrewAlertException(new SSLAlertX(ssl_version,a, true), msg); } void sendAlertNoException(int a,boolean fatal) throws java.io.IOException { SSLAlertX alertx=new SSLAlertX(ssl_version,a,fatal); if(fatal){ SSLDebug.debug(SSLDebug.DEBUG_STATE, "Throwing a fatal alert, lookup key "+ sessionLookupKey); makeUnresumable(); invalid=true; } SSLAlert alert=new SSLAlert(alertx); ByteArrayOutputStream bos=new ByteArrayOutputStream(); alert.encode(this,bos); SSLRecord r=new SSLRecord(this,SSLRecord.SSL_CT_ALERT, bos.toByteArray()); r.send(this); sock_out.flush(); } boolean processIncomingHandshakeRecord(byte[] data) throws IOException { byte[] helloRequest={0,0,0,0}; if(hs.finishedP()){ // If we're finished then only accept messages // that restart the handshake switch(data[0]){ case SSLHandshake.SSL_HT_HELLO_REQUEST: if(how!=SSL_CLIENT) alert(SSLAlertX.TLS_ALERT_UNEXPECTED_MESSAGE); if(!cryptix.util.core.ArrayUtil.areEqual(data,helloRequest)) alert(SSLAlertX.TLS_ALERT_ILLEGAL_PARAMETER); // Throw the exception from here so that the // data isn't written to the pipeline throw new SSLReHandshakeException(); // break; (good form but Java hates it) case SSLHandshake.SSL_HT_CLIENT_HELLO: if(how!=SSL_SERVER) alert(SSLAlertX.TLS_ALERT_UNEXPECTED_MESSAGE); // return true so that the caller will throw a // ReHandshakeException return true; // break; (good form but Java hates it) default: alert(SSLAlertX.TLS_ALERT_UNEXPECTED_MESSAGE); } } else { // Check for HelloRequest but otherwise ignore the // content type if(data[0]==SSLHandshake.SSL_HT_HELLO_REQUEST) alert(SSLAlertX.TLS_ALERT_UNEXPECTED_MESSAGE); } return false; } static boolean isDebugEnabled(int type) { return ((debugVal & type) > 0); } static void debug(int type,String val){ if((debugVal & type) > 0){ System.out.println(val); } } static void debug(int type,String label,byte[] hd){ if((debugVal & type) > 0){ COM.claymoresystems.util.Util.xdump(label,hd); } } public InputStream getInStream(){ if(!hs.finishedP()){ return null; } if(read_cipher_state==null) return null; return sock_in_data; } public OutputStream getOutStream(){ if(!hs.finishedP()){ return null; } if(write_cipher_state==null) return null; return sock_out_external; } void makeUnresumable(){ if(sessionLookupKey!=null){ SSLDebug.debug(SSLDebug.DEBUG_STATE,"Making session "+ sessionLookupKey + "Unresumable"); ctx.destroySession(sessionLookupKey); } } void sendClose() throws java.io.IOException { if(!sentClose){ sendAlertNoException(SSLAlertX.TLS_ALERT_CLOSE_NOTIFY,false); sentClose=true; } } void recvClose(boolean enforceFinished) throws java.io.IOException { InputStream in=getInStream(); byte[] buf=new byte[1024]; // Flush the in buffer until we hit a closed or a // FIN while(in.read(buf)>=0){ if(enforceFinished){ throw new SSLException("Excess data in pipe when closed"); } } } void close() throws java.io.IOException { sendClose(); if(policy.waitOnCloseP()){ recvClose(false); } if(s!=null) s.hardClose(); } public static void setDebug(int flag){ debugVal=flag; } }