Frage

Wie nehme ich ein selbst signiertes Zertifikat in Java auf Android?

Ein Codebeispiel wäre perfekt.

Ich habe überall im Internet geschaut und während einige Leute, die Lösung gefunden zu haben behaupten, entweder es funktioniert nicht oder es ist kein Beispielcode um es wieder auf.

War es hilfreich?

Lösung

Ich habe diese Funktionalität in EXCHANGEit, die auf Microsoft Exchange über WebDav verbindet. Hier einige Code ein Httpclient zu schaffen, die selbst signiertes Zertifikat ist über SSL verbinden:

SchemeRegistry schemeRegistry = new SchemeRegistry();
// http scheme
schemeRegistry.register(new Scheme("http", PlainSocketFactory.getSocketFactory(), 80));
// https scheme
schemeRegistry.register(new Scheme("https", new EasySSLSocketFactory(), 443));

HttpParams params = new BasicHttpParams();
params.setParameter(ConnManagerPNames.MAX_TOTAL_CONNECTIONS, 30);
params.setParameter(ConnManagerPNames.MAX_CONNECTIONS_PER_ROUTE, new ConnPerRouteBean(30));
params.setParameter(HttpProtocolParams.USE_EXPECT_CONTINUE, false);
HttpProtocolParams.setVersion(params, HttpVersion.HTTP_1_1);

ClientConnectionManager cm = new ThreadSafeClientConnManager(params, schemeRegistry);

Die EasySSLSocketFactory ist hier und die EasyX509TrustManager ist hier .

Der Code für EXCHANGEit ist Open Source und gehostete auf Googlecode hier , wenn Sie irgendwelche Probleme. Ich bin nicht aktiv arbeiten daran mehr, aber der Code sollte funktionieren.

Beachten Sie, dass seit Android 2.2 der Prozess ein wenig verändert hat, so überprüfen diese den Code über Arbeit zu machen.

Andere Tipps

Wie EJP richtig kommentierte "Die Leser werden darauf hingewiesen, dass diese Technik radikal unsicher ist. SSL ist nicht sicher, wenn mindestens eine Peer authentifiziert. Siehe RFC 2246."

Having said that, hier ist eine andere Art und Weise, ohne zusätzliche Klassen:

import java.security.SecureRandom;
import java.security.cert.CertificateException;
import java.security.cert.X509Certificate;

import javax.net.ssl.HostnameVerifier;
import javax.net.ssl.HttpsURLConnection;
import javax.net.ssl.SSLContext;
import javax.net.ssl.SSLSession;
import javax.net.ssl.X509TrustManager;

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();
    }
}

stand ich dieses Problem gestern, während unseres Unternehmens RESTful API zu HTTPS migrieren, aber unter Verwendung von selbstsignierten SSL-Zertifikaten.

Ich habe überall gesucht, aber alle die „richtigen“ markiert Antworten, die ich gefunden habe, bestand aus Zertifikatsüberprüfung zu deaktivieren, klar überschreibt alle das Gefühl von SSL.

Ich kam schließlich zu einer Lösung:

  1. Erstellen von lokalen Schlüsselspeicher

    Damit Ihre App Ihre selbstsignierte Zertifikate zu validieren, müssen Sie in einer Art und Weise einen benutzerdefinierten Schlüsselspeicher mit den Zertifikaten sorgen, dass Android Ihrem Endpunkt vertrauen kann.

Das Format für eine solche individuelle Schlüsselspeicher ist "BKS" von BouncyCastle , so dass Sie die 1.46 Version von BouncyCastleProvider müssen, dass Sie herunterladen können hier .

Sie Ihr selbst signiertes Zertifikat auch brauchen, werde ich nehme an, es ist genannt self_cert.pem.

Jetzt ist der Befehl Keystore- für die Erstellung ist:

<!-- language: lang-sh -->

    $ keytool -import -v -trustcacerts -alias 0 \
    -file *PATH_TO_SELF_CERT.PEM* \
    -keystore *PATH_TO_KEYSTORE* \
    -storetype BKS \
    -provider org.bouncycastle.jce.provider.BouncyCastleProvider \
    -providerpath *PATH_TO_bcprov-jdk15on-146.jar* \
    -storepass *STOREPASS*

PATH_TO_KEYSTORE zeigt auf eine Datei, in der Keystore- erstellt wird. Es darf nicht vorhanden sein .

PATH_TO_bcprov-jdk15on-146.jar.JAR ist der Pfad zu dem heruntergeladenen .jar libary.

STOREPASS ist Ihr neu erstelltes Kennwort für Schlüsselspeicher.

  1. Neue Schlüsselspeicher in Ihrer Anwendung

Kopieren Sie die neu erstellte Schlüsselspeicher von PATH_TO_KEYSTORE res/raw/certs.bks ( certs.bks ist nur der Name Datei, Sie können Sie verwenden, was benennende)

Erstellen Sie einen Schlüssel in res/values/strings.xml mit

<!-- language: lang-xml -->

    <resources>
    ...
        <string name="store_pass">*STOREPASS*</string>
    ...
    </resources>
  1. diese Klasse, die erbt DefaultHttpClient

    import android.content.Context;
    import android.util.Log;
    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.params.HttpParams;
    
    import java.io.IOException;
    import java.io.InputStream;
    import java.security.*;
    
    public class MyHttpClient extends DefaultHttpClient {
    
        private static Context appContext = null;
        private static HttpParams params = null;
        private static SchemeRegistry schmReg = null;
        private static Scheme httpsScheme = null;
        private static Scheme httpScheme = null;
        private static String TAG = "MyHttpClient";
    
        public MyHttpClient(Context myContext) {
    
            appContext = myContext;
    
            if (httpScheme == null || httpsScheme == null) {
                httpScheme = new Scheme("http", PlainSocketFactory.getSocketFactory(), 80);
                httpsScheme = new Scheme("https", mySSLSocketFactory(), 443);
            }
    
            getConnectionManager().getSchemeRegistry().register(httpScheme);
            getConnectionManager().getSchemeRegistry().register(httpsScheme);
    
        }
    
        private SSLSocketFactory mySSLSocketFactory() {
            SSLSocketFactory ret = null;
            try {
                final KeyStore ks = KeyStore.getInstance("BKS");
    
                final InputStream inputStream = appContext.getResources().openRawResource(R.raw.certs);
    
                ks.load(inputStream, appContext.getString(R.string.store_pass).toCharArray());
                inputStream.close();
    
                ret = new SSLSocketFactory(ks);
            } catch (UnrecoverableKeyException ex) {
                Log.d(TAG, ex.getMessage());
            } catch (KeyStoreException ex) {
                Log.d(TAG, ex.getMessage());
            } catch (KeyManagementException ex) {
                Log.d(TAG, ex.getMessage());
            } catch (NoSuchAlgorithmException ex) {
                Log.d(TAG, ex.getMessage());
            } catch (IOException ex) {
                Log.d(TAG, ex.getMessage());
            } catch (Exception ex) {
                Log.d(TAG, ex.getMessage());
            } finally {
                return ret;
            }
        }
    }
    

einfach nun eine Instanz von **MyHttpClient** verwenden wie bei **DefaultHttpClient** Ihre HTTPS-Abfragen zu machen, und es wird verwenden und die korrekte Schreibweise Ihrer selbst signierten SSL-Zertifikate validieren.

HttpResponse httpResponse;

HttpPost httpQuery = new HttpPost("https://yourserver.com");
... set up your query ...

MyHttpClient myClient = new MyHttpClient(myContext);

try{

    httpResponse = myClient.(peticionHttp);

    // Check for 200 OK code
    if (httpResponse.getStatusLine().getStatusCode() == HttpURLConnection.HTTP_OK) {
        ... do whatever you want with your response ...
    }

}catch (Exception ex){
    Log.d("httpError", ex.getMessage());
}

