256

私はかなり慣れHTTPS/SSL/TLSていないので、証明書で認証するときにクライアントが何を提示する必要があるのか​​ について少し混乱しています。

POST特定の .xml に対して単純なデータを処理する必要がある Java クライアントを作成していますURL。その部分は正常に機能しますが、唯一の問題は、それがやり直されることになっていることHTTPSです。この部分は (Java の組み込みサポートを使用するか、Java の組み込みサポートを使用して)HTTPS処理するのはかなり簡単ですが、クライアント証明書による認証に行き詰まっています。ここに非常によく似た質問が既にあることに気付きましたが、まだコードで試していません (すぐに試してみます)。私の現在の問題は、私が何をしても、Java クライアントが証明書を送信しないことです (これはダンプで確認できます)。HTTPclientHTTPSPCAP

証明書で認証するときに、クライアントがサーバーに何を提示する必要があるかを正確に知りたいです(特にJavaの場合-それが重要な場合)?これはJKSファイルですか、それともPKCS#12? それらの中にあるはずのもの。クライアント証明書だけですか、それともキーですか?もしそうなら、どのキーですか?さまざまな種類のファイル、証明書の種類などについて、かなりの混乱があります。

前に言ったHTTPS/SSL/TLSように、私は初心者なので、背景情報もいただければ幸いです (エッセイである必要はありません。良い記事へのリンクで解決します)。

4

9 に答える 9

258

ようやくすべての問題を解決できたので、私自身の質問に答えます。これらは、特定の問題を解決するために管理するために使用した設定/ファイルです。

クライアントのキーストアは、PKCS#12形式のファイルです。

  1. クライアントの公開証明書(この場合は自己署名CAによって署名されています)
  2. クライアントの秘密

それを生成するためにpkcs12、たとえば、OpenSSLのコマンドを使用しました。

openssl pkcs12 -export -in client.crt -inkey client.key -out client.p12 -name "Whatever"

ヒント:バージョン0.9.8hではなく最新のOpenSSLを入手するようにしてください。これは、PKCS#12ファイルを適切に生成できないバグに悩まされているようです。

このPKCS#12ファイルは、サーバーがクライアントに認証を明示的に要求したときに、Javaクライアントがクライアント証明書をサーバーに提示するために使用されます。クライアント証明書認証のプロトコルが実際にどのように機能するかの概要については、TLSに関するウィキペディアの記事を参照してください(クライアントの秘密鍵が必要な理由もここで説明しています)。

クライアントのトラストストアは、ルートまたは中間のCA証明書を含む単純なJKS形式のファイルです。これらのCA証明書は、通信を許可するエンドポイントを決定します。この場合、クライアントは、トラストストアのCAの1つによって署名された証明書を提示するサーバーに接続できます。

これを生成するには、たとえば、標準のJavaキーツールを使用できます。

keytool -genkey -dname "cn=CLIENT" -alias truststorekey -keyalg RSA -keystore ./client-truststore.jks -keypass whatever -storepass whatever
keytool -import -keystore ./client-truststore.jks -file myca.crt -alias myca

このトラストストアを使用して、クライアントは、で識別されるCAによって署名された証明書を提示するすべてのサーバーで完全なSSLハンドシェイクを実行しようとしますmyca.crt

上記のファイルは、厳密にはクライアント専用です。サーバーもセットアップする場合、サーバーには独自のキーストアファイルとトラストストアファイルが必要です。Javaクライアントとサーバー(Tomcatを使用)の両方で完全に機能する例を設定するための優れたウォークスルーは、このWebサイトにあります。

