I have a Java application to access web services hosted behind a HTTPS transport protocol. The client side java application gave me the error message like:
javax.net.ssl.SSLHandshakeException: sun.security.validator.ValidatorException: PKIX path building failed: sun.security.provider.certpath.SunCertPathBuilderException: unable to find valid certification path to requested target
I did the following to resolve the issue:
1. Try to access the web services endpoint from a browser and export its certificate using the 'X.509 certificate format' into a sample.cert file.
2. Locate the JVM's keystore. For example, \dev\Java\jdk1.6.0_34\jre\lib\security
3. Back up its original keystore and run the following command under the above directory:
\dev\Java\jdk1.6.0_34\jre\bin\keytool -import -keystore cacerts -file sample.cer -alias sampleXYZ
The default password is 'changeit'. It's fine if the certificate is a self-signed one. If so, be sure to answer 'Yes' for the question to trust the certificate.
4. After restart of the Java application, another error may show up as:
CertificateException: No subject alternative names present
I tried to edit C:\Windows\System32\drivers\etc\hosts as Administrator and put the ip address matching to the CN host name defined in the cert
To review the CN name, run the following command:
keytool -list -v -keystore .\cacerts -alias sampleXYZ
The dns needs to be refreshed from a command line console to pick up the new definition:
ipconfig /flushdns
Showing posts with label Java Security. Show all posts
Showing posts with label Java Security. Show all posts
Tuesday, September 18, 2012
Friday, May 7, 2010
Mutual Authentication with Client Certificate over HTTPS/SSL using Java
This blog is about SSL/TLS mutual authentication using Java. I am on the client side with a client certificate signed by an intermediate issuer and finally by Verisign. (aha, a certificate chain is here to make the situation not vanilla already.) The server requests a client certificate and recognizes Verisign as a Certification Authority (CA). You can safely ignore this blog if you have a self-certified certificate.
During the implementation, I got tripped more than a couple of times over error messages like:
During the implementation, I got tripped more than a couple of times over error messages like:
403.7 Forbidden: Client Certificate Required.To save somebody some time in the future, a step by step instruction is provided below:
javax.net.ssl.SSLException: HelloRequest followed by an unexpected handshake message
- I assume you have a valid certificate or a chain of certificates, whose root is acceptable by the server. The valid certificate contains its private key. Run the following command to verify:
keytool -list -v -keystore "your certificate file"
...
Entry type: PrivateKeyEntry - Import your certificate and intermediate certificates into a browser like IE or Firefox and test out the https URL. This step will validate the certificates and save you a lot of troubles down the road. Java version of the SSL implementation is not as simple/mature as the browsers'. Please make sure all the certificates have not expired.
- Backup your keystore located at /your_home_directory/.keystore by default and the truststore located at somewhere similar to \Java\jre6\lib\security\cacerts
- Use not-yet-commons-ssl utility to import your certificates into the Java keystore format. Sample command is:
java -cp not-yet-commons-ssl-0.3.9.jar org.apache.commons.ssl.KeyStoreBuilder
- Customize the following java code, replace the static final Strings to fit in your needs. Note that this implementation forcefully use a specific alias to present the corresponding certificate/certificate chain to the server. Somehow the default KeyManager simply disqualifies my certificate to be presented to the server.
public class Main {
private static final Logger logger = Logger.getLogger(Main.class.getName());
private static final String LINE_BREAKER = System.getProperty("line.separator");
private static final String CERTIFACATE_FILE = "your keystore location";
private static final String CERTIFACATE_PASS = "changeit";
private static final String CERTIFACATE_ALIAS = "your alias";
private static final String TARGET_URL = "https://xyz.com";
public static void main(String[] args) {
String targetURL = TARGET_URL;
URL url;
HttpsURLConnection connection = null;
BufferedReader bufferedReader = null;
InputStream is = null;
try {
//Create connection
url = new URL(targetURL);
//Uncomment this in case server demands some unsafe operations
//System.setProperty("sun.security.ssl.allowUnsafeRenegotiation", "true");
connection = (HttpsURLConnection) url.openConnection();
connection.setRequestMethod("POST");
connection.setRequestProperty("Content-Type", "application/x-www-form-urlencoded");
connection.setRequestProperty("Content-Language", "en-US");
SSLSocketFactory sslSocketFactory = getFactory(new File(CERTIFACATE_FILE), CERTIFACATE_PASS, CERTIFACATE_ALIAS);
connection.setSSLSocketFactory(sslSocketFactory);
//Process response
is = connection.getInputStream();
bufferedReader = new BufferedReader(new InputStreamReader(is));
String line;
StringBuffer lines = new StringBuffer();
while ((line = bufferedReader.readLine()) != null) {
lines.append(line).append(LINE_BREAKER);
}
logger.info("response from " + targetURL + ":" + LINE_BREAKER + lines);
} catch (Exception e) {
...
}
}
private static SSLSocketFactory getFactory(File pKeyFile, String pKeyPassword, String certAlias) throws Exception {
KeyManagerFactory keyManagerFactory = KeyManagerFactory.getInstance("SunX509");
KeyStore keyStore = KeyStore.getInstance("JKS");
InputStream keyInput = new FileInputStream(pKeyFile);
keyStore.load(keyInput, pKeyPassword.toCharArray());
keyInput.close();
keyManagerFactory.init(keyStore, pKeyPassword.toCharArray());
//Replace the original KeyManagers with the AliasForcingKeyManager
KeyManager[] kms = keyManagerFactory.getKeyManagers();
for (int i = 0; i < kms.length; i++) {
if (kms[i] instanceof X509KeyManager) {
kms[i] = new AliasForcingKeyManager((X509KeyManager) kms[i], certAlias);
}
}
SSLContext context = SSLContext.getInstance("TLS");
context.init(kms, null, null);
return context.getSocketFactory();
}
/*
* This wrapper class overwrites the default behavior of a X509KeyManager and
* always render a specific certificate whose alias matches that provided in the constructor
*/
private static class AliasForcingKeyManager implements X509KeyManager {
X509KeyManager baseKM = null;
String alias = null;
public AliasForcingKeyManager(X509KeyManager keyManager, String alias) {
baseKM = keyManager;
this.alias = alias;
}
/*
* Always render the specific alias provided in the constructor
*/
public String chooseClientAlias(String[] keyType, Principal[] issuers, Socket socket) {
return alias;
}
public String chooseServerAlias(String keyType, Principal[] issuers, Socket socket) {
return baseKM.chooseServerAlias(keyType, issuers, socket);
}
public X509Certificate[] getCertificateChain(String alias) {
return baseKM.getCertificateChain(alias);
}
public String[] getClientAliases(String keyType, Principal[] issuers) {
return baseKM.getClientAliases(keyType, issuers);
}
public PrivateKey getPrivateKey(String alias) {
return baseKM.getPrivateKey(alias);
}
public String[] getServerAliases(String keyType, Principal[] issuers) {
return baseKM.getServerAliases(keyType, issuers);
}
}
} - Try to set
-Dsun.security.ssl.allowUnsafeRenegotiation=true
if you get the error message like:javax.net.ssl.SSLException: HelloRequest followed by an unexpected handshake message
- If anything goes wrong, turn on -Djavax.net.debug=all to debug. Verify the keystore and truststore locations. Verify the presence of the certificates. Here is a sample log file with successful connection: http://java.sun.com/javase/6/docs/technotes/guides/security/jsse/ReadDebug.html
Subscribe to:
Posts (Atom)