Get server certificate from failed connection attempt with HttpClient 4.2 - java

I'm currently adding a https download functionality to my application using the Apache HttpClient, specifically version 4.2.3.
I want to get my hands on the server certificate chain if the certificate validation fails. The exception that gets thrown SSLPeerUnverifiedException has no fields that provide any information.
try {
HttpResponse response = client.execute(get);
} catch (SSLPeerUnverifiedException e) {
// retrieve server certificate here
}
There is a way by injecting a TrustManager (to capture the certificates) into the SSLContext and recreating the SSLContext, SSLSocketFactory and HttpClient for each request. But, I would like to be able to reuse those instances for multiple, possible parallel, requests.

I used HC 4.3 for this example but should work exactly the same way with HC 4.2 though I would recommend upgrading
public static void main(final String[] args) throws Exception {
TrustManagerFactory tmfactory = TrustManagerFactory.getInstance(TrustManagerFactory.getDefaultAlgorithm());
tmfactory.init((KeyStore) null);
TrustManager[] tms = tmfactory.getTrustManagers();
if (tms != null) {
for (int i = 0; i < tms.length; i++) {
final TrustManager tm = tms[i];
if (tm instanceof X509TrustManager) {
tms[i] = new TrustManagerDelegate((X509TrustManager) tm);
}
}
}
SSLContext sslContext = SSLContext.getInstance("TLS");
sslContext.init(null, tms, null);
CloseableHttpClient httpClient = HttpClients.custom()
.setSslcontext(sslContext)
.build();
try {
CloseableHttpResponse response = httpClient.execute(new HttpGet("https://google.com/"));
try {
// do something usefull
} finally {
response.close();
}
} catch (SSLException ex) {
Throwable cause = ex.getCause();
if (cause instanceof MyCertificateException) {
X509Certificate[] chain = ((MyCertificateException) cause).getChain();
for (X509Certificate cert: chain) {
System.out.println(cert);
}
}
}
}
static class TrustManagerDelegate implements X509TrustManager {
private final X509TrustManager trustManager;
TrustManagerDelegate(final X509TrustManager trustManager) {
super();
this.trustManager = trustManager;
}
#Override
public void checkClientTrusted(
final X509Certificate[] chain, final String authType) throws CertificateException {
this.trustManager.checkClientTrusted(chain, authType);
}
#Override
public void checkServerTrusted(
final X509Certificate[] chain, final String authType) throws CertificateException {
try {
this.trustManager.checkServerTrusted(chain, authType);
} catch (CertificateException ex) {
throw new MyCertificateException(chain, ex);
}
}
#Override
public X509Certificate[] getAcceptedIssuers() {
return this.trustManager.getAcceptedIssuers();
}
}
static class MyCertificateException extends CertificateException {
private final X509Certificate[] chain;
MyCertificateException(final X509Certificate[] chain, final CertificateException ex) {
super(ex);
this.chain = chain;
}
public X509Certificate[] getChain() {
return chain;
}
}

Related

okHttpClient proxy connection gives unexpected end of stream exception

