Unable to install SSL certificate for Java app - java

Am bit similar situation as explained in this question. I also have a WSDL at a particular link. When I open that link I get the There is a problem with this website's security certificate... error in IE. When I click continue it opens up WSDL file.
Now I am writing a client for this webservice in Java. And it throws following exception:
Exception in thread "main" com.sun.xml.internal.ws.wsdl.parser.InaccessibleWSDLException: 2 counts of InaccessibleWSDLException.
java.io.IOException: Got java.security.cert.CertificateException: No subject alternative names matching IP address 172.17.245.196 found while opening stream from https://172.17.245.196/ews/Services.wsdl
java.io.IOException: Got java.security.cert.CertificateException: No subject alternative names matching IP address 172.17.245.196 found while opening stream from https://172.17.245.196/ews/Services.wsdl?wsdl
at com.sun.xml.internal.ws.wsdl.parser.RuntimeWSDLParser.tryWithMex(Unknown Source)
at com.sun.xml.internal.ws.wsdl.parser.RuntimeWSDLParser.parse(Unknown Source)
at com.sun.xml.internal.ws.wsdl.parser.RuntimeWSDLParser.parse(Unknown Source)
at com.sun.xml.internal.ws.client.WSServiceDelegate.parseWSDL(Unknown Source)
at com.sun.xml.internal.ws.client.WSServiceDelegate.<init>(Unknown Source)
at com.sun.xml.internal.ws.client.WSServiceDelegate.<init>(Unknown Source)
at com.sun.xml.internal.ws.spi.ProviderImpl.createServiceDelegate(Unknown Source)
at javax.xml.ws.Service.<init>(Unknown Source)
at com.microsoft.schemas.exchange.services._2006.messages.ExchangeWebService.<init>(ExchangeWebService.java:58)
at com.xyz.cms.EWSJavaAPI.ExchangeAuthenticator.getExchangeServicePort(ExchangeAuthenticator.java:32)
at com.xyz.cms.test.ExchangeDevelopmentTest.main(ExchangeDevelopmentTest.java:31)
So I guess it is related to resolving certificates and since the guy on the said thread got similar exception, I am trying out the solution suggested there - downloading and adding the certificate to the private using keytool.exe, though I really dont think I have completely understood this certificate stuff and also keytool.
So I
Downloaded the certificate by visiting the link in browser and then copy pasted it in app directory in eclipse.
Also I copy pasted $JAVA_HOME/lib/security/cacerts to my app directory. So by now my app hierarchy looks something like this in eclipse:
Then opened command prompt and navigated to the app directory.
Finally executed the command (as suggested in that thread). It gave me following output. It gave me following output
However it is giving me exactly the same exception. What should I do?
Edit
Well this is my effort to write java client for Exchange Web Services. Their is ExchangeAuthenticator which manages web services authentication requests to the Exchange and ExchangeDevelopmentTest which contains main method to test functionality of above class. a Here is the code:
ExchangeAuthenticator
public class ExchangeAuthenticator {
/**
* Obtains an authenticated ExchangeServicePortType with given credentials.
*
*/
public ExchangeServicePortType getExchangeServicePort(String username, String password, String domain, URL wsdlURL) throws MalformedURLException {
// Concatinate our domain and username for the UID needed in authentication.
String uid = "domain" + "\\" + "uname";
// Create an ExchangeWebService object that uses the supplied WSDL file, wsdlURL.
ExchangeWebService exchangeWebService = new ExchangeWebService(wsdlURL, new QName("http://schemas.microsoft.com/exchange/services/2006/messages", "ExchangeWebService"));
ExchangeServicePortType port = exchangeWebService.getExchangeWebPort();
// Supply your username and password when the ExchangeServicePortType is used for binding in the SOAP request.
((BindingProvider)port).getRequestContext().put(BindingProvider.USERNAME_PROPERTY, uid);
((BindingProvider)port).getRequestContext().put(BindingProvider.PASSWORD_PROPERTY, password);
return port;
}
}
ExchangeDevelopmentTest
public class ExchangeDevelopmentTest {
public static void main (String[] args) {
ExchangeAuthenticator exchangeAuthenticator = new ExchangeAuthenticator();
// Print statement so we can easily see where our statements start in the Java console.
System.out.println("Let's get started!");
try {
// Create a URL object which points at the .wsdl we deployed in the previous step.
URL wsdlURL = new URL("https://172.17.245.196/ews/Services.wsdl");
//URL wsdlURL = new URL("https://172.17.245.196/ews/Services.wsdl");
// Call to the class we just created to return an ExchangeServicePortType with authentication credentials.
ExchangeServicePortType port = exchangeAuthenticator.getExchangeServicePort("uname", "password#123", "domain", wsdlURL);
// Prints out the default toString() for the ExchangeServicePortType.
System.out.println(port.toString());
} catch (MalformedURLException ex) {
// Catch any errors that may occur.
Logger.getLogger(ExchangeDevelopmentTest.class.getName()).log(Level.SEVERE, null, ex);
System.out.println(ex.getMessage()+"\n"+ex.getStackTrace());
}
}
}

