[OkHttp] REST-API Java SSL

Einführung

Ich habe den Code zum Aufrufen der REST-API mit SSL unter Verwendung von OkHttp3 geschrieben. Es gibt viele Beispiele auf der Welt, die eine Verbindung herstellen, ohne das Zertifikat zu validieren. Dieser Code führt jedoch die folgende Validierung durch.

Umgebung

Vorbereitung

Serverzertifikat abrufen

Beziehen Sie das Serverzertifikat von dem Ort, an dem die API veröffentlicht wird. Diesmal habe ich es von Recruit im Browser (Chrome) bekommen. Sie können verschiedene Möglichkeiten finden, um es zu erhalten, indem Sie nach "So erhalten Sie einen Serverzertifikat-Browser" suchen. Das Format der Exportdatei lautet "Base 64-codiertes X.509".

Beziehen Sie das Zertifikat der Zertifizierungsstelle, die das Serverzertifikat signiert hat

Es ist das über dem Zertifikat im "Zertifizierungspfad". Holen Sie es sich auf die gleiche Weise wie oben.

Kombinieren Sie Serverzertifikate

Öffnen Sie die beiden oben genannten Zertifikate mit einem Texteditor und kopieren Sie eines davon und fügen Sie es in eines ein. Ich habe die Eltern als "Zertifizierungspass" gestürzt.

Stellen Sie die zusammengeführten Dateien an einem Speicherort in Ihrem Klassenpfad bereit.

Quellcode

API-Ausführung

API(SSL)Ausführungsklasse


public class RestSecureApiExecutor extends RestApiExecutor {

    private CertificateManager certMgr;

    /**
     *Konstrukteur.
     *
     * @param apiAttr Secure API-Attribut
     */
    public RestSecureApiExecutor(SecureApiAttribute apiAttr) {
        super(apiAttr);
        this.certMgr = apiAttr.getCertMgr();
    }

    /**
     * {@inheritDoc}
     */
    @Override
    public Response get(Map<String, String> header, Map<String, String> param, boolean isValidCache) throws IOException {
        //URL-Assembly
        HttpUrl.Builder urlBuilder = createUrlBuilder(ProtocolType.Secure, param);
        //HTTP-Header zusammenstellen
        Request.Builder requestBuilder = createRequestBuilder(header);
        //Gibt an, ob der Cache verwendet werden soll
        if (!isValidCache) {
            requestBuilder.cacheControl(new CacheControl.Builder().noCache().noStore().maxAge(0, TimeUnit.SECONDS).build());
        }
        //Die Anfrage zusammenstellen
        Request request = requestBuilder.url(urlBuilder.build()).build();
        //Gibt an, ob der Cache verwendet werden soll
        OkHttpClient.Builder clientBuilder = createClientBuilder(isValidCache);

        addCertificatePinner(clientBuilder, certMgr.getCertificates());
        addSslSocketFactory(clientBuilder, certMgr.getTrustManager());

        //API-Ausführung
        return clientBuilder.build().newCall(request).execute();
    }

    /**
     *Fügen Sie dem Builder CertificatePinner hinzu.
     *
     * @param builder OkHttpClient.Builder-Objekt
     * @param-Zertifikate Zertifikatinformationen
     */
    private void addCertificatePinner(OkHttpClient.Builder builder, Certificate[] certificates) {
        CertificatePinner certificatePinner = new CertificatePinner.Builder()
                .add(this.hostname, CertificatePinner.pin(certificates[0]))
                .build();
        builder.certificatePinner(certificatePinner);
    }

    /**
     *Fügen Sie dem Builder SslSocketFactory hinzu.
     *
     * @param builder OkHttpClient.Builder-Objekt
     * @param trustManager TrustManager
     */
    private void addSslSocketFactory(OkHttpClient.Builder builder, X509TrustManager trustManager) {
        try {
            SSLContext sslContext = SSLContext.getInstance("TLS");
            sslContext.init(null, new TrustManager[] { trustManager }, null);
            builder.sslSocketFactory(sslContext.getSocketFactory(), trustManager);
        } catch (NoSuchAlgorithmException | KeyManagementException e) {
            throw new RuntimeException("____ failed to create ssl socket factory.", e);
        }
    }

Elternklasse


public class RestApiExecutor {

    /**
     *Protokolltyp.
     */
    public enum ProtocolType {
        /**Normal. */
        Normal("http"),
        /** SSL. */
        Secure("https"),;

        private String type;

        private ProtocolType(String type) {
            this.type = type;
        }

        public String getValue() {
            return this.type;
        }
    }

    /**Hostname. */
    protected String hostname;
    /**Segment. */
    protected String segment;