I am trying to make a proxy connection using okHttpClient. Target connection requires basic authentication that I am doing using Authenticator. Proxy requires a header to connect to the target URL. I am passing this header in the authenticator header.
Below is the code that I am trying but it gives unexpected end of stream exception.
#Configuration
public class FeignClientConfig {
#Value("localhost")
private String proxyHost;
#Value("8443")
private int proxyPort;
#Value("root")
private String user;
#Value("abcd")
private String password;
#Bean
public Client feignClientConfig() throws NoSuchAlgorithmException, KeyManagementException {
OkHttpClient okHttpClient;
Proxy proxy = new Proxy(Proxy.Type.HTTP, new InetSocketAddress(proxyHost, proxyPort));
TrustManager[] trustAllCerts = getTrustManagers();
// SSLContext sslContext = SSLContext.getInstance("SSL");
// sslContext.init(null, trustAllCerts, new SecureRandom());
// SSLSocketFactory sslSocketFactory = sslContext.getSocketFactory();
// Create all-trusting host name verifier
HostnameVerifier allHostsValid = (hostname, session) -> true;
okHttpClient = new OkHttpClient.Builder()
.retryOnConnectionFailure(true)
.sslSocketFactory(Objects.requireNonNull(getSSLSocketFactory()), (X509ExtendedTrustManager) trustAllCerts[0])
.hostnameVerifier(allHostsValid)
.proxy(proxy)
.proxyAuthenticator(authenticator())
.build();
return new feign.okhttp.OkHttpClient(okHttpClient);
}
private okhttp3.Authenticator authenticator() {
return (route, response) -> {
String credential = okhttp3.Credentials.basic(user, password);
return response.request().newBuilder()
.header("Authorization", credential)
.header("Content-Type", "application/json")
//this header is required by proxy not the target connection
.header("X-Connect-Client-Id", "xyz")
.build();
};
}
private SSLSocketFactory getSSLSocketFactory() {
try {
TrustStrategy acceptingTrustStrategy = new TrustStrategy() {
#Override
public boolean isTrusted(X509Certificate[] chain, String authType) throws CertificateException {
return true;
}
};
SSLContext sslContext = SSLContexts.custom().loadTrustMaterial(null, acceptingTrustStrategy).build();
return sslContext.getSocketFactory();
}
catch (Exception exception) {
log.error("Error while getting SSLSocketFactory: "+exception.getMessage());
}
return null;
}
private TrustManager[] getTrustManagers() {
return new TrustManager[]{
new X509ExtendedTrustManager() {
#Override
public X509Certificate[] getAcceptedIssuers() {
return new X509Certificate[]{};
}
#Override
public void checkClientTrusted(X509Certificate[] certs, String authType) {
}
#Override
public void checkServerTrusted(X509Certificate[] certs, String authType) {
}
#Override
public void checkServerTrusted(X509Certificate[] xcs, String string, Socket socket) throws CertificateException {
}
#Override
public void checkClientTrusted(X509Certificate[] chain, String authType, Socket socket) throws CertificateException {
}
#Override
public void checkClientTrusted(X509Certificate[] xcs, String string, SSLEngine ssle) throws CertificateException {
}
#Override
public void checkServerTrusted(X509Certificate[] xcs, String string, SSLEngine ssle) throws CertificateException {
}
}
};
}
}

How do I use an SSL client certificate with jersey client in java

I am trying to connect server using https url But still could not understand how should I add SSL certificate to jersey client
private static String post() throws Exception {
String url ="https://www.test.lk/services/erl/es/erl/view/index.action";
Client client =createClient();
try {
return client
.target(url)
.request()
.get()
.readEntity(String.class);
} finally {
client.close();
}
}
private static Client createClient() throws Exception {
SSLContext ctx = SSLContext.getInstance("SL");
ctx.init(null, getTrustManager(), new SecureRandom());
HostnameVerifier verifier = new HostnameVerifier() {
#Override
public boolean verify(String hostName, SSLSession sslSession) {
return true;
}
};
return ClientBuilder.newBuilder().sslContext(ctx).hostnameVerifier(verifier).build();
}
private static TrustManager[] getTrustManager() {
return new TrustManager[] { new X509TrustManager() {
public X509Certificate[] getAcceptedIssuers() {
return new X509Certificate[0];
}
public void checkServerTrusted(X509Certificate[] chain, String authType) throws CertificateException {
// Trust all servers
}
public void checkClientTrusted(X509Certificate[] chain, String authType) throws CertificateException {
// Trust all clients
}
} };
}
I found the solution. I just need to add certificate to the java KeyStore This helped me

Java equivalent to OpenSSL s_client command