問題/備考/ヒント

  1. クライアント証明書認証は、サーバーによってのみ実施できます。
  2. 重要!)サーバーが(TLSハンドシェイクの一部として)クライアント証明書を要求すると、証明書要求の一部として信頼できるCAのリストも提供されます。認証のために提示したいクライアント証明書がこれらのCAのいずれかによって署名されていない場合、それはまったく提示されません(私の意見では、これは奇妙な動作ですが、理由があると確信しています)。これが私の問題の主な原因でした。相手が私の自己署名クライアント証明書を受け入れるようにサーバーを適切に構成しておらず、リクエストでクライアント証明書を適切に提供しなかったことが問題であると想定したためです。
  3. Wiresharkを入手してください。優れたSSL/HTTPSパケット分析を備えており、問題のデバッグと発見に非常に役立ちます。-Djavax.net.debug=sslこれは、Java SSLデバッグ出力に慣れていない場合と似ていますが、より構造化されており、(おそらく)解釈が容易です。
  4. Apachehttpclientライブラリを使用することは完全に可能です。httpclientを使用する場合は、宛先URLを同等のHTTPSに置き換え、次のJVM引数を追加します(HTTP / HTTPSを介したデータの送受信に使用するライブラリに関係なく、他のクライアントでも同じです)。 :

    -Djavax.net.debug=ssl
    -Djavax.net.ssl.keyStoreType=pkcs12
    -Djavax.net.ssl.keyStore=client.p12
    -Djavax.net.ssl.keyStorePassword=whatever
    -Djavax.net.ssl.trustStoreType=jks
    -Djavax.net.ssl.trustStore=client-truststore.jks
    -Djavax.net.ssl.trustStorePassword=whatever
于 2009-11-10T19:27:27.213 に答える
83

他の回答は、クライアント証明書をグローバルに構成する方法を示しています。ただし、JVM で実行されているすべてのアプリケーションでクライアント キーをグローバルに定義するのではなく、1 つの特定の接続に対してクライアント キーをプログラムで定義する場合は、次のように独自の SSLContext を構成できます。

String keyPassphrase = "";

KeyStore keyStore = KeyStore.getInstance("PKCS12");
keyStore.load(new FileInputStream("cert-key-pair.pfx"), keyPassphrase.toCharArray());

SSLContext sslContext = SSLContexts.custom()
        .loadKeyMaterial(keyStore, null)
        .build();

HttpClient httpClient = HttpClients.custom().setSSLContext(sslContext).build();
HttpResponse response = httpClient.execute(new HttpGet("https://example.com"));
于 2015-09-10T23:54:04.997 に答える
32

それらの JKS ファイルは、証明書とキー ペアの単なるコンテナーです。クライアント側の認証シナリオでは、キーのさまざまな部分が次の場所に配置されます。

  • クライアントのストアには、クライアントの秘密鍵と公開鍵のペアが含まれます。これはキーストアと呼ばれます。
  • サーバーのストアには、クライアントの公開鍵が含まれます。これはトラストストアと呼ばれます。

トラストストアとキーストアの分離は必須ではありませんが、推奨されます。それらは同じ物理ファイルである場合があります。

2 つのストアのファイルシステムの場所を設定するには、次のシステム プロパティを使用します。

-Djavax.net.ssl.keyStore=clientsidestore.jks

そしてサーバー上:

-Djavax.net.ssl.trustStore=serversidestore.jks

クライアントの証明書 (公開鍵) をファイルにエクスポートして、サーバーにコピーできるようにするには、次を使用します。

keytool -export -alias MYKEY -file publicclientkey.cer -store clientsidestore.jks

クライアントの公開鍵をサーバーのキーストアにインポートするには、次を使用します(ポスターが言及したように、これはサーバー管理者によってすでに行われています)

keytool -import -file publicclientkey.cer -store serversidestore.jks
于 2009-11-03T09:18:10.183 に答える
14

Maven pom.xml:

<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
    <modelVersion>4.0.0</modelVersion>
    <groupId>some.examples</groupId>
    <artifactId>sslcliauth</artifactId>
    <version>1.0-SNAPSHOT</version>
    <packaging>jar</packaging>
    <name>sslcliauth</name>
    <dependencies>
        <dependency>
            <groupId>org.apache.httpcomponents</groupId>
            <artifactId>httpclient</artifactId>
            <version>4.4</version>
        </dependency>
    </dependencies>
