Consuming Java / Spring rest service using HTTPs(and certificate) - java

I have to consume a rest service which is running on HTTPs. The producer has given me the certificate and method arguments. Could you please let me know how to consume the service and how to use the certificate in code. I am using Spring 4, Java 8. Please share the code snippet.

If it is just an one way SSL where consumer validates the identity of the service, you simply need to import the certificate provided by the service(producers certificate) to you trust store (CACerts file) or write your own trust manager.
For 2 Way SSL where service also authenticate the client's identity, you not only need to validate the identity of the service, you also need to send your certificate to the service so that service can take decision on it.
Following snippet is for 2 way SSL, but you can easily adopt it to 1 way SSL by commenting out the portion which sends client certicate to the server.
// 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
RestTemplate restTemplate = new RestTemplate();
try {
String keyPassphrase = "changeit";
KeyStore keyStore = KeyStore.getInstance("jks");
keyStore.load(new FileInputStream("c:\\jks\\client.jks"), keyPassphrase.toCharArray());
KeyManagerFactory kmfactory = KeyManagerFactory.getInstance(KeyManagerFactory.getDefaultAlgorithm());
kmfactory.init(keyStore, keyPassphrase.toCharArray());
KeyManager[] keyManager = kmfactory.getKeyManagers();
SSLContext sc = SSLContext.getInstance("TLS");
sc.init(keyManager, trustAllCerts, new SecureRandom());
HttpsURLConnection.setDefaultSSLSocketFactory(sc.getSocketFactory());
HttpsURLConnection.setDefaultHostnameVerifier(new javax.net.ssl.HostnameVerifier(){
public boolean verify(String hostname,
javax.net.ssl.SSLSession sslSession) {
if (hostname.equals("localhost")) {
return true;
}
return false;
}
});
} catch (Exception e) {
;
}
System.out.println("Testing listAllUsers API-----------");
restTemplate.setErrorHandler(new ResponseErrorHandler(){
#Override
public void handleError(ClientHttpResponse rs) throws IOException {
InputStream in = rs.getBody();
String str = new String("");
int len =0;
while((len = in.available()) > 0){
byte[] bytes = new byte[len];
in.read(bytes);
str = str + new String (bytes, "UTF-8");
}
System.out.println(str);
}
#Override
public boolean hasError(ClientHttpResponse rs) throws IOException {
return false;
}
});
try{
String usersMap = restTemplate.getForObject(REST_SERVICE_URI+"/user/shailendra/", String.class);`

Related

Swagger generated client code not able to call a https localhost endpoint

I have a pretty brittle and complicated development setup. I have a ssh tunnel to a machine that exposes a machine on that network, when ssh tunnel is in place, I can access my api easily via Postman or curl like this.
curl -X GET -u admin:admin123 -k "https://172.23.1.175/api/storage/aggregates/?fields=space&return_records=true&return_timeout=15" -H "accept: application/hal+json"
My client code is created via swagger, for development purpose I want to be able to try within my java application in IntelliJ to make the actual API call. I know this is completely frowned up on. The error I get is this:
io.swagger.ontap.client.ApiException: javax.net.ssl.SSLPeerUnverifiedException: Hostname localhost not verified:
certificate: sha1/o6KF0+STnresD1HaPMVoHNMgwpY=
DN: C=US, CN=ontapcloud-net0ejmuaf-1
subjectAltNames: []
This is a newer error after I tried to import the cert to my java config, before it was like this.
cause 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
Can the code below be modified to accommodate so the request will pass through?
private void applySslSettings() {
try {
TrustManager[] trustManagers = null;
HostnameVerifier hostnameVerifier = null;
System.out.println("verifyingSsl: " + verifyingSsl);
if (!verifyingSsl) {
TrustManager trustAll = new X509TrustManager() {
#Override
public void checkClientTrusted(X509Certificate[] chain, String authType) throws CertificateException {}
#Override
public void checkServerTrusted(X509Certificate[] chain, String authType) throws CertificateException {}
#Override
public X509Certificate[] getAcceptedIssuers() { return null; }
};
SSLContext sslContext = SSLContext.getInstance("TLS");
trustManagers = new TrustManager[]{ trustAll };
hostnameVerifier = new HostnameVerifier() {
#Override
public boolean verify(String hostname, SSLSession session) { return true; }
};
} else if (sslCaCert != null) {
char[] password = null; // Any password will work.
CertificateFactory certificateFactory = CertificateFactory.getInstance("X.509");
Collection<? extends Certificate> certificates = certificateFactory.generateCertificates(sslCaCert);
if (certificates.isEmpty()) {
throw new IllegalArgumentException("expected non-empty set of trusted certificates");
}
KeyStore caKeyStore = newEmptyKeyStore(password);
int index = 0;
for (Certificate certificate : certificates) {
String certificateAlias = "ca" + Integer.toString(index++);
caKeyStore.setCertificateEntry(certificateAlias, certificate);
}
TrustManagerFactory trustManagerFactory = TrustManagerFactory.getInstance(TrustManagerFactory.getDefaultAlgorithm());
trustManagerFactory.init(caKeyStore);
trustManagers = trustManagerFactory.getTrustManagers();
}
if (keyManagers != null || trustManagers != null) {
SSLContext sslContext = SSLContext.getInstance("TLS");
sslContext.init(keyManagers, trustManagers, new SecureRandom());
httpClient.setSslSocketFactory(sslContext.getSocketFactory());
} else {
httpClient.setSslSocketFactory(null);
}
httpClient.setHostnameVerifier(hostnameVerifier);
} catch (GeneralSecurityException e) {
throw new RuntimeException(e);
}
}
The solution was rather simple, there is attribute that swagger offers that took care of my issue.
ApiClient apiClient = new ApiClient();
apiClient.setBasePath("https://localhost:8077/api");
apiClient.setVerifyingSsl(false);

Request with automatic or user selection of appropriate client certificate

I'm developing an hybrid cordova app which might connect to different servers. Some of them do require a client certificate.
On an Android mobile the corresponding root cert + client certificate is installed.
On Chrome browser I get the following dialog to choose the corresponding client certificate for the Web connection.
With the cordova plugin cordova-client-cert-authentication the same dialog pops up for Http(s) requests within the WebView.
My question is how to achieve a automatic certificate selection on Http(s) requests on the native Android platform without explicitly declaring the corresponding client certificate. Or is there something similiar to the user selection of certificate like implemented on Chrome?
This is the current implementation, which throws a handshake exception:
try {
URL url = new URL( versionUrl );
HttpsURLConnection urlConnection = ( HttpsURLConnection ) url.openConnection();
urlConnection.setConnectTimeout( 10000 );
InputStream in = urlConnection.getInputStream();
}
catch(Exception e)
{
//javax.net.ssl.SSLHandshakeException: Handshake failed
}
You can use a certificate previously installed in Android KeyChain (the system key store) extending X509ExtendedKeyManager to configure the SSLContext used by URLConnection
The certificate is referenced by an alias that you need. To prompt user for selection with a dialog similar to chrome use:
KeyChain.choosePrivateKeyAlias(this, this, // Callback
new String[] {"RSA", "DSA"}, // Any key types.
null, // Any issuers.
null, // Any host
-1, // Any port
DEFAULT_ALIAS);
This is the code to configure the SSL connection using a custom KeyManager. It uses the default TrustManager and HostnameVerifier. You will need to configure them if the server is using a self signed certificate not present in Android default truststore (trusting all certificates is not recommended)
//Configure trustManager if needed
TrustManager[] trustManagers = null;
//Configure keyManager to select the private key and the certificate chain from KeyChain
KeyManager keyManager = KeyChainKeyManager.fromAlias(
context, mClientCertAlias);
//Configure SSLContext
SSLContext sslContext = SSLContext.getInstance("TLS");
sslContext.init(new KeyManager[] {keyManager}, trustManagers, null);
//Perform the connection
URL url = new URL( versionUrl );
HttpsURLConnection urlConnection = ( HttpsURLConnection ) url.openConnection();
urlConnection.setSSLSocketFactory(sslContext.getSocketFactory());
//urlConnection.setHostnameVerifier(hostnameVerifier); //Configure hostnameVerifier if needed
urlConnection.setConnectTimeout( 10000 );
InputStream in = urlConnection.getInputStream();
Finally here you have and a full implementation of the custom X509ExtendedKeyManager extracted from here and here that is in charge of selecting the client certificate. I have extracted the required code.
public static class KeyChainKeyManager extends X509ExtendedKeyManager {
private final String mClientAlias;
private final X509Certificate[] mCertificateChain;
private final PrivateKey mPrivateKey;
/**
* Builds an instance of a KeyChainKeyManager using the given certificate alias.
* If for any reason retrieval of the credentials from the system {#link android.security.KeyChain} fails,
* a {#code null} value will be returned.
*/
public static KeyChainKeyManager fromAlias(Context context, String alias)
throws CertificateException {
X509Certificate[] certificateChain;
try {
certificateChain = KeyChain.getCertificateChain(context, alias);
} catch (KeyChainException e) {
throw new CertificateException(e);
} catch (InterruptedException e) {
throw new CertificateException(e);
}
PrivateKey privateKey;
try {
privateKey = KeyChain.getPrivateKey(context, alias);
} catch (KeyChainException e) {
throw new CertificateException(e);
} catch (InterruptedException e) {
throw new CertificateException(e);
}
if (certificateChain == null || privateKey == null) {
throw new CertificateException("Can't access certificate from keystore");
}
return new KeyChainKeyManager(alias, certificateChain, privateKey);
}
private KeyChainKeyManager(
String clientAlias, X509Certificate[] certificateChain, PrivateKey privateKey) {
mClientAlias = clientAlias;
mCertificateChain = certificateChain;
mPrivateKey = privateKey;
}
#Override
public String chooseClientAlias(String[] keyTypes, Principal[] issuers, Socket socket) {
return mClientAlias;
}
#Override
public X509Certificate[] getCertificateChain(String alias) {
return mCertificateChain;
}
#Override
public PrivateKey getPrivateKey(String alias) {
return mPrivateKey;
}
#Override
public final String chooseServerAlias( String keyType, Principal[] issuers, Socket socket) {
// not a client SSLSocket callback
throw new UnsupportedOperationException();
}
#Override
public final String[] getClientAliases(String keyType, Principal[] issuers) {
// not a client SSLSocket callback
throw new UnsupportedOperationException();
}
#Override
public final String[] getServerAliases(String keyType, Principal[] issuers) {
// not a client SSLSocket callback
throw new UnsupportedOperationException();
}
}
}
I did not test it. Report any error!
If your URLs are still in development stage (not production version), you can skip those SSL/NON-SSL certificates installing to access the URLs.
Here is how to skip SSL validation :
Call when activity onCreate() or where your need before accessing URL.
public static void skipSSLValidation() {
try {
TrustManager[] trustAllCerts = new TrustManager[]{
new X509TrustManager() {
public X509Certificate[] getAcceptedIssuers() {
/* Create a new array with room for an additional trusted certificate. */
return new X509Certificate[0];
}
#Override
public void checkClientTrusted(X509Certificate[] certs, String authType) {
}
#Override
public void checkServerTrusted(X509Certificate[] certs, String authType) {
}
}
};
SSLContext sc = SSLContext.getInstance("SSL");
sc.init(null, trustAllCerts, new SecureRandom());
HttpsURLConnection.setDefaultSSLSocketFactory(sc.getSocketFactory());
HttpsURLConnection.setDefaultHostnameVerifier(new HostnameVerifier() {
#Override
public boolean verify(String arg0, SSLSession arg1) {
return true;
}
});
} catch (Exception e) {
// pass
}
}
Note : If your HTTPS URLs are valid, you will no require to use server-generated certificates. You should using this method for testing/development only. For release/production you don't have to use this method.

Java webservices Tomcat Jax-WS ssl, How to set up Client to authenticate certificate

Hell everyone,
I have set up the java webservices on tomcat + ssl Connection by the link below
http://www.mkyong.com/webservices/jax-ws/deploy-jax-ws-web-services-on-tomcat-ssl-connection/ . It works fine.
My question now is here in this code the client part does not authencticate certificate or ssl connection, I have part only to check the hostname,by hostname verifier,but now I have a self-signed certificate, and not sure what should I do.
how to extend this class. I find few codes from forum but i do not get an entire idea, where the keystore or truststore come from. Reference to any blog or link that guide me is much appreciated.
My Client code is below
public IExample create() throws MalformedURLException{
try{
TrustManager[] trustAllCerts = new TrustManager[] { new X509TrustManager() {
public X509Certificate[] getAcceptedIssuers() {
return null;
}
public void checkClientTrusted(X509Certificate[] certs, String authType) {
// Trust always
}
public void checkServerTrusted(X509Certificate[] certs, String authType) {
// Trust always
}
} };
// Install the all-trusting trust manager
sc = SSLContext.getInstance("SSL");
// Create empty HostnameVerifier
HostnameVerifier hv = new HostnameVerifier() {
public boolean verify(String hostname, SSLSession arg1) {
if(hostname.equals(arg1.getPeerHost()) && hostname.equals("example.com"))
{
return true;
}else{
return false;
}
}
};
sc.init(null,trustAllCerts , new java.security.SecureRandom());
HttpsURLConnection.setDefaultSSLSocketFactory(sc.getSocketFactory());
HttpsURLConnection.setDefaultHostnameVerifier(hv);
}
catch(Exception e){
e.printStackTrace();
}
try{
URL url = new URL( urlString );
//1st argument service URI, refer to wsdl document above
//2nd argument is service name, refer to wsdl document above
QName qname = new QName("http://synchronization.ws/", "ExampleImplclass");
Service service = Service.create(url, qname);
IExample iExample = service.getPort(IExample.class);
return iExample;
}catch(Exception e)
{
e.printStackTrace();
return null;
}
}

Does OkHttp support accepting self-signed SSL certs?

I'm working for a customer who has a server with self-signed SSL cert.
I'm using Retrofit + CustomClient using wrapped OkHttp client:
RestAdapter restAdapter = new RestAdapter.Builder().setEndpoint(Config.BASE_URL + Config.API_VERSION)
.setClient(new CustomClient(new OkClient(), context))
.build();
Does OkHttp support calling Self-Signed SSL cert server by default?
By the way. Which client is using Retrofit by default? I thought it was OkHttp but when I researched a bit more I realized I needed to import OkHttp dependencies
Yes, It does.
Retrofit allows you to set your custom HTTP client, that is configured to your needs.
As for self-signed SSL certs there is a discussion here. The link contains code samples to add self-signed SSL to Android's DefaultHttpClient and to load this client to Retrofit.
If you need OkHttpClient to accept self signed SSL, you need to pass it custom javax.net.ssl.SSLSocketFactory instance via setSslSocketFactory(SSLSocketFactory sslSocketFactory) method.
The easiest method to get a socket factory is to get one from javax.net.ssl.SSLContext as discussed here.
Here is a sample for configuring OkHttpClient:
OkHttpClient client = new OkHttpClient();
KeyStore keyStore = readKeyStore(); //your method to obtain KeyStore
SSLContext sslContext = SSLContext.getInstance("SSL");
TrustManagerFactory trustManagerFactory = TrustManagerFactory.getInstance(TrustManagerFactory.getDefaultAlgorithm());
trustManagerFactory.init(keyStore);
KeyManagerFactory keyManagerFactory = KeyManagerFactory.getInstance(KeyManagerFactory.getDefaultAlgorithm());
keyManagerFactory.init(keyStore, "keystore_pass".toCharArray());
sslContext.init(keyManagerFactory.getKeyManagers(),trustManagerFactory.getTrustManagers(), new SecureRandom());
client.setSslSocketFactory(sslContext.getSocketFactory());
Updated code for okhttp3 (using builder):
OkHttpClient client = new OkHttpClient.Builder()
.sslSocketFactory(sslContext.getSocketFactory())
.build();
the client here is now configured to use certificates from your KeyStore. However it will only trust the certificates in your KeyStore and will not trust anything else, even if your system trust them by default. (If you have only self signed certs in your KeyStore and try to connect to Google main page via HTTPS you will get SSLHandshakeException).
You can obtain KeyStore instance from file as seen in docs:
KeyStore readKeyStore() {
KeyStore ks = KeyStore.getInstance(KeyStore.getDefaultType());
// get user password and file input stream
char[] password = getPassword();
java.io.FileInputStream fis = null;
try {
fis = new java.io.FileInputStream("keyStoreName");
ks.load(fis, password);
} finally {
if (fis != null) {
fis.close();
}
}
return ks;
}
If you are on android you can put it in res/raw folder and get it from a Context instance using
fis = context.getResources().openRawResource(R.raw.your_keystore_filename);
There are several discussions on how to create your keystore. For example here
Another thing to note, if you pre-install the CA on the device, you can make regular https calls with OKHttp, and no special ssl hoops. The key is to add the network security configs to your manifest.
The key for me to know to do this was that I was getting the following exception.
"Trust anchor for certification path not found."
Here is a good article from Google about how to configure it.
https://developer.android.com/training/articles/security-config
Here is an example of my network_security_config.xml
<?xml version="1.0" encoding="UTF-8" ?>
<network-security-config>
<base-config cleartextTrafficPermitted="false">
<trust-anchors>
<certificates src="user"/>
<certificates src="system"/>
</trust-anchors>
</base-config>
</network-security-config>
For okhttp3.OkHttpClient Version com.squareup.okhttp3:okhttp:3.2.0 you have to use the code below :
import okhttp3.Call;
import okhttp3.Cookie;
import okhttp3.CookieJar;
import okhttp3.Headers;
import okhttp3.HttpUrl;
import okhttp3.MediaType;
import okhttp3.OkHttpClient;
import okhttp3.Request;
import okhttp3.RequestBody;
import okhttp3.Response;
import okhttp3.ResponseBody;
......
OkHttpClient.Builder clientBuilder = client.newBuilder().readTimeout(LOGIN_TIMEOUT_SEC, TimeUnit.SECONDS);
boolean allowUntrusted = true;
if ( allowUntrusted) {
Log.w(TAG,"**** Allow untrusted SSL connection ****");
final TrustManager[] trustAllCerts = new TrustManager[]{new X509TrustManager() {
#Override
public X509Certificate[] getAcceptedIssuers() {
X509Certificate[] cArrr = new X509Certificate[0];
return cArrr;
}
#Override
public void checkServerTrusted(final X509Certificate[] chain,
final String authType) throws CertificateException {
}
#Override
public void checkClientTrusted(final X509Certificate[] chain,
final String authType) throws CertificateException {
}
}};
SSLContext sslContext = SSLContext.getInstance("SSL");
sslContext.init(null, trustAllCerts, new java.security.SecureRandom());
clientBuilder.sslSocketFactory(sslContext.getSocketFactory());
HostnameVerifier hostnameVerifier = new HostnameVerifier() {
#Override
public boolean verify(String hostname, SSLSession session) {
Log.d(TAG, "Trust Host :" + hostname);
return true;
}
};
clientBuilder.hostnameVerifier( hostnameVerifier);
}
final Call call = clientBuilder.build().newCall(request);
Two methods from our app to get OkHttpClient 3.0 instance that recognizes your self-signed certificates from your keystore (uses prepared pkcs12 certificate file in your Android project "raw" resources folder):
private static OkHttpClient getSSLClient(Context context) throws
NoSuchAlgorithmException,
KeyStoreException,
KeyManagementException,
CertificateException,
IOException {
OkHttpClient client;
SSLContext sslContext;
SSLSocketFactory sslSocketFactory;
TrustManager[] trustManagers;
TrustManagerFactory trustManagerFactory;
X509TrustManager trustManager;
trustManagerFactory = TrustManagerFactory.getInstance(TrustManagerFactory.getDefaultAlgorithm());
trustManagerFactory.init(readKeyStore(context));
trustManagers = trustManagerFactory.getTrustManagers();
if (trustManagers.length != 1 || !(trustManagers[0] instanceof X509TrustManager)) {
throw new IllegalStateException("Unexpected default trust managers:" + Arrays.toString(trustManagers));
}
trustManager = (X509TrustManager) trustManagers[0];
sslContext = SSLContext.getInstance("TLS");
sslContext.init(null, new TrustManager[]{trustManager}, null);
sslSocketFactory = sslContext.getSocketFactory();
client = new OkHttpClient.Builder()
.sslSocketFactory(sslSocketFactory, trustManager)
.build();
return client;
}
/**
* Get keys store. Key file should be encrypted with pkcs12 standard. It can be done with standalone encrypting java applications like "keytool". File password is also required.
*
* #param context Activity or some other context.
* #return Keys store.
* #throws KeyStoreException
* #throws CertificateException
* #throws NoSuchAlgorithmException
* #throws IOException
*/
private static KeyStore readKeyStore(Context context) throws
KeyStoreException,
CertificateException,
NoSuchAlgorithmException,
IOException {
KeyStore keyStore;
char[] PASSWORD = "12345678".toCharArray();
ArrayList<InputStream> certificates;
int certificateIndex;
InputStream certificate;
certificates = new ArrayList<>();
certificates.add(context.getResources().openRawResource(R.raw.ssl_pkcs12));
keyStore = KeyStore.getInstance("pkcs12");
for (Certificate certificate : certificates) {
try {
keyStore.load(certificate, PASSWORD);
} finally {
if (certificate != null) {
certificate.close();
}
}
}
return keyStore;
}
I had the same problem and I fixed it with the okhttp client as follow:
1.) Add the certificate file to src/main/res/raw/, which includes this content:
-----BEGIN CERTIFICATE-----
...=
-----END CERTIFICATE-----
2.) Instanciate the okHttpClient:
OkHttpClient client = new OkHttpClient.Builder()
.sslSocketFactory(getSslContext(context).getSocketFactory())
.build();
3.) Here is the used getSslContext(Context context) method:
SSLContext getSslContext(Context context) throws Exception {
KeyStore ks = KeyStore.getInstance(KeyStore.getDefaultType()); // "BKS"
ks.load(null, null);
InputStream is = context.getResources().openRawResource(R.raw.certificate);
String certificate = Converter.convertStreamToString(is);
// generate input stream for certificate factory
InputStream stream = IOUtils.toInputStream(certificate);
// CertificateFactory
CertificateFactory cf = CertificateFactory.getInstance("X.509");
// certificate
Certificate ca;
try {
ca = cf.generateCertificate(stream);
} finally {
is.close();
}
ks.setCertificateEntry("my-ca", ca);
// TrustManagerFactory
String algorithm = TrustManagerFactory.getDefaultAlgorithm();
TrustManagerFactory tmf = TrustManagerFactory.getInstance(algorithm);
// Create a TrustManager that trusts the CAs in our KeyStore
tmf.init(ks);
// Create a SSLContext with the certificate that uses tmf (TrustManager)
sslContext = SSLContext.getInstance("TLS");
sslContext.init(null, tmf.getTrustManagers(), new SecureRandom());
return sslContext;
}
If there is the need to add multiple certificates to the SslContext, here is the solution.
Against Retrofit 1.9 I was able to accept any certificate with the following strategy: use at your own risk! Accepting any certificate is dangerous and you should understand the consequences. Some relevant parts come from org.apache.http.ssl, so you may require some imports here.
// ...
Client httpClient = getHttpClient();
RestAdapter adapter = new RestAdapter.Builder()
.setClient(httpClient)
// ... the rest of your builder setup
.build();
// ...
private Client getHttpClient() {
try {
// Allow self-signed (and actually any) SSL certificate to be trusted in this context
TrustStrategy acceptingTrustStrategy = (X509Certificate[] chain, String authType) -> true;
SSLContext sslContext = org.apache.http.ssl.SSLContexts.custom()
.loadTrustMaterial(null, acceptingTrustStrategy)
.build();
sslContext.getSocketFactory();
SSLSocketFactory sf = sslContext.getSocketFactory();
OkHttpClient client = new OkHttpClient();
client.setSslSocketFactory(sf);
return new OkClient(client);
} catch (Exception e) {
throw new RuntimeException("Failed to create new HTTP client", e);
}
}
I know that this post is quite old, bui i want to share the solution that worked for me with the latest update of OkHttp, the 3.12.1 version in the time i'm writing.
First of all you need to obtain the KeyStore object that will be then added to the TrustManager:
/**
* #param context The Android context to be used for retrieving the keystore from raw resource
* #return the KeyStore read or null on error
*/
private static KeyStore readKeyStore(Context context) {
char[] password = "keystore_password".toCharArray();
// for non-android usage:
// try(FileInputStream is = new FileInputStream(keystoreName)) {
try(InputStream is = context.getResources().openRawResource(R.raw.keystore)) {
KeyStore ks = KeyStore.getInstance(KeyStore.getDefaultType());
ks.load(is, password);
return ks;
} catch (CertificateException e) {
e.printStackTrace();
} catch (NoSuchAlgorithmException e) {
e.printStackTrace();
} catch (IOException e) {
e.printStackTrace();
} catch (KeyStoreException e) {
e.printStackTrace();
}
return null;
}
Now you can get the builded OkHttpClient with the self-signed certificate in your keystore:
/**
* #param context The Android context used to obtain the KeyStore
* #return the builded OkHttpClient or null on error
*/
public static OkHttpClient getOkHttpClient(Context context) {
try {
TrustManagerFactory trustManagerFactory = TrustManagerFactory
.getInstance(TrustManagerFactory.getDefaultAlgorithm());
trustManagerFactory.init(readKeyStore(context));
X509TrustManager trustManager = (X509TrustManager) trustManagerFactory.getTrustManagers()[0];
SSLContext sslContext = SSLContext.getInstance("TLS");
sslContext.init(null, new TrustManager[]{trustManager}, null);
return new OkHttpClient.Builder()
.hostnameVerifier((hostname, session) -> {
HostnameVerifier hv = HttpsURLConnection.getDefaultHostnameVerifier();
/* Never return true without verifying the hostname, otherwise you will be vulnerable
to man in the middle attacks. */
return hv.verify("your_hostname_here", session);
})
.sslSocketFactory(sslContext.getSocketFactory(), trustManager)
.build();
} catch (NoSuchAlgorithmException e) {
e.printStackTrace();
} catch (KeyStoreException e) {
e.printStackTrace();
} catch (KeyManagementException e) {
e.printStackTrace();
} catch (Exception e) {
e.printStackTrace();
}
return null;
}
Remember that it is highly discouraged to return always true in the hostnameVerifier to avoid risk of man in the middle attacks.
I find answer from :
https://github.com/square/okhttp/blob/master/samples/guide/src/main/java/okhttp3/recipes/CustomTrust.java
It uses HandshakeCertificates to add certificates.
HandshakeCertificates certificates = new HandshakeCertificates.Builder()
.addTrustedCertificate(letsEncryptCertificateAuthority)
.addTrustedCertificate(entrustRootCertificateAuthority)
.addTrustedCertificate(comodoRsaCertificationAuthority)
// Uncomment if standard certificates are also required.
//.addPlatformTrustedCertificates()
.build();
client = new OkHttpClient.Builder()
.sslSocketFactory(certificates.sslSocketFactory(), certificates.trustManager())
.build();
If you have trust certificates in store, you can use it as below:
.......
List<X509Certificate> certificates = getCertificatesFromTrustStore();
Builder certificateBuilder = new HandshakeCertificates.Builder();
for (X509Certificate x509Certificate : certificates) {
certificateBuilder.addTrustedCertificate(x509Certificate);
}
HandshakeCertificates handshakeCertificates = certificateBuilder.build();
.......
//To get certificates from a keystore
private List<X509Certificate> getCertificatesFromTrustStore() throws Exception {
KeyStore truststore = KeyStore.getInstance("JKS");
truststore.load(new FileInputStream("d:\certs.jsk"), "mypassword".toCharArray());
PKIXParameters params = new PKIXParameters(truststore);
Set<TrustAnchor> trustAnchors = params.getTrustAnchors();
LOG.debug("{} certificates found in {} which will be used", trustAnchors.size(), trustStorePath);
List<X509Certificate> certificates = trustAnchors.stream()
.map(TrustAnchor::getTrustedCert)
.collect(Collectors.toList());
return certificates;
}
If you need to provide your own certificate, you can pass it like this:
Manifest:
<application android:networkSecurityConfig="#xml/network_security_config"
... >
res/xml/network_security_config.xml:
<?xml version="1.0" encoding="UTF-8" ?>
<network-security-config>
<base-config cleartextTrafficPermitted="false">
<trust-anchors>
<certificates src="#raw/your_PEM_formatted_cert" />
<certificates src="user" />
<certificates src="system" />
</trust-anchors>
</base-config>
The following piece of code allows you to create an OkHttp client that can be used with Retrofit. Mailmustdie's answer is "better" in the sense that it is more secure, but the code snippet below is faster to implement
import com.squareup.okhttp.Headers;
import com.squareup.okhttp.MediaType;
import com.squareup.okhttp.OkHttpClient;
import com.squareup.okhttp.RequestBody;
import com.squareup.okhttp.ResponseBody;
import java.io.IOException;
import java.io.InputStream;
import java.util.ArrayList;
import java.util.List;
import java.util.concurrent.TimeUnit;
import okio.BufferedSink;
import retrofit.client.Header;
import retrofit.client.OkClient;
import retrofit.client.Request;
import retrofit.client.Response;
import retrofit.mime.TypedInput;
import retrofit.mime.TypedOutput;
import java.security.SecureRandom;
import java.security.cert.CertificateException;
import java.security.cert.X509Certificate;
import javax.net.ssl.HostnameVerifier;
import javax.net.ssl.SSLContext;
import javax.net.ssl.SSLSession;
import javax.net.ssl.TrustManager;
import javax.net.ssl.X509TrustManager;
public class TrustingOkClient extends OkClient {
static final int CONNECT_TIMEOUT_MILLIS = 15 * 1000; // 15s
static final int READ_TIMEOUT_MILLIS = 20 * 1000; // 20s
private static OkHttpClient generateDefaultOkHttp() {
OkHttpClient client = new OkHttpClient();
client.setConnectTimeout(CONNECT_TIMEOUT_MILLIS, TimeUnit.MILLISECONDS);
client.setReadTimeout(READ_TIMEOUT_MILLIS, TimeUnit.MILLISECONDS);
final TrustManager[] certs = new TrustManager[]{new X509TrustManager() {
#Override
public X509Certificate[] getAcceptedIssuers() {
return null;
}
#Override
public void checkServerTrusted(final X509Certificate[] chain,
final String authType) throws CertificateException {
}
#Override
public void checkClientTrusted(final X509Certificate[] chain,
final String authType) throws CertificateException {
}
}};
SSLContext ctx = null;
try {
ctx = SSLContext.getInstance("TLS");
ctx.init(null, certs, new SecureRandom());
} catch (final java.security.GeneralSecurityException ex) {
}
try {
final HostnameVerifier hostnameVerifier = new HostnameVerifier() {
#Override
public boolean verify(final String hostname,
final SSLSession session) {
return true;
}
};
client.setHostnameVerifier(hostnameVerifier);
client.setSslSocketFactory(ctx.getSocketFactory());
} catch (final Exception e) {
}
return client;
}
private final OkHttpClient client;
public TrustingOkClient() {
this(generateDefaultOkHttp());
}
public TrustingOkClient(OkHttpClient client) {
if (client == null) throw new NullPointerException("client == null");
this.client = client;
}
#Override public Response execute(Request request) throws IOException {
return parseResponse(client.newCall(createRequest(request)).execute());
}
static com.squareup.okhttp.Request createRequest(Request request) {
com.squareup.okhttp.Request.Builder builder = new com.squareup.okhttp.Request.Builder()
.url(request.getUrl())
.method(request.getMethod(), createRequestBody(request.getBody()));
List<Header> headers = request.getHeaders();
for (int i = 0, size = headers.size(); i < size; i++) {
Header header = headers.get(i);
String value = header.getValue();
if (value == null) value = "";
builder.addHeader(header.getName(), value);
}
return builder.build();
}
static Response parseResponse(com.squareup.okhttp.Response response) {
return new Response(response.request().urlString(), response.code(), response.message(),
createHeaders(response.headers()), createResponseBody(response.body()));
}
private static RequestBody createRequestBody(final TypedOutput body) {
if (body == null) {
return null;
}
final MediaType mediaType = MediaType.parse(body.mimeType());
return new RequestBody() {
#Override public MediaType contentType() {
return mediaType;
}
#Override public void writeTo(BufferedSink sink) throws IOException {
body.writeTo(sink.outputStream());
}
#Override public long contentLength() {
return body.length();
}
};
}
private static TypedInput createResponseBody(final ResponseBody body) {
try {
if (body.contentLength() == 0) {
return null;
}
return new TypedInput() {
#Override public String mimeType() {
MediaType mediaType = body.contentType();
return mediaType == null ? null : mediaType.toString();
}
#Override public long length() {
try {
return body.contentLength();
} catch (Exception exception) {
System.out.println(exception.toString());
}
throw new Error("createResponseBody has invalid length for its response");
}
#Override public InputStream in() throws IOException {
return body.byteStream();
}
};
} catch (Exception exception) {
System.out.println(exception.toString());
}
throw new Error("createResponseBody has invalid content length for its response");
}
private static List<Header> createHeaders(Headers headers) {
int size = headers.size();
List<Header> headerList = new ArrayList<Header>(size);
for (int i = 0; i < size; i++) {
headerList.add(new Header(headers.name(i), headers.value(i)));
}
return headerList;
}
}

Soap SSL handshake

My client is successfully getting response from server through HTTP.
SOAPConnectionFactory sfc = SOAPConnectionFactory.newInstance();
SOAPConnection connection = sfc.createConnection();
SOAPMessage soapMessageResponse = connection.call(soapRequest, new URL(serviceLocation));
I want SSL communication between client/server.
In another project I am successfully creating SSLSocketFactory from a KeyStore and TrustManagerFactory for SSL handshake.
How can I use SSLSocketFactory code in webservice client to make client SSL communication successful to call server.
I'm pretty sure it will use the default SSLContext. You can change that with SSLContext.setDefault().
SSLContext c = SSLContext.getInstance("SSL");
TrustManagerFactory tmf = TrustManagerFactory.getInstance("PKIX");
tmf.init(yourKeystore);
TrustManager tm = tmf.getTrustManagers()[0];
tm.
c.init(null, tm, null);
Here are some other values for the string parameters above.
If you need more complete control, you can implement your own subclass of SSLContext which returns your own implementation of SSLSocketFactory and set that SSLContext as the default:
public class MySSLContext extends SSLContext {
private SSLContext wrapped;
private SSLSocketFactory mySocketFactory;
public MySSLContext(SSLContext toWrap, SSLSocketFactory mySocketFactory) {
wrapped = toWrap;
this.mySocketFactory = mySocketFactory;
}
public SSLSocketFactory getSocketFactory() {
return mySocketFactory;
}
public SSLSessionContext getClientSessionContext() {
return wrapped;
}
// other delegates
}
Hi if you add this code your webservice class ı think your problem will be solve .
`
//just put it your somewhere
public static class miTM implements javax.net.ssl.TrustManager,
javax.net.ssl.X509TrustManager {
public java.security.cert.X509Certificate[] getAcceptedIssuers() {
return null;
}
public boolean isServerTrusted(
java.security.cert.X509Certificate[] certs) {
return true;
}
public boolean isClientTrusted(
java.security.cert.X509Certificate[] certs) {
return true;
}
public void checkServerTrusted(
java.security.cert.X509Certificate[] certs, String authType)
throws java.security.cert.CertificateException {
return;
}
public void checkClientTrusted(
java.security.cert.X509Certificate[] certs, String authType)
throws java.security.cert.CertificateException {
return;
}
}
// CAll This function in your webservice class .
private static void trustAllHttpsCertificates() throws Exception {
// Create a trust manager that does not validate certificate chains:
javax.net.ssl.TrustManager[] trustAllCerts =
new javax.net.ssl.TrustManager[1];
javax.net.ssl.TrustManager tm = new miTM();
trustAllCerts[0] = tm;
javax.net.ssl.SSLContext sc =
javax.net.ssl.SSLContext.getInstance("SSL");
sc.init(null, trustAllCerts, null);
javax.net.ssl.HttpsURLConnection.setDefaultSSLSocketFactory(
sc.getSocketFactory());
}
This line of code will not work in case of SSL.
SOAPMessage soapMessageResponse = connection.call(soapRequest, new URL(serviceLocation));
Create trustmanager and keymanagers from here.
In order to get response through SSL from axis2 webservice you need to open streams like given here

Categories

Resources