Springboot : PKIX path building failed / javax.net.ssl.SSLHandshakeException - ssl

I have a SpringBoot app that must call a REST API which requires a certificate. I was provided 2 files from the service that propose this REST Service : a P12 file and a CA Root file.
I first created a keystore (JKS) :
keytool -keystore keystore.jks -genkey -alias client
Then I added a CA root to the JKS file :
keytool -keystore keystore.jks -import -file certeurope_root_ca_3.cer -alias cacert
Now in my app I have to call the rest API :
public DocumentDto sendRequest(DocumentDto documentDto) throws Exception {
// Set variables
String ts = "C:\\keystore\\keystore.jks";
String ks = "C:\\keystore\\CERTIFICATE.p12";
String tsPassword = properties.getProperty("signature.api.passphrase");
String ksPassword = properties.getProperty("signature.api.passphrase");
KeyStore clientStore = KeyStore.getInstance("PKCS12");
clientStore.load(new FileInputStream(ks), ksPassword.toCharArray());
log.warn("# clientStore : " + clientStore);
KeyManagerFactory kmf = KeyManagerFactory.getInstance(KeyManagerFactory.getDefaultAlgorithm());
kmf.init(clientStore, ksPassword.toCharArray());
KeyManager[] kms = kmf.getKeyManagers();
KeyStore trustStore = KeyStore.getInstance("JKS");
trustStore.load(new FileInputStream(ts), tsPassword.toCharArray());
TrustManagerFactory tmf = TrustManagerFactory.getInstance("SunX509");
tmf.init(trustStore);
TrustManager[] tms = tmf.getTrustManagers();
SSLContext sslContext = null;
sslContext = SSLContext.getInstance("TLS");
sslContext.init(kms, tms, new SecureRandom());
// set the URL to send the request
HttpsURLConnection.setDefaultSSLSocketFactory(sslContext.getSocketFactory());
URL url = new URL(properties.getProperty("signature.api.url.full"));
// opening the connection
HttpsURLConnection urlConnection = (HttpsURLConnection) url.openConnection();
// Create all-trusting host name verifier
HostnameVerifier allHostsValid = new HostnameVerifier() {
public boolean verify(String hostname, SSLSession session) { return true; }
};
// Install the all-trusting host verifier
HttpsURLConnection.setDefaultHostnameVerifier(allHostsValid);
HttpsURLConnection.setDefaultAllowUserInteraction( true );
HttpsURLConnection.setFollowRedirects( false );
HttpsURLConnection.setDefaultSSLSocketFactory(sslContext.getSocketFactory());
urlConnection.setRequestMethod("POST");
urlConnection.setDoOutput(true);
urlConnection.setDoInput(true);
urlConnection.setUseCaches(false);
urlConnection.setAllowUserInteraction(true);
urlConnection.setReadTimeout(15000);
// create the JSON String
ObjectMapper mapper = new ObjectMapper();
// convert an oject to a json string
String jsonInString = mapper.writerWithDefaultPrettyPrinter().writeValueAsString(documentDto);
InputStreamReader isr=null;
try(OutputStream os = urlConnection.getOutputStream()) {
byte[] input = jsonInString.getBytes(StandardCharsets.UTF_8);
os.write(input, 0, input.length);
// check 400 & 403
if(urlConnection.getResponseCode() == 400 || urlConnection.getResponseCode() == 403) {
isr = new InputStreamReader(urlConnection.getErrorStream(), StandardCharsets.UTF_8);
String st= IOUtils.toString(isr);
log.warn("# errorStream :" + st );
} else if(urlConnection.getResponseCode() != 200) {
isr = new InputStreamReader(urlConnection.getErrorStream(), StandardCharsets.UTF_8);
String st= IOUtils.toString(isr);
} else {
isr = new InputStreamReader(urlConnection.getInputStream(), StandardCharsets.UTF_8);
}
}
// read the response
try(BufferedReader br = new BufferedReader(new InputStreamReader(urlConnection.getInputStream(), StandardCharsets.UTF_8))) {
StringBuilder response = new StringBuilder();
String responseLine = null;
while ((responseLine = br.readLine()) != null) {
response.append(responseLine.trim());
}
System.out.println(response.toString());
}
System.out.println(jsonInString);
return documentDto;
}
I also changed my port server : server.port=8443. I have 2 issues :
If i have : TrustManagerFactory tmf = TrustManagerFactory.getInstance("SunX509");
I obtain : javax.net.ssl.SSLHandshakeException: No trusted certificate found
If I have : TrustManagerFactory tmf = TrustManagerFactory.getInstance(TrustManagerFactory.getDefaultAlgorithm());
I obtain : javax.net.ssl.SSLHandshakeException: PKIX path building failed: sun.security.provider.certpath.SunCertPathBuilderException: unable to find valid certification path to requested target
I'm stuck on that stuff for a while and I don't see what's going wrong.

