Related
I am unable to preserve the order of my ciphersuite list. I have set the order to be in my preferred way however, when the request is made and I check which order the ciphersuites are in, it is slightly different from my original. I am wondering if possibly okhttp is reordering them via a MAP structure or something else. If they are, I would like to learn where this is happening to be able to adjust it to preserve the order.
My connection spec used:
ConnectionSpec spec = new ConnectionSpec.Builder(ConnectionSpec.MODERN_TLS)
.tlsVersions(TlsVersion.TLS_1_1, TlsVersion.TLS_1_2,TlsVersion.TLS_1_3)
.cipherSuites(
CipherSuite.TLS_AES_128_GCM_SHA256,
CipherSuite.TLS_AES_256_GCM_SHA384,
CipherSuite.TLS_CHACHA20_POLY1305_SHA256,
CipherSuite.TLS_ECDHE_ECDSA_WITH_AES_256_GCM_SHA384,
CipherSuite.TLS_ECDHE_ECDSA_WITH_AES_128_GCM_SHA256,
CipherSuite.TLS_ECDHE_ECDSA_WITH_AES_256_CBC_SHA384,
CipherSuite.TLS_ECDHE_ECDSA_WITH_AES_128_CBC_SHA256,
CipherSuite.TLS_ECDHE_ECDSA_WITH_AES_256_CBC_SHA,
CipherSuite.TLS_ECDHE_ECDSA_WITH_AES_128_CBC_SHA,
CipherSuite.TLS_ECDHE_ECDSA_WITH_CHACHA20_POLY1305_SHA256,
CipherSuite.TLS_ECDHE_RSA_WITH_AES_256_GCM_SHA384,
CipherSuite.TLS_ECDHE_RSA_WITH_AES_128_GCM_SHA256,
CipherSuite.TLS_ECDHE_RSA_WITH_AES_256_CBC_SHA384,
CipherSuite.TLS_ECDHE_RSA_WITH_AES_128_CBC_SHA256,
CipherSuite.TLS_ECDHE_RSA_WITH_AES_256_CBC_SHA,
CipherSuite.TLS_ECDHE_RSA_WITH_AES_128_CBC_SHA,
CipherSuite.TLS_ECDHE_RSA_WITH_CHACHA20_POLY1305_SHA256,
CipherSuite.TLS_RSA_WITH_AES_256_GCM_SHA384,
CipherSuite.TLS_RSA_WITH_AES_128_GCM_SHA256,
CipherSuite.TLS_RSA_WITH_AES_256_CBC_SHA256,
CipherSuite.TLS_RSA_WITH_AES_128_CBC_SHA256,
CipherSuite.TLS_RSA_WITH_AES_256_CBC_SHA,
CipherSuite.TLS_RSA_WITH_AES_128_CBC_SHA,
CipherSuite.TLS_ECDHE_ECDSA_WITH_3DES_EDE_CBC_SHA,
CipherSuite.TLS_ECDHE_RSA_WITH_3DES_EDE_CBC_SHA,
CipherSuite.TLS_RSA_WITH_3DES_EDE_CBC_SHA
)
.build();
I want to also share that I am using conscrypt which enables these cipher suites. They are all used by okhttp but not in the correct order.
This looks like a bug in OkHttp https://github.com/square/okhttp/issues/6390
/**
* Returns an array containing only elements found in this array and also in [other]. The returned
* elements are in the same order as in this.
*/
fun Array<String>.intersect(
other: Array<String>,
comparator: Comparator<in String>
)
private fun supportedSpec(sslSocket: SSLSocket, isFallback: Boolean): ConnectionSpec {
var cipherSuitesIntersection = if (cipherSuitesAsString != null) {
sslSocket.enabledCipherSuites.intersect(cipherSuitesAsString, CipherSuite.ORDER_BY_NAME)
} else {
sslSocket.enabledCipherSuites
}
https://www.rfc-editor.org/rfc/rfc5246
The cipher suite list, passed from the client to the server in the
ClientHello message, contains the combinations of cryptographic
algorithms supported by the client in order of the client's
preference (favorite choice first). Each cipher suite defines a key
exchange algorithm, a bulk encryption algorithm (including secret key
length), a MAC algorithm, and a PRF. The server will select a cipher
suite or, if no acceptable choices are presented, return a handshake
failure alert and close the connection. If the list contains cipher
suites the server does not recognize, support, or wish to use, the
server MUST ignore those cipher suites, and process the remaining
ones as usual.
Yuri already mentioned that this is a bug within OkHttp. I am not quite sure how fast they will fix it but as an alternative you can still get it working with a delegate sslsocketfactory instead of using the ConnectionSpec
An example would setup would be:
Your SSL properties
SSLParameters sslParameters = new SSLParameters();
sslParameters.setProtocols(new String[]{"TLSv1.3", "TLSv1.2", "TLSv1.1"});
sslParameters.setCipherSuites(new String[]{
"TLS_AES_128_GCM_SHA256",
"TLS_AES_256_GCM_SHA384",
"TLS_ECDHE_ECDSA_WITH_AES_256_GCM_SHA384",
"TLS_ECDHE_ECDSA_WITH_AES_128_GCM_SHA256",
"TLS_ECDHE_ECDSA_WITH_AES_256_CBC_SHA384",
"TLS_ECDHE_ECDSA_WITH_AES_128_CBC_SHA256",
"TLS_ECDHE_ECDSA_WITH_AES_256_CBC_SHA",
"TLS_ECDHE_ECDSA_WITH_AES_128_CBC_SHA",
"TLS_ECDHE_RSA_WITH_AES_256_GCM_SHA384",
"TLS_ECDHE_RSA_WITH_AES_128_GCM_SHA256",
"TLS_ECDHE_RSA_WITH_AES_256_CBC_SHA384",
"TLS_ECDHE_RSA_WITH_AES_128_CBC_SHA256",
"TLS_ECDHE_RSA_WITH_AES_256_CBC_SHA",
"TLS_ECDHE_RSA_WITH_AES_128_CBC_SHA",
"TLS_RSA_WITH_AES_256_GCM_SHA384",
"TLS_RSA_WITH_AES_128_GCM_SHA256",
"TLS_RSA_WITH_AES_256_CBC_SHA256",
"TLS_RSA_WITH_AES_128_CBC_SHA256",
"TLS_RSA_WITH_AES_256_CBC_SHA",
"TLS_RSA_WITH_AES_128_CBC_SHA",
"TLS_ECDHE_ECDSA_WITH_3DES_EDE_CBC_SHA",
"TLS_ECDHE_RSA_WITH_3DES_EDE_CBC_SHA",
"TLS_RSA_WITH_3DES_EDE_CBC_SHA"
});
Your custom SSLSocketFactory
import javax.net.ssl.SSLParameters;
import javax.net.ssl.SSLSocket;
import javax.net.ssl.SSLSocketFactory;
import java.io.IOException;
import java.io.InputStream;
import java.net.InetAddress;
import java.net.Socket;
import java.net.UnknownHostException;
public final class CompositeSSLSocketFactory extends SSLSocketFactory {
private final SSLSocketFactory sslSocketFactory;
private final SSLParameters sslParameters;
public CompositeSSLSocketFactory(SSLSocketFactory sslSocketFactory, SSLParameters sslParameters) {
this.sslSocketFactory = sslSocketFactory;
this.sslParameters = sslParameters;
}
#Override
public String[] getDefaultCipherSuites() {
return sslParameters.getCipherSuites();
}
#Override
public String[] getSupportedCipherSuites() {
return sslParameters.getCipherSuites();
}
#Override
public Socket createSocket() throws IOException {
Socket socket = sslSocketFactory.createSocket();
return withSslParameters(socket);
}
#Override
public Socket createSocket(Socket socket, InputStream inputStream, boolean autoClosable) throws IOException {
Socket newSocket = sslSocketFactory.createSocket(socket, inputStream, autoClosable);
return withSslParameters(newSocket);
}
#Override
public Socket createSocket(Socket socket, String host, int port, boolean autoClosable) throws IOException {
Socket newSocket = sslSocketFactory.createSocket(socket, host, port, autoClosable);
return withSslParameters(newSocket);
}
#Override
public Socket createSocket(String host, int port) throws IOException, UnknownHostException {
Socket socket = sslSocketFactory.createSocket(host, port);
return withSslParameters(socket);
}
#Override
public Socket createSocket(String host, int port, InetAddress localAddress, int localPort) throws IOException, UnknownHostException {
Socket socket = sslSocketFactory.createSocket(host, port, localAddress, localPort);
return withSslParameters(socket);
}
#Override
public Socket createSocket(InetAddress address, int port) throws IOException {
Socket socket = sslSocketFactory.createSocket(address, port);
return withSslParameters(socket);
}
#Override
public Socket createSocket(InetAddress address, int port, InetAddress localAddress, int localPort) throws IOException {
Socket socket = sslSocketFactory.createSocket(address, port, localAddress, localPort);
return withSslParameters(socket);
}
private Socket withSslParameters(Socket socket) {
if (socket instanceof SSLSocket) {
SSLSocket sslSocket = (SSLSocket) socket;
sslSocket.setSSLParameters(sslParameters);
}
return socket;
}
}
Your OkHttp client configuration
SSLContext sslContext = ...; //your already initialised SSLContext
X509TrustManager trustManager = ...; //your already initialised TrustManager
SSLSocketFactory baseSocketFactory = sslContext.getSocketFactory();
SSLSocketFactory customSocketFactory = new CompositeSSLSocketFactory(baseSocketFactory, sslParameters);
OkHttpClient client = new OkHttpClient.Builder()
.sslSocketFactory(customSocketFactory, trustManager)
.build();
Yes it is verbose, however it will keep the order as you defined :) I would advise to wait for the bug-fix, but if you want to fix it by your self for the time being than this would be an option.
I am trying to make a Soap request using Spring WS. The request needs to send across client certificate to the server. I have figured out the configuration that makes the correct request to the server. Code presented below
#Bean
public HttpsUrlConnectionMessageSender messageSender() throws Exception {
HttpsUrlConnectionMessageSender httpsUrlConnectionMessageSender = new HttpsUrlConnectionMessageSender();
httpsUrlConnectionMessageSender.setTrustManagers(<TrustManager>);
httpsUrlConnectionMessageSender.setKeyManagers(<KeyManager>);
httpsUrlConnectionMessageSender.setHostnameVerifier(new HostnameVerifier() {
public boolean verify(String hostname, SSLSession session) {
return true;
}
});
return httpsUrlConnectionMessageSender;
}
#Bean
#Scope
public WebServiceTemplate webServiceTemplate() throws Exception {
WebServiceTemplate webServiceTemplate = new WebServiceTemplate(<messageFactory>);
webServiceTemplate.setMarshaller(<jaxb2Marshaller>);
webServiceTemplate.setUnmarshaller(<jaxb2Marshaller>);
webServiceTemplate.setDefaultUri(<URL>);
webServiceTemplate.setMessageSender(());
return webServiceTemplate;
}
Now I was wondering how does HttpsUrlConnectionMessageSender manage connections? Does it create new connection for every request or it has internal connection pooling. How do we use external connection pooling here?
The client has default connection pool of 20 and default connection pool per request 2.
But you can overwrite it like this:
public HttpComponentsMessageSender httpComponentsMessageSender(Integer readTimeout, Integer connectionTimeOut,
Integer throttlingLimit) {
HttpComponentsMessageSender sender = new HttpComponentsMessageSender();
sender.setReadTimeout(readTimeout);
sender.setConnectionTimeout(connectionTimeOut);
PoolingHttpClientConnectionManager connManager = new PoolingHttpClientConnectionManager();
CloseableHttpClient poolingHttpSoapClient = HttpClients.custom().setConnectionManager(connManager)
.addInterceptorFirst(new RemoveSoapHeadersInterceptor()).setMaxConnPerRoute(throttlingLimit)
.setMaxConnTotal(throttlingLimit + 1).build();
sender.setHttpClient(poolingHttpSoapClient);
return sender;
}
I wish to force Apache Commons HTTP-Client (version 3.1) to use TLS 1.2 as the only protocol for HTTPS.
This is due to the server supposedly being upgraded to TLS 1.2 and not accepting any older protocol anymore (causing 'Connection Reset' to be returned).
For further context, probably irrelevant, the HTTP-Client is used along with Axis2 to make a SOAP; some of the code used for setting up the HttpClient is below:
MultiThreadedHttpConnectionManager connMgr = new MultiThreadedHttpConnectionManager();
this.httpClient = new HttpClient(connMgr);
// initialize HttpClient parameters
HttpClientParams hcParams = this.httpClient.getParams();
// Maximum time to wait to receive connection from pool
hcParams.setConnectionManagerTimeout(this.maxWait);
hcParams.setSoTimeout(this.timeout);
hcParams.setParameter(HttpMethodParams.RETRY_HANDLER, new DefaultHttpMethodRetryHandler(this.retryCount, false));
// Initialize global Connection manager parameters
HttpConnectionManagerParams cmParams = connMgr.getParams();
cmParams.setDefaultMaxConnectionsPerHost(this.maxActive);
cmParams.setStaleCheckingEnabled(this.checkStaleConnections);
cmParams.setConnectionTimeout(this.timeout);
Thanks a lot for the help!
Too bad nobody answered; I was able to do it, first you write a CustomHttpSocketFactory, then you do:
String scheme = "https";
Protocol baseHttps = Protocol.getProtocol(scheme);
int defaultPort = baseHttps.getDefaultPort();
ProtocolSocketFactory baseFactory = baseHttps.getSocketFactory();
ProtocolSocketFactory customFactory = new CustomHttpsSocketFactory(baseFactory);
Protocol customHttps = new Protocol(scheme, customFactory, defaultPort);
Protocol.registerProtocol(scheme, customHttps);
A sample custom socket factory code is found here, but instead I did:
public class CustomHttpsSocketFactory implements SecureProtocolSocketFactory
{
private final SecureProtocolSocketFactory base;
public CustomHttpsSocketFactory(ProtocolSocketFactory base)
{
if(base == null || !(base instanceof SecureProtocolSocketFactory)) throw new IllegalArgumentException();
this.base = (SecureProtocolSocketFactory) base;
}
private Socket acceptOnlyTLS12(Socket socket)
{
if(!(socket instanceof SSLSocket)) return socket;
SSLSocket sslSocket = (SSLSocket) socket;
sslSocket.setEnabledProtocols(new String[]{"TLSv1.2" });
return sslSocket;
}
#Override
public Socket createSocket(String host, int port) throws IOException
{
return acceptOnlyTLS12(base.createSocket(host, port));
}
#Override
public Socket createSocket(String host, int port, InetAddress localAddress, int localPort) throws IOException
{
return acceptOnlyTLS12(base.createSocket(host, port, localAddress, localPort));
}
#Override
public Socket createSocket(String host, int port, InetAddress localAddress, int localPort, HttpConnectionParams params) throws IOException
{
return acceptOnlyTLS12(base.createSocket(host, port, localAddress, localPort, params));
}
#Override
public Socket createSocket(Socket socket, String host, int port, boolean autoClose) throws IOException
{
return acceptOnlyTLS12(base.createSocket(socket, host, port, autoClose));
}
}
You need a Socket reference in your code. Then you can set enabled protocols on it like this:
if (socket != null && (socket instanceof SSLSocket)) {
((SSLSocket)socket).setEnabledProtocols(new String[] {"TLSv1.2"});
}
It depends on how you are writing your clients and what JRE versions you are using:
If you are using JRE8 (unless you have replaced the default SunJSSE that comes with JRE8), there is a system property "jdk.tls.client.protocols". By default, whatever you mention here will be used for all client communication.
If you are using HttpsURLConnection object for client connection, u can use the system property "https.protocols". This will work for all JRE versions, not just JRE8.
If you don't specify anything, for TLS clients, in JRE8, TLSv1, v1.1 and v1.2 are enabled, so it will work with a server what supports any one of this versions. However in JRE7 by default TLSv1 alone is enabled.
In your code u can always override the default or what u pass through the system properties. What u set in the code will take higher precedence. To override in the code...
1) If you are using raw socket and SSLEngine, u can set the protocol and ciphers in the SSLEngine (sslEngine.setEnabledProtocols(..)
2) If you are using SSLSocket, u can set the protocol and ciphers in the SSLSocket (sslSocket.setEnabledProtocols(..)
You can also get an SSLContext with the required protocol enabled and use that for whatever SSL components you use. SSLContext.getInstance("TLSvx.x"). Note that by default it will return a context with all the protocols lesser that TLSvx.x enabled. If u have configured "jdk.tls.client.protocols", this will return a context with those protocols enabled.
It would not be a good idea to hard coded the protocols in the code. Quite often, we will encounter certain customers want specific version either because they use old servers or some serious vulnerabilities are encountered in some TLS versions. Either set it through the system properties or even if you are explicitly setting in sslsocket or sslengine, read that from some conf file.
Also refer:
https://docs.oracle.com/javase/8/docs/technotes/guides/security/jsse/JSSERefGuide.html
http://docs.oracle.com/javase/8/docs/technotes/guides/security/SunProviders.html
I have been trying to use a custom SocketFactory in the httpclient library from the Apache HTTPComponents project. So far without luck. I was expecting that I could just set a socket factory for a HttpClient instance, but it is obviously not so easy.
The documentation for HttpComponents at http://hc.apache.org/httpcomponents-client-ga/tutorial/html/connmgmt.html does mention socket factories, but does not say how to use them.
Does anybody know how this is done?
oleg's answer is of course correct, I just wanted to put the information directly here, in case the link goes bad. In the code that creates a HttpClient, I use this code to let it use my socket factory:
CustomSocketFactory socketFactory = new CustomSocketFactory();
Scheme scheme = new Scheme("http", 80, socketFactory);
httpclient.getConnectionManager().getSchemeRegistry().register(scheme);
CustomSocketFactory is my own socket factory, and I want to use it for normal HTTP traffic, that's why I use "http" and 80 as parameters.
My CustomSchemeSocketFactory looks similar to this:
public class CustomSchemeSocketFactory implements SchemeSocketFactory {
#Override
public Socket connectSocket( Socket socket, InetSocketAddress remoteAddress, InetSocketAddress localAddress, HttpParams params ) throws IOException, UnknownHostException, ConnectTimeoutException {
if (localAddress != null) {
socket.setReuseAddress(HttpConnectionParams.getSoReuseaddr(params));
socket.bind(localAddress);
}
int connTimeout = HttpConnectionParams.getConnectionTimeout(params);
int soTimeout = HttpConnectionParams.getSoTimeout(params);
try {
socket.setSoTimeout(soTimeout);
socket.connect(remoteAddress, connTimeout );
} catch (SocketTimeoutException ex) {
throw new ConnectTimeoutException("Connect to " + remoteAddress + " timed out");
}
return socket;
}
#Override
public Socket createSocket( HttpParams params ) throws IOException {
// create my own socket and return it
}
#Override
public boolean isSecure( Socket socket ) throws IllegalArgumentException {
return false;
}
}
We use a custom socket factory to allow HttpClient connections to connect to HTTPS URLs with untrusted certificates.
Here is how we did it:
We adapted implementations of both the 'EasySSLProtocolSocketFactory' and 'EasyX509TrustManager' classes from the examples source directory referenced by Oleg.
In our HttpClient startup code, we do the following to enable the new socket factory:
HttpClient httpClient = new HttpClient();
Protocol easyhttps = new Protocol("https", new EasySSLProtocolSocketFactory(), 443);
Protocol.registerProtocol("https", easyhttps);
So that any time we request an https:// URL, this socket factory is used.
I'm developing an Android app which uses SSLSocket to connect to a server. This is the code I'm using:
// Connect
if (socket == null || socket.isClosed() || !socket.isConnected()) {
if (socket != null && !socket.isClosed())
socket.close();
Log.i(getClass().toString(), "Connecting...");
if (sslContext == null) {
sslContext = SSLContext.getInstance("TLS");
sslContext.init(null, trustAllCerts, new SecureRandom());
}
SSLSocketFactory socketFactory = sslContext.getSocketFactory();
socket = (SSLSocket)socketFactory.createSocket(host, port);
socket.setSoTimeout(20000);
socket.setUseClientMode(true);
connected = true;
Log.i(getClass().toString(), "Connected.");
}
// Secure
if (connected) {
Log.i(getClass().toString(), "Securing...");
SSLSession session = socket.getSession();
secured = session.isValid();
if (secured) {
Log.i(getClass().toString(), "Secured.");
}
else
Log.i(getClass().toString(), "Securing failed.");
}
The problem is that it takes about 5 seconds or event more to do the TLS handshake in the line below:
SSLSession session = socket.getSession();
I have made a similar iPhone app, the handshake takes just 1 second there, so I think the problem is not in the server I'm connecting to, it's maybe in the code above. The connection itself is fast enough, just the TLS handshake is slow.
Does anybody know if it's normal in Android, or if it is not, how to make it faster?
Thank you.
EDITED on 21.01.11:
I have found out, that the handshake is fast when I connect to another server, for example paypal.com:443.
But I had been connecting to another server before - a .NET service written by me. As I had said before, I did not think the problem was in that server because if I connect to it with my iPhone App the handshake is fast. Now I don't know why it is fast on iPhone and slow on Android. After the connection is established, the only thing I do in the .NET server is:
Console.WriteLine("New client connected.");
this.sslStream = new SslStream(tcpClient.GetStream(), true);
this.sslStream.ReadTimeout = 15000;
this.sslStream.WriteTimeout = 15000;
Console.WriteLine("Beginning TLS handshake...");
this.sslStream.AuthenticateAsServer(connection.ServerCertificate, false, SslProtocols.Tls, false);
Console.WriteLine("TLS handshake completed.");
There was a bug on earlier versions of the Android SDK. Apparently, it's doing an unnecessary DNS reverse lookup. You need to prevent this from happening. Here's a workaround that worked for me. It used to take 15 seconds, now it takes 0-1 seconds. Hope it helps.
Here's the link to the Google issue.
boolean connected = false;
if (socket == null || socket.isClosed() || !socket.isConnected()) {
if (socket != null && !socket.isClosed()) {
socket.close();
}
Log.i(getClass().toString(), "Connecting...");
messages.getText().append("Connecting...");
final KeyStore keyStore = KeyStore.getInstance("BKS");
keyStore.load(getResources().openRawResource(R.raw.serverkey), null);
final KeyManagerFactory keyManager = KeyManagerFactory.getInstance(KeyManagerFactory.getDefaultAlgorithm());
keyManager.init(keyStore, null);
//keyManager.init(null, null);
final TrustManagerFactory trustFactory = TrustManagerFactory.getInstance(TrustManagerFactory.getDefaultAlgorithm());
trustFactory.init(keyStore);
sslContext = SSLContext.getInstance("TLS");
sslContext.init(keyManager.getKeyManagers(), trustFactory.getTrustManagers(), rnd);
final SSLSocketFactory delegate = sslContext.getSocketFactory();
SocketFactory factory = new SSLSocketFactory() {
#Override
public Socket createSocket(String host, int port)
throws IOException, UnknownHostException {
InetAddress addr = InetAddress.getByName(host);
injectHostname(addr, host);
return delegate.createSocket(addr, port);
}
#Override
public Socket createSocket(InetAddress host, int port)
throws IOException {
return delegate.createSocket(host, port);
}
#Override
public Socket createSocket(String host, int port, InetAddress localHost, int localPort)
throws IOException, UnknownHostException {
return delegate.createSocket(host, port, localHost, localPort);
}
#Override
public Socket createSocket(InetAddress address, int port, InetAddress localAddress, int localPort)
throws IOException {
return delegate.createSocket(address, port, localAddress, localPort);
}
private void injectHostname(InetAddress address, String host) {
try {
Field field = InetAddress.class.getDeclaredField("hostName");
field.setAccessible(true);
field.set(address, host);
} catch (Exception ignored) {
}
}
#Override
public Socket createSocket(Socket s, String host, int port, boolean autoClose) throws IOException {
injectHostname(s.getInetAddress(), host);
return delegate.createSocket(s, host, port, autoClose);
}
#Override
public String[] getDefaultCipherSuites() {
return delegate.getDefaultCipherSuites();
}
#Override
public String[] getSupportedCipherSuites() {
return delegate.getSupportedCipherSuites();
}
};
socket = (SSLSocket)factory.createSocket("192.168.197.133", 9999);
socket.setSoTimeout(20000);
socket.setUseClientMode(true);
connected = true;
Log.i(getClass().toString(), "Connected.");
messages.getText().append("Connected.");
}
// Secure
if (connected) {
Log.i(getClass().toString(), "Securing...");
messages.getText().append("Securing...");
SSLSession session = socket.getSession();
boolean secured = session.isValid();
if (secured) {
Log.i(getClass().toString(), "Secured.");
messages.getText().append("Secured.");
}
}
You are using a new SecureRandom per connection, instead of using a single static pre-initialized SecureRandom. Everytime you create a new SecureRandom(), you need to gather entropy for seeding (a slow process).
SecureRandom does not self-seed until it is first used, which is why the delay does not occur until the call to getSession()
I have done something similar to this and it is slower than an unsecured connection. Granted my case was https vs http and it is a little different the SSL/TLS factor will add slowness to the deal.
I have two identical apps that comunicate with the same protocol to the same server, one in android and one in iPhone, both using https. When I tested them both in http I would see more or less the same response time, in https iOS was slightly faster in my case, but not terribly.
The problem is most likely in the way the device validates server certificates. Validation can involve contacting third-party for CRLs and OCSP responses. If this happens, it takes time. iPhone probably just doesn't do this (at least by default) which is a security hole BTW.