[OkHttp] REST-API Java SSL

introduction

J'ai écrit le code pour appeler REST-API avec SSL en utilisant OkHttp3. Il existe de nombreux exemples dans le monde qui se connectent sans valider le certificat, mais ce code effectue la validation suivante.

environnement

Préparation

Obtenir le certificat du serveur

Obtenez le certificat de serveur à partir de l'endroit où l'API est publiée. Cette fois, je l'ai obtenu de Recruit dans le navigateur (Chrome). Vous pouvez trouver différentes manières de l'obtenir en recherchant «Comment obtenir un navigateur de certificat de serveur». Le format du fichier d'exportation est "X.509 encodé en base 64".

Obtenez le certificat de l'autorité de certification qui a signé le certificat de serveur

C'est celui au dessus du certificat dans le "chemin de certification". Obtenez-le de la même manière que ci-dessus.

Combiner les certificats de serveur

Ouvrez les deux certificats ci-dessus avec un éditeur de texte et copiez-collez l'un d'eux dans un seul. J'ai présenté les parents comme une «carte de certification».

Déployez les fichiers fusionnés vers un emplacement de votre chemin de classe.

Code source

Exécution de l'API

API(SSL)Classe d'exécution


public class RestSecureApiExecutor extends RestApiExecutor {

    private CertificateManager certMgr;

    /**
     *constructeur.
     *
     * @Attribut d'API sécurisé param apiAttr
     */
    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 {
        //Assemblage d'URL
        HttpUrl.Builder urlBuilder = createUrlBuilder(ProtocolType.Secure, param);
        //Assembler les en-têtes HTTP
        Request.Builder requestBuilder = createRequestBuilder(header);
        //Utiliser ou non le cache
        if (!isValidCache) {
            requestBuilder.cacheControl(new CacheControl.Builder().noCache().noStore().maxAge(0, TimeUnit.SECONDS).build());
        }
        //Assembler la demande
        Request request = requestBuilder.url(urlBuilder.build()).build();
        //Utiliser ou non le cache
        OkHttpClient.Builder clientBuilder = createClientBuilder(isValidCache);

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

        //Exécution de l'API
        return clientBuilder.build().newCall(request).execute();
    }

    /**
     *Ajouter CertificatePinner au générateur.
     *
     * @param builder OkHttpClient.Objet constructeur
     * @certificats param Informations sur le certificat
     */
    private void addCertificatePinner(OkHttpClient.Builder builder, Certificate[] certificates) {
        CertificatePinner certificatePinner = new CertificatePinner.Builder()
                .add(this.hostname, CertificatePinner.pin(certificates[0]))
                .build();
        builder.certificatePinner(certificatePinner);
    }

    /**
     *Ajouter SslSocketFactory au générateur.
     *
     * @param builder OkHttpClient.Objet constructeur
     * @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);
        }
    }

Classe parent


public class RestApiExecutor {

    /**
     *Type de protocole.
     */
    public enum ProtocolType {
        /**Ordinaire. */
        Normal("http"),
        /** SSL. */
        Secure("https"),;

        private String type;

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

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

    /**nom d'hôte. */
    protected String hostname;
    /**segment. */
    protected String segment;

    /**
     *constructeur.
     *
     * @attribut API param apiAttr
     */
    public RestApiExecutor(ApiAttribute apiAttr) {
        //Nom d'hôte, segment
        this.hostname = apiAttr.getHostname();
        this.segment = apiAttr.getSegment();
    }

    /**
     * REST API (get)Courir.
     *
     * @param header paramètres d'en-tête HTTP
     * @param param paramètres de requête
     * @param isValidCache Utiliser le cache (true: use, false: not use)
     * @réponse de retour
     * @jette IOException si le traitement échoue
     */
    public Response get(Map<String, String> header, Map<String, String> param, boolean isValidCache) throws IOException {
        //Assemblage d'URL
        HttpUrl.Builder urlBuilder = createUrlBuilder(ProtocolType.Normal, param);
        //Assembler les en-têtes HTTP
        Request.Builder requestBuilder = createRequestBuilder(header);
        //Utiliser ou non le cache
        if (!isValidCache) {
            requestBuilder.cacheControl(new CacheControl.Builder().noCache().noStore().maxAge(0, TimeUnit.SECONDS).build());
        }
        //Assembler la demande
        Request request = requestBuilder.url(urlBuilder.build()).build();
        //Utiliser ou non le cache
        OkHttpClient.Builder clientBuilder = createClientBuilder(isValidCache);
        //Exécution de l'API
        return clientBuilder.build().newCall(request).execute();
    }