</project>

Java コード:

package some.examples;

import java.io.FileInputStream;
import java.io.IOException;
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.util.logging.Level;
import java.util.logging.Logger;
import javax.net.ssl.SSLContext;
import org.apache.http.HttpEntity;
import org.apache.http.HttpHost;
import org.apache.http.client.config.RequestConfig;
import org.apache.http.client.methods.CloseableHttpResponse;
import org.apache.http.client.methods.HttpPost;
import org.apache.http.conn.ssl.SSLConnectionSocketFactory;
import org.apache.http.ssl.SSLContexts;
import org.apache.http.impl.client.CloseableHttpClient;
import org.apache.http.impl.client.HttpClients;
import org.apache.http.util.EntityUtils;
import org.apache.http.entity.InputStreamEntity;

public class SSLCliAuthExample {

private static final Logger LOG = Logger.getLogger(SSLCliAuthExample.class.getName());

private static final String CA_KEYSTORE_TYPE = KeyStore.getDefaultType(); //"JKS";
private static final String CA_KEYSTORE_PATH = "./cacert.jks";
private static final String CA_KEYSTORE_PASS = "changeit";

private static final String CLIENT_KEYSTORE_TYPE = "PKCS12";
private static final String CLIENT_KEYSTORE_PATH = "./client.p12";
private static final String CLIENT_KEYSTORE_PASS = "changeit";

public static void main(String[] args) throws Exception {
    requestTimestamp();
}

public final static void requestTimestamp() throws Exception {
    SSLConnectionSocketFactory csf = new SSLConnectionSocketFactory(
            createSslCustomContext(),
            new String[]{"TLSv1"}, // Allow TLSv1 protocol only
            null,
            SSLConnectionSocketFactory.getDefaultHostnameVerifier());
    try (CloseableHttpClient httpclient = HttpClients.custom().setSSLSocketFactory(csf).build()) {
        HttpPost req = new HttpPost("https://changeit.com/changeit");
        req.setConfig(configureRequest());
        HttpEntity ent = new InputStreamEntity(new FileInputStream("./bytes.bin"));
        req.setEntity(ent);
        try (CloseableHttpResponse response = httpclient.execute(req)) {
            HttpEntity entity = response.getEntity();
            LOG.log(Level.INFO, "*** Reponse status: {0}", response.getStatusLine());
            EntityUtils.consume(entity);
            LOG.log(Level.INFO, "*** Response entity: {0}", entity.toString());
        }
    }
}

public static RequestConfig configureRequest() {
    HttpHost proxy = new HttpHost("changeit.local", 8080, "http");
    RequestConfig config = RequestConfig.custom()
            .setProxy(proxy)
            .build();
    return config;
}

public static SSLContext createSslCustomContext() throws KeyStoreException, IOException, NoSuchAlgorithmException, CertificateException, KeyManagementException, UnrecoverableKeyException {
    // Trusted CA keystore
    KeyStore tks = KeyStore.getInstance(CA_KEYSTORE_TYPE);
    tks.load(new FileInputStream(CA_KEYSTORE_PATH), CA_KEYSTORE_PASS.toCharArray());

    // Client keystore
    KeyStore cks = KeyStore.getInstance(CLIENT_KEYSTORE_TYPE);
    cks.load(new FileInputStream(CLIENT_KEYSTORE_PATH), CLIENT_KEYSTORE_PASS.toCharArray());

    SSLContext sslcontext = SSLContexts.custom()
            //.loadTrustMaterial(tks, new TrustSelfSignedStrategy()) // use it to customize
            .loadKeyMaterial(cks, CLIENT_KEYSTORE_PASS.toCharArray()) // load client certificate
            .build();
    return sslcontext;
}

}
于 2015-03-10T15:37:29.887 に答える