    /**
     *Konstrukteur.
     *
     * @param apiAttr API-Attribut
     */
    public RestApiExecutor(ApiAttribute apiAttr) {
        //Hostname, Segment
        this.hostname = apiAttr.getHostname();
        this.segment = apiAttr.getSegment();
    }

    /**
     * REST API (get)Laufen.
     *
     * @Parameter-Header HTTP-Header-Parameter
     * @param param Abfrageparameter
     * @param isValidCache Cache verwenden (true: use, false: not use)
     * @Antwort zurückgeben
     * @löst IOException aus Wenn die Verarbeitung fehlschlägt
     */
    public Response get(Map<String, String> header, Map<String, String> param, boolean isValidCache) throws IOException {
        //URL-Assembly
        HttpUrl.Builder urlBuilder = createUrlBuilder(ProtocolType.Normal, param);
        //HTTP-Header zusammenstellen
        Request.Builder requestBuilder = createRequestBuilder(header);
        //Gibt an, ob der Cache verwendet werden soll
        if (!isValidCache) {
            requestBuilder.cacheControl(new CacheControl.Builder().noCache().noStore().maxAge(0, TimeUnit.SECONDS).build());
        }
        //Die Anfrage zusammenstellen
        Request request = requestBuilder.url(urlBuilder.build()).build();
        //Gibt an, ob der Cache verwendet werden soll
        OkHttpClient.Builder clientBuilder = createClientBuilder(isValidCache);
        //API-Ausführung
        return clientBuilder.build().newCall(request).execute();
    }

    /**
     *Stellen Sie die URL zusammen.
     *
     * @param type ProtocolType
     * @param param parameter
     * @return HttpUrl.Builder-Objekt
     */
    protected HttpUrl.Builder createUrlBuilder(ProtocolType type, Map<String, String> param) {
        //URL Builder
        HttpUrl.Builder builder = new HttpUrl.Builder();
        //Schema(http or https)
        builder.scheme(type.getValue());
        //Hostname
        builder.host(this.hostname);
        //Segment
        builder.addPathSegments(this.segment);
        if (param != null) {
            //Parameter abfragen
            param.forEach(builder::addQueryParameter);
        }
        return builder;
    }

    /**
     *Stellen Sie den HTTP-Header zusammen.
     *
     * @param param parameter
     * @return Request.Builder-Objekt
     */
    protected Request.Builder createRequestBuilder(Map<String, String> param) {
        Request.Builder builder = new Request.Builder();
        builder.addHeader("Content-Type", "application/json");
        if (param != null) {
            param.forEach(builder::addHeader);
        }
        return builder;
    }

    /**
     *Stellen Sie den HTTP-Client zusammen.
     *
     * @param isValidCache Mit oder ohne Cache
     * @return OkHttpClient.Builder-Objekt
     */
    protected OkHttpClient.Builder createClientBuilder(boolean isValidCache) {
        //Gibt an, ob der Cache verwendet werden soll
        OkHttpClient.Builder builder = new OkHttpClient.Builder();
        if (!isValidCache) {
            builder.cache(null);
        }
        return builder;
    }

Rund um das Zertifikat

Zertifikat lesen


public class CertificateLoader {

    /**
     *Lesen Sie das Zertifikat.
     * 
     * @Parameterpfad Zertifikatspfad
     * @X509Certificate-Array zurückgeben
     */
    public X509Certificate[] load(String path) {
        List<X509Certificate> certificateList = new ArrayList<>();
        try {
            Collection<? extends Certificate> certificates = getCertificates(path);
            for (Certificate certificate : certificates) {
                X509Certificate x509certificate = (X509Certificate) certificate;
                x509certificate.checkValidity(); //Stellen Sie fest, ob das Zertifikat derzeit gültig ist
                certificateList.add(x509certificate);
            }
        } catch (CertificateExpiredException | CertificateNotYetValidException e) {
            throw new RuntimeException("____ failed to check valid cer file.", e);
        }
        return certificateList.toArray(new X509Certificate[certificateList.size()]);
    }

    /**
    *Zertifikatinformationen abrufen.
    * 
    * @Parameterpfad Zertifikatspfad
    * @Zertifikatsinformationen zurückgeben
    */
    private Collection<? extends Certificate> getCertificates(String path) {
        try {
            CertificateFactory certificateFactory = CertificateFactory.getInstance("X.509");
            InputStream in = this.getClass().getClassLoader().getResourceAsStream(path);
            Collection<? extends Certificate> certificates = certificateFactory.generateCertificates(in);
            if (certificates.isEmpty()) {
                throw new IllegalArgumentException("expected non-empty set of trusted certificates");
            }
            return certificates;
        } catch (CertificateException e) {
            throw new RuntimeException("____ failed to get certificates.");
        }
    }

Zertifikatsverwaltung


public class CertificateManager {