Well i found out a solution which may not be the most elegant at all. But at least it work. I also made some refactor...
public DocumentCreateRequestDto sendRequest(DocumentDto documentDto) throws CertificateException, UnrecoverableKeyException, NoSuchAlgorithmException, KeyStoreException, KeyManagementException, IOException {
// Set variables
String certificate = properties.getProperty("signature.api.certificate");
String PwdPk12 = properties.getProperty("signature.api.passphrase");
String httpsRestUrl = properties.getProperty("signature.api.url.full");
HttpsURLConnection con = getHttpsURLConnection(certificate, PwdPk12, httpsRestUrl);
// create the JSON String
ObjectMapper mapper = new ObjectMapper();
// convert an oject to a json string
String jsonInString = mapper.writerWithDefaultPrettyPrinter().writeValueAsString(documentDto);
jsonInString = "[" + jsonInString + "]";
StringBuilder response = getStringBuilder(con, jsonInString);
String output = response.toString();
output = output.substring(1, output.length()-1);
return mapper.readValue(output, DocumentCreateRequestDto.class);
}
private HttpsURLConnection getHttpsURLConnection(String certificate, String pwdPk12, String httpsRestUrl) throws KeyStoreException, IOException, NoSuchAlgorithmException, CertificateException, UnrecoverableKeyException, KeyManagementException {
KeyStore ks = KeyStore.getInstance("PKCS12");
ks.load(new FileInputStream(certificate), pwdPk12.toCharArray());
TrustManager[] trustAllCerts = new TrustManager[] {
new X509TrustManager() {
#Override
public void checkClientTrusted(X509Certificate[] x509Certificates, String s) throws CertificateException {
}
#Override
public void checkServerTrusted(X509Certificate[] x509Certificates, String s) throws CertificateException {
}
#Override
public X509Certificate[] getAcceptedIssuers() {
// return new X509Certificate[0];
return null;
}
}
};
SSLContext ctx = SSLContext.getInstance("TLS");
// Create all-trusting host name verifier
HostnameVerifier allHostsValid = new HostnameVerifier() {
public boolean verify(String hostname, SSLSession session) { return true; }
};
KeyManagerFactory kmf = KeyManagerFactory.getInstance( "SunX509" );
kmf.init( ks, pwdPk12.toCharArray() );
ctx.init( kmf.getKeyManagers(), trustAllCerts, new SecureRandom() );
// Install the all-trusting host verifier
HttpsURLConnection.setDefaultHostnameVerifier(allHostsValid);
HttpsURLConnection.setDefaultAllowUserInteraction( true );
HttpsURLConnection.setFollowRedirects( false );
HttpsURLConnection.setDefaultSSLSocketFactory(ctx.getSocketFactory());
URL url = new URL(httpsRestUrl);
HttpsURLConnection con = (HttpsURLConnection) url.openConnection();
//connection
con.setRequestMethod("POST");
con.setDoOutput(true);
con.setRequestProperty("Accept-Encoding", "gzip,deflate");
con.setRequestProperty("Content-Type", "application/json");
con.setRequestProperty("Connection", "Keep-Alive");
con.setRequestProperty("User-Agent", "Apache-HttpClient/4.1.1 (java 1.5)");
con.setReadTimeout(15000);
con.setDoInput(true);
con.setUseCaches(false);
con.setAllowUserInteraction(true);
return con;
}
And :
private StringBuilder getStringBuilder(HttpsURLConnection con, String jsonInString) throws IOException {
InputStreamReader isr = null;
try (OutputStream os = con.getOutputStream()) {
byte[] input = jsonInString.getBytes(StandardCharsets.UTF_8);
os.write(input, 0, input.length);
// check 400 & 403
if (con.getResponseCode() == 400 || con.getResponseCode() == 403) {
isr = new InputStreamReader(con.getErrorStream(), StandardCharsets.UTF_8);
String st = IOUtils.toString(isr);
log.warn("# errorStream :" + st);
} else if (con.getResponseCode() != 200) {
isr = new InputStreamReader(con.getErrorStream(), StandardCharsets.UTF_8);
} else {
isr = new InputStreamReader(con.getInputStream(), StandardCharsets.UTF_8);
}
}
// read the response
String responseLine;
StringBuilder response = new StringBuilder();
try (BufferedReader br = new BufferedReader(new InputStreamReader(con.getInputStream(), StandardCharsets.UTF_8))) {
while ((responseLine = br.readLine()) != null) {
response.append(responseLine.trim());
}
}
return response;
}

Related

API Chorus Pro Oauth2 authentication in Java

