Robię post https i otrzymuję wyjątek wyjątku ssl Niezaufany certyfikat serwera. Jeśli robię normalny http, działa idealnie. Czy muszę jakoś akceptować certyfikat serwera?
Robię post https i otrzymuję wyjątek wyjątku ssl Niezaufany certyfikat serwera. Jeśli robię normalny http, działa idealnie. Czy muszę jakoś akceptować certyfikat serwera?
Odpowiedzi:
Zgaduję, ale jeśli chcesz, aby rzeczywiście nastąpił uścisk dłoni, musisz powiadomić Androida o swoim certyfikacie. Jeśli chcesz zaakceptować bez względu na wszystko, użyj tego pseudokodu, aby uzyskać to, czego potrzebujesz z klientem Apache HTTP:
SchemeRegistry schemeRegistry = new SchemeRegistry ();
schemeRegistry.register (new Scheme ("http",
PlainSocketFactory.getSocketFactory (), 80));
schemeRegistry.register (new Scheme ("https",
new CustomSSLSocketFactory (), 443));
ThreadSafeClientConnManager cm = new ThreadSafeClientConnManager (
params, schemeRegistry);
return new DefaultHttpClient (cm, params);
CustomSSLSocketFactory:
public class CustomSSLSocketFactory extends org.apache.http.conn.ssl.SSLSocketFactory
{
private SSLSocketFactory FACTORY = HttpsURLConnection.getDefaultSSLSocketFactory ();
public CustomSSLSocketFactory ()
{
super(null);
try
{
SSLContext context = SSLContext.getInstance ("TLS");
TrustManager[] tm = new TrustManager[] { new FullX509TrustManager () };
context.init (null, tm, new SecureRandom ());
FACTORY = context.getSocketFactory ();
}
catch (Exception e)
{
e.printStackTrace();
}
}
public Socket createSocket() throws IOException
{
return FACTORY.createSocket();
}
// TODO: add other methods like createSocket() and getDefaultCipherSuites().
// Hint: they all just make a call to member FACTORY
}
FullX509TrustManager to klasa, która implementuje javax.net.ssl.X509TrustManager, ale żadna z metod nie wykonuje żadnej pracy, pobierz próbkę tutaj .
Powodzenia!
FullX509TrustManager
To właśnie robię. Po prostu nie sprawdza już certyfikatu.
// always verify the host - dont check for certificate
final static HostnameVerifier DO_NOT_VERIFY = new HostnameVerifier() {
public boolean verify(String hostname, SSLSession session) {
return true;
}
};
/**
* Trust every server - dont check for any certificate
*/
private static void trustAllHosts() {
// Create a trust manager that does not validate certificate chains
TrustManager[] trustAllCerts = new TrustManager[] { new X509TrustManager() {
public java.security.cert.X509Certificate[] getAcceptedIssuers() {
return new java.security.cert.X509Certificate[] {};
}
public void checkClientTrusted(X509Certificate[] chain,
String authType) throws CertificateException {
}
public void checkServerTrusted(X509Certificate[] chain,
String authType) throws CertificateException {
}
} };
// Install the all-trusting trust manager
try {
SSLContext sc = SSLContext.getInstance("TLS");
sc.init(null, trustAllCerts, new java.security.SecureRandom());
HttpsURLConnection
.setDefaultSSLSocketFactory(sc.getSocketFactory());
} catch (Exception e) {
e.printStackTrace();
}
}
i
HttpURLConnection http = null;
if (url.getProtocol().toLowerCase().equals("https")) {
trustAllHosts();
HttpsURLConnection https = (HttpsURLConnection) url.openConnection();
https.setHostnameVerifier(DO_NOT_VERIFY);
http = https;
} else {
http = (HttpURLConnection) url.openConnection();
}
javax.net.ssl.SSLException: Received fatal alert: bad_record_mac
. Próbowałem również zastąpienie TLS
z SSL
ale to nie pomogło. Proszę, pomóż mi, dzięki
Próbując odpowiedzieć na to pytanie, znalazłem lepszy tutorial. Dzięki niemu nie musisz narażać się na sprawdzanie certyfikatu.
http://blog.crazybob.org/2010/02/android-trusting-ssl-certificates.html
* Nie napisałem tego, ale dzięki Bobowi Lee za pracę
Możesz również spojrzeć na mój artykuł na blogu, bardzo podobny do crazybobs.
To rozwiązanie nie zagraża również sprawdzaniu certyfikatów i wyjaśnia, jak dodać zaufane certyfikaty do własnego magazynu kluczy.
http://blog.antoine.li/index.php/2010/10/android-trusting-ssl-certificates/
http://madurangasblogs.blogspot.in/2013/08/avoiding-javaxnetsslsslpeerunverifiedex.html
Dzięki uprzejmości Maduranga
Podczas tworzenia aplikacji korzystającej z protokołu https serwer testowy nie ma ważnego certyfikatu SSL. Czasami witryna internetowa korzysta z certyfikatu z podpisem własnym lub korzysta z bezpłatnego certyfikatu SSL. Więc jeśli spróbujesz połączyć się z serwerem za pomocą Apache HttpClient
, pojawi się wyjątek informujący, że „peer nie uwierzytelniony”. Chociaż ufanie wszystkim certyfikatom oprogramowania produkcyjnego nie jest dobrą praktyką, może być to konieczne w zależności od sytuacji. To rozwiązanie rozwiązuje wyjątek spowodowany przez „nieuwierzytelniony element równorzędny”.
Zanim jednak przejdziemy do rozwiązania, muszę Cię ostrzec, że nie jest to dobry pomysł na aplikację produkcyjną. Naruszy to cel używania certyfikatu bezpieczeństwa. Więc jeśli nie masz dobrego powodu lub jesteś pewien, że nie spowoduje to żadnego problemu, nie korzystaj z tego rozwiązania.
Zwykle tworzysz coś HttpClient
takiego.
HttpClient httpclient = new DefaultHttpClient();
Ale musisz zmienić sposób tworzenia HttpClient.
Najpierw musisz utworzyć rozszerzającą klasę org.apache.http.conn.ssl.SSLSocketFactory
.
import org.apache.http.conn.ssl.SSLSocketFactory;
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;
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();
}
}
Następnie utwórz taką metodę.
public 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();
}
}
Następnie możesz utworzyć plik HttpClient
.
HttpClient httpclient = getNewHttpClient();
Jeśli próbujesz wysłać prośbę o wpis na stronę logowania, reszta kodu będzie wyglądać tak.
private URI url = new URI("url of the action of the form");
HttpPost httppost = new HttpPost(url);
List<NameValuePair> nameValuePairs = new ArrayList<NameValuePair>();
nameValuePairs.add(new BasicNameValuePair("username", "user"));
nameValuePairs.add(new BasicNameValuePair("password", "password"));
try {
httppost.setEntity(new UrlEncodedFormEntity(nameValuePairs));
HttpResponse response = httpclient.execute(httppost);
HttpEntity entity = response.getEntity();
InputStream is = entity.getContent();
} catch (UnsupportedEncodingException e) {
// TODO Auto-generated catch block
e.printStackTrace();
} catch (ClientProtocolException e) {
// TODO Auto-generated catch block
e.printStackTrace();
} catch (IOException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
Otrzymasz stronę HTML do InputStream. Następnie możesz zrobić, co chcesz, ze zwróconą stroną HTML.
Ale tutaj napotkasz problem. Jeżeli będziesz chciał zarządzać sesją za pomocą plików cookies, nie będziesz mógł tego zrobić tą metodą. Jeśli chcesz otrzymywać pliki cookie, musisz to zrobić za pośrednictwem przeglądarki. Tylko wtedy otrzymasz ciasteczka.
Jeśli używasz certyfikatu StartSSL lub Thawte, nie powiedzie się to w przypadku Froyo i starszych wersji. Możesz użyć repozytorium CAcert nowszej wersji zamiast ufać każdemu certyfikatowi.
Żaden z nich nie działał dla mnie (dodatkowo pogarszany przez błąd Thawte ). W końcu udało mi się to naprawić dzięki akceptacji SSL z podpisem własnym na Androidzie, a obsługa niestandardowego SSL przestała działać na Androidzie 2.2 FroYo
Żadna z tych odpowiedzi nie zadziałała dla mnie, więc oto kod, który ufa jakimkolwiek certyfikatom.
import java.io.IOException;
import java.net.Socket;
import java.security.KeyManagementException;
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.client.ClientProtocolException;
import org.apache.http.client.HttpClient;
import org.apache.http.client.methods.HttpPost;
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.conn.ssl.X509HostnameVerifier;
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.HttpConnectionParams;
import org.apache.http.params.HttpParams;
public class HttpsClientBuilder {
public static DefaultHttpClient getBelieverHttpsClient() {
DefaultHttpClient client = null;
SchemeRegistry Current_Scheme = new SchemeRegistry();
Current_Scheme.register(new Scheme("http", PlainSocketFactory.getSocketFactory(), 80));
try {
Current_Scheme.register(new Scheme("https", new Naive_SSLSocketFactory(), 8443));
} catch (KeyManagementException e) {
// TODO Auto-generated catch block
e.printStackTrace();
} catch (UnrecoverableKeyException e) {
// TODO Auto-generated catch block
e.printStackTrace();
} catch (NoSuchAlgorithmException e) {
// TODO Auto-generated catch block
e.printStackTrace();
} catch (KeyStoreException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
HttpParams Current_Params = new BasicHttpParams();
int timeoutConnection = 8000;
HttpConnectionParams.setConnectionTimeout(Current_Params, timeoutConnection);
int timeoutSocket = 10000;
HttpConnectionParams.setSoTimeout(Current_Params, timeoutSocket);
ThreadSafeClientConnManager Current_Manager = new ThreadSafeClientConnManager(Current_Params, Current_Scheme);
client = new DefaultHttpClient(Current_Manager, Current_Params);
//HttpPost httpPost = new HttpPost(url);
//client.execute(httpPost);
return client;
}
public static class Naive_SSLSocketFactory extends SSLSocketFactory
{
protected SSLContext Cur_SSL_Context = SSLContext.getInstance("TLS");
public Naive_SSLSocketFactory ()
throws NoSuchAlgorithmException, KeyManagementException,
KeyStoreException, UnrecoverableKeyException
{
super(null, null, null, null, null, (X509HostnameVerifier)null);
Cur_SSL_Context.init(null, new TrustManager[] { new X509_Trust_Manager() }, null);
}
@Override
public Socket createSocket(Socket socket, String host, int port,
boolean autoClose) throws IOException
{
return Cur_SSL_Context.getSocketFactory().createSocket(socket, host, port, autoClose);
}
@Override
public Socket createSocket() throws IOException
{
return Cur_SSL_Context.getSocketFactory().createSocket();
}
}
private static class X509_Trust_Manager implements X509TrustManager
{
public void checkClientTrusted(X509Certificate[] chain, String authType)
throws CertificateException {
// TODO Auto-generated method stub
}
public void checkServerTrusted(X509Certificate[] chain, String authType)
throws CertificateException {
// TODO Auto-generated method stub
}
public X509Certificate[] getAcceptedIssuers() {
// TODO Auto-generated method stub
return null;
}
};
}
Nie wiem, jakie są specyfikacje Androida dotyczące certyfikatów ssl, ale miałoby sens, gdyby Android nie akceptował samodzielnie podpisanego certyfikatu ssl. Znalazłem ten post na forach Androida, który wydaje się dotyczyć tego samego problemu: http://androidforums.com/android-applications/950-imap-self-signed-ssl-certificates.html
Jest to znany problem występujący w systemie Android 2.x. Zmagałem się z tym problemem przez tydzień, aż natrafiłem na następujące pytanie, które nie tylko daje dobre tło problemu, ale także zapewnia działające i skuteczne rozwiązanie pozbawione jakichkolwiek luk w zabezpieczeniach.
Błąd „Brak certyfikatu równorzędnego” w systemie Android 2.3, ale NIE w wersji 4
Z jakiegoś powodu rozwiązanie wspomniane powyżej dla httpClient nie zadziałało. Na koniec udało mi się sprawić, że to zadziała, poprawnie nadpisując metodę podczas implementacji niestandardowej klasy SSLSocketFactory.
@Override
public Socket createSocket(Socket socket, String host, int port, boolean autoClose)
throws IOException, UnknownHostException
{
return sslFactory.createSocket(socket, host, port, autoClose);
}
@Override
public Socket createSocket() throws IOException {
return sslFactory.createSocket();
}
Tak to działało idealnie dla mnie. Możesz zobaczyć pełną klasę niestandardową i implementację w następującym wątku: http://blog.syedgakbar.com/2012/07/21/android-https-and-not-trusted-server-certificate-error/
Zrobiłem tę klasę i znalazłem
package com.example.fakessl;
import java.security.KeyManagementException;
import java.security.NoSuchAlgorithmException;
import java.security.SecureRandom;
import java.security.cert.CertificateException;
import java.security.cert.X509Certificate;
import javax.net.ssl.HostnameVerifier;
import javax.net.ssl.SSLSession;
import javax.net.ssl.TrustManager;
import android.util.Log;
public class CertificadoAceptar {
private static TrustManager[] trustManagers;
public static class _FakeX509TrustManager implements
javax.net.ssl.X509TrustManager {
private static final X509Certificate[] _AcceptedIssuers = new X509Certificate[] {};
public void checkClientTrusted(X509Certificate[] arg0, String arg1)
throws CertificateException {
}
public void checkServerTrusted(X509Certificate[] arg0, String arg1)
throws CertificateException {
}
public boolean isClientTrusted(X509Certificate[] chain) {
return (true);
}
public boolean isServerTrusted(X509Certificate[] chain) {
return (true);
}
public X509Certificate[] getAcceptedIssuers() {
return (_AcceptedIssuers);
}
}
public static void allowAllSSL() {
javax.net.ssl.HttpsURLConnection
.setDefaultHostnameVerifier(new HostnameVerifier() {
public boolean verify(String hostname, SSLSession session) {
return true;
}
});
javax.net.ssl.SSLContext context = null;
if (trustManagers == null) {
trustManagers = new javax.net.ssl.TrustManager[] { new _FakeX509TrustManager() };
}
try {
context = javax.net.ssl.SSLContext.getInstance("TLS");
context.init(null, trustManagers, new SecureRandom());
} catch (NoSuchAlgorithmException e) {
Log.e("allowAllSSL", e.toString());
} catch (KeyManagementException e) {
Log.e("allowAllSSL", e.toString());
}
javax.net.ssl.HttpsURLConnection.setDefaultSSLSocketFactory(context
.getSocketFactory());
}
}
w tobie kod biały to
CertificadoAceptar ca = new CertificadoAceptar();
ca.allowAllSSL();
HttpsTransportSE Transport = new HttpsTransportSE("iphost or host name", 8080, "/WS/wsexample.asmx?WSDL", 30000);
Źródła, które pomogły mi w pracy z własnym certyfikatem z podpisem własnym na moim serwerze AWS Apache i łączeniu się z HttpsURLConnection z urządzenia z systemem Android:
SSL na instancji AWS - samouczek Amazon na temat
SSL Android Security z HTTPS i SSL - tworzenie własnego menedżera zaufania na kliencie do akceptowania twój certyfikat
Tworzenie certyfikatu z podpisem własnym - łatwy skrypt do tworzenia certyfikatów
Następnie wykonałem następujące czynności:
create_my_certs.sh
:#!/bin/bash FQDN=$1 # make directories to work from mkdir -p server/ client/ all/ # Create your very own Root Certificate Authority openssl genrsa \ -out all/my-private-root-ca.privkey.pem \ 2048 # Self-sign your Root Certificate Authority # Since this is private, the details can be as bogus as you like openssl req \ -x509 \ -new \ -nodes \ -key all/my-private-root-ca.privkey.pem \ -days 1024 \ -out all/my-private-root-ca.cert.pem \ -subj "/C=US/ST=Utah/L=Provo/O=ACME Signing Authority Inc/CN=example.com" # Create a Device Certificate for each domain, # such as example.com, *.example.com, awesome.example.com # NOTE: You MUST match CN to the domain name or ip address you want to use openssl genrsa \ -out all/privkey.pem \ 2048 # Create a request from your Device, which your Root CA will sign openssl req -new \ -key all/privkey.pem \ -out all/csr.pem \ -subj "/C=US/ST=Utah/L=Provo/O=ACME Tech Inc/CN=${FQDN}" # Sign the request from Device with your Root CA openssl x509 \ -req -in all/csr.pem \ -CA all/my-private-root-ca.cert.pem \ -CAkey all/my-private-root-ca.privkey.pem \ -CAcreateserial \ -out all/cert.pem \ -days 500 # Put things in their proper place rsync -a all/{privkey,cert}.pem server/ cat all/cert.pem > server/fullchain.pem # we have no intermediates in this case rsync -a all/my-private-root-ca.cert.pem server/ rsync -a all/my-private-root-ca.cert.pem client/
bash create_my_certs.sh yourdomain.com
Umieść certyfikaty w odpowiednich miejscach na serwerze (konfigurację znajdziesz w /etc/httpd/conf.d/ssl.conf). Wszystko to należy ustawić:
SSLCertificateFile
SSLCertificateKeyFile
SSLCertificateChainFile
SSLCACertificateFile
Uruchom ponownie httpd za pomocą sudo service httpd restart
i upewnij się, że httpd rozpoczęte:
Zatrzymywanie httpd: [OK]
Uruchamianie httpd: [OK]
Skopiuj my-private-root-ca.cert
do folderu zasobów projektu systemu Android
Utwórz swojego menedżera zaufania:
SSLContext SSLContext;
CertificateFactory cf = CertificateFactory.getInstance ("X.509"); InputStream caInput = context.getAssets (). Open ("my-private-root-ca.cert.pem"); Certyfikat ca; try {ca = cf.generateCertificate (caInput); } wreszcie {caInput.close (); }
// Create a KeyStore containing our trusted CAs
String keyStoreType = KeyStore.getDefaultType();
KeyStore keyStore = KeyStore.getInstance(keyStoreType);
keyStore.load(null, null);
keyStore.setCertificateEntry("ca", ca);
// Create a TrustManager that trusts the CAs in our KeyStore
String tmfAlgorithm = TrustManagerFactory.getDefaultAlgorithm();
TrustManagerFactory tmf = TrustManagerFactory.getInstance(tmfAlgorithm);
tmf.init(keyStore);
// Create an SSLContext that uses our TrustManager
SSLContext = SSLContext.getInstance("TLS");
SSSLContext.init(null, tmf.getTrustManagers(), null);
I nawiąż połączenie za pomocą HttpsURLConnection:
Połączenie HttpsURLConnection = (HttpsURLConnection) url.openConnection (); connection.setSSLSocketFactory (SSLContext.getSocketFactory ());
To wszystko, wypróbuj połączenie https.
Po prostu użyj tej metody jako swojego HTTPClient:
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();
}
}