Wenn ich etwas verpasst haben, sind die anderen Antworten auf dieser Seite DANGEROUS und sind funktional gleichwertig durchaus nicht mit SSL. Wenn Sie selbst signierten Zertifikaten vertrauen, ohne weitere Prüfungen zu tun, um sicherzustellen, sind die Zertifikate diejenigen, die Sie erwarten, dann jemand ein selbst signiertes Zertifikat erstellen können und so tun, können Sie Ihre Server. An diesem Punkt haben Sie keine wirkliche Sicherheit.

Die nur legitime Art und Weise, dies zu tun (ohne einen vollständigen SSL-Stack zu schreiben) ist ein zusätzlichen vertrauenswürdigen Anker hinzuzufügen während des Zertifikat-Verifizierungsprozesses vertraut werden. Beide beinhalten Hartcodierung des vertrauenswürdigen Anker-Zertifikat in Ihrer Anwendung und Hinzufügen zu, was auch immer Anker darauf vertraut, dass das Betriebssystem bietet (sonst werden Sie nicht in der Lage sein, um Ihre Website zu verbinden, wenn Sie ein echtes Zertifikat erhalten).

Ich bin mir dessen bewusst zwei Möglichkeiten, dies zu tun:

  1. ein Geschäft individuelles Vertrauens erstellen, wie unter http : //www.ibm.com/developerworks/java/library/j-customssl/#8

  2. eine benutzerdefinierte Instanz X509TrustManager erstellen und die getAcceptedIssuers Methode überschreiben, um ein Array zurück, die Ihr Zertifikat enthält:

    public X509Certificate[] getAcceptedIssuers()
    {
        X509Certificate[] trustedAnchors =
            super.getAcceptedIssuers();
    
        /* Create a new array with room for an additional trusted certificate. */
        X509Certificate[] myTrustedAnchors = new X509Certificate[trustedAnchors.length + 1];
        System.arraycopy(trustedAnchors, 0, myTrustedAnchors, 0, trustedAnchors.length);  
    
        /* Load your certificate.
    
           Thanks to http://stackoverflow.com/questions/11857417/x509trustmanager-override-without-allowing-all-certs
           for this bit.
         */
        InputStream inStream = new FileInputStream("fileName-of-cert");
        CertificateFactory cf = CertificateFactory.getInstance("X.509");
        X509Certificate cert = (X509Certificate)cf.generateCertificate(inStream);
        inStream.close();
    
        /* Add your anchor cert as the last item in the array. */
        myTrustedAnchors[trustedAnchors.length] = cert;
    
        return myTrustedAnchors;
    }
    

Beachten Sie, dass dieser Code völlig ungetestet ist und nicht einmal kompilieren kann, aber zumindest sollten Sie in die richtige Richtung lenken.

Brian Yarger Antwort arbeitet in Android 2.2 als auch, wenn Sie die größere create -Methodenüberladung wie folgt ändern. Es dauerte eine Weile, selbst signiertes SZLs zum Laufen zu bringen.

public Socket createSocket(Socket socket, String host, int port, boolean autoClose) throws IOException, UnknownHostException {
    return getSSLContext().getSocketFactory().createSocket(socket, host, port, autoClose);
}

Auf Android HttpProtocolParams akzeptiert ProtocolVersion statt HttpVersion.

ProtocolVersion pv = new ProtocolVersion("HTTP", 1, 1);
HttpProtocolParams.setVersion(params, pv);

@ Chris - Posting dies als eine Antwort, da ich nicht Kommentare hinzufügen (noch) nicht. Ich frage mich, ob Ihr Ansatz funktionieren soll, wenn ein WebView verwenden. Ich kann es nicht so auf Android 2.3 erhalten tun -. Stattdessen bekomme ich nur einen weißen Bildschirm

Nach etwas mehr suchen, ich kam in diesem

scroll top