0

I need desperate help to figure out why my application is not creating a webservice.

Here's my webservice Java class:

  @WebService
  @Component
  public class LoginWs extends AbstractWs
{
private static final Logger logger=MiscUtils.getLogger();

@Autowired
private PersonDao personDao = null;

/**
 * Returns PersonTransfer on valid login
 * @throws NotAuthorisedException if password is incorrect
 */
public PersonTransfer login(String userNameOrEmailAddress, String password) throws NotAuthorisedException
{
    Person person=personDao.findByUserNameOrEmailAddress(userNameOrEmailAddress, true);

    if (person != null && person.checkPassword(password))
    {
        PersonTransfer personTransfer = PersonTransfer.getTransfer(person);

        personDao.setLastLogin(person.getId(), new GregorianCalendar());

        EventLogDao.logEvent(ActionType.READ_DATA.name(), "LoginWs.login()", "personId=" + person.getId());         

        return(personTransfer);
    }

    logger.debug("Login failed : u/p="+userNameOrEmailAddress+"/"+password);

    throw(new NotAuthorisedException("Invalid Username/Password"));
}
}

The code that is calling this service is:

  public static LoginWs getLoginWs()
{

    LoginWsService service = new LoginWsService(buildURL("LoginService"));

    LoginWs port = service.getLoginWsPort();

    CxfClientUtils.configureClientConnection(port);

    return(port);
}

The exception is thrown at :

   LoginWsService service = new LoginWsService(buildURL("LoginService"));

Here is the full exception:

    Error
    javax.xml.ws.WebServiceException:  org.apache.cxf.service.factory.ServiceConstructionException: Failed to create service.
at org.apache.cxf.jaxws.ServiceImpl.<init>(ServiceImpl.java:149)
at org.apache.cxf.jaxws.spi.ProviderImpl.createServiceDelegate(ProviderImpl.java:65)
at javax.xml.ws.Service.<init>(Service.java:56)
at org.websr.my_server.ws.LoginWsService.<init>(Unknown Source)

    Caused by: javax.wsdl.WSDLException: WSDLException: faultCode=PARSER_ERROR: Problem parsing 'https://192.168.2.184:8443/my_server/ws/LoginService?wsdl'.: javax.net.ssl.SSLHandshakeException: java.security.cert.CertificateException: No subject alternative names present

Can someone tell me why it is failing at creating Service itself. In LoginWs method, this line

   CxfClientUtils.configureClientConnection(port);

configures the SSL connection but my code is not even getting there. It's trying to connect at LoginWsService service = new LoginWsService(buildURL("LoginService")); and failing.

Can someone please tell me what's going on here? Thanks!

cert.pem:

        MIID1DCCArygAwIBAgIJAPAlC2JvlPsZMA0GCSqGSIb3DQEBBQUAMIGSMQswCQYD
