web-dev-qa-db-ja.com

Spring RestTemplateを使用してHttps Restサービスにアクセスする

スプリングレストテンプレートを使用してhttpsで保護されたRESTサービスのURLにアクセスするためのコードサンプルを提供してください。

証明書、ユーザー名、パスワードがあります。基本認証はサーバー側で使用され、提供された証明書、ユーザー名、パスワード(必要な場合)を使用してそのサーバーに接続できるクライアントを作成したいと思います。

40
zdesam
KeyStore keyStore = KeyStore.getInstance(KeyStore.getDefaultType());
keyStore.load(new FileInputStream(new File(keyStoreFile)),
  keyStorePassword.toCharArray());

SSLConnectionSocketFactory socketFactory = new SSLConnectionSocketFactory(
  new SSLContextBuilder()
    .loadTrustMaterial(null, new TrustSelfSignedStrategy())
    .loadKeyMaterial(keyStore, keyStorePassword.toCharArray())
    .build(),
    NoopHostnameVerifier.INSTANCE);

HttpClient httpClient = HttpClients.custom().setSSLSocketFactory(
  socketFactory).build();

ClientHttpRequestFactory requestFactory = new HttpComponentsClientHttpRequestFactory(
  httpClient);
RestTemplate restTemplate = new RestTemplate(requestFactory);
MyRecord record = restTemplate.getForObject(uri, MyRecord.class);
LOG.debug(record.toString());
27
Headroller

一般的なアイデアを提供するコードを次に示します。

証明書を信頼するには、カスタムClientHttpRequestFactoryを作成する必要があります。次のようになります。

final ClientHttpRequestFactory clientHttpRequestFactory =
        new MyCustomClientHttpRequestFactory(org.Apache.http.conn.ssl.SSLSocketFactory.ALLOW_ALL_HOSTNAME_VERIFIER, serverInfo);
    restTemplate.setRequestFactory(clientHttpRequestFactory);

これはMyCustomClientHttpRequestFactoryの実装です:

public class MyCustomClientHttpRequestFactory  extends SimpleClientHttpRequestFactory {

private final HostnameVerifier hostNameVerifier;
private final ServerInfo serverInfo;

public MyCustomClientHttpRequestFactory (final HostnameVerifier hostNameVerifier,
    final ServerInfo serverInfo) {
    this.hostNameVerifier = hostNameVerifier;
    this.serverInfo = serverInfo;
}

@Override
protected void prepareConnection(final HttpURLConnection connection, final String httpMethod)
    throws IOException {
    if (connection instanceof HttpsURLConnection) {
        ((HttpsURLConnection) connection).setHostnameVerifier(hostNameVerifier);
        ((HttpsURLConnection) connection).setSSLSocketFactory(initSSLContext()
            .getSocketFactory());
    }
    super.prepareConnection(connection, httpMethod);
}

private SSLContext initSSLContext() {
    try {
        System.setProperty("https.protocols", "TLSv1");

        // Set ssl trust manager. Verify against our server thumbprint
        final SSLContext ctx = SSLContext.getInstance("TLSv1");
        final SslThumbprintVerifier verifier = new SslThumbprintVerifier(serverInfo);
        final ThumbprintTrustManager thumbPrintTrustManager =
            new ThumbprintTrustManager(null, verifier);
        ctx.init(null, new TrustManager[] { thumbPrintTrustManager }, null);
        return ctx;
    } catch (final Exception ex) {
        LOGGER.error(
            "An exception was thrown while trying to initialize HTTP security manager.", ex);
        return null;
    }
}

この場合、serverInfoオブジェクトにはサーバーのserver印が含まれています。 TrustManagerインターフェースを実装して、SslThumbprintVerifierまたは証明書を検証する他のメソッドを取得する必要があります(常にtrueを返すこともできます)。

org.Apache.http.conn.ssl.SSLSocketFactory.ALLOW_ALL_HOSTNAME_VERIFIERは、すべてのホスト名を許可します。ホスト名を確認する必要がある場合は、別の方法で実装する必要があります。

ユーザーとパスワード、およびその実装方法についてはわかりません。多くの場合、restTemplateという名前のAuthorizationに、次のような値を持つヘッダーを追加する必要があります。Base: <encoded user+password>user+password でなければなりません Base64エンコード済み。

16
Avi

これは、非推奨ではないクラスまたはメソッドのソリューションです:(-Java 8承認済み)

CloseableHttpClient httpClient = HttpClients.custom().setSSLHostnameVerifier(new NoopHostnameVerifier()).build();

HttpComponentsClientHttpRequestFactory requestFactory = new HttpComponentsClientHttpRequestFactory();
requestFactory.setHttpClient(httpClient);

RestTemplate restTemplate = new RestTemplate(requestFactory);
8
MaximeF

ここに私が似たような問題で終わったものがあります。考え方は@Aviの答えと同じですが、静的な "System.setProperty(" https.protocols "、" TLSv1 ");"も避けて、調整がシステムに影響を与えないようにしました。ここからの回答に触発されました http://www.coderanch.com/t/637177/Security/Disabling-handshake-message-Java

public class MyCustomClientHttpRequestFactory extends SimpleClientHttpRequestFactory {

@Override
protected void prepareConnection(HttpURLConnection connection, String httpMethod) {
    try {
        if (!(connection instanceof HttpsURLConnection)) {
            throw new RuntimeException("An instance of HttpsURLConnection is expected");
        }

        HttpsURLConnection httpsConnection = (HttpsURLConnection) connection;

        TrustManager[] trustAllCerts = new TrustManager[]{
                new X509TrustManager() {
                    public Java.security.cert.X509Certificate[] getAcceptedIssuers() {
                        return null;
                    }

                    public void checkClientTrusted(X509Certificate[] certs, String authType) {
                    }

                    public void checkServerTrusted(X509Certificate[] certs, String authType) {
                    }

                }
        };
        SSLContext sslContext = SSLContext.getInstance("SSL");
        sslContext.init(null, trustAllCerts, new Java.security.SecureRandom());
        httpsConnection.setSSLSocketFactory(new MyCustomSSLSocketFactory(sslContext.getSocketFactory()));

        httpsConnection.setHostnameVerifier((hostname, session) -> true);

        super.prepareConnection(httpsConnection, httpMethod);
    } catch (Exception e) {
        throw Throwables.propagate(e);
    }
}

/**
 * We need to invoke sslSocket.setEnabledProtocols(new String[] {"SSLv3"});
 * see http://www.Oracle.com/technetwork/Java/javase/documentation/cve-2014-3566-2342133.html (Java 8 section)
 */
private static class MyCustomSSLSocketFactory extends SSLSocketFactory {