The problem is that your certificate is not issued for 172.17.245.196 IP address, so the client used to parse WSDL does not trust it. That IP address shall be in subject field of the certificate.
Is your certificate trusted by official certification authority or is it self signed? Probably you will need Java to trust it. Add it to keystore and then set system properties:
System.setProperty("javax.net.ssl.keyStore", "lfkeystore2");
System.setProperty("javax.net.ssl.keyStorePassword", "wshr.ut");

Related

JAVA 6 does not support SNI, Any Alternative way to verify and accept SSL Certificate over TLS?

I know the reason why browsers and java7 are not affected is because they send Server Name Indication-SNI as part of the SSL information. So, apache knows what virtual host to use before starting SSL handshake and returns the proper certificate. Java 6 doesn't support SNI. So My question is, how do I verify and allow certificate to be accepted in java 6.
I made a spring client to consume webservice, Here is my code
public class classname1 {
static {
System.setProperty("javax.net.ssl.trustStore", "E://Workspace//keystore");
System.setProperty("javax.net.ssl.trustStorePassword", "changeit");
System.setProperty("https.protocols", "SSLv3");
System.setProperty("https.protocols", "TLSv1");
}
static {
HttpsURLConnection.setDefaultHostnameVerifier(new HostnameVerifier()
{
public boolean verify(String hostname, SSLSession session)
{
if (hostname.equals("192.168.10.22"))
return true;
return false;
}
});
}
private static void main(String[] args) {
try {
SOAPConnectionFactory soapConnectionFactory = SOAPConnectionFactory.newInstance();
SOAPConnection soapConnection = soapConnectionFactory.createConnection();
String url = "https://192.168.10.22/getInformationSearch.asmx?wsdl";
SOAPMessage soapResponse = soapConnection.call(createSOAPRequest(), url);
printSOAPResponse(soapResponse);
soapConnection.close();
} catch (Exception e) {
e.printStackTrace();
}
}
private static SOAPMessage createSOAPRequest() throws Exception {
// ... Code for request, which will be hitted to url
}
private static void printSOAPResponse(SOAPMessage soapResponse) throws Exception {
// ... Code for response, which will fetch information from webservice
// URL
}
}
As you can see code, I made two methods, 1. for Request createSOAPRequest() 2. for Response printSOAPResponse(). (Url name is changed in above snippet)
In main() method, below line will generate request and send that request to given url SOAPMessage soapResponse = soapConnection.call(createSOAPRequest(), url); after that it goes to static block as shown above HttpsURLConnection.setDefaultHostnameVerifier() method.
At that time,debugger says: ssl handshake failed and SSL peer shut down incorrectly. It happens only in JAVA 6 but these code working proper with java 7/8.
Instead of that static block, I have tried below code in java 6
HostnameVerifier hv = new HostnameVerifier() {
public boolean verify(String urlHostName,
SSLSession session) {
return true;
}
};
But it won't work anyhow!!!
I am using following jars xercesImpl.jar,saaj-api.jar,saaj-impl.jar,spring-ws-core-1.5.6.jar,spring-ws-security-1.5.6.jar and Certificate for this SSL domain has been imported into keystore and It works in java7/8, So there is no issue in truststore right? (I made 2 certificate using keytool of java 6 and 7 as well, both works fine in java7/8 but not in 6)
I followed this thread but it wont work. So is there any alternative way to verify certificate and get response in java 6 environment, or should I change any JAR ?
You can't if the server isn't configured to allow you to connect without SNI.
Without SNI, the server cannot tell which virtual host you want until after the SSL connection has been established.
To see the difference, if you have OpenSSL, you can use the s_client option to extract the different certificates presented, or perhaps note a failure to connect when you don't use SNI:
SNI:
echo | openssl s_client -servername www.virtualhost.com -connect 192.168.1.55:443 | openssl x509 -text
Replace 192.168.1.55 with the actual IP address of the server, and www.virtualhost.com with the host name of the virtual host you want to connect to. The echo command prepended to the pipeline prevents openssl from hanging in a wait for input.
Without SNI:
echo | openssl s_client -connect 192.168.1.55:443 | openssl x509 -text
Without SNI, you may see a completely different certificate, or you may get a connection error.
As you already mentioned Java 6 does not support SNI. But, the problem of missing SNI is not only with verifying the certificate (because the server sends the wrong certificate) but also that depending on the servers configuration the server will cause a handshake error if the SNI extension is not present and does not point to a hostname available at the server. And this is obviously the case here:
... ssl handshake failed and SSL peer shut down incorrectly. It happens only in JAVA 6 but these code working proper with java 7/8.
Trying to change the validation of the certificate as you've tried will not help at all in this case because the error occurs before the client even has a certificate to verify. This means that there is no way around it: you need to support SNI in your client if you want to communicate with this specific server.