VQQGEwJDQTEQMA4GA1UECBMHT250YXJpbzEQMA4GA1UEBxMHVG9yb250bzERMA8G
A1UEChMISW5kaXZpY2ExETAPBgNVBAsTCEluZGl2aWNhMRYwFAYDVQQDEw0xOTIu
MTY4LjIuMTg0MSEwHwYJKoZIhvcNAQkBFhJkaXZ5YUBpbmRpdmljYS5jb20wHhcN
MTIwMTA2MTYxMTQwWhcNMTMwMTA1MTYxMTQwWjCBkjELMAkGA1UEBhMCQ0ExEDAO
BgNVBAgTB09udGFyaW8xEDAOBgNVBAcTB1Rvcm9udG8xETAPBgNVBAoTCEluZGl2
aWNhMREwDwYDVQQLEwhJbmRpdmljYTEWMBQGA1UEAxMNMTkyLjE2OC4yLjE4NDEh
MB8GCSqGSIb3DQEJARYSZGl2eWFAaW5kaXZpY2EuY29tMIIBIjANBgkqhkiG9w0B
AQEFAAOCAQ8AMIIBCgKCAQEAxY8+fsw2pP4ToHN6XFNli4vOGbt+O/ANsr1A8iJh
nCb6cpQ58xF4pvYmETHrAUpv4zpi31SzZvWYI1tMaCEv9IpcX6Kc1B8NB9sLUnhR
gyblF37rZ7eMmSAXXeDS0CTtDEJoHOkGxoUdCN6N+vZjJ5+ZONiiuLqZ4x4HwBFr
ucIlwl2FkMMSxylg90tttSIyUHGz/p2DvNA2goYih4d89c/FLNpqwku+G3/gnL7U
l0OmNuFwJa/qMjy/V1orfpT8egxxh8DMp+fLAv1gjbeoizUs2bHo9kQSbUSp9Cwb
VDCol9jGI14cBuuEpWSANx2gTekN1ktoxztFPCh7H3OK/wIDAQABoyswKTAPBgNV
HREECDAGhwTAqAK4MAkGA1UdEwQCMAAwCwYDVR0PBAQDAgXgMA0GCSqGSIb3DQEB
BQUAA4IBAQALvpU0/5gQedlET2+r2MV0oksTmM2hV677yVsrOnGCOnTcgMZHp5i4
A0B24Ed2iDesX60OAViIocQkOiwYTRnubg5SoWyL1nhmaa/98U6M/re8R/bvq6OK
qrzEO6hHOtunJg1HcZDiJZop7R/pM52yRhRoXU6upZEhbPr6Eh+zfysA0TD6uMs7
9k2VeJo++XUvbG3dkVJ9kYhqfx2vC0HiMI4H2eomzl2ymS+R9Kg/9o29K8oCYjDI
jWPbl2hmf2cQuC4gG8GUDZi7zJkFsBuJpD6XgpIVK9zNhg1e89eP0nABupIFqBOI
iz0C+tRB4z4TezPL6yC7BDMY2nJ/Cg5e

vs what the server is actually using:

MIICVTCCAb6gAwIBAgIETr3AxTANBgkqhkiG9w0BAQUFADBvMQswCQYDVQQGEwJD
QTEQMA4GA1UECBMHT250YXJpbzEQMA4GA1UEBxMHVG9yb250bzERMA8GA1UEChMI
SW5kaXZpY2ExETAPBgNVBAsTCEluZGl2aWNhMRYwFAYDVQQDEw0xOTIuMTY4LjIu
MTg0MB4XDTExMTExMjAwNDE0MVoXDTIxMTEwOTAwNDE0MVowbzELMAkGA1UEBhMC
Q0ExEDAOBgNVBAgTB09udGFyaW8xEDAOBgNVBAcTB1Rvcm9udG8xETAPBgNVBAoT
CEluZGl2aWNhMREwDwYDVQQLEwhJbmRpdmljYTEWMBQGA1UEAxMNMTkyLjE2OC4y
LjE4NDCBnzANBgkqhkiG9w0BAQEFAAOBjQAwgYkCgYEAzvnBMOM2YpM4Ch0MkesA
ryqX3YD8O22kJJrpRuOMyqgt6fKEDxkcGjiEZ7qLfWbzv3eX9DE0nVeS4m65Ucr2
LLZN6iZoqP8J+AmkSXKapIQpX7tZM5UuTDy82vUdOiYJELB3NSJc/4nkZkTaN8Uj
h3Ph366kRUP+QWiq2y97KKMCAwEAATANBgkqhkiG9w0BAQUFAAOBgQBFeOQOKq9u
4nq/IUgNpILrhcpiAP5LB49bCXeTCi8Ls51qUCaezceUQKrWM60a6w8FxQF+yopB
PSqGMrUBHnvewkThgZbS12t5vOEoXnWjOwiXhMhRsk5i9YUh1QCYfOFF23aXNfRu
NLL5svksUHm1IzBJJANnL/YdJHRrR0IEQg==
Sapphire
  • 1,107
  • 8
  • 20
  • 35
  • You own the server code and the client code?How did you create the certificate? Can you paste the server certificate in the post? – Cratylus Jan 06 '12 at 20:04
  • @user384706 Just added it. I created the cert by following the instructions in this link [http://www.crsr.net/Notes/SSL.html] – Sapphire Jan 06 '12 at 20:12
  • The certificate you've pasted in your latest edit doesn't have a SAN entry. Please double check you're using the right certificate (see my edited answer). – Bruno Jan 06 '12 at 21:14

2 Answers2

5
java.security.cert.CertificateException: No subject alternative names present

It sounds like you're connecting using an IP address directly (and not a host name) to a certificate that doesn't have a Subject Alternative Name entry.

This is of course related to this question:

If you've chosen not to use a SAN entry but to rely an a host name in the CN (which you've also configured to resolve to the correct IP address in your client), you must also use it to specify the connection. Your URL builder is probably building a URL that still relies on the IP address.

