5

I am trying to write a client (a middleware in fact, which is a client to an entity, but also acts as a server to others). In its client capacity it is supposed to talk to another server (VMware's VirtualCenter), and ask it to do stuff on its behalf.

To give you more context, VirtualCenter allows an application to register as an extension. Said application could register its certificate at the time of registration (setCertificate). Afterwards, the application can login to VirtualCenter using its certificate (loginExtensionByCertificate() method) , and thereby not needing to store username and passwords. However, for this to work, the client (my app) must send a certificate as part of its SSL connection, even though the server (VirtualCenter) is not asking for it particularly.

I am writing my app with Java. Created my own key manager, hooked it up to my keystore and specified the alias to use. Then initialized my ssl context to use that key manager. In the created sockets, I do see their SSLContext has my key manager in them. However, I do not see that key manager ever being called to get the certificate. For some reason, the socket does not feel it needs to send a cert.

I understand that the server may ask the client to present its cert. In this case, it does not happen. What I am wondering whether there is a way to force the created socket to present a cert regardless of whether the server asks for it.

Virtually Real
  • 1,442
  • 4
  • 16
  • 20
  • I would suggest debugging the handshake using `-Djavax.net.debug=ssl` or at least `Djavax.net.debug=ssl:handshake` to see whether you get a `CertificateRequest` and check the `certificate_authorities` list it contains. – Bruno Mar 10 '12 at 15:31

5 Answers5

4

Client certificates can only be sent if the server requests it. See TLS Spec. - Client Certificate message (RFC 4346, Section 7.4.6):

This is the first message the client can send after receiving a server hello done message. This message is only sent if the server requests a certificate.

You can't force the client to send a client certificate is the server hasn't requested one.

EDIT: Of course, you'll also need to make sure your keymanager/keystore is set up properly. You might be facing problems similar to those described in why doesn't java send the client certificate during SSL handshake?

Community
  • 1
  • 1
Bruno
  • 110,518
  • 24
  • 258
  • 357
2

However, for this to work, the client (my app) must send a certificate as part of its SSL connection, even though the server (VirtualCenter) is not asking for it particularly

For that to work, the server must ask for it. That's how the SSL protocol is defined. If you could arrange for your client to send it unsolicited, which you can't, the server would be bound to drop the connection.

The solution to this is at the server end. Are you 100% sure it isn't asking? Maybe it is asking but your client isn't sending? for example, because the certificate isn't signed by a CA the server trusts.

user207421
  • 289,834
  • 37
  • 266
  • 440
  • Are you sure about the `drop the connection`? AFAIK IIS can be set to ignore client certificates. – One-One Mar 10 '12 at 06:54
  • @desaivv RFC 4346 'This message is only sent if the server requests a certificate' seems pretty clear; maybe it isn't obliged to drop the connection, but JSSE certainly won't send a certificate unless requested. I don't know anything about IIS. – user207421 Mar 10 '12 at 07:38
  • 1
    Section 7.4 also says "*The handshake protocol messages are presented below in the order they MUST be sent; sending handshake messages in an unexpected order results in a fatal error*". I would consider sending an unrequested `ClientCertificate` message as "unexpected order" (so this would break the connection). @desaivv, what do you mean by "IIS can be set to ignore client certificates"? (Any doc/pointer?) Isn't "ignoring" here just about not using them for authentication/authorisation of the application sitting behind? – Bruno Mar 10 '12 at 15:27
  • Thanks for the hint. I will dig deepere. As for the quip of "is not asking for it particularly": I meant to say, it does not require it. I.e., it will not drop connection if it does not come. I think this is the difference between Java's SSLSocket.setWantClientAuth() and SSLSocket.setNeedClientAuth(). I think VirtualCenter has the flag set to want, but not need. Will dig deeper and report back. – Virtually Real Mar 12 '12 at 17:45
  • @VirtuallyReal That is indeed the difference. In both cases the server asks for the certificate, via the `CertificateRequest` message: in the `need` case it aborts the connection if the client doesn't send it; in the `want` case it continues if the client doesn't send it, but will throw a `PeerUnverifiedException` if the server asks for the peer certificate. `IIS` will do something similar in the same circumstance, don't ask me what. None of this allows the client to send an unsolicited certificate. – user207421 Mar 13 '12 at 01:05
0

When vCenter is installed it uses a reverse proxy on port 443 that forwards requests over HTTP to the actual service. The proxy never asks for the client certificate neither would it be able to forward it.

Try to find the actual port that the service is running on and connect to that.

root
  • 380
  • 2
  • 8
0

The VMware VC will always ask for the SSL certificate of the client, as this is how many authenticated extensions log into VC. So, you're on the right track.

I suspect you're just having problems with SSL/Java interactions, and its not specifically a VMware issue (though its always hard to tell with SSL ...).

I think you want to follow the suggestions over here: Java client certificates over HTTPS/SSL

Community
  • 1
  • 1
P.T.
  • 23,368
  • 7
  • 60
  • 92
0

Finally, solved the problem. It appears VMware takes a very different path. The question was misconstrued.

First, I traced the connection using WireShark, and at no point the server asks (need or want) a certificate. However, VirtualCenter (VC) does provide a way to register a certificate as part of a connection and then use that certificate authenticate. For this to work, they provide a way to tunnel connections.

The sequence I used is:

  • First specify the URL to connect to as https://: (note the protocol is secure, but the port is not).

  • The initial socket will be opened to the clear text port. But having https as the protocol will force calling my own SSLSocketFactory. In my socket factory, I get a call to my public Socket createSocket(Socket s, String host, int port, boolean autoClose) method, which allows me to convert my socket to SSLSocket to my taste.

My code essentially looks like:

@Override
public Socket createSocket(Socket s, String host, int port, boolean autoClose)
    throws IOException {
    s.setKeepAlive(true);
    String connectReq = "CONNECT /sdkTunnel HTTP/1.1\r\n\r\n";
    OutputStreamWriter writer = new OutputStreamWriter(socket.getOutputStream());
    writer.write(connectReq, 0, connectReq.length());
    writer.flush();
    // In this initial response, all that matters is the http code. If
    // it is 200, the rest can be ignored.
    READ_AND_VERIFY_INITIAL_RESPONSE();
    SSLSocket sock = (SSLSocket) origSslSockFactory
        .createSocket(s, s.getInetAddress().getHostName(), s.getPort(), true);
    // Depending on whether the caller actually does the handshake
    // or not, you may need to call handshake here. In my case, I
    // did not need to, since HTTPClient I am using calls the
    // handshake. Axis does not call, so you may have to in that
    // case.

    // sock.startHandshake();
    return sock;
}

In the above block, origSslSockFactory is the SSLSocketFactory that got replaced by your implementation. In my case it was an instance of sun.security.ssl.SSLSocketFactoryImpl.

After that procedure, calling ExtensionManager.setExtensionCertificate() succeeds. And subsequently, all calls to SessionManager.loginExtensionByCertificate() also succeeds.

I wanted to post this as there seem to be a lot of questions about this.

Virtually Real
  • 1,442
  • 4
  • 16
  • 20