I created an account on https://developer.aife.economie.gouv.fr/ website and I want to try API on the sandbox. For this an application has been generated
For this application, I obtain API key and OAuth2 Credentials. Here are my previous API keys.
By reading the documentation, I have the following entry points for authentication
My objective is to get authenticated and get an auth token in order to consume this API. Here is my code:
package com.oauth.app;
import org.apache.oltu.oauth2.client.OAuthClient;
import org.apache.oltu.oauth2.client.URLConnectionClient;
import org.apache.oltu.oauth2.client.request.OAuthClientRequest;
import org.apache.oltu.oauth2.client.response.OAuthJSONAccessTokenResponse;
import org.apache.oltu.oauth2.common.OAuth;
import org.apache.oltu.oauth2.common.exception.OAuthProblemException;
import org.apache.oltu.oauth2.common.exception.OAuthSystemException;
import org.apache.oltu.oauth2.common.message.types.GrantType;
import javax.net.ssl.HttpsURLConnection;
import javax.net.ssl.SSLContext;
import javax.net.ssl.TrustManager;
import javax.net.ssl.X509TrustManager;
import java.security.SecureRandom;
import java.security.cert.X509Certificate;
public class OAuthApp {
/**
* URL for requesting OAuth access tokens.
*/
private static final String TOKEN_REQUEST_URL =
"https://sandbox-oauth.aife.economie.gouv.fr/api/oauth/token";
/**
* Client ID of your client credential. Change this to match whatever credential you have created.
*/
private static final String CLIENT_ID =
"1f80aa43-e12f-4e1c-ad42-87ec16baf060";
/**
* Client secret of your client credential. Change this to match whatever credential you have created.
*/
private static final String CLIENT_SECRET =
"a232af0e-513e-4a64-9977-410d237dc421";
/**
* Account on which you want to request a resource. Change this to match the account you want to
* retrieve resources on.
*/
private static final String ACCOUNT_ID =
"a232af0e-513e-4a64-9977-410d237dc421";
/**
* Request a fresh access token using the given client ID, client secret, and token request URL,
* then request the resource at the given resource URL using that access token, and get the resource
* content. If an exception is thrown, print the stack trace instead.
*
* #param args Command line arguments are ignored.
*/
public static void main(String[] args) {
try {
OAuthClient client = new OAuthClient(new URLConnectionClient());
System.out.println("OAuthClient " + client.toString());
OAuthClientRequest request =
OAuthClientRequest.tokenLocation(TOKEN_REQUEST_URL)
.setGrantType(GrantType.CLIENT_CREDENTIALS)
.setClientId(CLIENT_ID)
.setClientSecret(CLIENT_SECRET)
// .setScope() here if you want to set the token scope
.buildQueryMessage();
request.addHeader("Accept", "application/json");
// request.addHeader("Content-Type", "application/json");
// request.addHeader("Authorization", base64EncodedBasicAuthentication());
System.out.println("OAuthClientRequest body\n\t " + request.getBody());
System.out.println("OAuthClientRequest headers\n\t " + request.getHeaders());
System.out.println("OAuthClientRequest locationUri\n\t " + request.getLocationUri());
// Create a trust manager that does not validate certificate chains
TrustManager[] trustAllCerts = new TrustManager[]{new X509TrustManager() {
public X509Certificate[] getAcceptedIssuers() {
return null;
}
public void checkClientTrusted(X509Certificate[] certs, String authType) {
}
public void checkServerTrusted(X509Certificate[] certs, String authType) {
}
}};
// Install the all-trusting trust manager
try {
SSLContext sc = SSLContext.getInstance("TLS");
sc.init(null, trustAllCerts, new SecureRandom());
HttpsURLConnection.setDefaultSSLSocketFactory(sc.getSocketFactory());
} catch (Exception e) {
e.printStackTrace();
}
String token = client.accessToken(
request,
OAuth.HttpMethod.GET,
OAuthJSONAccessTokenResponse.class).getAccessToken();
} catch (OAuthSystemException | OAuthProblemException e) {
e.printStackTrace();
}
}
}
I obtain this in my console:
OAuthClient org.apache.oltu.oauth2.client.OAuthClient#7e0ea639
OAuthClientRequest body
null
OAuthClientRequest headers
{Accept=application/json, Content-Type=application/json}
OAuthClientRequest locationUri
https://sandbox-oauth.aife.economie.gouv.fr/api/oauth/token?grant_type=client_credentials&client_secret=a232af0e-513e-4a64-9977-410d237dc421&client_id=42b214ec-7eaf-4f37-aeb5-ae91057a0e27
OAuthProblemException{error='unsupported_response_type', description='Invalid response! Response body is not application/json encoded', uri='null', state='null', scope='null', redirectUri='null', responseStatus=0, parameters={}}
at org.apache.oltu.oauth2.common.exception.OAuthProblemException.error(OAuthProblemException.java:63)
at org.apache.oltu.oauth2.client.response.OAuthJSONAccessTokenResponse.setBody(OAuthJSONAccessTokenResponse.java:76)
at org.apache.oltu.oauth2.client.response.OAuthClientResponse.init(OAuthClientResponse.java:92)
at org.apache.oltu.oauth2.client.response.OAuthAccessTokenResponse.init(OAuthAccessTokenResponse.java:65)
at org.apache.oltu.oauth2.client.response.OAuthClientResponse.init(OAuthClientResponse.java:101)
at org.apache.oltu.oauth2.client.response.OAuthAccessTokenResponse.init(OAuthAccessTokenResponse.java:60)
at org.apache.oltu.oauth2.client.response.OAuthClientResponse.init(OAuthClientResponse.java:120)
at org.apache.oltu.oauth2.client.response.OAuthClientResponseFactory.createCustomResponse(OAuthClientResponseFactory.java:82)
at org.apache.oltu.oauth2.client.URLConnectionClient.execute(URLConnectionClient.java:111)
at org.apache.oltu.oauth2.client.OAuthClient.accessToken(OAuthClient.java:65)
at com.oauth.app.OAuthApp.main(OAuthApp.java:101)
I obtain this error message:
OAuthProblemException{error='unsupported_response_type', description='Invalid response! Response body is not application/json encoded'
I also tried to use a curl call to the API :
curl –k –H "content-type :application/x-www-form-urlencoded" –d "grant_type=client_credentials&client_id=42b214ec-7eaf-4f37-aeb5-ae91057a0e27&client_secret=a232af0e-513e-4a64-9977-410d237dc421&scope=openid" –X POST https://sandbox-oauth.aife.finances.rie.gouv.fr/api/oauth/token
curl: (6) Could not resolve host: -k
curl: (6) Could not resolve host: -H
curl: (3) Port number ended with 'a'
curl: (6) Could not resolve host: -d
curl: (6) Could not resolve host: grant_type=client_credentials&client_id=42b214ec-7eaf-4f37-aeb5-ae91057a0e27&client_secret=a232af0e-513e-4a64-9977-410d237dc421&scope=openid
curl: (6) Could not resolve host: -X
curl: (6) Could not resolve host: POST
curl: (6) Could not resolve host: sandbox-oauth.aife.finances.rie.gouv.fr
Ok i finally solved my own issue. There was no need to use OAuth stuff.
It's divided onto 2 classes. This code is just for testing purpose.
public class OAuthApp {
private static final String TOKEN_REQUEST_URL = "https://sandbox-oauth.aife.economie.gouv.fr/api/oauth/token";
private static final String CLIENT_ID = "xxxxxx";
private static final String CLIENT_SECRET = "xxxxxx";
private static final String GRANT_TYPE = "client_credentials";
private static final String SCOPE = "openid";
public static void main(String[] args) throws IOException {
try {
Map<String, String> headers = new HashMap<>();
HttpsPostForm httpsPostForm = new HttpsPostForm(TOKEN_REQUEST_URL, "utf-8", headers);
httpsPostForm.addFormField("grant_type", GRANT_TYPE);
httpsPostForm.addFormField("client_id", CLIENT_ID);
httpsPostForm.addFormField("client_secret", CLIENT_SECRET);
httpsPostForm.addFormField("scope", SCOPE);
// Result
String response = httpsPostForm.finish();
System.out.println(response);
} catch (IOException e) {
e.printStackTrace();
}
}
}
My second class is just building the HTTPS request and set the headers elements. The empty trust manager helps to avoid error messages.
public class HttpsPostForm {
private HttpsURLConnection conn;
private Map<String, Object> queryParams;
private String charset;
public HttpsPostForm(String requestURL, String charset, Map<String, String> headers, Map<String, Object> queryParams) throws IOException {
this.charset = charset;
if (queryParams == null) {
this.queryParams = new HashMap<>();
} else {
this.queryParams = queryParams;
}
// Create a trust manager that does not validate certificate chains
TrustManager[] trustAllCerts = new TrustManager[]{new X509TrustManager() {
public X509Certificate[] getAcceptedIssuers() {
return null;
}
public void checkClientTrusted(X509Certificate[] certs, String authType) {
}
public void checkServerTrusted(X509Certificate[] certs, String authType) {
}
}};
// Install the all-trusting trust manager
try {
SSLContext sc = SSLContext.getInstance("TLS");
sc.init(null, trustAllCerts, new SecureRandom());
HttpsURLConnection.setDefaultSSLSocketFactory(sc.getSocketFactory());
} catch (Exception e) {
e.printStackTrace();
}
URL url = new URL(requestURL);
conn = (HttpsURLConnection) url.openConnection();
conn.setUseCaches(false);
conn.setDoOutput(true); // indicates POST method
conn.setDoInput(true);
conn.setRequestProperty("Content-Type", "application/x-www-form-urlencoded");
if (headers != null && headers.size() > 0) {
Iterator<String> it = headers.keySet().iterator();
while (it.hasNext()) {
String key = it.next();
String value = headers.get(key);
conn.setRequestProperty(key, value);
}
}
}
public HttpsPostForm(String requestURL, String charset, Map<String, String> headers) throws IOException {
this(requestURL, charset, headers, null);
}
public HttpsPostForm(String requestURL, String charset) throws IOException {
this(requestURL, charset, null, null);
}
public void addFormField(String name, Object value) {
queryParams.put(name, value);
}
public void addHeader(String key, String value) {
conn.setRequestProperty(key, value);
}
private byte[] getParamsByte(Map<String, Object> params) {
byte[] result = null;
StringBuilder postData = new StringBuilder();
for (Map.Entry<String, Object> param : params.entrySet()) {
if (postData.length() != 0) {
postData.append('&');
}
postData.append(this.encodeParam(param.getKey()));
postData.append('=');
postData.append(this.encodeParam(String.valueOf(param.getValue())));
}
try {
result = postData.toString().getBytes("UTF-8");
} catch (UnsupportedEncodingException e) {
e.printStackTrace();
}
return result;
}
private String encodeParam(String data) {
String result = "";
result = URLEncoder.encode(data, StandardCharsets.UTF_8);
return result;
}
public String finish() throws IOException {
String response = "";
byte[] postDataBytes = this.getParamsByte(queryParams);
conn.getOutputStream().write(postDataBytes);
// Check the http status
int status = conn.getResponseCode();
if (status == HttpsURLConnection.HTTP_OK) {
ByteArrayOutputStream result = new ByteArrayOutputStream();
byte[] buffer = new byte[1024];
int length;
while ((length = conn.getInputStream().read(buffer)) != -1) {
result.write(buffer, 0, length);
}
response = result.toString(this.charset);
conn.disconnect();
} else {
throw new IOException("Server returned non-OK status: " + status);
}
return response;
}
}
Finally I can print my Json string :
{
"access_token":"Js1NYJvtQREj0I0Dz5b0qrMh8gjJBlltJAit2Yx6BGJDloixPv2JwB",
"token_type":"Bearer",
"expires_in":3600,
"scope":"openid resource.READ"
}
I also had some difficulties with Chorus API but I achieve to get the tokenKey with that with the same method but buildBodyMessage() at the end.
// Création requête pour obtenir le token Oauth2 API CHORUS
request = OAuthClientRequest
.tokenLocation(urlToken)
.setGrantType(GrantType.CLIENT_CREDENTIALS)
.setClientId(clientid)
.setClientSecret(clientsecret)
.setScope(OidcScopes.OPENID)
.buildBodyMessage();
// Ajout du Cpro-account
request.addHeader("cpro-account", cproAccount);
tokenChorus = client.accessToken(request, OAuth.HttpMethod.POST, OAuthJSONAccessTokenResponse.class)
.getAccessToken();
that create token formated in String. And afterthat you must create HttpUrlConnection with this token with headers like that
HttpURLConnection connexion = null;
try {
URL url = new URL(currentUrl);
connexion = (HttpURLConnection) url.openConnection();
} catch (IOException e) {
e.printStackTrace();
}
connexion.setRequestProperty("Content-type", "application/json");
connexion.setRequestProperty("Authorization", "Bearer " + tokenChorus);
connexion.setRequestProperty("cpro-account", cproAccount);
try {
connexion.setRequestMethod("POST");
} catch (ProtocolException e) {
e.printStackTrace();
}
connexion.setDoInput(true);
connexion.setDoOutput(true);
return connexion;

Need help on TLS 1.2 https service call from Java 1.4 client

I am facing issue while calling RestAPI call (which is running on Java 1.8 and TlS 1.2) using postmethod call from Java 1.4 client application. I have written below client code and I am end up with below issue.
I am providing the required .jks files in both trust store and keystore.
Error:
>java.security.KeyStoreException
>at com.ibm.jsse.bg.<init>(Unknown Source)
>at com.ibm.jsse.bo.a(Unknown Source)
>at com.ibm.jsse.bo.engineInit(Unknown Source)
>at javax.net.ssl.KeyManagerFactory.init(Unknown Source)
>bundles.ubuntu.vas.tiaa.AuthSSLProtocolSocketFactory.createKeyManagers(AuthSSLP>rotocolSocketFactory.java:223)
Code:
AuthSSLProtocolSocketFactory authSSLProtocolSocketFactory = new
AuthSSLProtocolSocketFactory(new URL("file:C:/cert/testwithsigner.jks"), "password1",
new URL("file:C:/cert/testwithsigner.jks"), "password1");
authSSLProtocolSocketFactory.createSocket("sample.intranet",8443);
final PostMethod post = new PostMethod("https://sample.intranet:8443/ext/ref/callservice");
HttpClient httpClient = new HttpClient();
post.setRequestHeader("Content-type", "application/json");
System.setProperty("https.protocols", "TLSv1,TLSv1.1,TLSv1.2");
final int status = httpClient.executeMethod(post);
final InputStream is = post.getResponseBodyAsStream();
final StringBuffer content = new StringBuffer();
final String charset = post.getResponseCharSet();
Code:
public class AuthSSLProtocolSocketFactory implements SecureProtocolSocketFactory {
/** Log object for this class. */
private static final Log LOG = LogFactory.getLog(AuthSSLProtocolSocketFactory.class);
private URL keystoreUrl = null;
private String keystorePassword = null;
private URL truststoreUrl = null;
private String truststorePassword = null;
private SSLContext sslcontext = null;
public AuthSSLProtocolSocketFactory(
final URL keystoreUrl, final String keystorePassword,
final URL truststoreUrl, final String truststorePassword)
{
super();
this.keystoreUrl = keystoreUrl;
this.keystorePassword = keystorePassword;
this.truststoreUrl = truststoreUrl;
this.truststorePassword = truststorePassword;
}
private static KeyStore createKeyStore(final URL url, final String password)
throws KeyStoreException, NoSuchAlgorithmException, CertificateException, IOException
{
if (url == null) {
throw new IllegalArgumentException("Keystore url may not be null");
}
LOG.debug("Initializing key store");
KeyStore keystore = KeyStore.getInstance("jks");
InputStream is = null;
try {
is = url.openStream();
keystore.load(is, password != null ? password.toCharArray(): null);
} finally {
if (is != null) is.close();
}
return keystore;
}
private static KeyManager[] createKeyManagers(final KeyStore keystore, final String password)
throws KeyStoreException, NoSuchAlgorithmException, UnrecoverableKeyException
{
if (keystore == null) {
throw new IllegalArgumentException("Keystore may not be null");
}
LOG.debug("Initializing key manager");
KeyManagerFactory kmfactory = KeyManagerFactory.getInstance(
KeyManagerFactory.getDefaultAlgorithm());
if(password != null && password.toCharArray()!= null)
kmfactory.init(keystore, password.toCharArray());
return kmfactory.getKeyManagers();
}
private static TrustManager[] createTrustManagers(final KeyStore keystore)
throws KeyStoreException, NoSuchAlgorithmException
{
if (keystore == null) {
throw new IllegalArgumentException("Keystore may not be null");
}
LOG.debug("Initializing trust manager");
TrustManagerFactory tmfactory = TrustManagerFactory.getInstance(
TrustManagerFactory.getDefaultAlgorithm());
tmfactory.init(keystore);
TrustManager[] trustmanagers = tmfactory.getTrustManagers();
for (int i = 0; i < trustmanagers.length; i++) {
if (trustmanagers[i] instanceof X509TrustManager) {
trustmanagers[i] = new AuthSSLX509TrustManager(
(X509TrustManager)trustmanagers[i]);
}
}
return trustmanagers;
}
private SSLContext createSSLContext() {
try {
KeyManager[] keymanagers = null;
TrustManager[] trustmanagers = null;
if (this.keystoreUrl != null) {
KeyStore keystore = createKeyStore(this.keystoreUrl, this.keystorePassword);
//if (LOG.isDebugEnabled()) {
Enumeration aliases = keystore.aliases();
while (aliases.hasMoreElements()) {
String alias = (String)aliases.nextElement();
Certificate[] certs = keystore.getCertificateChain(alias);
if (certs != null) {
LOG.debug("Certificate chain '" + alias + "':");
for (int c = 0; c < certs.length; c++) {
if (certs[c] instanceof X509Certificate) {
X509Certificate cert = (X509Certificate)certs[c];
System.out.println(" Certificate " + (c + 1) + ":");
System.out.println(" Subject DN: " + cert.getSubjectDN());
System.out.println(" Signature Algorithm: " + cert.getSigAlgName());
System.out.println(" Valid from: " + cert.getNotBefore() );
System.out.println(" Valid until: " + cert.getNotAfter());
System.out.println(" Issuer: " + cert.getIssuerDN());
}
}
}
}
// }
keymanagers = createKeyManagers(keystore, this.keystorePassword);
}
if (this.truststoreUrl != null) {
KeyStore keystore = createKeyStore(this.truststoreUrl, this.truststorePassword);
if (LOG.isDebugEnabled()) {
Enumeration aliases = keystore.aliases();
while (aliases.hasMoreElements()) {
String alias = (String)aliases.nextElement();
LOG.debug("Trusted certificate '" + alias + "':");
Certificate trustedcert = keystore.getCertificate(alias);
if (trustedcert != null && trustedcert instanceof X509Certificate) {
X509Certificate cert = (X509Certificate)trustedcert;
LOG.debug(" Subject DN: " + cert.getSubjectDN());
LOG.debug(" Signature Algorithm: " + cert.getSigAlgName());
LOG.debug(" Valid from: " + cert.getNotBefore() );
LOG.debug(" Valid until: " + cert.getNotAfter());
LOG.debug(" Issuer: " + cert.getIssuerDN());
}
}
}
trustmanagers = createTrustManagers(keystore);
}
SSLContext sslcontext = SSLContext.getInstance("SSL");
sslcontext.init(keymanagers, trustmanagers, null);
return sslcontext;
} catch (NoSuchAlgorithmException e) {
LOG.error(e.getMessage(), e);
throw new AuthSSLInitializationError("Unsupported algorithm exception: " + e.getMessage());
} catch (KeyStoreException e) {
LOG.error(e.getMessage(), e);
throw new AuthSSLInitializationError("Keystore exception: " + e.getMessage());
} catch (GeneralSecurityException e) {
LOG.error(e.getMessage(), e);
throw new AuthSSLInitializationError("Key management exception: " + e.getMessage());
} catch (IOException e) {
LOG.error(e.getMessage(), e);
throw new AuthSSLInitializationError("I/O error reading keystore/truststore file: " + e.getMessage());
}
}
private SSLContext getSSLContext() {
if (this.sslcontext == null) {
this.sslcontext = createSSLContext();
}
return this.sslcontext;
}
public Socket createSocket(String host, int port)
throws IOException, UnknownHostException
{
return getSSLContext().getSocketFactory().createSocket(
host,
port
);
}
}

J2EE No subject alternative names present Revenge of the Jedi

Scenario, I'm dealing with a Webserver that it is a mess (no I do not control this part, I have to play the game, this was coded by one of the biggest software vendors in the world)
By default, the webserver has 2 SSL services, each one of those might have a totally different SSL Certificate
Certificate A
Signature Algorithm: sha1WithRSAEncryption
RSA Key Strength: 1024
Subject: *.dummy.nodomain
Issuer: *.dummy.nodomain
Certificate B
Signature Algorithm: sha1WithRSAEncryption
RSA Key Strength: 2048
Subject: vhcalnplcs_NPL_01
Issuer: root_NPL
Following the examples of this page
public List<String> doPostWithSSL(String direction, String dataToSend, String contentType, boolean OverrideSecurityVerifications) {
try {
URL url = new URL(direction);
List<String> webcontent = new ArrayList();
HttpsURLConnection conn = (HttpsURLConnection) url.openConnection();
conn.setRequestMethod("POST");
conn.setRequestProperty("Host", getHostByUrl(direction));
conn = new UserAgentsLibrary().getRandomUserAgent(conn);
if (contentType != null) {
conn.setRequestProperty("Content-Type", contentType);
} else {
conn.setRequestProperty("Content-Type", "application/x-www-form-urlencoded");
}
conn.setDoOutput(true);
if (OverrideSecurityVerifications) {
try {
TrustManager[] trustAllCerts;
trustAllCerts = new TrustManager[]{new X509TrustManager() {
#Override
public X509Certificate[] getAcceptedIssuers() {
return new X509Certificate[0];
}
#Override
public void checkClientTrusted(X509Certificate[] certs, String authType) throws CertificateException {
}
#Override
public void checkServerTrusted(X509Certificate[] certs, String authType) throws CertificateException {
}
}};
// We want to override the SSL verifications
SSLContext ctx = SSLContext.getInstance("TLS");
ctx.init(null, trustAllCerts, null);
SSLSocketFactory factory = ctx.getSocketFactory();
conn.setDefaultSSLSocketFactory(ctx.getSocketFactory());
HostnameVerifier allHostsValid = (String hostname1, SSLSession session) -> true;
conn.setDefaultHostnameVerifier(allHostsValid);
conn.setSSLSocketFactory(factory);
} catch (KeyManagementException kex) {
System.out.println("[+] Error bypassing SSL Security " + kex.getMessage());
} catch (NoSuchAlgorithmException nsex) {
System.out.println("[+] Error forgeting TLS " + nsex.getMessage());
}
}
OutputStreamWriter wr = new OutputStreamWriter(conn.getOutputStream());
wr.write(dataToSend);
wr.flush();
BufferedReader rd = new BufferedReader(new InputStreamReader(conn.getInputStream()));
String line;
while ((line = rd.readLine()) != null) { //todo+=line+"\n";
webcontent.add(line);
}
wr.close();
rd.close();
return webcontent;
} catch (MalformedURLException mex) {
System.out.println("[+] Error: I received a malformed URL");
return null;
} catch (SSLHandshakeException sslex) {
System.out.println("[+] Error: SSL Handshake Error!" + sslex.getMessage());
return null;
} catch (IOException ioex) {
System.out.println("[+] Error: Input/Output Error!" + ioex.getMessage());
return null;
}
}
I was able to make my program work with certificate B (no issue here) but I cannot make it to work with certificate A (I suspect that the * is causing me trouble)
Things to consider
This is a sample code, do not look for irrelevant details ;)
Yes, I know that this code is vulnerable to MITM attacks and the user is being warned
No, I do not want to add the certificates to my keystore!
I'm using pure J2EE code, I do not wish to use anything that it is not standard
I would like to find a solution that will work for Windows, Mac and Linux
Someone had to have this issue in the past, could you lend me a hand?
I was too tired yesterday.
Replaced conn.setDefaultHostnameVerifier(allHostsValid);
by conn.setHostnameVerifier(allHostsValid);
And now even the cert with the wildcard works!

