FileDocCategorySizeDatePackage
Protocol.javaAPI DocphoneME MR2 API (J2ME)18061Wed May 02 18:00:14 BST 2007com.sun.midp.io.j2me.https

Protocol

public class Protocol extends com.sun.midp.io.j2me.http.Protocol implements HttpsConnection
This class implements the necessary functionality for an HTTPS connection. With support for HTTPS tunneling.
https diagram

Handshake error codes at the beginning of IOException messages:

(1) certificate is expired

(2) certificate is not yet valid

(3) certificate failed signature verification

(4) certificate was signed using an unsupported algorithm

(5) certificate was issued by an unrecognized certificate authority

(6) certificate does not contain the correct site name

(7) certificate chain exceeds the length allowed

(8) certificate does not contain a signature

(9) version 3 certificate has unrecognized critical extensions

(10) version 3 certificate has an inappropriate keyUsage or extendedKeyUsage extension

(11) certificate in the a chain was not issued by the next authority in the chain

(12) trusted certificate authority's public key is expired

Fields Summary
private static final String
COMMON_NAME_LABEL
Common name label.
private static final int
COMMON_NAME_LABEL_LENGTH
Common name label length.
private static SecurityToken
classSecurityToken
This class has a different security domain than the MIDlet suite
private boolean
permissionChecked
The methods other than openPrim need to know that the permission occurred.
private boolean
ownerTrusted
True if the owner of this connection is trusted.
private com.sun.midp.util.Properties
proxyHeaders
collection of "Proxy-" headers as name/value pairs
private SSLStreamConnection
sslConnection
Underlying SSL connection.
Constructors Summary
public Protocol()
Create a new instance of this class. Override the some of the values in our super class.


                          
      
        protocol = "https";
        default_port = 443; // 443 is the default port for HTTPS
    
Methods Summary
private voidcheckForPermission(java.lang.String name)
Check for the required permission.

param
name name of resource to insert into the permission question
exception
IOInterruptedException if another thread interrupts the calling thread while this method is waiting to preempt the display.

        MIDletStateHandler midletStateHandler;
        MIDletSuite midletSuite;

        midletStateHandler = MIDletStateHandler.getMidletStateHandler();
        midletSuite = midletStateHandler.getMIDletSuite();

        if (midletSuite == null) {
            throw new IllegalStateException("This class can't be used " +
                "before a suite is started.");
        }

        name = protocol + ":" + name;

        try {
            midletSuite.checkForPermission(Permissions.HTTPS, name);
            ownerTrusted = midletSuite.isTrusted();
            permissionChecked = true;
        } catch (InterruptedException ie) {
            throw new InterruptedIOException(
                "Interrupted while trying to ask the user permission");
        }
    
private static booleancheckSiteName(java.lang.String siteName, java.lang.String certName)
Check to see if the site name given by the user matches the site name of subject in the certificate. The method supports the wild card character for the machine name if a domain name is included after it.