JSF - How can I take full control of a deployed managed bean if I want to troubleshoot?

I was given a URL that returns a name of person based on an unique id parameter (rest web service).
When I entered below url to a browser I was able to get expected name wrapped in json format via GET method. If I enter "id=1234" then name comes back as "DaeYoung". However it has to be https instead of http so that I have to download a CA cert from the server and import it to my browser otherwise it won't work.
https://[host:port]/fooService/queryName?id=1234
What I want to accomplish is create a managed bean that makes rest ws call in JSF (2.x) web application. First I ran a test code from a standalone Java class below from eclipse IDE to verify if I could get name value back. And yes, I was able to get expected name back.
However I had to add a CA cert from the server (rest ws service provider) to a local Java_Home/jre/lib/security/cacerts file and include HostnameVerifier in the code otherwise I would get java.security.cert.CertificateException: No subject alternative names matching IP address xxx.xxx.xxx.xxx found.
public void findName() throws Exception {
HttpsURLConnection.setDefaultHostnameVerifier(new HostnameVerifier()
{
public boolean verify(String hostname, SSLSession session) {
// ip address of rest service provider
if (hostname.equals(127.0.0.1))
return true;
return false;
}
});
String url = "https://[host:port]/fooService/queryOIN";
String query = "id=1234";
String charset = "utf-8";
URLConnection conn = new URL(url + "?" + query).openConnection();
conn.setRequestProperty("Accept-Charset", charset);
InputStream in = conn.getInputStream();
String name = IOUtils.toString(in,charset);
System.out.println("name=" + name);
}
Now when I put above code into a bean and deployed a web app into an app server (websphere), I observed two events.
1. with a valid id "1234" provided, name "DaeYoung" expected however return value was empty string
2. no exception was thrown with above condition which I expected
I was tailing system log file but nothing abnormal behaviors are thrown.
My questions is...
How can I troubleshoot or verify above code running inside of the app server in order to grasp what is happening inside of findName() method?

