har*_*lee 386 java ssl https certificate apache-httpclient-4.x
最近发布了一个关于HttpClient
过度Https 的问题(在这里找到).我已经取得了一些进展,但我遇到了新的问题.和我的上一个问题一样,我似乎无法找到适合我的任何地方的例子.基本上,我希望我的客户端接受任何证书(因为我只指向一个服务器),但我一直在接受javax.net.ssl.SSLException: Not trusted server certificate exception.
所以这就是我所拥有的:
public void connect() throws A_WHOLE_BUNCH_OF_EXCEPTIONS {
HttpPost post = new HttpPost(new URI(PROD_URL));
post.setEntity(new StringEntity(BODY));
KeyStore trusted = KeyStore.getInstance("BKS");
trusted.load(null, "".toCharArray());
SSLSocketFactory sslf = new SSLSocketFactory(trusted);
sslf.setHostnameVerifier(SSLSocketFactory.ALLOW_ALL_HOSTNAME_VERIFIER);
SchemeRegistry schemeRegistry = new SchemeRegistry();
schemeRegistry.register(new Scheme ("https", sslf, 443));
SingleClientConnManager cm = new SingleClientConnManager(post.getParams(),
schemeRegistry);
HttpClient client = new DefaultHttpClient(cm, post.getParams());
HttpResponse result = client.execute(post);
}
Run Code Online (Sandbox Code Playgroud)
这是我得到的错误:
W/System.err( 901): javax.net.ssl.SSLException: Not trusted server certificate
W/System.err( 901): at org.apache.harmony.xnet.provider.jsse.OpenSSLSocketImpl.startHandshake(OpenSSLSocketImpl.java:360)
W/System.err( 901): at org.apache.http.conn.ssl.AbstractVerifier.verify(AbstractVerifier.java:92)
W/System.err( 901): at org.apache.http.conn.ssl.SSLSocketFactory.connectSocket(SSLSocketFactory.java:321)
W/System.err( 901): at org.apache.http.impl.conn.DefaultClientConnectionOperator.openConnection(DefaultClientConnectionOperator.java:129)
W/System.err( 901): at org.apache.http.impl.conn.AbstractPoolEntry.open(AbstractPoolEntry.java:164)
W/System.err( 901): at org.apache.http.impl.conn.AbstractPooledConnAdapter.open(AbstractPooledConnAdapter.java:119)
W/System.err( 901): at org.apache.http.impl.client.DefaultRequestDirector.execute(DefaultRequestDirector.java:348)
W/System.err( 901): at org.apache.http.impl.client.AbstractHttpClient.execute(AbstractHttpClient.java:555)
W/System.err( 901): at org.apache.http.impl.client.AbstractHttpClient.execute(AbstractHttpClient.java:487)
W/System.err( 901): at org.apache.http.impl.client.AbstractHttpClient.execute(AbstractHttpClient.java:465)
W/System.err( 901): at me.harrisonlee.test.ssl.MainActivity.connect(MainActivity.java:129)
W/System.err( 901): at me.harrisonlee.test.ssl.MainActivity.access$0(MainActivity.java:77)
W/System.err( 901): at me.harrisonlee.test.ssl.MainActivity$2.run(MainActivity.java:49)
W/System.err( 901): Caused by: java.security.cert.CertificateException: java.security.InvalidAlgorithmParameterException: the trust anchors set is empty
W/System.err( 901): at org.apache.harmony.xnet.provider.jsse.TrustManagerImpl.checkServerTrusted(TrustManagerImpl.java:157)
W/System.err( 901): at org.apache.harmony.xnet.provider.jsse.OpenSSLSocketImpl.startHandshake(OpenSSLSocketImpl.java:355)
W/System.err( 901): ... 12 more
W/System.err( 901): Caused by: java.security.InvalidAlgorithmParameterException: the trust anchors set is empty
W/System.err( 901): at java.security.cert.PKIXParameters.checkTrustAnchors(PKIXParameters.java:645)
W/System.err( 901): at java.security.cert.PKIXParameters.<init>(PKIXParameters.java:89)
W/System.err( 901): at org.apache.harmony.xnet.provider.jsse.TrustManagerImpl.<init>(TrustManagerImpl.java:89)
W/System.err( 901): at org.apache.harmony.xnet.provider.jsse.TrustManagerFactoryImpl.engineGetTrustManagers(TrustManagerFactoryImpl.java:134)
W/System.err( 901): at javax.net.ssl.TrustManagerFactory.getTrustManagers(TrustManagerFactory.java:226)W/System.err( 901): at org.apache.http.conn.ssl.SSLSocketFactory.createTrustManagers(SSLSocketFactory.java:263)
W/System.err( 901): at org.apache.http.conn.ssl.SSLSocketFactory.<init>(SSLSocketFactory.java:190)
W/System.err( 901): at org.apache.http.conn.ssl.SSLSocketFactory.<init>(SSLSocketFactory.java:216)
W/System.err( 901): at me.harrisonlee.test.ssl.MainActivity.connect(MainActivity.java:107)
W/System.err( 901): ... 2 more
Run Code Online (Sandbox Code Playgroud)
emm*_*mby 484
您基本上有四种可能的解决方案来使用httpclient修复Android上的"不受信任"异常:
这个答案使用的解决方案#4,在我看来是最强大的.
解决方案是使用可以接受多个KeyStore的SSLSocketFactory,允许您使用自己的证书提供自己的KeyStore.这允许您加载其他顶级证书,例如某些Android设备上可能缺少的Thawte.它还允许您加载自己的自签名证书.它将首先使用内置的默认设备证书,并仅在必要时使用其他证书.
首先,您需要确定KeyStore中缺少哪个证书.运行以下命令:
openssl s_client -connect www.yourserver.com:443
Run Code Online (Sandbox Code Playgroud)
你会看到如下输出:
Certificate chain
0 s:/O=www.yourserver.com/OU=Go to
https://www.thawte.com/repository/index.html/OU=Thawte SSL123
certificate/OU=Domain Validated/CN=www.yourserver.com
i:/C=US/O=Thawte, Inc./OU=Domain Validated SSL/CN=Thawte DV SSL CA
1 s:/C=US/O=Thawte, Inc./OU=Domain Validated SSL/CN=Thawte DV SSL CA
i:/C=US/O=thawte, Inc./OU=Certification Services Division/OU=(c)
2006 thawte, Inc. - For authorized use only/CN=thawte Primary Root CA
Run Code Online (Sandbox Code Playgroud)
如您所见,我们的根证书来自Thawte.转到您的提供商的网站,找到相应的证书.对我们来说,就在这里,您可以看到我们需要的是版权所有2006.
如果您使用的是自签名证书,则由于您已拥有签名证书,因此无需执行上一步.
然后,创建包含缺少的签名证书的密钥库文件.Crazybob 详细介绍了如何在Android上执行此操作,但其目的是执行以下操作:
如果您还没有,请从http://www.bouncycastle.org/latest_releases.html下载充气城堡提供程序库.这将在下面的类路径中进行.
运行命令以从服务器提取证书并创建pem文件.在这种情况下,mycert.pem.
echo | openssl s_client -connect ${MY_SERVER}:443 2>&1 | \
sed -ne '/-BEGIN CERTIFICATE-/,/-END CERTIFICATE-/p' > mycert.pem
Run Code Online (Sandbox Code Playgroud)
然后运行以下命令以创建密钥库.
export CLASSPATH=/path/to/bouncycastle/bcprov-jdk15on-155.jar
CERTSTORE=res/raw/mystore.bks
if [ -a $CERTSTORE ]; then
rm $CERTSTORE || exit 1
fi
keytool \
-import \
-v \
-trustcacerts \
-alias 0 \
-file <(openssl x509 -in mycert.pem) \
-keystore $CERTSTORE \
-storetype BKS \
-provider org.bouncycastle.jce.provider.BouncyCastleProvider \
-providerpath /path/to/bouncycastle/bcprov-jdk15on-155.jar \
-storepass some-password
Run Code Online (Sandbox Code Playgroud)
您会注意到上面的脚本将结果放入res/raw/mystore.bks
.现在您有一个文件,您将加载到您的Android应用程序中,提供缺少的证书.
为此,请为SSL方案注册SSLSocketFactory:
final SchemeRegistry schemeRegistry = new SchemeRegistry();
schemeRegistry.register(new Scheme("http", PlainSocketFactory.getSocketFactory(), 80));
schemeRegistry.register(new Scheme("https", createAdditionalCertsSSLSocketFactory(), 443));
// and then however you create your connection manager, I use ThreadSafeClientConnManager
final HttpParams params = new BasicHttpParams();
...
final ThreadSafeClientConnManager cm = new ThreadSafeClientConnManager(params,schemeRegistry);
Run Code Online (Sandbox Code Playgroud)
要创建SSLSocketFactory:
protected org.apache.http.conn.ssl.SSLSocketFactory createAdditionalCertsSSLSocketFactory() {
try {
final KeyStore ks = KeyStore.getInstance("BKS");
// the bks file we generated above
final InputStream in = context.getResources().openRawResource( R.raw.mystore);
try {
// don't forget to put the password used above in strings.xml/mystore_password
ks.load(in, context.getString( R.string.mystore_password ).toCharArray());
} finally {
in.close();
}
return new AdditionalKeyStoresSSLSocketFactory(ks);
} catch( Exception e ) {
throw new RuntimeException(e);
}
}
Run Code Online (Sandbox Code Playgroud)
最后,AdditionalKeyStoresSSLSocketFactory代码接受您的新KeyStore并检查内置KeyStore是否无法验证SSL证书:
/**
* Allows you to trust certificates from additional KeyStores in addition to
* the default KeyStore
*/
public class AdditionalKeyStoresSSLSocketFactory extends SSLSocketFactory {
protected SSLContext sslContext = SSLContext.getInstance("TLS");
public AdditionalKeyStoresSSLSocketFactory(KeyStore keyStore) throws NoSuchAlgorithmException, KeyManagementException, KeyStoreException, UnrecoverableKeyException {
super(null, null, null, null, null, null);
sslContext.init(null, new TrustManager[]{new AdditionalKeyStoresTrustManager(keyStore)}, null);
}
@Override
public Socket createSocket(Socket socket, String host, int port, boolean autoClose) throws IOException {
return sslContext.getSocketFactory().createSocket(socket, host, port, autoClose);
}
@Override
public Socket createSocket() throws IOException {
return sslContext.getSocketFactory().createSocket();
}
/**
* Based on http://download.oracle.com/javase/1.5.0/docs/guide/security/jsse/JSSERefGuide.html#X509TrustManager
*/
public static class AdditionalKeyStoresTrustManager implements X509TrustManager {
protected ArrayList<X509TrustManager> x509TrustManagers = new ArrayList<X509TrustManager>();
protected AdditionalKeyStoresTrustManager(KeyStore... additionalkeyStores) {
final ArrayList<TrustManagerFactory> factories = new ArrayList<TrustManagerFactory>();
try {
// The default Trustmanager with default keystore
final TrustManagerFactory original = TrustManagerFactory.getInstance(TrustManagerFactory.getDefaultAlgorithm());
original.init((KeyStore) null);
factories.add(original);
for( KeyStore keyStore : additionalkeyStores ) {
final TrustManagerFactory additionalCerts = TrustManagerFactory.getInstance(TrustManagerFactory.getDefaultAlgorithm());
additionalCerts.init(keyStore);
factories.add(additionalCerts);
}
} catch (Exception e) {
throw new RuntimeException(e);
}
/*
* Iterate over the returned trustmanagers, and hold on
* to any that are X509TrustManagers
*/
for (TrustManagerFactory tmf : factories)
for( TrustManager tm : tmf.getTrustManagers() )
if (tm instanceof X509TrustManager)
x509TrustManagers.add( (X509TrustManager)tm );
if( x509TrustManagers.size()==0 )
throw new RuntimeException("Couldn't find any X509TrustManagers");
}
/*
* Delegate to the default trust manager.
*/
public void checkClientTrusted(X509Certificate[] chain, String authType) throws CertificateException {
final X509TrustManager defaultX509TrustManager = x509TrustManagers.get(0);
defaultX509TrustManager.checkClientTrusted(chain, authType);
}
/*
* Loop over the trustmanagers until we find one that accepts our server
*/
public void checkServerTrusted(X509Certificate[] chain, String authType) throws CertificateException {
for( X509TrustManager tm : x509TrustManagers ) {
try {
tm.checkServerTrusted(chain,authType);
return;
} catch( CertificateException e ) {
// ignore
}
}
throw new CertificateException();
}
public X509Certificate[] getAcceptedIssuers() {
final ArrayList<X509Certificate> list = new ArrayList<X509Certificate>();
for( X509TrustManager tm : x509TrustManagers )
list.addAll(Arrays.asList(tm.getAcceptedIssuers()));
return list.toArray(new X509Certificate[list.size()]);
}
}
}
Run Code Online (Sandbox Code Playgroud)
小智 414
注意:请勿在您不会完全信任的网络上使用的生产代码中实现此功能.特别是在公共互联网上的任何事情.
你的问题正是我想知道的.在我做了一些搜索后,结论如下.
在HttpClient方式中,您应该从org.apache.http.conn.ssl.SSLSocketFactory创建一个自定义类,而不是一个org.apache.http.conn.ssl.SSLSocketFactory本身.在这篇文章中可以找到一些线索.自定义SSL处理在Android 2.2 FroYo上停止工作.
一个例子是......
import java.io.IOException;
import java.net.Socket;
import java.net.UnknownHostException;
import java.security.KeyManagementException;
import java.security.KeyStore;
import java.security.KeyStoreException;
import java.security.NoSuchAlgorithmException;
import java.security.UnrecoverableKeyException;
import java.security.cert.CertificateException;
import java.security.cert.X509Certificate;
import javax.net.ssl.SSLContext;
import javax.net.ssl.TrustManager;
import javax.net.ssl.X509TrustManager;
import org.apache.http.conn.ssl.SSLSocketFactory;
public class MySSLSocketFactory extends SSLSocketFactory {
SSLContext sslContext = SSLContext.getInstance("TLS");
public MySSLSocketFactory(KeyStore truststore) throws NoSuchAlgorithmException, KeyManagementException, KeyStoreException, UnrecoverableKeyException {
super(truststore);
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();
}
}
Run Code Online (Sandbox Code Playgroud)
并在创建HttpClient实例时使用此类.
public HttpClient getNewHttpClient() {
try {
KeyStore trustStore = KeyStore.getInstance(KeyStore.getDefaultType());
trustStore.load(null, null);
MySSLSocketFactory 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.UTF_8);
SchemeRegistry registry = new SchemeRegistry();
registry.register(new Scheme("http", PlainSocketFactory.getSocketFactory(), 80));
registry.register(new Scheme("https", sf, 443));
ClientConnectionManager ccm = new ThreadSafeClientConnManager(params, registry);
return new DefaultHttpClient(ccm, params);
} catch (Exception e) {
return new DefaultHttpClient();
}
}
Run Code Online (Sandbox Code Playgroud)
顺便说一句,下面的链接适用于正在寻找HttpURLConnection解决方案的人. Https Connection Android
我已经在froyo上测试了上述两种解决方案,在我的案例中它们都像魅力一样.最后,使用HttpURLConnection可能会面临重定向问题,但这超出了主题范围.
注意:在您决定信任所有证书之前,您可能应该完全了解该网站,并且不会对最终用户造成损害.
事实上,你应该仔细考虑你所承担的风险,包括我非常赞赏的以下评论中提到的黑客模拟网站的影响.在某些情况下,虽然可能很难处理所有证书,但您最好知道信任所有证书的隐含缺点.
Alo*_*pta 71
在此之前添加此代码HttpsURLConnection
,它将完成.我知道了.
private void trustEveryone() {
try {
HttpsURLConnection.setDefaultHostnameVerifier(new HostnameVerifier(){
public boolean verify(String hostname, SSLSession session) {
return true;
}});
SSLContext context = SSLContext.getInstance("TLS");
context.init(null, new X509TrustManager[]{new X509TrustManager(){
public void checkClientTrusted(X509Certificate[] chain,
String authType) throws CertificateException {}
public void checkServerTrusted(X509Certificate[] chain,
String authType) throws CertificateException {}
public X509Certificate[] getAcceptedIssuers() {
return new X509Certificate[0];
}}}, new SecureRandom());
HttpsURLConnection.setDefaultSSLSocketFactory(
context.getSocketFactory());
} catch (Exception e) { // should never happen
e.printStackTrace();
}
}
Run Code Online (Sandbox Code Playgroud)
我希望这可以帮助你.
Dan*_*Dan 34
这是一个坏主意.信任任何证书只会(非常)比完全不使用SSL更好.当你说"我希望我的客户端接受任何证书(因为我只指向一台服务器)"时,你假设这意味着以某种方式指向"一台服务器"是安全的,而不是在公共网络上.
通过信任任何证书,您完全可以接受中间人攻击.任何人都可以通过与您和终端服务器建立单独的SSL连接来代理您的连接.然后,MITM可以访问您的整个请求和响应.除非您首先不需要SSL(您的消息没有任何敏感信息,并且不进行身份验证),否则您不应盲目信任所有证书.
您应该考虑使用keytool将公共证书添加到jks,并使用它来构建套接字工厂,例如:
KeyStore ks = KeyStore.getInstance("JKS");
// get user password and file input stream
char[] password = ("mykspassword")).toCharArray();
ClassLoader cl = this.getClass().getClassLoader();
InputStream stream = cl.getResourceAsStream("myjks.jks");
ks.load(stream, password);
stream.close();
SSLContext sc = SSLContext.getInstance("TLS");
KeyManagerFactory kmf = KeyManagerFactory.getInstance("SunX509");
TrustManagerFactory tmf = TrustManagerFactory.getInstance("SunX509");
kmf.init(ks, password);
tmf.init(ks);
sc.init(kmf.getKeyManagers(), tmf.getTrustManagers(),null);
return sc.getSocketFactory();
Run Code Online (Sandbox Code Playgroud)
这有一点需要注意.证书最终将过期,代码将在此时停止工作.您可以通过查看证书轻松确定何时发生这种情况.
hfm*_*son 19
从API 8开始,您可以通过这种方式禁用HttpURLConnection SSL检查以进行测试:
HttpURLConnection conn = (HttpURLConnection) url.openConnection();
if (conn instanceof HttpsURLConnection) {
HttpsURLConnection httpsConn = (HttpsURLConnection) conn;
httpsConn.setSSLSocketFactory(SSLCertificateSocketFactory.getInsecure(0, null));
httpsConn.setHostnameVerifier(new AllowAllHostnameVerifier());
}
Run Code Online (Sandbox Code Playgroud)
小智 10
HttpComponents的API已经改变.它适用于以下代码.
public static HttpClient getTestHttpClient() {
try {
SSLSocketFactory sf = new SSLSocketFactory(new TrustStrategy(){
@Override
public boolean isTrusted(X509Certificate[] chain,
String authType) throws CertificateException {
return true;
}
}, new AllowAllHostnameVerifier());
SchemeRegistry registry = new SchemeRegistry();
registry.register(new Scheme("https",8444, sf));
ClientConnectionManager ccm = new ThreadSafeClientConnManager(registry);
return new DefaultHttpClient(ccm);
} catch (Exception e) {
e.printStackTrace();
return new DefaultHttpClient();
}
}
Run Code Online (Sandbox Code Playgroud)
小智 10
/sf/answers/446521071/上面的代码是正确的,除了它还必须调用主机名验证器:
@Override
public Socket createSocket(Socket socket, String host, int port, boolean autoClose) throws IOException {
SSLSocket sslSocket = (SSLSocket)sslContext.getSocketFactory().createSocket(socket, host, port, autoClose);
getHostnameVerifier().verify(host, sslSocket);
return sslSocket;
}
Run Code Online (Sandbox Code Playgroud)
我明确注册stackoverflow以添加此修复程序.注意我的警告!
信任所有证书对我来说并不是真正的替代方案,因此我执行以下操作以使 HttpsURLConnection 信任新证书(另请参阅http://nelenkov.blogspot.jp/2011/12/using-custom-certificate-trust-store- .html)。
拿到证书;我通过在 Firefox 中导出证书(单击小锁图标,获取证书详细信息,单击导出)来完成此操作,然后使用portecle导出信任库 (BKS)。
使用以下代码从 /res/raw/geotrust_cert.bks 加载 Truststore:
final KeyStore trustStore = KeyStore.getInstance("BKS");
final InputStream in = context.getResources().openRawResource(
R.raw.geotrust_cert);
trustStore.load(in, null);
final TrustManagerFactory tmf = TrustManagerFactory
.getInstance(TrustManagerFactory.getDefaultAlgorithm());
tmf.init(trustStore);
final SSLContext sslCtx = SSLContext.getInstance("TLS");
sslCtx.init(null, tmf.getTrustManagers(),
new java.security.SecureRandom());
HttpsURLConnection.setDefaultSSLSocketFactory(sslCtx
.getSocketFactory());
Run Code Online (Sandbox Code Playgroud)我正在为那些使用httpclient-4.5的人添加一个响应,也可能适用于4.4.
import java.security.cert.CertificateException;
import java.security.cert.X509Certificate;
import org.apache.http.HttpResponse;
import org.apache.http.client.HttpClient;
import org.apache.http.client.HttpResponseException;
import org.apache.http.client.fluent.ContentResponseHandler;
import org.apache.http.client.methods.HttpPost;
import org.apache.http.conn.ssl.NoopHostnameVerifier;
import org.apache.http.conn.ssl.SSLConnectionSocketFactory;
import org.apache.http.conn.ssl.TrustStrategy;
import org.apache.http.impl.client.CloseableHttpClient;
import org.apache.http.impl.client.HttpClients;
import org.apache.http.ssl.SSLContextBuilder;
public class HttpClientUtils{
public static HttpClient getHttpClientWithoutSslValidation_UsingHttpClient_4_5_2() {
try {
SSLContextBuilder builder = new SSLContextBuilder();
builder.loadTrustMaterial(null, new TrustStrategy() {
@Override
public boolean isTrusted(X509Certificate[] chain, String authType) throws CertificateException {
return true;
}
});
SSLConnectionSocketFactory sslsf = new SSLConnectionSocketFactory(builder.build(), new NoopHostnameVerifier());
CloseableHttpClient httpclient = HttpClients.custom().setSSLSocketFactory(sslsf).build();
return httpclient;
} catch (Exception e) {
throw new RuntimeException(e);
}
}
}
Run Code Online (Sandbox Code Playgroud)
归档时间: |
|
查看次数: |
566607 次 |
最近记录: |