    /**
     *Assemblez l'URL.
     *
     * @param type ProtocolType
     * @paramètre param paramètre
     * @return HttpUrl.Objet constructeur
     */
    protected HttpUrl.Builder createUrlBuilder(ProtocolType type, Map<String, String> param) {
        //Générateur d'URL
        HttpUrl.Builder builder = new HttpUrl.Builder();
        //Schéma(http or https)
        builder.scheme(type.getValue());
        //nom d'hôte
        builder.host(this.hostname);
        //segment
        builder.addPathSegments(this.segment);
        if (param != null) {
            //Paramètres de requête
            param.forEach(builder::addQueryParameter);
        }
        return builder;
    }

    /**
     *Assemblez l'en-tête HTTP.
     *
     * @paramètre param paramètre
     * @return Request.Objet constructeur
     */
    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;
    }

    /**
     *Assemblez le client HTTP.
     *
     * @param isValidCache Avec ou sans cache
     * @return OkHttpClient.Objet constructeur
     */
    protected OkHttpClient.Builder createClientBuilder(boolean isValidCache) {
        //Utiliser ou non le cache
        OkHttpClient.Builder builder = new OkHttpClient.Builder();
        if (!isValidCache) {
            builder.cache(null);
        }
        return builder;
    }

Autour du certificat

Lecture de certificat


public class CertificateLoader {

    /**
     *Lire le certificat.
     * 
     * @chemin du paramètre Chemin du certificat
     * @retourne le tableau X509Certificate
     */
    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(); //Déterminez si le certificat est actuellement valide
                certificateList.add(x509certificate);
            }
        } catch (CertificateExpiredException | CertificateNotYetValidException e) {
            throw new RuntimeException("____ failed to check valid cer file.", e);
        }
        return certificateList.toArray(new X509Certificate[certificateList.size()]);
    }

    /**
    *Obtenir des informations sur le certificat.
    * 
    * @chemin du paramètre Chemin du certificat
    * @retourner les informations du certificat
    */
    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.");
        }
    }

Gestion des certificats


public class CertificateManager {

    private CertificateLoader loader;

    private X509Certificate[] certificates;

    private X509TrustManager trustManager;

    /**
     *constructeur.
     * 
     * @chemin du paramètre Chemin du certificat
     */
    public CertificateManager(String path) {
        this.loader = new CertificateLoader();
        this.certificates = loader.load(path);

        //Créez un gestionnaire de certificats et vérifiez qu'il s'agit d'un certificat de serveur de confiance
        this.trustManager = createTrustManager(certificates);
        checkServerTrusted(trustManager, certificates);
    }

    /**
     *Obtenir des informations sur le certificat.
     * 
     * @retourner les informations du certificat
     */
    public X509Certificate[] getCertificates() {
        return this.certificates;
    }

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

    /**
     *Générer TrustManager.
     * 
     * @certificats param Informations sur le certificat
     * @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];
    }

    /**
     *Créer un objet KeyStore vide.
     * 
     * @mot de passe param mot de passe
     * @retourne un objet KeyStore vide
     */
    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;
    }

    /**
     *Obtient l'objet KeyStore par défaut.
     * 
     * @retourne l'objet KeyStore par défaut
     */
    private KeyStore getDefaultKeyStoreInstance() {
        try {
            return KeyStore.getInstance(KeyStore.getDefaultType());
        } catch (KeyStoreException e) {
            throw new RuntimeException("____ failed to get KeyStore default instance.", e);
        }
    }

    /**
     *Vérifiez le certificat.
     * 
     * @param trustManager TrustManager
     * @certificats param Certificats
     */
    private void checkServerTrusted(X509TrustManager trustManager, X509Certificate[] certificates) {
        try {
            trustManager.checkServerTrusted(certificates, "SHA256withRSA");
        } catch (CertificateException e) {
            throw new RuntimeException("____ failed to check server trust.", e);
        }
    }

à la fin

La vérification du nom d'hôte le fait probablement, car elle n'implémente pas (remplace) la méthode de vérification HostnameVerifier.

c'est tout

Recommended Posts

[OkHttp] REST-API Java SSL
Java
Paramètres de débogage SSL dans Java
Java
Remarques sur la communication HTTP avec Java (OkHttp)
Essayez d'utiliser l'API REST de JobScheduler - implémentation Java RestClient--