param
siteName site name the user provided
param
certName site name of the subject from a certificate
return
true if the common name checks out, else false

        int startOfDomain;
        int domainLength;

        if (certName == null) {
            return false;
        }

        // try the easy way first, ignoring case
        if ((siteName.length() == certName.length()) &&
            siteName.regionMatches(true, 0, certName, 0,
                                   certName.length())) {
            return true;
        }

        if (!certName.startsWith("*.")) {
            // not a wild card, done
            return false;
        }

        startOfDomain = siteName.indexOf('.");
        if (startOfDomain == -1) {
            // no domain name
            return false;
        }

        // skip past the '.'
        startOfDomain++;

        domainLength = siteName.length() - startOfDomain;
        if ((certName.length() - 2) != domainLength) {
            return false;
        }

        // compare the just the domain names, ignoring case
        if (siteName.regionMatches(true, startOfDomain, certName, 2,
                                   domainLength)) {
            return true;
        }

        return false;
    
protected StreamConnectionconnect()
Connect to the underlying secure socket transport. Perform the SSL handshake and then proceeded to the underlying HTTP protocol connect semantics.

return
SSL/TCP stream connection
exception
IOException is thrown if the connection cannot be opened

        StreamConnection sc;
        String httpsTunnel;
        com.sun.midp.io.j2me.socket.Protocol tcpConnection;
        OutputStream tcpOutputStream;
        InputStream tcpInputStream;
        X509Certificate serverCert;

        if (!permissionChecked) {
            throw new SecurityException();
        }

        sc = connectionPool.get(classSecurityToken, protocol,
                                url.host, url.port);

        if (sc != null) {
            return sc;
        }

        // Open socket connection
        tcpConnection =
            new com.sun.midp.io.j2me.socket.Protocol();

        // check to see if a protocol is specified for the tunnel
        httpsTunnel = Configuration.getProperty("com.sun.midp.io.http.proxy");
        if (httpsTunnel != null) {
            // Make the connection to the ssl tunnel
            tcpConnection.openPrim(classSecurityToken, "//" + httpsTunnel);

            // Do not delay request since this delays the response.
            tcpConnection.setSocketOption(SocketConnection.DELAY, 0);

            tcpOutputStream = tcpConnection.openOutputStream();
            tcpInputStream = tcpConnection.openInputStream();
            
            // Do the handshake with the ssl tunnel
            try {
                doTunnelHandshake(tcpOutputStream, tcpInputStream);
            } catch (IOException ioe) {
                String temp = ioe.getMessage();

                tcpConnection.close();
                tcpOutputStream.close();
                tcpInputStream.close();

                if (temp.indexOf(" 500 ") > -1) {
                    throw new ConnectionNotFoundException(temp);
                }

                throw ioe;
            }    
        } else {
            tcpConnection.openPrim(classSecurityToken, "//" + hostAndPort);

            // Do not delay request since this delays the response.
            tcpConnection.setSocketOption(SocketConnection.DELAY, 0);

            tcpOutputStream = tcpConnection.openOutputStream();
            tcpInputStream = tcpConnection.openInputStream();
        }

        tcpConnection.close();

        try {
            // Get the SSLStreamConnection
            sslConnection = new SSLStreamConnection(url.host, url.port,
                                tcpInputStream, tcpOutputStream,
                                WebPublicKeyStore.getTrustedKeyStore());
        } catch (Exception e) {
            try {
                tcpInputStream.close();
            } catch (Throwable t) {
                // Ignore, we are processing an exception
            }

            try {
                tcpOutputStream.close();
            } catch (Throwable t) {
                // Ignore, we are processing an exception
            }

            if (e instanceof IOException) {
                throw (IOException)e;
            } else {
                throw (RuntimeException)e;
            }
        }

        try {
            serverCert = sslConnection.getServerCertificate();

            /*
             * if the subject alternate name is a DNS name,
             * then use that instead of the common name for a
             * site name match
             */
            if (serverCert.getSubjectAltNameType() ==
                X509Certificate.TYPE_DNS_NAME) {
                if (!checkSiteName(url.host,
                        (String)serverCert.getSubjectAltName())) {
                    throw new CertificateException(
                        "Subject alternative name did not match site name",
                        serverCert, CertificateException.SITENAME_MISMATCH);
                }
            } else {
                String cname = getCommonName(serverCert.getSubject());
                if (cname == null) {
                    throw new CertificateException(
                        "Common name missing from subject name",
                        serverCert, CertificateException.SITENAME_MISMATCH);
                }
                
                if (!checkSiteName(url.host, cname)) {
                    throw new CertificateException(serverCert,
                        CertificateException.SITENAME_MISMATCH);
                }
            }

            return sslConnection;
        } catch (Exception e) {
            try {
                sslConnection.close();
            } catch (Throwable t) {
                // Ignore, we are processing an exception
            }

            if (e instanceof IOException) {
                throw (IOException)e;
            } else {
                throw (RuntimeException)e;
            }
        }
    
protected voiddisconnect(StreamConnection connection, java.io.InputStream inputStream, java.io.OutputStream outputStream)
disconnect the current connection.

param
connection connection return from {@link #connect()}
param
inputStream input stream opened from connection
param
outputStream output stream opened from connection
exception
IOException if an I/O error occurs while the connection is terminated.

        try {
            try {
                inputStream.close();
            } finally {
                try {
                    outputStream.close();
                } finally {
                    connection.close();
                }
            }
        } catch (IOException e) {
	    if (Logging.REPORT_LEVEL <= Logging.WARNING) {
	        Logging.report(Logging.WARNING, LogChannels.LC_PROTOCOL,
	    	          "Exception while closing  streams|connection");
	    }

        } catch (NullPointerException e) {

        }
    
private static java.lang.StringgetCommonName(java.lang.String name)
Parse the common name out of a distinguished name.

param
name distinguished name
return
common name attribute without the label


                             
         
        int start;
        int end;

        if (name == null) {
            return null;
        }

        /* The common name starts with "CN=" label */
        start = name.indexOf(COMMON_NAME_LABEL);
        if (start < 0) {
            return null;
        }

        start += COMMON_NAME_LABEL_LENGTH;
        end = name.indexOf(';", start);
        if (end < 0) {
            end = name.length();
        }

        return name.substring(start, end);
    
public java.lang.StringgetRequestProperty(java.lang.String key)
Get the request header value for the named property.

param
key property name of specific HTTP 1.1 header field
return
value of the named property, if found, null otherwise.

        /* https handles the proxy fields in a different way */
        if (key.toLowerCase().startsWith("proxy-")) {
            return proxyHeaders.getPropertyIgnoreCase(key);
        }

        return super.getRequestProperty(key);
    
public SecurityInfogetSecurityInfo()
Return the security information associated with this connection. If the connection is still in Setup state then the connection is initiated to establish the secure connection to the server. The method returns when the connection is established and the Certificate supplied by the server has been validated. The SecurityInfo is only returned if the connection has been successfully made to the server.

return
the security information associated with this open connection.
exception
CertificateException if the Certificate supplied by the server cannot be validated. The CertificateException will contain the information about the error and indicate the certificate in the validation chain with the error.
exception
IOException if an arbitrary connection failure occurs

        ensureOpen();

        sendRequest();

        if (sslConnection == null) {
            /*
             * This is a persistent connection so the connect method did 
             * not get called, so the stream connection of HTTP class
             * will be a SSL connection. Get the info from that.
             */
            StreamConnection sc =
                ((StreamConnectionElement)getStreamConnection()).
                    getBaseConnection();

            return ((SSLStreamConnection)sc).getSecurityInfo();
        }

        return sslConnection.getSecurityInfo();
    
public ConnectionopenPrim(java.lang.String name, int mode, boolean timeouts)
Sets up the state of the connection, but does not actually connect to the server until there's something to do.

Warning: A subclass that implements this method, not call this method and should also implement the disconnect method.

param
name The URL for the connection, without the without the protocol part.
param
mode The access mode, ignored
param
timeouts A flag to indicate that the called wants timeout exceptions, ignored
return
reference to this connection
exception
IllegalArgumentException If a parameter is invalid.
exception
ConnectionNotFoundException If the connection cannot be found.
exception
IOException If some other kind of I/O error occurs.


        checkForPermission(name);

        initStreamConnection(mode);

        url = new HttpUrl(protocol, name);

        if (url.port == -1) {
            url.port = default_port;
        }

        if (url.host == null) {
            throw new IllegalArgumentException("missing host in URL");
        }

        hostAndPort = url.host + ":" + url.port;

        return this;
    
protected voidsetRequestField(java.lang.String key, java.lang.String value)
Add the named field to the list of request fields.

param
key key for the request header field.
param
value the value for the request header field.

        /* https handles the proxy fields in a different way */
        if (key.toLowerCase().startsWith("proxy-")) {
            proxyHeaders.setPropertyIgnoreCase(key, value);
            return;
        }

        super.setRequestField(key, value);