EDIT: (Following comments)

As I was saying in the answer to the other question linked above, there are (at least) two ways of creating a self-signed cert with a Subject Alt Name for Java:

You've chosen the second option (possibly a bit more difficult?). OpenSSL is capable of producing a PKCS#12 file (.p12), which the default Java security providers should be able to use as a keystore directly (although keytool in Java 6 and above is capable of converting them to a JKS store via -importkeystore). To use them directly, use the "PKCS12" store type.

To build a PKCS#12 file, with OpenSSL, using the result of the self-signed certificate generation (assuming the files are called cert.pem for the cert and key.pem for the private key):

openssl pkcs12 -export -in cert.pem -inkey key.pem -out store.p12

Then, configure it in Apache Tomcat using (and restart Tomcat):

<Connector port="8443" ... scheme="https" secure="true" 
     keystoreFile="/path/to/store.p12"  
     keystorePass="..." keystoreType="PKCS12" sslProtocol="TLS" />

To extract the content of the cert in the PKCS#12 file:

openssl pkcs12 -in store.p12 -nokeys -clcerts | openssl x509 -text -noout

To check the certificate the server is actually using:

echo "" | openssl s_client -showcerts -connect hostname_or_ip_address:port
Community
  • 1
  • 1
Bruno
  • 119,590
  • 31
  • 270
  • 376
  • I tried to deploy this on another computer and specified a hostname. It fails by name "no matching hostname found". Actually, in the configureSSLConnection method, CNCheck is disabled by "tslClientParameters.setDisableCNCheck(true);" so why does it still fail? Is this line: (service = new LoginWsService(buildURL("LoginService"));) trying to establish a SSL connection before it's even configured properly? – Sapphire Jan 06 '12 at 15:42
  • Of course, if you deploy it on another host (and since in your example, the IP address was local), you'll need every client to be aware of the name. No way around that if you're using a host name. If you're using an IP address directly, the cert MUST have a SAN entry according to the specification (see RFC 2818, which Java implements). (I've already answered how to solve this in your previous question.) There's no point using SSL/TLS if you don't check the server's identity (either in SAN or CN), btw. – Bruno Jan 06 '12 at 15:58
  • Did you make sure you're using an `IP` SAN entry instead of a `DNS` one (`subjectAltName="IP:192.168.2.184"`)? You seem to have gone about it the hard way: `keytool` from Java 7 would have been easier. With OpenSSL, you then need to bundle the private key and cert into a keystore (although it's easier to do with a `PKCS12` store type). – Bruno Jan 06 '12 at 16:32
  • So I did bundle the key/cert pain into a keystore using this `keytool -importkeystore -srckeystore server.pkcs12 -destkeystore server.keystore -srcstoretype pkcs12`. Now how do I tell Java to look into this keystore for the cert? Do I have to edit this entry of tomcat's server.xml: `Connector port="8443" protocol="HTTP/1.1" SSLEnabled="true" maxThreads="150" scheme="https" secure="true" keystoreFile="/root/.keystore" keystorePass="dj650c" compression="on" compressionMinSize="2048" clientAuth="false" sslProtocol="TLS" />` – Sapphire Jan 06 '12 at 16:57
  • You should call your `server.pkcs12` file `server.p12` (it's the usual extension). Not sure how literal your quote above is, but your dest keystore isn't `/root/.keystore`. You don't actually need to do this keytool import/export. Just point your `keystoreFile=` to your p12 file and use `keystoreType="PKCS12"` in the connector configuration. – Bruno Jan 06 '12 at 17:08
  • So I changed the connector to point to my "keystore.p12" file. Tomcat loaded properly but I'm still getting the Subject Alternative names not found exception. I did `openssl x509 -in cert.pem -noout -text` and saw that 192.168.2.184 is listed under SubjectAlternativeNames. What am I missing? I'm so fed up of this problem :( – Sapphire Jan 06 '12 at 18:05
  • Just in case, check that it's indeed the cert that the server is using. You can use `openssl s_client -connect 192.168.2.184:8443 -showcerts` – Bruno Jan 06 '12 at 20:19
  • Thanks a lot for all your help. I had to change the cxf-* jars for it work. I was doing everything right but because I had the old jars, my code wasn't working. – Sapphire Jan 10 '12 at 16:06
1

The certificate you have posted has an issue.

I can open it directly via Windows and I bet if you open Internet Explorer and type the web service URL and view the certificate via IE you should not have any issue.

BUT for some reason Java can not parse it.
For example if I try to read the certificate via default java libraries:

public static void main(String[] args) throws Exception{  

 CertificateFactory f = CertificateFactory.getInstance("X.509");
 X509Certificate certificate = (X509Certificate) f.generateCertificate(new FileInputStream("C:\\certificate.pem"));
System.out.println(certificate);

}

I get parsing exception:

Exception in thread "main" java.security.cert.CertificateException: Could not parse certificate: java.io.IOException: Unsupported encoding
    at sun.security.provider.X509Factory.engineGenerateCertificate(Unknown Source)
    at java.security.cert.CertificateFactory.generateCertificate(Unknown Source)
    at test.Test.main(Test.java:15)
Caused by: java.io.IOException: Unsupported encoding
    at sun.security.provider.X509Factory.base64_to_binary(Unknown Source)
    ... 3 more

There is a problem in the decoding from base64.
Trying Bouncy Castle it failed to read it as well.

I have seen before this discrepancy between security libraries and Windows being able to decode certificates while Java's libraries can not.

In your case, your certificate can not be parsed by your web service client that uses java and the exception thrown up from CXF wrappers has the missleading message about subject alternative names.

I can not tell what is the problem with your certificate because I am not very familiar with open ssl.

But if you create (just to verify what I am saying) a new keystore using java tools you should have no problem.

Cratylus
  • 52,998
  • 69
  • 209
  • 339
  • It all started with creating a keystore using Java keytool and then a cert for the server I want to connect. That was throwing "Subject Alternative names" error so I resorted to creating a new keystore with openssl using a subject alternative name for IP:192.168.2.184. – Sapphire Jan 06 '12 at 20:46
  • Am I right that you can connect using IE and see the certificate? – Cratylus Jan 06 '12 at 20:57
  • I'm on mac and my client and server apps are deployed on remote ubuntu machines. – Sapphire Jan 06 '12 at 21:00
  • How about your browser? Firefox or Chrome? – Cratylus Jan 06 '12 at 21:02
  • This certificate works fine for me. I'd also avoid to call it `.der` unless you've converted it into DER format (in which case it's probably the reason it doesn't work, since the X.509 certificate factory is only documented for PEM format). – Bruno Jan 06 '12 at 21:03
  • My server that I'm trying to connect to (i.e. the webserver is on 184) and the client is on 188. When I create a cert using the keytool, I create it for 184 and add it to the keystore on 188. Is this correct? Yes I can connect to https://192.168.2.184:8443/myserver/ws from firefox but not able to get my application to connect to it. – Sapphire Jan 06 '12 at 21:11
  • I added the cert that my server is using. It's different from what I pasted before. – Sapphire Jan 06 '12 at 21:12
  • @Sapphire, create the self-signed cert for the server and use it with its private key on the server (via `server.p12` for example in my answer). Then take *only* the cert (without the private key) either from your PEM file or by extracting it again from the PKCS#12 file and import it into your *trust* store on the client. – Bruno Jan 06 '12 at 21:13
  • @Bruno: I updated the answer.I read it as pem. If you run the code java can not read it.BouncyCastle also can not read it. – Cratylus Jan 06 '12 at 21:13
  • @Sapphire: So you can connect through firefox?If you click on the certificate icon, you do see the certificate of your server, right?So the only problem is with your client code in java.Using a browser there is no problem.Is this correct? – Cratylus Jan 06 '12 at 21:20
  • @Bruno Since my (server=184), you're saying I need to run `openssl genrsa -out key.pem 2048` and `openssl req -new -x509 -key key.pem -out cert.pem -days 365` on 184 and get cert.pem from 184 and add it to $JAVA_HOME/jre/lib/cacerts store on 188 (myclient) ? – Sapphire Jan 06 '12 at 21:33
  • @user384706 Yes I can connect using a browser. It doesn't work from Java and command line. (I tried `wget https://192.168.2.184/myserver/ws` - it returned saying _Connecting to 192.168.2.184:443... failed: Connection refused._) – Sapphire Jan 06 '12 at 21:36
  • @Sapphire, where you actually run it doesn't really matter, but you may indeed need to transfer the resulting files across. The result of `openssl genrsa, req, pkcs12 ...` (i.e. the PKCS12 keystore with your self-signed cert for 192.168.2.184 and its private key) should be on the server. The same cert (but the cert only, i.e. `cert.pem`) should then be copied on your client machine and imported into its trust store (`cacerts` if you want to change the default trust store indeed). – Bruno Jan 06 '12 at 21:38
  • @ Sapphire: There is some problem with the certificate (java can't parse it) but I am not familiar with openssl to tell you why. Use a java tool to create keystore and certificate. E.g. try this java tool to create the keystore and export the certificate http://sourceforge.net/projects/certhelper/ – Cratylus Jan 06 '12 at 21:38
  • @Sapphire, regarding "connection" refused, your server is running on port 8443 (not the default 443), so you need to use `https://192.168.2.184:8443/.....`. Note that if it works in a browser, it doesn't mean it's going to work in Java. Java implements RFC 2818's verification (in particular IP SANs, see section 3.1) strictly, whereas some browsers are quite lenient. – Bruno Jan 06 '12 at 21:40
  • @user384706, just tried again to read the initial PEM cert (the one with the IP SAN) using Java 6 on OSX and Windows, without BouncyCastle. It works fine (same code too). – Bruno Jan 06 '12 at 21:42
  • @ Bruno: If this is indeed the case (because I certainly can not load it) then I (and the OP) have stepped on a bug in Java that was fixed and that is why (in your version) you don't get the error.I don't have any other explanation.I think I am using U21 which is old – Cratylus Jan 06 '12 at 21:47
  • @Bruno I imported Keystore.p12 to the server machine and copied cert.pem into client's trust store (i.e. cacerts). I imported using this command (`keytool -import -alias 192.168.2.184 -keystore $JAVA_HOME/jre/lib/security/cacerts -file cert.pem`) I changed tomcat's server.xml on the server machine to point to Keystore.p12 and restarted tomcat on both machines. Now I'm getting this _sun.security.validator.ValidatorException: PKIX path building failed: sun.security.provider.certpath.SunCertPathBuilderException: unable to find valid certification path to requested target_ – Sapphire Jan 06 '12 at 22:06
  • @Sapphire, Try perhaps to import with `-trustedcacerts`. Is your server using the correct certificate now at least? – Bruno Jan 06 '12 at 22:10
  • @Bruno I added what my cert.pem looks like and what the `echo "" | openssl s_client -showcerts -connect hostname_or_ip_address:port` gives me. Please take a look. – Sapphire Jan 06 '12 at 22:34
  • @Sapphire: you're clearly not using the right file on the server (perhaps you haven't restarted Tomcat properly, or configured it at the wrong location). – Bruno Jan 06 '12 at 22:39
  • 1
    Guys, take this to a chatroom. – slugster Jan 07 '12 at 00:14