    private final SSLSocketFactory delegate;

    public MyCustomSSLSocketFactory(SSLSocketFactory delegate) {
        this.delegate = delegate;
    }

    @Override
    public String[] getDefaultCipherSuites() {
        return delegate.getDefaultCipherSuites();
    }

    @Override
    public String[] getSupportedCipherSuites() {
        return delegate.getSupportedCipherSuites();
    }

    @Override
    public Socket createSocket(final Socket socket, final String Host, final int port, final boolean autoClose) throws IOException {
        final Socket underlyingSocket = delegate.createSocket(socket, Host, port, autoClose);
        return overrideProtocol(underlyingSocket);
    }

    @Override
    public Socket createSocket(final String Host, final int port) throws IOException {
        final Socket underlyingSocket = delegate.createSocket(Host, port);
        return overrideProtocol(underlyingSocket);
    }

    @Override
    public Socket createSocket(final String Host, final int port, final InetAddress localAddress, final int localPort) throws IOException {
        final Socket underlyingSocket = delegate.createSocket(Host, port, localAddress, localPort);
        return overrideProtocol(underlyingSocket);
    }

    @Override
    public Socket createSocket(final InetAddress Host, final int port) throws IOException {
        final Socket underlyingSocket = delegate.createSocket(Host, port);
        return overrideProtocol(underlyingSocket);
    }

    @Override
    public Socket createSocket(final InetAddress Host, final int port, final InetAddress localAddress, final int localPort) throws IOException {
        final Socket underlyingSocket = delegate.createSocket(Host, port, localAddress, localPort);
        return overrideProtocol(underlyingSocket);
    }

    private Socket overrideProtocol(final Socket socket) {
        if (!(socket instanceof SSLSocket)) {
            throw new RuntimeException("An instance of SSLSocket is expected");
        }
        ((SSLSocket) socket).setEnabledProtocols(new String[] {"SSLv3"});
        return socket;
    }
}
}
3
Ruslan

私から一点。スプリングブートマイクロサービスで相互証明書認証を使用しました。以下は私のために働いています、ここでのキーポイントはそれらなしのコードのkeyManagerFactory.init(...)sslcontext.init(keyManagerFactory.getKeyManagers(), null, new SecureRandom())行です、少なくとも私にとっては、物事はうまくいきませんでした。証明書はPKCS12によってパッケージ化されています。

@Value("${server.ssl.key-store-password}")
private String keyStorePassword;
@Value("${server.ssl.key-store-type}")
private String keyStoreType;
@Value("${server.ssl.key-store}")
private Resource resource;

private RestTemplate getRestTemplate() throws Exception {
    return new RestTemplate(clientHttpRequestFactory());
}

private ClientHttpRequestFactory clientHttpRequestFactory() throws Exception {
    return new HttpComponentsClientHttpRequestFactory(httpClient());
}

private HttpClient httpClient() throws Exception {

    KeyManagerFactory keyManagerFactory = KeyManagerFactory.getInstance("SunX509");
    KeyStore trustStore = KeyStore.getInstance(keyStoreType);

    if (resource.exists()) {
        InputStream inputStream = resource.getInputStream();

        try {
            if (inputStream != null) {
                trustStore.load(inputStream, keyStorePassword.toCharArray());
                keyManagerFactory.init(trustStore, keyStorePassword.toCharArray());
            }
        } finally {
            if (inputStream != null) {
                inputStream.close();
            }
        }
    } else {
        throw new RuntimeException("Cannot find resource: " + resource.getFilename());
    }

    SSLContext sslcontext = SSLContexts.custom().loadTrustMaterial(trustStore, new TrustSelfSignedStrategy()).build();
    sslcontext.init(keyManagerFactory.getKeyManagers(), null, new SecureRandom());
    SSLConnectionSocketFactory sslConnectionSocketFactory =
            new SSLConnectionSocketFactory(sslcontext, new String[]{"TLSv1.2"}, null, getDefaultHostnameVerifier());

    return HttpClients.custom().setSSLSocketFactory(sslConnectionSocketFactory).build();
}
2
toootooo