最近Https上のHttpClient
に関する質問を投稿しました( ここで見つけました )。私はいくらか前進しましたが、私は新しい問題に遭遇しました。私の最後の問題と同様に、私は私のために働く例をどこにも見つけることができないようです。基本的に、私は自分のクライアントがどんな証明書も受け入れることを望みます(私はこれまで1つのサーバーを指しているだけなので)が、私は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);
}
そして、これは私が得ているエラーです:
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
注:これを完全に信頼できないネットワーク上で使用することになる本番コードでは実装しないでください。特に公衆インターネットを経由するものすべて。
あなたの質問は私が知りたいことだけです。いくつか検索した後の結論は次のとおりです。
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();
}
}
そして、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();
}
}
ところで、以下のリンクはHttpURLConnectionソリューションを探している人のためのものです。 HTTP接続アンドロイド
私はfroyoで上記の2種類の解決策をテストしました、そして、それらはすべて私の場合魅力のように働きます。最後に、HttpURLConnectionを使用するとリダイレクトの問題が発生する可能性がありますが、これはトピックを超えています。
注:すべての証明書を信頼することを決定する前に、おそらくサイトを完全に知っている必要があり、エンドユーザーにとってそれが害を及ぼすことはないでしょう。
確かに、私が深く感謝している以下のコメントで言及されているハッカーの模擬サイトの影響を含めて、あなたが取るリスクは慎重に考慮されるべきです。状況によっては、すべての証明書の面倒を見るのは難しいかもしれませんが、それらすべてを信頼することの暗黙の欠点を知っておくほうがよいでしょう。
Httpclientを使用してAndroidの「信頼されていない」例外を修正するには、基本的に4つの解決策があります。
この回答では、解決方法4を使用します。これが私にとって最も堅牢な方法のようです。
解決策は、複数のKeyStoreを受け入れることができるSSLSocketFactoryを使用することです。これにより、独自のKeyStoreに独自の証明書を提供することができます。これにより、Thawteなど、一部のAndroidデバイスでは見つからない可能性がある追加の最上位証明書をロードできます。それはまたあなた自身の自己署名証明書を同様にロードすることを可能にします。最初に組み込みのデフォルトのデバイス証明書が使用され、必要に応じて追加の証明書が使用されます。
まず、KeyStoreに欠けている証明書を特定します。次のコマンドを実行してください。
openssl s_client -connect www.yourserver.com:443
そして、あなたは以下のような出力を見るでしょう:
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
ご覧のとおり、私たちのルート証明書はThawteからのものです。プロバイダのWebサイトにアクセスして、対応する証明書を見つけてください。私たちにとって、それは here であり、私たちが必要としていたのはCopyright 2006です。
自己署名証明書を使用している場合は、すでに署名証明書があるため、前の手順を実行する必要はありません。
次に、不足している署名証明書を含むキーストアファイルを作成します。 Crazybobには Androidでこれを実行する方法の詳細 がありますが、アイデアは次のようになります。
まだ持っていない場合は、以下からbouncy castle providerライブラリをダウンロードしてください: 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
その後、以下のコマンドを実行してキーストアを作成します。
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
あなたは上記のスクリプトが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);
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);
}
}
そして最後に、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()]);
}
}
}
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();
}
}
これがお役に立てば幸いです。
これは悪い考えです。証明書を信頼することは、まったくSSLを使用しないことよりも(非常に)わずかに優れています。 「クライアントに証明書を受け入れてほしい(私はこれまで1つのサーバーを指しているだけなので)」と言ったときは、「1つのサーバー」を指すのは安全だと考えています。
証明書を信頼することで、中間者攻撃に完全にオープンです。あなたとエンドサーバーとの間で別々の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();
これには注意が必要です。証明書は最終的に期限切れになり、その時点でコードは機能しなくなります。証明書を見れば、いつこれが発生するかを簡単に判断できます。
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());
}
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();
}
}
https://stackoverflow.com/a/6378872/1553004 にある上記のコードは正しいものです。ただし、ホスト名検証機能も呼び出す必要があります。
@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;
}
この修正を追加するために、明示的にstackoverflowにサインアップしました。私の警告に注意してください。
私は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);
}
}
}
すべての証明書を信頼することは私にとって本当の代替手段ではなかったので、私はHttpsURLConnectionが新しい証明書を信頼するようにするために次のようにしました( も参照してください。 -store-on.html )。
証明書を入手してください。 Firefoxで証明書をエクスポートし(小さな鍵のアイコンをクリックし、証明書の詳細を取得し、エクスポートをクリックし)、次に portecle を使用してトラストストア(BKS)をエクスポートします。
次のコードを使用して/res/raw/geotrust_cert.bksからトラストストアをロードします。
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());
私はこれを使用しました、そしてそれはすべてのOSで私のために働きます。
/**
* Disables the SSL certificate checking for new instances of {@link HttpsURLConnection} This has been created to
* aid testing on a local box, not for use on production.
*/
private static void disableSSLCertificateChecking() {
TrustManager[] trustAllCerts = new TrustManager[] { new X509TrustManager() {
public X509Certificate[] getAcceptedIssuers() {
return null;
}
@Override
public void checkClientTrusted(X509Certificate[] arg0, String arg1) throws CertificateException {
// Not implemented
}
@Override
public void checkServerTrusted(X509Certificate[] arg0, String arg1) throws CertificateException {
// Not implemented
}
} };
try {
SSLContext sc = SSLContext.getInstance("TLS");
sc.init(null, trustAllCerts, new Java.security.SecureRandom());
HttpsURLConnection.setDefaultSSLSocketFactory(sc.getSocketFactory());
} catch (KeyManagementException e) {
e.printStackTrace();
} catch (NoSuchAlgorithmException e) {
e.printStackTrace();
}
}
"emmby"(21:29に2011年6月16日に回答)の項目#4からの応答に見えます。 "組み込みの証明書KeyStoreを使用するカスタムSSLSocketFactoryを作成します。デフォルトで確認します。」
これは単純化された実装です。システムキーストアをロードし、アプリケーションキーストアとマージします。
public HttpClient getNewHttpClient() {
try {
InputStream in = null;
// Load default system keystore
KeyStore trusted = KeyStore.getInstance(KeyStore.getDefaultType());
try {
in = new BufferedInputStream(new FileInputStream(System.getProperty("javax.net.ssl.trustStore"))); // Normally: "/system/etc/security/cacerts.bks"
trusted.load(in, null); // no password is "changeit"
} finally {
if (in != null) {
in.close();
in = null;
}
}
// Load application keystore & merge with system
try {
KeyStore appTrusted = KeyStore.getInstance("BKS");
in = context.getResources().openRawResource(R.raw.mykeystore);
appTrusted.load(in, null); // no password is "changeit"
for (Enumeration<String> e = appTrusted.aliases(); e.hasMoreElements();) {
final String alias = e.nextElement();
final KeyStore.Entry entry = appTrusted.getEntry(alias, null);
trusted.setEntry(System.currentTimeMillis() + ":" + alias, entry, null);
}
} finally {
if (in != null) {
in.close();
in = null;
}
}
HttpParams params = new BasicHttpParams();
HttpProtocolParams.setVersion(params, HttpVersion.HTTP_1_1);
HttpProtocolParams.setContentCharset(params, HTTP.UTF_8);
SSLSocketFactory sf = new SSLSocketFactory(trusted);
sf.setHostnameVerifier(SSLSocketFactory.BROWSER_COMPATIBLE_HOSTNAME_VERIFIER);
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();
}
}
JKSからBKSに変換する簡単なモード:
keytool -importkeystore -destkeystore cacerts.bks -deststoretype BKS -providerclass org.bouncycastle.jce.provider.BouncyCastleProvider -providerpath bcprov-jdk16-141.jar -deststorepass changeit -srcstorepass changeit -srckeystore $Java_HOME/jre/lib/security/cacerts -srcstoretype JKS -noprompt
*注:Android 4.0(ICS)では、トラストストアが変更されました。詳細: http://nelenkov.blogspot.com.es/2011/12/ics-trust-store-implementation.html
これは4.1.2 httpclientコードを使った非常に単純なバージョンです。これは、あなたが適切と思う信頼アルゴリズムに変更することができます。
public static HttpClient getTestHttpClient() {
try {
SSLSocketFactory sf = new SSLSocketFactory(new TrustStrategy(){
@Override
public boolean isTrusted(X509Certificate[] chain,
String authType) throws CertificateException {
return true;
}
});
SchemeRegistry registry = new SchemeRegistry();
registry.register(new Scheme("https", 443, sf));
ClientConnectionManager ccm = new ThreadSafeClientConnManager(registry);
return new DefaultHttpClient(ccm);
} catch (Exception e) {
return new DefaultHttpClient();
}
}
すべての証明書をOAuthを介して(テスト目的で)機能させることを許可する場合は、次の手順に従います。
1)ここにAndroid OAuth APIのソースコードをダウンロードしてください: https://github.com/kaeppler/signpost
2) "CommonsHttpOAuthProvider"クラスのファイルを見つけます
3)以下のように変更してください。
public class CommonsHttpOAuthProvider extends AbstractOAuthProvider {
private static final long serialVersionUID = 1L;
private transient HttpClient httpClient;
public CommonsHttpOAuthProvider(String requestTokenEndpointUrl, String accessTokenEndpointUrl,
String authorizationWebsiteUrl) {
super(requestTokenEndpointUrl, accessTokenEndpointUrl, authorizationWebsiteUrl);
//this.httpClient = new DefaultHttpClient();//Version implemented and that throws the famous "javax.net.ssl.SSLException: Not trusted server certificate" if the certificate is not signed with a CA
this.httpClient = MySSLSocketFactory.getNewHttpClient();//This will work with all certificates (for testing purposes only)
}
上記の「MySSLSocketFactory」は、受け入れられた回答に基づいています。それをさらに簡単にするために、ここに完全なクラスがあります:
package com.netcomps.oauth_example;
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.HttpVersion;
import org.Apache.http.client.HttpClient;
import org.Apache.http.conn.ClientConnectionManager;
import org.Apache.http.conn.scheme.PlainSocketFactory;
import org.Apache.http.conn.scheme.Scheme;
import org.Apache.http.conn.scheme.SchemeRegistry;
import org.Apache.http.conn.ssl.SSLSocketFactory;
import org.Apache.http.impl.client.DefaultHttpClient;
import org.Apache.http.impl.conn.tsccm.ThreadSafeClientConnManager;
import org.Apache.http.params.BasicHttpParams;
import org.Apache.http.params.HttpParams;
import org.Apache.http.params.HttpProtocolParams;
import org.Apache.http.protocol.HTTP;
//http://stackoverflow.com/questions/2642777/trusting-all-certificates-using-httpclient-over-https
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() {
@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.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();
}
public static HttpClient getNewHttpClient() {
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.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();
}
}
}
これが誰かに役立つことを願っています。
VM引数に-Dtrust_all_cert=true
を追加するだけでよいのです。この引数は、証明書チェックを無視するようにJavaに指示します。
Android 2.1でStartCom SSL証明書にまだ苦労している人は誰でも https://www.startssl.com/certs/ にアクセスしてダウンロードca.pem、今では answer で提供されています @ emmby replace
`export CLASSPATH=bcprov-jdk16-145.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 /usr/share/Java/bcprov.jar \
-storepass some-password`
と
`export CLASSPATH=bcprov-jdk16-145.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 ca.pem) \
-keystore $CERTSTORE \
-storetype BKS \
-provider org.bouncycastle.jce.provider.BouncyCastleProvider \
-providerpath /usr/share/Java/bcprov.jar \
-storepass some-password`
箱から出して動作するはずです。 @ emmby による完璧な回答の後でも、1日以上苦労していました。
Sspiがxamarin Androidで失敗しました。
私はこの解決策を見つけました。あなたがHTTPSリンクにぶつかる前にこのコードを入れてください
const SslProtocols _Tls12 = (SslProtocols)0x00000C00;
const SecurityProtocolType Tls12 = (SecurityProtocolType)_Tls12;
ServicePointManager.SecurityProtocol = Tls12;
このクラスを使う
public class WCFs
{
// https://192.168.30.8/myservice.svc?wsdl
private static final String NAMESPACE = "http://tempuri.org/";
private static final String URL = "192.168.30.8";
private static final String SERVICE = "/myservice.svc?wsdl";
private static String SOAP_ACTION = "http://tempuri.org/iWCFserviceMe/";
public static Thread myMethod(Runnable rp)
{
String METHOD_NAME = "myMethod";
SoapObject request = new SoapObject(NAMESPACE, METHOD_NAME);
request.addProperty("Message", "Https WCF Running...");
return _call(rp,METHOD_NAME, request);
}
protected static HandlerThread _call(final RunProcess rp,final String METHOD_NAME, SoapObject soapReq)
{
final SoapSerializationEnvelope envelope = new SoapSerializationEnvelope(SoapEnvelope.VER11);
int TimeOut = 5*1000;
envelope.dotNet = true;
envelope.bodyOut = soapReq;
envelope.setOutputSoapObject(soapReq);
final HttpsTransportSE httpTransport_net = new HttpsTransportSE(URL, 443, SERVICE, TimeOut);
try
{
HttpsURLConnection.setDefaultHostnameVerifier(new HostnameVerifier() // use this section if crt file is handmake
{
@Override
public boolean verify(String hostname, SSLSession session)
{
return true;
}
});
KeyStore k = getFromRaw(R.raw.key, "PKCS12", "password");
((HttpsServiceConnectionSE) httpTransport_net.getServiceConnection()).setSSLSocketFactory(getSSLSocketFactory(k, "SSL"));
}
catch(Exception e){}
HandlerThread thread = new HandlerThread("wcfTd"+ Generator.getRandomNumber())
{
@Override
public void run()
{
Handler h = new Handler(Looper.getMainLooper());
Object response = null;
for(int i=0; i<4; i++)
{
response = send(envelope, httpTransport_net , METHOD_NAME, null);
try
{if(Thread.currentThread().isInterrupted()) return;}catch(Exception e){}
if(response != null)
break;
ThreadHelper.threadSleep(250);
}
if(response != null)
{
if(rp != null)
{
rp.setArguments(response.toString());
h.post(rp);
}
}
else
{
if(Thread.currentThread().isInterrupted())
return;
if(rp != null)
{
rp.setExceptionState(true);
h.post(rp);
}
}
ThreadHelper.stopThread(this);
}
};
thread.start();
return thread;
}
private static Object send(SoapSerializationEnvelope envelope, HttpTransportSE androidHttpTransport, String METHOD_NAME, List<HeaderProperty> headerList)
{
try
{
if(headerList != null)
androidHttpTransport.call(SOAP_ACTION + METHOD_NAME, envelope, headerList);
else
androidHttpTransport.call(SOAP_ACTION + METHOD_NAME, envelope);
Object res = envelope.getResponse();
if(res instanceof SoapPrimitive)
return (SoapPrimitive) envelope.getResponse();
else if(res instanceof SoapObject)
return ((SoapObject) envelope.getResponse());
}
catch(Exception e)
{}
return null;
}
public static KeyStore getFromRaw(@RawRes int id, String algorithm, String filePassword)
{
try
{
InputStream inputStream = ResourceMaster.openRaw(id);
KeyStore keystore = KeyStore.getInstance(algorithm);
keystore.load(inputStream, filePassword.toCharArray());
inputStream.close();
return keystore;
}
catch(Exception e)
{}
return null;
}
public static SSLSocketFactory getSSLSocketFactory(KeyStore trustKey, String SSLAlgorithm)
{
try
{
TrustManagerFactory tmf = TrustManagerFactory.getInstance(TrustManagerFactory.getDefaultAlgorithm());
tmf.init(trustKey);
SSLContext context = SSLContext.getInstance(SSLAlgorithm);//"SSL" "TLS"
context.init(null, tmf.getTrustManagers(), null);
return context.getSocketFactory();
}
catch(Exception e){}
return null;
}
}