    private CertificateLoader loader;

    private X509Certificate[] certificates;

    private X509TrustManager trustManager;

    /**
     *Konstrukteur.
     * 
     * @Parameterpfad Zertifikatspfad
     */
    public CertificateManager(String path) {
        this.loader = new CertificateLoader();
        this.certificates = loader.load(path);

        //Erstellen Sie einen Zertifikatmanager und stellen Sie sicher, dass es sich um ein vertrauenswürdiges Serverzertifikat handelt
        this.trustManager = createTrustManager(certificates);
        checkServerTrusted(trustManager, certificates);
    }

    /**
     *Zertifikatinformationen abrufen.
     * 
     * @Zertifikatsinformationen zurückgeben
     */
    public X509Certificate[] getCertificates() {
        return this.certificates;
    }

    /**
     *Holen Sie sich TrustManager.
     * 
     * @return TrustManager
     */
    public X509TrustManager getTrustManager() {
        return this.trustManager;
    }

    /**
     *Generieren Sie TrustManager.
     * 
     * @param-Zertifikate Zertifikatinformationen
     * @return TrustManager
     */
    private X509TrustManager createTrustManager(X509Certificate[] certificates) {
        TrustManager[] trustManagers = null;
        try {
            char[] password = "password".toCharArray();
            KeyStore keyStore = newEmptyKeyStore(password);

            int index = 0;
            for (X509Certificate certificate : certificates) {
                String certificateAlias = Integer.toString(index++);
                keyStore.setCertificateEntry(certificateAlias, certificate);
            }

            KeyManagerFactory keyManagerFactory = KeyManagerFactory.getInstance(KeyManagerFactory.getDefaultAlgorithm());
            keyManagerFactory.init(keyStore, password);
            TrustManagerFactory trustManagerFactory = TrustManagerFactory.getInstance(TrustManagerFactory.getDefaultAlgorithm());
            trustManagerFactory.init(keyStore);

            trustManagers = trustManagerFactory.getTrustManagers();
            if (trustManagers.length != 1 || !(trustManagers[0] instanceof X509TrustManager)) {
                throw new IllegalStateException("Unexpected default trust managers : " + Arrays.toString(trustManagers));
            }
        } catch (KeyStoreException | NoSuchAlgorithmException | UnrecoverableKeyException e) {
            throw new RuntimeException("____ failed to create trust manager.", e);
        }
        return (X509TrustManager) trustManagers[0];
    }

    /**
     *Erstellen Sie ein leeres KeyStore-Objekt.
     * 
     * @param Passwort Passwort
     * @Rückgabe des leeren KeyStore-Objekts
     */
    private KeyStore newEmptyKeyStore(char[] password) {
        KeyStore keyStore = getDefaultKeyStoreInstance();
        try {
            InputStream in = null;
            keyStore.load(in, password);
        } catch (NoSuchAlgorithmException | CertificateException | IOException e) {
            throw new RuntimeException("____ failed to load from KeyStore.", e);
        }
        return keyStore;
    }

    /**
     *Ruft das Standard-KeyStore-Objekt ab.
     * 
     * @Standard-KeyStore-Objekt zurückgeben
     */
    private KeyStore getDefaultKeyStoreInstance() {
        try {
            return KeyStore.getInstance(KeyStore.getDefaultType());
        } catch (KeyStoreException e) {
            throw new RuntimeException("____ failed to get KeyStore default instance.", e);
        }
    }

    /**
     *Überprüfen Sie das Zertifikat.
     * 
     * @param trustManager TrustManager
     * @param-Zertifikate Zertifikate
     */
    private void checkServerTrusted(X509TrustManager trustManager, X509Certificate[] certificates) {
        try {
            trustManager.checkServerTrusted(certificates, "SHA256withRSA");
        } catch (CertificateException e) {
            throw new RuntimeException("____ failed to check server trust.", e);
        }
    }

schließlich

Die Überprüfung des Hostnamens funktioniert wahrscheinlich, da die Überprüfungsmethode HostnameVerifier nicht implementiert (überschrieben) wird.

das ist alles

Recommended Posts

[OkHttp] REST-API Java SSL
Java
Einstellungen für das SSL-Debugging in Java
Java
Hinweise zur HTTP-Kommunikation mit Java (OkHttp)
Versuchen Sie es mit der REST-API von JobScheduler - Java RestClient-Implementierung -