How to establish ssl connection from itext bouncycastle tsaclient

I am trying to timestamp with itext tsaclient trough HTTPS connection, I am having problems with the connection in the moment of the sign, when tsaclient try to establish the connection against the server. Analizing by whireshark the traffic I have received
"Received fatal alert: bad_certificate"
The point is I can establish the connection but not from the tsaclient,
//Preparing keystore with Private Key
BouncyCastleProvider provider = new BouncyCastleProvider();
Security.addProvider(provider);
KeyStore ks = KeyStore.getInstance("pkcs12", provider.getName());
ks.load(new FileInputStream(Constantes.CERT), Constantes.PASS_CERT.toCharArray());
String alias = (String)ks.aliases().nextElement();
PrivateKey pk = (PrivateKey) ks.getKey(alias, Constantes.PASS_CERT.toCharArray());
Certificate[] chain = ks.getCertificateChain(alias);
//Adding Server public key to the keystore ks
FileInputStream is = new FileInputStream(new File(Constantes.SERVER_CERT_CER));
CertificateFactory cf = CertificateFactory.getInstance("X.509");
java.security.cert.X509Certificate cert = (X509Certificate) cf.generateCertificate(is);
ks.setCertificateEntry("alias", cert);
//Preparing SSL Context
KeyManagerFactory kmf = KeyManagerFactory.getInstance(KeyManagerFactory.getDefaultAlgorithm());
kmf.init(ks, "pass".toCharArray());
TrustManagerFactory tmf = TrustManagerFactory.getInstance(TrustManagerFactory.getDefaultAlgorithm());
tmf.init(ks);
SSLContext ctx = SSLContext.getInstance("TLS");
ctx.init(kmf.getKeyManagers(),tmf.getTrustManagers(), new SecureRandom());
SSLSocketFactory sf = ctx.getSocketFactory();
URL url = new URL("https://...");
HttpsURLConnection conn = (HttpsURLConnection)url.openConnection();
conn.setSSLSocketFactory(sf);
conn.setConnectTimeout(0);
conn.connect();
//Until here the connection is OK, "Certificate Verify" say whireshark
//Preparing TSA Client and Sign
ExternalDigest digest = new BouncyCastleDigest();
TSAClient tsaClient = new TSAClientBouncyCastle("https://...");
ExternalSignature es = new PrivateKeySignature(pk,"SHA-1","BC");
MakeSignature.signDetached(appearance, digest, es, chain, null, null, tsaClient, 0, CryptoStandard.CMS);
It is in the moment of MakeSignature when I can't connect with the server.
I wasn't preparing well the connection from TSAClient, now I have implemented properly the client.
//Preparing keystore with my Private Key and Server Certificate
BouncyCastleProvider provider = new BouncyCastleProvider();
Security.addProvider(provider);
ks = KeyStore.getInstance("pkcs12", provider.getName()); //public static KeyStore ks;
ks.load(new FileInputStream(Constantes.CERT), Constantes.PASS_CERT.toCharArray());
String alias = (String)ks.aliases().nextElement();
PrivateKey pk = (PrivateKey) ks.getKey(alias, Constantes.PASS_CERT.toCharArray());
Certificate[] chain = ks.getCertificateChain(alias);
FileInputStream is = new FileInputStream(new File(Constantes.SERVER_CERT_CER));
CertificateFactory cf = CertificateFactory.getInstance("X.509");
java.security.cert.X509Certificate cert = (X509Certificate) cf.generateCertificate(is);
ks.setCertificateEntry("alias_server_cert", cert);
//Making TSA and Sign
ExternalDigest digest = new BouncyCastleDigest();
TSAClient tsaClient = new Sellado("https://url");
//Digital signature
ExternalSignature es = new PrivateKeySignature(pk,"SHA-1","BC");
MakeSignature.signDetached(appearance,digest,es,chain,null,null,tsaClient,0,CryptoStandard.CMS);
and the TSAClient "Sellado" is based in TSAClientBouncycastle, changing the next class with SSL context.
public class Sellado implements TSAClient{
//Code
protected byte[] getTSAResponse(byte[] requestBytes)
throws IOException, NoSuchAlgorithmException, UnrecoverableKeyException, KeyStoreException, KeyManagementException
{
//Preparing SSL Context
KeyManagerFactory kmf = KeyManagerFactory.getInstance(KeyManagerFactory.getDefaultAlgorithm());
kmf.init(EmpadronamientoI.ks, "pass".toCharArray());
TrustManagerFactory tmf = TrustManagerFactory.getInstance(TrustManagerFactory.getDefaultAlgorithm());
tmf.init(EmpadronamientoI.ks);
SSLContext ctx = SSLContext.getInstance("TLS");
ctx.init(kmf.getKeyManagers(),tmf.getTrustManagers(), new SecureRandom());
SSLSocketFactory sf = ctx.getSocketFactory();
URL url = new URL(this.tsaURL);
HttpsURLConnection tsaconn = (HttpsURLConnection)url.openConnection();
tsaconn.setSSLSocketFactory(sf);
tsaconn.setConnectTimeout(0);
tsaconn.setDoInput(true);
tsaconn.setDoOutput(true);
tsaconn.setUseCaches(false);
tsaconn.setRequestProperty("Content-Type", "application/timestamp-query");
tsaconn.setRequestProperty("Content-Transfer-Encoding", "binary");
try{
tsaconn.connect();
}
catch (IOException ioe)
{
throw new IOException(MessageLocalization.getComposedMessage("failed.to.get.tsa.response.from.1", new Object[] { this.tsaURL }));
}
OutputStream out = tsaconn.getOutputStream();
out.write(requestBytes);
out.close();
InputStream inp = tsaconn.getInputStream();
ByteArrayOutputStream baos = new ByteArrayOutputStream();
byte[] buffer = new byte['?'];
int bytesRead = 0;
while ((bytesRead = inp.read(buffer, 0, buffer.length)) >= 0) {
baos.write(buffer, 0, bytesRead);
}
byte[] respBytes = baos.toByteArray();
String encoding = tsaconn.getContentEncoding();
if ((encoding != null) && (encoding.equalsIgnoreCase("base64"))) {
respBytes = Base64.decode(new String(respBytes));
}
return respBytes;
}
//Code
}