GSSContext with null SrcName

I'm working a web appliaction with SSO based on the Windows domain login, for this purpose I have chosen to validate Kerberos tickets. But now I'm facing a problem for which I can't find a solution. I manage to validate a ticket without exceptions, but when I'm trying to get the userName, NullPointerException is thrown, because the username is null and I don't know where is problem.
Why is user name null if I don't get any exception during the validation?
How I get userName:
String clientName = gssContext.getSrcName().toString();
I create my client based on this:
Using GSSManager to validate a Kerberos ticket
How to obtain a kerberos service ticket via GSS-API?
http://docs.oracle.com/javase/7/docs/technotes/guides/security/jgss/single-signon.html
Update 1:
How I setup content, just copy-paste form here https://stackoverflow.com/a/25450862/1646082:
final Oid spnegoOid = new Oid("1.3.6.1.5.5.2");
GSSManager gssmgr = GSSManager.getInstance();
// tell the GSSManager the Kerberos name of the service
GSSName serviceName = gssmgr.createName(this.servicePrincipal, GSSName.NT_USER_NAME);
// get the service's credentials. note that this run() method was called by Subject.doAs(),
// so the service's credentials (Service Principal Name and password) are already
// available in the Subject
GSSCredential serviceCredentials = gssmgr.createCredential(serviceName,
GSSCredential.INDEFINITE_LIFETIME, spnegoOid, GSSCredential.ACCEPT_ONLY);
// create a security context for decrypting the service ticket
GSSContext gssContext = gssmgr.createContext(serviceCredentials);
// decrypt the service ticket
System.out.println("Entering accpetSecContext...");
System.out.println( new String (Base64.encodeBase64( gssContext.acceptSecContext(this.kerberosTicket, 0,
this.kerberosTicket.length) ) ));
// get the client name from the decrypted service ticket
// note that Active Directory created the service ticket, so we can trust it
String clientName = gssContext.getSrcName().toString();
Update 2:
If I setup spring security based on this https://spring.io/blog/2009/09/28/spring-security-kerberos-spnego-extension i also got the same error:
java.lang.NullPointerException at
org.springframework.security.extensions.kerberos.SunJaasKerberosTicketValidator$KerberosValidateAction.run(SunJaasKerberosTicketValidator.java:136)
at
org.springframework.security.extensions.kerberos.SunJaasKerberosTicketValidator$KerberosValidateAction.run(SunJaasKerberosTicketValidator.java:125)
at java.security.AccessController.doPrivileged(Native Method) at
javax.security.auth.Subject.doAs(Subject.java:422)
private static class KerberosValidateAction implements PrivilegedExceptionAction<String> {
byte[] kerberosTicket;
public KerberosValidateAction(byte[] kerberosTicket) {
this.kerberosTicket = kerberosTicket;
}
#Override
public String run() throws Exception {
GSSContext context = GSSManager.getInstance().createContext((GSSCredential) null);
context.acceptSecContext(kerberosTicket, 0, kerberosTicket.length);
String user = context.getSrcName().toString(); // ERROR!
context.dispose();
return user;
}
}
Update 3:
Also tried change Java version from 1.8 to 1.7 as suggested here Domain authentication with Kerberos fails. No result.
Update 4:
First of all. Don't user Java 1.8 b40 and b45, both of them are broken. And don't test it on local PC, it doesn't work(I don't know why).
After changing on newest(b65) Java version, I got exception about encription(Cannot find key of appropriate type to decrypt AP REP - AES256 ...). This I have fixed by Java Cryptography Extension (JCE) for Java 1.8 and re-create keytab with /crypto AES256-SHA1 after all this i got exception:
GSSException: Failure unspecified at GSS-API level (Mechanism level:
Checksum failed) at
sun.security.jgss.krb5.Krb5Context.acceptSecContext(Unknown Source) at
sun.security.jgss.GSSContextImpl.acceptSecContext(Unknown Source) at
sun.security.jgss.GSSContextImpl.acceptSecContext(Unknown Source) at
GssServer$GssServerAction.run(GssServer.java:159)
... 4 more
Caused by: KrbException: Checksum failed at
sun.security.krb5.internal.crypto.ArcFourHmacEType.decrypt(Unknown
Source) at
sun.security.krb5.internal.crypto.ArcFourHmacEType.decrypt(Unknown
Source) at sun.security.krb5.EncryptedData.decrypt(Unknown Source) at
sun.security.krb5.KrbApReq.authenticate(Unknown Source) at
sun.security.krb5.KrbApReq.(Unknown Source) at
sun.security.jgss.krb5.InitSecContextToken.(Unknown Source)
... 8 more
Caused by: java.security.GeneralSecurityException: Checksum
failed at
sun.security.krb5.internal.crypto.dk.ArcFourCrypto.decrypt(Unknown
Source) at
sun.security.krb5.internal.crypto.ArcFourHmac.decrypt(Unknown Source)
... 14 more
I tried this tutorial and other way to create keytabfile, but i still don't have solution.
I faced the same Checksum failed error when implementing my GSSAPI socket demo that is a modification of the Oracle GSSAPI tutorial code.
I executed my code on a Linux machine enrolled into a FreeIPA kerberos realm.
I have used the vanilla krb5.conf file of my Linux system. No constraints on ticket etype are there:
...
[libdefaults]
default_realm = AUTHDEMO.IT
dns_lookup_realm = true
dns_lookup_kdc = true
rdns = false
ticket_lifetime = 24h
forwardable = true
udp_preference_limit = 0
...
The FreeIPA realm default is to use type 18 tickets (AES-256).
About my application, it has this policy file configured:
grant CodeBase "file:./app.jar" {
permission java.security.AllPermission;
};
grant CodeBase "file:./app.jar"
Principal javax.security.auth.kerberos.KerberosPrincipal
"servicename#AUTHDEMO.IT" {
permission java.net.SocketPermission "*", "accept";
permission javax.security.auth.kerberos.ServicePermission
"servicename#AUTHDEMO.IT", "accept";
};
When executing the application I got this error on the acceptor side:
GSSException: Failure unspecified at GSS-API level (Mechanism level:
Encryption type AES256CTS mode with HMAC SHA1-96 is not
supported/enabled)
In my case the error arises in the GSS acceptor side. In my application I generate the Jaas configuration programmatically (I'll refer at this as DConfig) and I don't use a config file.
The first solution, I found, is to use config files instead of DConfig and the problem disappeared, it worked fine.
The temporary solution, Jaas Config file:
DemoServer {
com.sun.security.auth.module.Krb5LoginModule required
principal="servicename#AUTHDEMO.IT"
storeKey=true
debug=true; #not mandatory
};
With this configuration, no problem arises on the acceptor side and the application were able to check the service ticket validity and accept the connection.
I asked myself.. WHY?
I checked differences in the Subject(s) acquired with the two configurations. In the working case, with the config file, the subject contains, into the private credentials, both the password hashes credentials and the principal TGT ticket.
With DConfig, I obtain a Subject with only password hashes but there is no principal TGT ticket in private credentials.
My fix
DConfig contains the same settings of the configuration file and the other options are the replica of Krb5LoginModule defaults, at first I cannot see a reason for the misbehaviour.
Setting isInitiator = true, into the acceptor side DConfig, solved the issue. `isInitiator = true has forced the persistence of TGT ticket into the subject.
With this workaround the error has disappeared with no change into the system krb5.conf.
My cent is... after Jaas login, let's check your subject private credentials for lacking creds (You need the service principal TGT into your acceptor side subject!) and in case try to set isInitiator = true to the acceptor side too.
Regards
It seems that the context is not fully established when you are trying to get SrcName. It seems to be the reason for ScrName to be null. According to https://www-01.ibm.com/support/knowledgecenter/SSYKE2_7.0.0/com.ibm.java.security.api.doc/jgss/org/ietf/jgss/GSSContext.html, acceptSecContext() generates a token and if it's not null, this token should be sent to the peer.
After the call acceptSecContext() you should check if isEstablished() returns false. If it's so,
If this method returns false it indicates that a token is needed from its peer in order to continue the context establishment phase. A return value of true signals that the local end of the context is established. This may still require that a token be sent to the peer, if one is produced by GSS-API. During the context establishment phase, the isProtReady() method may be called to determine if the context can be used for the per-message operations. This allows applications to use per-message operations on contexts which aren't fully established.
The same is explained in more details in the tutorial http://www.cs.mun.ca/java-api-1.5/guide/security/jgss/tutorials/BasicClientServer.html:
The acceptSecContext method may in turn return a token. If it does, the acceptor should send that token to the initiator, which should then call initSecContext again and pass it this token. Each time initSecContext or acceptSecContext returns a token, the application that called the method should send the token to its peer and that peer should pass the token to its appropriate method (acceptSecContext or initSecContext). This continues until the context is fully established (which is the case when the context's isEstablished method returns true).

EJBCA how create keystore for communication between webservice and java code (wsdl exception)

i have installed ejbca with jboss on a ubuntu server.
The version of jboss is 5.1.0.GA, while the version of ejbca is 4_0_10.
Now i would use ejbca function in my java project (eclipse) so I implemented the following code:
CryptoProviderTools.installBCProvider();
String urlstr = "https://EJBCA.cloud:8443/ejbca/ejbcaws/ejbcaws?wsdl";
System.setProperty("javax.net.ssl.trustStore","C:\\Users\\PcEclipse.jks");
System.setProperty("javax.net.ssl.trustStorePassword","Prova");
System.setProperty("javax.net.ssl.keyStore","C:\\Users\\PcEclipse.jks");
System.setProperty("javax.net.ssl.keyStorePassword", "Prova");
QName qname = new QName("http://ws.protocol.core.ejbca.org/", "EjbcaWSService");
EjbcaWSService service = null;
try {
service = new EjbcaWSService(new URL(urlstr),qname);
} catch (MalformedURLException e) {
// TODO Auto-generated catch block
System.out.println("errore nell'url");
}
EjbcaWS ejbcaraws = service.getEjbcaWSPort();
but i have the exception
Exception in thread "main" javax.xml.ws.WebServiceException: Failed to access the WSDL at: https://EJBCA.cloud:8443/ejbca/ejbcaws/ejbcaws?wsdl. It failed with:
Got java.security.cert.CertificateException: No name matching EJBCA.cloud found while opening stream from https://EJBCA.cloud:8443/ejbca/ejbcaws/ejbcaws?wsdl.
The keystore is generated with ejbca web ui creating a new endentity ed create a keystore in jks extension.
This is a problem of keystore?How i can create the correct keystore/truststore?
Thanks
I have generated a new keystore and truststore but now the error is:
Caused by: java.security.UnrecoverableKeyException: Password must not be null
I was experiencing the same issue as you. In my case the cert CN did seem to match the server domain name so I couldn't work out why it wasn't working.
I've not yet found an solution however I was able to confirm I wasn't going insane using the following code snippet which allowed me to compare the server and cert hostname values. It will also allow you to ignore the host validation by returning true.
But note that the validation is no longer taking place and this is a security control that is now being bypassed.
I can't take credit for the code snippet, albeit simple I found it online but can't recall the source.
HostnameVerifier hv = new HostnameVerifier() {
public boolean verify(String urlHostName, SSLSession session) {
System.out.println("Warning: URL Host: " + urlHostName + " vs. "
+ session.getPeerHost());
return true;
}
};
HttpsURLConnection.setDefaultHostnameVerifier(hv);

Failed to create service exception javax.xml.ws.WebServiceException:

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==
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:
how to add subject alernative name to ssl certs?
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:
Using Java 7's keytool
Using OpenSSL, as described here: http://www.crsr.net/Notes/SSL.html
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
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.

Categories