I have a requirement to convert certain bash scripts to java and one such script connects to a server using openssl with a vanity-url as a parameter to check if that is connectable using that vanity-url. See command below
/usr/bin/openssl s_client -connect api.sys.found1.cf.company.com:443 -servername www.app.company.com 2>/dev/null
I wanted to do the similar activity in java and test the connectivity. Any ideas on how to make a open-ssl connection using Java .. Is this something that I need to use external Library ?
I was able to achieve this by referring the document over here
Basically, a SSLEngine needs to be created and make a successful handshake along with SNI
private SocketChannel createSocketChannel() throws IOException {
InetSocketAddress socketAddress = new InetSocketAddress(PROXY_ADDRESS, PROXY_PORT);
SocketChannel socketChannel = SocketChannel.open();
socketChannel.connect(socketAddress);
socketChannel.configureBlocking(false);
return socketChannel;
}
private SSLContext createSSLContext() throws KeyManagementException, NoSuchAlgorithmException {
SSLContext sslContext = SSLContext.getInstance(TLS_VERSION);
sslContext.init(null,null,null);
return sslContext;
}
private SSLEngine createSSLEngine() throws KeyManagementException, NoSuchAlgorithmException {
SSLContext sslContext = createSSLContext();
SSLEngine sslEngine = sslContext.createSSLEngine(PROXY_ADDRESS, PROXY_PORT);
sslEngine.setUseClientMode(true);
List<SNIServerName> serverNameList = new ArrayList<>();
serverNameList.add(new SNIHostName(SNI_HOST_NAME));
SSLParameters sslParameters = sslEngine.getSSLParameters();
sslParameters.setServerNames(serverNameList);
sslEngine.setSSLParameters(sslParameters);
return sslEngine;
}
After creating SSLEngine, the handShake has to begin
SocketChannel channel = createSocketChannel();
SSLEngine sslEngine = createSSLEngine();
doHandShake(sslEngine,channel);
private void doHandShake(SSLEngine sslEngine, SocketChannel socketChannel) throws Exception {
System.out.println("Going to do Handshake");
SSLSession session = sslEngine.getSession();
ByteBuffer myAppData = ByteBuffer.allocate(session.getApplicationBufferSize());
ByteBuffer peerAppData = ByteBuffer.allocate(session.getApplicationBufferSize());
ByteBuffer myNetData = ByteBuffer.allocate(session.getPacketBufferSize());
ByteBuffer peerNetData = ByteBuffer.allocate(session.getPacketBufferSize());
sslEngine.beginHandshake();
SSLEngineResult result;
handshakeStatus = sslEngine.getHandshakeStatus();
while (handshakeStatus != SSLEngineResult.HandshakeStatus.FINISHED &&
handshakeStatus != SSLEngineResult.HandshakeStatus.NOT_HANDSHAKING) {
switch (handshakeStatus) {
case NEED_UNWRAP:
if (! (socketChannel.read(peerNetData) <0)) {
peerNetData.flip();
result = sslEngine.unwrap(peerNetData,peerAppData);
peerNetData.compact();
handshakeStatus = result.getHandshakeStatus();
switch (result.getStatus()) {
case OK:
break;
}
}
break;
case NEED_WRAP:
myNetData.clear() ;// Empty the local network packet buffer
result = sslEngine.wrap(myAppData,myNetData);
handshakeStatus = result.getHandshakeStatus();
switch (result.getStatus()) {
case OK:
myNetData.flip();
while (myNetData.hasRemaining()) {
socketChannel.write(myNetData);
}
}
break;
case NEED_TASK:
Runnable task = sslEngine.getDelegatedTask();
if (null!=task) {
task.run();
}
handshakeStatus = sslEngine.getHandshakeStatus();
break;
}
}
Once the handShake is done. you can get the Principal object
Principal principal = sslEngine.getSession().getPeerPrincipal();
if (principal.getName().contains(SNI_HOST_NAME)) {
System.out.println("available ... ");
}else {
System.out.println("Not available");
}
call isAliasExists with your values ,
isAliasExists("api.sys.found1.cf.company.com","www.app.company.com");
Returns true if your alias (servername) is part of the cert,
private static boolean isAliasExists(String hostName, String alias) throws Exception {
String host;
int port;
String[] parts = hostName.split(":");
host = parts[0];
port = (parts.length == 1) ? 443 : Integer.parseInt(parts[1]);
// key store password
char[] passphrase = "changeit".toCharArray();
File file = new File("jssecacerts");
if (file.isFile() == false) {
char SEP = File.separatorChar;
File dir = new File(System.getProperty("java.home") + SEP + "lib" + SEP + "security");
file = new File(dir, "jssecacerts");
if (file.isFile() == false) {
file = new File(dir, "cacerts");
}
}
InputStream in = new FileInputStream(file);
KeyStore ks = KeyStore.getInstance(KeyStore.getDefaultType());
ks.load(in, passphrase);
in.close();
SSLContext context = SSLContext.getInstance("TLS");
TrustManagerFactory tmf = TrustManagerFactory.getInstance(TrustManagerFactory.getDefaultAlgorithm());
tmf.init(ks);
X509TrustManager defaultTrustManager = (X509TrustManager) tmf.getTrustManagers()[0];
SavingTrustManager tm = new SavingTrustManager(defaultTrustManager);
context.init(null, new TrustManager[] { tm }, null);
SSLSocketFactory factory = context.getSocketFactory();
SSLSocket socket = (SSLSocket) factory.createSocket(host, port);
socket.setSoTimeout(10000);
try {
System.out.println("Starting SSL handshake...");
socket.startHandshake();
socket.close();
System.out.println("Certificate is already trusted");
} catch (SSLException e) {
e.printStackTrace();
}
X509Certificate[] chain = tm.chain;
List<String> altNames=new ArrayList<String>();
for (X509Certificate cert: chain)
{
altNames.addAll(getSubjectAltNames(cert));
}
for(String altName: altNames) {
if(altName.trim().contains(alias))
return true;
}
if (chain == null) {
System.out.println("Could not obtain server certificate chain");
return false;
}
return false;
}
Returns list of alternative names from cert,
private static List<String> getSubjectAltNames(X509Certificate certificate) throws CertificateParsingException {
List<String> result = new ArrayList<>();
try {
Collection<?> subjectAltNames = certificate.getSubjectAlternativeNames();
if (subjectAltNames == null) {
return Collections.emptyList();
}
for (Object subjectAltName : subjectAltNames) {
List<?> entry = (List<?>) subjectAltName;
if (entry == null || entry.size() < 2) {
continue;
}
Integer altNameType = (Integer) entry.get(0);
if (altNameType == null) {
continue;
}
String altName = (String) entry.get(1);
if (altName != null) {
result.add(altName);
}
}
return result;
} catch (CertificateParsingException e) {
return Collections.emptyList();
}
}
custom trust manager,
private static class SavingTrustManager implements X509TrustManager {
private final X509TrustManager tm;
private X509Certificate[] chain;
SavingTrustManager(X509TrustManager tm) {
this.tm = tm;
}
public X509Certificate[] getAcceptedIssuers() {
return new X509Certificate[0];
// throw new UnsupportedOperationException();
}
public void checkClientTrusted(X509Certificate[] chain, String authType) throws CertificateException {
throw new UnsupportedOperationException();
}
public void checkServerTrusted(X509Certificate[] chain, String authType) throws CertificateException {
this.chain = chain;
tm.checkServerTrusted(chain, authType);
}
}
Without really knowing what SNI was I tried to get some insight with the test-program shown below.
I don't know the output from the openssl s_client command, but the test-program might prove to be a starting point. When the javax.net.debug output is turned on a lot of output is dumped of which only a few lines are relevant (see also the comments). That is a bit annoying and I do not have an easy solution for that. The TrustAllServers class can be reworked to inspect the certificates you expect to receive from the server (a.ka. host) for a particular domain. There might be other options (e.g. the socket's handshake methods) but this is as far as I got.
import java.io.BufferedReader;
import java.io.InputStreamReader;
import java.io.OutputStream;
import java.net.InetSocketAddress;
import java.net.Socket;
import java.nio.charset.StandardCharsets;
import java.security.KeyStore;
import java.security.cert.CertificateException;
import java.security.cert.X509Certificate;
import java.util.Arrays;
import javax.net.ssl.SNIHostName;
import javax.net.ssl.SSLContext;
import javax.net.ssl.SSLEngine;
import javax.net.ssl.SSLParameters;
import javax.net.ssl.SSLSocket;
import javax.net.ssl.SSLSocketFactory;
import javax.net.ssl.TrustManager;
import javax.net.ssl.TrustManagerFactory;
import javax.net.ssl.X509ExtendedTrustManager;
// https://stackoverflow.com/questions/56005883/java-equivalent-to-openssl-s-client-command
// Please use latest Java 8 version, bugs are around in earlier versions.
public class ServerNameTest {
public static void main(String[] args) {
// SSL debug options, see https://stackoverflow.com/q/23659564/3080094 and https://access.redhat.com/solutions/973783
// System.setProperty("javax.net.debug", "all");
// System.setProperty("javax.net.debug", "ssl:handshake");
// System.setProperty("jsse.enableSNIExtension", "true"); // "true" is the default
try {
ServerNameTest sn = new ServerNameTest();
// This will show 2 different server certificate chains.
// Note this is a random server - please pick your own one.
sn.test("major.io", "rackerhacker.com");
sn.test("major.io", "major.io");
} catch (Exception e) {
e.printStackTrace();
}
System.out.println("Done");
}
/*
* With javax.net.debug output you should see something like:
* <pre>
* *** ClientHello
* ...
* Extension server_name, server_name: [type=host_name (0), value=DOMAIN;]
* ...
* *** ServerHello
* ...
* Extension server_name, server_name:
* ...
* </pre>
* Note that if the server does not provide a value for server_name,
* it does not actually mean the server does not support SNI/server_name (see https://serverfault.com/a/506303)
*/
void test(String host, String domain) throws Exception {
SSLParameters sslParams = new SSLParameters();
if (domain != null && !domain.isEmpty()) {
sslParams.setServerNames(Arrays.asList(new SNIHostName(domain)));
}
// Only for webservers: set endpoint algorithm to HTTPS
sslParams.setEndpointIdentificationAlgorithm("HTTPS");
SSLSocketFactory sslsf = serverTrustingSSLFactory();
try (SSLSocket socket = (SSLSocket) sslsf.createSocket()) {
socket.setSSLParameters(sslParams);
socket.setSoTimeout(3_000);
System.out.println("Connecting to " + host + " for domain " + domain);
socket.connect(new InetSocketAddress(host, 443), 3_000);
// Trigger actual connection by getting the session.
socket.getSession();
System.out.println("Connected to remote " + socket.getRemoteSocketAddress());
try (BufferedReader input = new BufferedReader(new InputStreamReader(socket.getInputStream(), StandardCharsets.UTF_8))) {
try (OutputStream out = socket.getOutputStream()) {
System.out.println(">> OPTIONS");
out.write("OPTIONS * HTTP/1.1\r\n\r\n".getBytes(StandardCharsets.UTF_8));
System.out.println("<< " + input.readLine());
}
} catch (Exception e) {
System.err.println("No line read: " + e);
}
}
}
SSLSocketFactory serverTrustingSSLFactory() throws Exception {
SSLContext ctx = SSLContext.getInstance("TLS");
ctx.init(null, trustManager(), null);
return ctx.getSocketFactory();
}
TrustManager[] trustManager() throws Exception {
TrustManagerFactory tmf = TrustManagerFactory.getInstance(TrustManagerFactory.getDefaultAlgorithm());
tmf.init( (KeyStore) null);
// Must use "extended" type versus the default javax.net.ssl.X509TrustManager,
// otherwise the error "No subject alternative DNS name matching" keeps showing up.
X509ExtendedTrustManager defaultManager = null;
for (TrustManager trustManager : tmf.getTrustManagers()) {
if (trustManager instanceof X509ExtendedTrustManager) {
defaultManager = (X509ExtendedTrustManager) trustManager;
break;
}
}
if (defaultManager == null) {
throw new RuntimeException("Cannot find default X509ExtendedTrustManager");
}
return new TrustManager[] { new TrustAllServers(defaultManager) };
}
static void printChain(X509Certificate[] chain) {
try {
for (int i = 0; i < chain.length; i++) {
X509Certificate cert = chain[i];
System.out.println("Cert[" + i + "] " + cert.getSubjectX500Principal() + " :alt: " + cert.getSubjectAlternativeNames());
}
} catch (Exception e) {
e.printStackTrace();
}
}
static class TrustAllServers extends X509ExtendedTrustManager {
final X509ExtendedTrustManager defaultManager;
public TrustAllServers(X509ExtendedTrustManager defaultManager) {
this.defaultManager = defaultManager;
}
public void checkServerTrusted(X509Certificate[] chain, String authType) throws CertificateException {
try {
defaultManager.checkServerTrusted(chain, authType);
} catch (Exception e) {
System.err.println("Untrusted server: " + e);
}
printChain(chain);
}
public void checkServerTrusted(X509Certificate[] chain, String authType, Socket socket) throws CertificateException {
try {
defaultManager.checkServerTrusted(chain, authType, socket);
} catch (Exception e) {
System.err.println("Untrusted server for socket: " + e);
}
printChain(chain);
}
public void checkServerTrusted(X509Certificate[] chain, String authType, SSLEngine engine) throws CertificateException {
try {
defaultManager.checkServerTrusted(chain, authType, engine);
} catch (Exception e) {
System.err.println("Untrusted server for engine: " + e);
}
printChain(chain);
}
public void checkClientTrusted(X509Certificate[] chain, String authType) throws CertificateException {
defaultManager.checkClientTrusted(chain, authType);
}
public void checkClientTrusted(X509Certificate[] chain, String authType, Socket socket) throws CertificateException {
defaultManager.checkClientTrusted(chain, authType, socket);
}
public void checkClientTrusted(X509Certificate[] chain, String authType, SSLEngine engine) throws CertificateException {
defaultManager.checkClientTrusted(chain, authType, engine);
}
public X509Certificate[] getAcceptedIssuers() {
return defaultManager.getAcceptedIssuers();
}
}
}