SSL connection to ldap using certitificates and custom ssl socket factory

This is my scenario , i want to connect to ldap usign jndi , i am using custom SSLSOcketfactory which reads the truststore and keystore . The context is created successful but when i try to authenticate using the same credentials it throws an error telling that the authentication method is not supported.
here is my code of the custom ssl socket -
try {
StringBuffer trustStore = new StringBuffer("c:/Temp/certs/TrustStore");
StringBuffer keyStore = new StringBuffer("c:/Temp/certs/keystore.arun");
StringBuffer keyStorePass = new StringBuffer("xxxxx");
StringBuffer keyAlias = new StringBuffer("user");
StringBuffer keyPass = new StringBuffer("XXXX");
TrustManagerFactory tmf =TrustManagerFactory.getInstance(TrustManagerFactory.getDefaultAlgorithm());
FileInputStream fis = new FileInputStream(trustStore.toString());
KeyStore ks1 = KeyStore.getInstance("jks");
ks1.load(fis, trustStorePass.toString().toCharArray());
fis.close();
tmf.init(ks1);
TrustManager[] tms = tmf.getTrustManagers();
FileInputStream fin = new FileInputStream(keyStore.toString());
KeyStore ks2 = KeyStore.getInstance("jks");
ks2.load(fin, keyStorePass.toString().toCharArray());
fin.close();
KeyManagerFactory kmf =
KeyManagerFactory.getInstance(KeyManagerFactory.getDefaultAlgorithm());
kmf.init(ks2, keyStorePass.toString().toCharArray());
KeyManager[] kms = kmf.getKeyManagers();
if (keyAlias != null && keyAlias.length() > 0) {
for (int i = 0; i < kms.length; i++) {
// We can only deal with instances of X509KeyManager
if (kms[i] instanceof X509KeyManager)
kms[i] = new CustomKeyManager(
(X509KeyManager) kms[i], keyAlias.toString());
}
}
SSLContext context = SSLContext.getInstance("TLS");
context.init(kms,tms, null);
ssf = context.getSocketFactory();
} catch (Exception e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
}
public static SocketFactory getDefault() {
return new CustomSSLSocketFactory();
}
And the jndi code which uses this CustomSSLSocketFactory is as follows
env.put(Context.INITIAL_CONTEXT_FACTORY, "com.sun.jndi.ldap.LdapCtxFactory");
env.put(Context.PROVIDER_URL, "ldaps://wx64ads01a.vapps.esca.com:636");
env.put(Context.REFERRAL, "follow");
env.put("java.naming.ldap.derefAliases", "always");
env.put("java.naming.ldap.factory.socket","com.eterra.security.authz.dao.CustomSSLSocketFactory" );
try {
ctx = new InitialLdapContext(env, null);
// start ssl session for server authentication
}catch(Exception e ){
System.out.println(e);
}
try{
ctx.addToEnvironment(Context.SECURITY_AUTHENTICATION,
"EXTERNAL");
String path = "CN=domain,DC=casa,DC=com"
String inFilter = "(&(objectClass=*))";
SearchControls sc = new SearchControls();
sc.setSearchScope(SearchControls.SUBTREE_SCOPE);
NamingEnumeration<SearchResult> results = null;
results = ctx.search(path, inFilter, sc);
}
My Context is created perfectly but when i try to authenticate and bind to the ldap , i get Invalid Authentication method . ANy help will be appreciated , Struggling with these error over a long time now . Thanks in advance .
Context.SECURITY_AUTHENTICATION, "EXTERNAL"
when i try to authenticate and bind to the ldap , i get Invalid Authentication method
So your LDAP server doesn't support EXTERNAL authentication.