HTTPS를 통한 HttpClient를 사용한 모든 인증서 신뢰
에 관한 질문을 최근 게시했습니다.HttpClient
over Https(여기에 있습니다)진전을 보긴 했지만 새로운 문제에 부딪혔어요.지난번 문제와 마찬가지로 나에게 맞는 예를 어디서도 찾을 수 없을 것 같다.기본적으로 클라이언트가 임의의 증명서를 받아들이도록 하고 싶은데(나는 항상 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를 사용하여 Android에서 "신뢰할 수 없음" 예외를 해결할 수 있는 4가지 솔루션이 있습니다.
- 모든 증명서를 신뢰합니다.네가 정말 뭘 하고 있는지 모르는 이상, 이러지 마
- 증명서만 신뢰하는 커스텀 SSL Socket Factory를 만듭니다.이 방법은 연결할 서버를 정확히 알고 있으면 작동하지만, 다른 SSL 인증서를 사용하여 새 서버에 연결해야 하는 즉시 앱을 업데이트해야 합니다.
- Android의 "마스터 목록" 인증서를 포함하는 키 저장소 파일을 만든 다음 자신의 인증서를 추가합니다.이러한 자격증 중 하나가 나중에 만료되면 앱에서 해당 자격증을 업데이트할 책임이 있습니다.나는 이것을 할 이유가 생각나지 않는다.
- 내장된 증명서 KeyStore를 사용하지만 기본값에서 검증에 실패한 경우 대체 KeyStore에 폴백하는 커스텀SSLSocketFactory를 만듭니다.
이 답변은 솔루션 #4를 사용합니다.이 솔루션이 가장 강력하다고 생각합니다.
해결책은 여러 KeyStore를 수용할 수 있는SSLSocketFactory를 사용하여 KeyStore에 자체 증명서를 제공하는 것입니다.이를 통해 일부 Android 장치에서 누락되었을 수 있는 Thawte와 같은 추가 최상위 인증서를 로드할 수 있습니다.또한 자체 서명된 인증서도 로드할 수 있습니다.기본 디바이스 증명서를 먼저 사용하고 필요한 경우에만 추가 증명서에 폴백합니다.
먼저 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 사이트에 액세스 해, 대응하는 증명서를 찾습니다.저희에게는 여기에 있습니다.저작권 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
다음으로 다음 명령을 실행하여 키스토어를 만듭니다.
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 스킴에 SSL Socket Factory를 등록합니다.
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);
SSL Socket Factory를 작성하려면:
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);
}
}
마지막으로 Additional KeyStoresSSL Socket Factory 코드: 새로운 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()]);
}
}
}
주의: 완전히 신뢰하지 않는 네트워크상에서 사용하는 실가동 코드에 실장하지 말아 주세요.특히 공중 인터넷을 통한 어떤 것도요.
당신의 질문이 바로 제가 알고 싶은 것입니다.제가 몇 가지 검색을 해보니 결론은 다음과 같습니다.
HttpClient 방식으로 org.apache.http.conn.ssl에서 커스텀클래스를 만들어야 합니다.하나의 org.apache.http.conn.ssl이 아닌 SSL Socket Factory.SSL Socket Factory 자체.몇 가지 단서는 Android 2.2 FroYo에서 커스텀 SSL 처리가 중지되었습니다.
예를 들면...
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();
}
}
참고로, 아래 링크는 Http를 찾는 사람을 위한 것입니다.URL Connection 솔루션.Https Connection Android
위의 2종류의 용액을 프로요로 시험해 보았습니다만, 모두 제 경우는 매우 효과가 있습니다.마지막으로 Http 사용URL 연결은 리다이렉트 문제에 직면할 수 있지만 이 문제는 주제에서 벗어납니다.
주의: 모든 증명서를 신뢰하기 전에 사이트를 충분히 파악하고 최종 사용자에게 해를 끼치지 않도록 해야 합니다.
확실히, 이하의 코멘트에 기재되어 있는 해커의 모의 사이트의 효과를 포함해, 리스크를 신중하게 고려할 필요가 있습니다.경우에 따라서는 모든 인증서를 관리하는 것이 어려울 수 있지만 모든 인증서를 신뢰하는 데 내재된 단점을 알아야 합니다.
를 앞에 합니다.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이 실제로 필요하지 않은 경우(메시지는 중요하지 않고 인증도 하지 않음)를 제외하고 모든 인증서를 맹목적으로 신뢰해서는 안 됩니다.
키툴을 사용하여 퍼블릭 증명서를 jk에 추가하고 이를 사용하여 다음과 같은 소켓팩토리를 구축하는 것을 검토해 주십시오.
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();
여기에는 주의해야 할 점이 하나 있습니다.증명서는 최종적으로 유효기간이 만료되고, 그 시점에서 코드는 동작을 정지합니다.증명서를 보면 언제 이런 일이 일어날지 쉽게 판단할 수 있습니다.
Http를 디세블로 할 수 있습니다.URL API 8은 다음과 같이 테스트 목적으로 접속 SSL을 확인합니다.
HttpURLConnection conn = (HttpURLConnection) url.openConnection();
if (conn instanceof HttpsURLConnection) {
HttpsURLConnection httpsConn = (HttpsURLConnection) conn;
httpsConn.setSSLSocketFactory(SSLCertificateSocketFactory.getInsecure(0, null));
httpsConn.setHostnameVerifier(new AllowAllHostnameVerifier());
}
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에 서명했습니다.내 경고에 귀 기울이시오!
Http Components의 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();
}
}
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);
}
}
}
모든 증명서를 신뢰하는 것은 나에게 진정한 대안이 아니었기 때문에 나는 Https를 얻기 위해 다음을 수행했다.URL 새로운 증명서를 신뢰하기 위한 접속(http://nelenkov.blogspot.jp/2011/12/using-custom-certificate-trust-store-on.html)도 참조).
증명서를 취득합니다.이 작업을 수행하려면 Firefox에서 증명서를 내보낸 후(작은 잠금 아이콘을 클릭하여 증명서 상세 내용을 가져오고 내보내기를 클릭), 포틀을 사용하여 신뢰 저장소(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());
다음은 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();
}
}
항목 #4: "emby" (6월 16일 21:29에 응답)에서 "기본 인증서 KeyStore를 사용하지만 기본 인증서 KeyStore에서 확인할 수 없는 경우 대체 KeyStore에 폴백하는 커스텀 SSL Socket Factory를 만듭니다."
이것은 간단한 구현입니다.시스템 키스토어를 로드하여 응용 프로그램 키스토어와 병합합니다.
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 를 참조해 주세요.
모든 증명서가 OAuth 상에서 (테스트 목적으로) 동작하도록 하는 경우는, 다음의 순서에 따라 주세요.
1) 여기에서 Android OAuth API 소스 코드를 다운로드합니다.https://github.com/kaeppler/signpost
2) CommonsHttp 파일을 찾습니다.OAuthProvider" 클래스
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)
}
상기의 「MySSLSocket Factory」는, 인정된 회답에 근거하고 있습니다.한층 더 알기 쉽게 하기 위해서, 클래스 전체를 다음에 나타냅니다.
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();
}
}
}
이게 도움이 됐으면 좋겠네요.
이것을 사용했는데, 모든 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();
}
}
a-lot이라는 노래입니다. 코드 (커스텀)등의 커스텀 코드를 포함하지 않는 경우TrustManager
커스텀 "" " " " "SSLSocketFactory
GitHub - SSLContext Kickstart 및 다음 코드 스니펫을 사용해 보는 것이 좋습니다.
<dependency>
<groupId>io.github.hakky54</groupId>
<artifactId>sslcontext-kickstart</artifactId>
<version>7.0.2</version>
</dependency>
SSL 설정
SSLFactory sslFactory = SSLFactory.builder()
.withUnsafeTrustMaterial()
.withUnsafeHostnameVerifier()
.build();
SSLSocketFactory sslSocketFactory = sslFactory.getSslSocketFactory();
HttpClient 컨피규레이션
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("https", sslSocketFactory, 443));
ClientConnectionManager ccm = new ThreadSafeClientConnManager(params, registry);
HttpClient httpClient = new DefaultHttpClient(ccm, params);
HttpsUrlConnection
HttpsURLConnection.setDefaultSSLSocketFactory(sslSocketFactory);
나는 또한 몇 가지 면책사항을 말할 필요가 있다. 나는 도서관의 관리인이다.
★★★★★★★★★★★-Dtrust_all_cert=true
VM을 사용합니다.이 인수는 Java에게 증명서 체크를 무시하도록 지시합니다.
Android 2.1에서 StartCom SSL 증명서에 어려움을 겪고 있는 고객은 https://www.startssl.com/certs/에 접속하여 ca.pem을 다운로드하십시오.이것에 대해서는, @emby 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`
바로 사용할 수 있을 겁니다.@emby의 완벽한 답변에도 하루 넘게 고심했습니다.이게 도움이 됐으면 좋겠는데...
이 클래스를 이용하다
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;
}
}
xamarin 안드로이드에서 sspi가 실패했습니다.
이 솔루션을 찾았습니다.HTTPS 링크를 누르기 전에 이 코드를 입력해 주세요.
const SslProtocols _Tls12 = (SslProtocols)0x00000C00;
const SecurityProtocolType Tls12 = (SecurityProtocolType)_Tls12;
ServicePointManager.SecurityProtocol = Tls12;
all https로 동작하다
httpClient = new DefaultHttpClient();
SSLContext ctx = SSLContext.getInstance("TLS");
X509TrustManager tm = new X509TrustManager() {
public void checkClientTrusted(X509Certificate[] xcs, String string) throws CertificateException { }
public void checkServerTrusted(X509Certificate[] xcs, String string) throws CertificateException { }
public X509Certificate[] getAcceptedIssuers() {
return null;
}
};
ctx.init(null, new TrustManager[]{tm}, null);
SSLSocketFactory ssf = new SSLSocketFactory(ctx, SSLSocketFactory.ALLOW_ALL_HOSTNAME_VERIFIER);
httpClient.getConnectionManager().getSchemeRegistry().register(new Scheme("https", 443, ssf));
위에는 많은 답변이 있습니다만, 그 중 어느 것도 올바르게 동작할 수 없었습니다(시간이 한정되어 있습니다).따라서, 같은 상황에 있는 누구라도, 다음의 코드를 사용해 주세요.이 코드는, Java 테스트의 목적에 최적인 것입니다.
public static HttpClient wrapClient(HttpClient base) {
try {
SSLContext ctx = SSLContext.getInstance("TLS");
X509TrustManager tm = new X509TrustManager() {
public void checkClientTrusted(X509Certificate[] xcs, String string) throws CertificateException { }
public void checkServerTrusted(X509Certificate[] xcs, String string) throws CertificateException { }
public X509Certificate[] getAcceptedIssuers() {
return null;
}
};
ctx.init(null, new TrustManager[]{tm}, null);
SSLSocketFactory ssf = new SSLSocketFactory(ctx);
ssf.setHostnameVerifier(SSLSocketFactory.ALLOW_ALL_HOSTNAME_VERIFIER);
ClientConnectionManager ccm = base.getConnectionManager();
SchemeRegistry sr = ccm.getSchemeRegistry();
sr.register(new Scheme("https", ssf, 443));
return new DefaultHttpClient(ccm, base.getParams());
} catch (Exception ex) {
return null;
}
}
다음과 같이 호출합니다.
DefaultHttpClient baseClient = new DefaultHttpClient();
HttpClient httpClient = wrapClient(baseClient );
참고 자료: http://tech.chitgoks.com/2011/04/24/how-to-avoid-javax-net-ssl-sslpeerunverifiedexception-peer-not-authenticated-problem-using-apache-httpclient/
간단히 이것을 사용하세요.
public DefaultHttpClient wrapClient(HttpClient base) {
try {
SSLContext ctx = SSLContext.getInstance("TLS");
X509TrustManager tm = new X509TrustManager() {
public void checkClientTrusted(X509Certificate[] xcs, String string) throws CertificateException { }
public void checkServerTrusted(X509Certificate[] xcs, String string) throws CertificateException { }
public X509Certificate[] getAcceptedIssuers() {
return null;
}
};
ctx.init(null, new TrustManager[]{tm}, null);
SSLSocketFactory ssf = new SSLSocketFactory(ctx);
ssf.setHostnameVerifier(SSLSocketFactory.ALLOW_ALL_HOSTNAME_VERIFIER);
ClientConnectionManager ccm = base.getConnectionManager();
SchemeRegistry sr = ccm.getSchemeRegistry();
sr.register(new Scheme("https", ssf, 443));
return new DefaultHttpClient(ccm, base.getParams());
} catch (Exception ex) {
return null;
}
}
Daniel의 대답은 좋았지만 이 코드를 변경해야 했어
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);
이 코드로...
ClientConnectionManager ccm = new ThreadSafeClientConnManager(params, registry);
SchemeRegistry registry = ccm.getShemeRegistry()
registry.register(new Scheme("http", PlainSocketFactory.getSocketFactory(), 80));
registry.register(new Scheme("https", sf, 443));
작동시킬 수 있습니다.
언급URL : https://stackoverflow.com/questions/2642777/trusting-all-certificates-using-httpclient-over-https
'programing' 카테고리의 다른 글
Java를 사용하여 파일 이름 변경 (0) | 2022.08.01 |
---|---|
Vue 인스턴스의 수명 주기 이해 (0) | 2022.08.01 |
C#과 C - 퍼포먼스의 큰 차이 (0) | 2022.08.01 |
메이븐 실행 프로젝트 (0) | 2022.08.01 |
2 - Vue 2 - Vue 2 선택 방법2 - Vue 2 - Vue 2 선택 방법2 - Vue 2 - Vue 2 선택 방법 (0) | 2022.08.01 |