an unsafe implementation of the interface X509TrustManager from google

I hava an app in Google Play, I received a mail from Google saying that:
Your app(s) listed at the end of this email use an unsafe implementation of the interface X509TrustManager. Specifically, the implementation ignores all SSL certificate validation errors when establishing an HTTPS connection to a remote host, thereby making your app vulnerable to man-in-the-middle attacks.
To properly handle SSL certificate validation, change your code in the checkServerTrusted method of your custom X509TrustManager interface to raise either CertificateException or IllegalArgumentException whenever the certificate presented by the server does not meet your expectations.
My app uses "https", my checkServerTrusted() is the following:
TrustManager tm = new X509TrustManager() {
public void checkClientTrusted(X509Certificate[] chain, String authType) throws CertificateException {
}
public void checkServerTrusted(X509Certificate[] chain, String authType) throws CertificateException {
}
public X509Certificate[] getAcceptedIssuers() {
return null;
}
};
Then I modify this function:
TrustManager tm = new X509TrustManager() {
public void checkClientTrusted(X509Certificate[] chain, String authType) throws CertificateException {
}
public void checkServerTrusted(X509Certificate[] chain, String authType) throws CertificateException {
if (chain == null) {
throw new IllegalArgumentException("checkServerTrusted: X509Certificate array is null");
}
if (!(chain.length > 0)) {
throw new IllegalArgumentException("checkServerTrusted: X509Certificate is empty");
}
if (!(null != authType && authType.equalsIgnoreCase("RSA"))) {
throw new CertificateException("checkServerTrusted: AuthType is not RSA");
}
}
public X509Certificate[] getAcceptedIssuers() {
return null;
}
the custom SSLSocketFactory:
public class MySSLSocketFactory extends SSLSocketFactory {
SSLContext sslContext = SSLContext.getInstance("TLS");
public MySSLSocketFactory(KeyStore ctx) throws NoSuchAlgorithmException, KeyManagementException, KeyStoreException, UnrecoverableKeyException {
super(ctx);
TrustManager tm = new X509TrustManager() {
public void checkClientTrusted(X509Certificate[] chain, String authType) throws CertificateException {
}
public void checkServerTrusted(X509Certificate[] chain, String authType) throws CertificateException {
}
public X509Certificate[] getAcceptedIssuers() {
return null;
}
};
sslContext.init(null, new TrustManager[]{tm}, null);
}
#Override
public Socket createSocket(Socket socket, String host, int port, boolean autoClose) throws IOException, UnknownHostException {
return sslContext.getSocketFactory().createSocket(socket, host, port, autoClose);
}
#Override
public Socket createSocket() throws IOException {
return sslContext.getSocketFactory().createSocket();
}
}
the HttpClient function:
private static HttpClient getHttpClient(int timeout) {
if (null == mHttpClient) {
try {
KeyStore trustStore = KeyStore.getInstance(KeyStore
.getDefaultType());
trustStore.load(null, null);
SSLSocketFactory sf = new MySSLSocketFactory(trustStore);
sf.setHostnameVerifier(SSLSocketFactory.ALLOW_ALL_HOSTNAME_VERIFIER);
HttpParams params = new BasicHttpParams();
HttpProtocolParams.setVersion(params, HttpVersion.HTTP_1_1);
HttpProtocolParams.setContentCharset(params,
HTTP.DEFAULT_CONTENT_CHARSET);
HttpProtocolParams.setUseExpectContinue(params, true);
ConnManagerParams.setTimeout(params, timeout);
HttpConnectionParams.setConnectionTimeout(params, timeout);
HttpConnectionParams.setSoTimeout(params, timeout);
SchemeRegistry schReg = new SchemeRegistry();
schReg.register(new Scheme("http", PlainSocketFactory
.getSocketFactory(), 80));
schReg.register(new Scheme("https", sf, 443));
ClientConnectionManager conManager = new ThreadSafeClientConnManager(
params, schReg);
mHttpClient = new DefaultHttpClient(conManager, params);
} catch (Exception e) {
e.printStackTrace();
return new DefaultHttpClient();
}
}
return mHttpClient;
}
But I do not know well about this,I just modify my code by what the email said,I think I have not sloved this problem.What is this warning all about? How to solve it?
I found this solution ,it works well!
X509TrustManager:
public class EasyX509TrustManager
implements X509TrustManager {
private X509TrustManager standardTrustManager = null;
/**
* Constructor for EasyX509TrustManager.
*/
public EasyX509TrustManager(KeyStore keystore)
throws NoSuchAlgorithmException, KeyStoreException {
super();
TrustManagerFactory factory = TrustManagerFactory.getInstance(TrustManagerFactory.getDefaultAlgorithm());
factory.init(keystore);
TrustManager[] trustmanagers = factory.getTrustManagers();
if (trustmanagers.length == 0) {
throw new NoSuchAlgorithmException("no trust manager found");
}
this.standardTrustManager = (X509TrustManager) trustmanagers[0];
}
/**
* #see X509TrustManager#checkClientTrusted(X509Certificate[], String authType)
*/
public void checkClientTrusted(X509Certificate[] certificates, String authType)
throws CertificateException {
standardTrustManager.checkClientTrusted(certificates, authType);
}
/**
* #see X509TrustManager#checkServerTrusted(X509Certificate[], String authType)
*/
public void checkServerTrusted(X509Certificate[] certificates, String authType)
throws CertificateException {
if ((certificates != null) && (certificates.length == 1)) {
certificates[0].checkValidity();
} else {
standardTrustManager.checkServerTrusted(certificates, authType);
}
}
/**
* #see X509TrustManager#getAcceptedIssuers()
*/
public X509Certificate[] getAcceptedIssuers() {
return this.standardTrustManager.getAcceptedIssuers();
}
}
SSLSocketFactory:
public class EasySSLSocketFactory implements LayeredSocketFactory {
private SSLContext sslcontext = null;
private static SSLContext createEasySSLContext() throws IOException {
try {
SSLContext context = SSLContext.getInstance("TLS");
context.init(null, new TrustManager[]{new EasyX509TrustManager(
null)}, null);
return context;
} catch (Exception e) {
throw new IOException(e.getMessage());
}
}
private SSLContext getSSLContext() throws IOException {
if (this.sslcontext == null) {
this.sslcontext = createEasySSLContext();
}
return this.sslcontext;
}
/**
* #see org.apache.http.conn.scheme.SocketFactory#connectSocket(Socket,
* String, int, InetAddress, int,
* HttpParams)
*/
public Socket connectSocket(Socket sock, String host, int port,
InetAddress localAddress, int localPort, HttpParams params)
throws IOException, UnknownHostException, ConnectTimeoutException {
int connTimeout = HttpConnectionParams.getConnectionTimeout(params);
int soTimeout = HttpConnectionParams.getSoTimeout(params);
InetSocketAddress remoteAddress = new InetSocketAddress(host, port);
SSLSocket sslsock = (SSLSocket) ((sock != null) ? sock : createSocket());
if ((localAddress != null) || (localPort > 0)) {
// we need to bind explicitly
if (localPort < 0) {
localPort = 0; // indicates "any"
}
InetSocketAddress isa = new InetSocketAddress(localAddress,
localPort);
sslsock.bind(isa);
}
sslsock.connect(remoteAddress, connTimeout);
sslsock.setSoTimeout(soTimeout);
return sslsock;
}
/**
* #see org.apache.http.conn.scheme.SocketFactory#createSocket()
*/
public Socket createSocket() throws IOException {
return getSSLContext().getSocketFactory().createSocket();
}
/**
* #see org.apache.http.conn.scheme.SocketFactory#isSecure(Socket)
*/
public boolean isSecure(Socket socket) throws IllegalArgumentException {
return true;
}
/**
* #see LayeredSocketFactory#createSocket(Socket,
* String, int, boolean)
*/
public Socket createSocket(Socket socket, String host, int port,
boolean autoClose) throws IOException, UnknownHostException {
return getSSLContext().getSocketFactory().createSocket(socket, host, port, autoClose);
}
// -------------------------------------------------------------------
// javadoc in org.apache.http.conn.scheme.SocketFactory says :
// Both Object.equals() and Object.hashCode() must be overridden
// for the correct operation of some connection managers
// -------------------------------------------------------------------
public boolean equals(Object obj) {
return ((obj != null) && obj.getClass().equals(
EasySSLSocketFactory.class));
}
public int hashCode() {
return EasySSLSocketFactory.class.hashCode();
}
}
Then:
SchemeRegistry schReg = new SchemeRegistry();
schReg.register(new Scheme("http", PlainSocketFactory
.getSocketFactory(), 80));
schReg.register(new Scheme("https", new EasySSLSocketFactory(), 443));
Your proposed modifications do not fix the security vulnerability. Your code will still accept any correctly formatted certificate, regardless of validity.
If you aren't sure how to properly verify certificates, you should just remove the custom trust manager. You don't need one unless you are doing something unusual.
The simplest way, is by not providing own custom TrustManager. Just use default TrustManager and it will do public key(X.509) validation and verification for you.
Make use of the default X509trustmanager's method only which are checkServerTrusted(chain, authType) and they will take care of all validation appropriately.

Can I add a new certificate to the keystore without restarting the JVM?

I'd like to import a new certificate into the keystore without restarting a running service. Is that possible?
Alternatively, is it possible to specify a certificate to use that's not in the keystore for a specific URL connection?
Turns out you can specify specific certificates to use for specific URL fetches; essentially, you need to create your own TrustManager and swap it in, like so:
public String fetchFromUrl(String urlString) throws IOException {
URL url = new URL(urlString);
URLConnection conn = url.openConnection();
if (conn instanceof HttpsURLConnection && shouldSubstituteCert(url)) {
HttpsURLConnection sslConn = (HttpsURLConnection) conn;
try {
SSLContext context = SSLContext.getInstance("SSL");
context.init(null, new TrustManager[] {new MyTrustManager()}, null);
sslConn.setSSLSocketFactory(context.getSocketFactory());
} catch (Exception e) {
e.printStackTrace();
throw new IOException("Error creating custom keystore", e);
}
}
return readAll(conn.getInputStream());
}
private static class MyTrustManager implements X509TrustManager {
private final X509TrustManager trustManager;
public MyTrustManager() throws
KeyStoreException, NoSuchAlgorithmException,
CertificateException, IOException {
// Load a KeyStore with only our certificate
KeyStore store = KeyStore.getInstance(KeyStore.getDefaultType());
store.load(null, null);
Certificate cert = loadPemCert();
store.setCertificateEntry("me.com", cert);
// create a TrustManager using our KeyStore
TrustManagerFactory factory = TrustManagerFactory.getInstance(
TrustManagerFactory.getDefaultAlgorithm());
factory.init(store);
this.trustManager = getX509TrustManager(factory.getTrustManagers());
}
public void checkClientTrusted(X509Certificate[] chain, String authType)
throws CertificateException {
trustManager.checkClientTrusted(chain, authType);
}
public void checkServerTrusted(X509Certificate[] chain, String authType)
throws CertificateException {
trustManager.checkServerTrusted(chain, authType);
}
public X509Certificate[] getAcceptedIssuers() {
return trustManager.getAcceptedIssuers();
}
private static X509TrustManager getX509TrustManager(TrustManager[] managers) {
for (TrustManager tm : managers) {
if (tm instanceof X509TrustManager) {
return (X509TrustManager) tm;
}
}
return null;
}
private Certificate loadPemCert()
throws CertificateException, IOException {
InputStream stream =
this.getClass().getClassLoader().getResourceAsStream("cert.pem");
CertificateFactory factory = CertificateFactory.getInstance("X.509");
return factory.generateCertificate(stream);
}
}

Categories

Resources