Uwierzytelnianie certyfikatu klienta Java HTTPS

Jestem całkiem nowy w HTTPS / SSL / TLS i jestem trochę zdezorientowany tym, co dokładnie klienci powinni prezentować podczas uwierzytelniania za pomocą certyfikatów.

Piszę klienta Javy, który musi wykonać prosty POST z danymi do określonego adresu URL. Ta część działa dobrze, jedynym problemem jest to, że powinno być zrobione przez HTTPS. Część HTTPS jest dość łatwa w obsłudze( z HTTPclient lub przy użyciu wbudowanej obsługi HTTPS w Javie), ale utknąłem na uwierzytelnianiu za pomocą certyfikatów klienta. Zauważyłem, że jest już bardzo podobne pytanie na Tutaj, które nie próbowałem z moim kodem jeszcze (zrobi to wystarczająco szybko). Mój obecny problem polega na tym, że - cokolwiek robię-Klient Java nigdy nie wysyła certyfikatu (mogę to sprawdzić za pomocą zrzutów PCAP).

Chciałbym wiedzieć, co dokładnie Klient ma przedstawić serwerowi podczas uwierzytelniania certyfikatami (specjalnie dla Javy - jeśli to w ogóle ma znaczenie)? To plik JKS, czy PKCS#12? Co powinno być w tylko certyfikat klienta, czy klucz? Jeśli tak, to który klucz? Istnieje sporo zamieszania na temat różnych rodzajów plików, typów certyfikatów i tym podobnych.

Jak już mówiłem, jestem nowy w HTTPS / SSL / TLS, więc byłbym wdzięczny za kilka podstawowych informacji, jak również(nie musi być esej; będę zadowolić się linki do dobrych artykułów).

Author: Vertexwahn, 2009-11-03

6 answers

W końcu udało się rozwiązać wszystkie problemy, więc odpowiem na własne pytanie. Są to ustawienia/pliki, których używałem, aby rozwiązać mój konkretny problem(y);

Klucz klienta jest plikiem w formacie PKCS#12 zawierającym

    W ten sposób klient może uzyskać dostęp do swoich danych osobowych w celu uzyskania dostępu do swoich danych osobowych.]}
  1. Klucz klienta prywatny

Aby go wygenerować użyłem komendy OpenSSL pkcs12, dla przykład;

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

Wskazówka: upewnij się, że masz najnowszą wersję OpenSSL, nie w wersji 0.9.8 h, ponieważ wydaje się, że cierpi na błąd, który nie pozwala poprawnie generować plików PKCS#12.

Ten plik PKCS#12 będzie używany przez Klienta Java do przedstawienia certyfikatu klienta serwerowi, gdy serwer wyraźnie zażąda uwierzytelnienia klienta. Zobacz artykuł W Wikipedii na temat TLS, aby uzyskać przegląd tego, jak protokół dla certyfikatu klienta uwierzytelnianie faktycznie działa (wyjaśnia również, dlaczego potrzebujemy tutaj klucza prywatnego klienta).

The client ' s truststore to prosty plik w formacie JKS zawierający root lub pośredni certyfikat CA. Te certyfikaty CA określi, z którymi punktami końcowymi będziesz mógł się komunikować, w tym przypadku pozwoli Twojemu klientowi połączyć się z serwerem, który przedstawia certyfikat podpisany przez jednego z truststore CA ' s.

Aby go wygenerować, możesz użyć standardowego Java keytool, na przykład;

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

Korzystając z tego truststore, twój Klient spróbuje wykonać kompletny uścisk dłoni SSL ze wszystkimi serwerami, które prezentują certyfikat podpisany przez CA zidentyfikowany przez myca.crt.

Powyższe pliki są przeznaczone wyłącznie dla klienta. Gdy chcesz skonfigurować serwer, serwer potrzebuje własnego klucza-i plików truststore. Świetny sposób na skonfigurowanie w pełni działającego przykładu dla Klienta Java a serwer (za pomocą Tomcat) można znaleźć na tej stronie .

Zagadnienia/Uwagi / Porady

  1. uwierzytelnianie certyfikatu klienta może być wymuszone tylko przez serwer.
  2. (Ważne!) gdy serwer zażąda certyfikatu klienta( jako część TLS handshake), dostarczy również listę zaufanych certyfikatów jako część żądania certyfikatu. Jeśli certyfikat klienta, który chcesz przedstawić do uwierzytelnienia, jest podpisany , a nie przez jednego z tych CA w ogóle nie będzie prezentowany (moim zdaniem to dziwne zachowanie, ale jestem pewien, że jest ku temu powód). To była główna przyczyna moich problemów, ponieważ druga strona nie skonfigurowała swojego serwera poprawnie, aby akceptował mój własny podpisany certyfikat klienta i założyliśmy, że problem był po mojej stronie, ponieważ nie dostarczyłem poprawnie certyfikatu klienta w żądaniu.
  3. Pobierz Wireshark. Ma świetną analizę pakietów SSL/HTTPS i będzie ogromną pomocą w debugowaniu i znalezienie problemu. Jest podobny do -Djavax.net.debug=ssl, ale jest bardziej ustrukturyzowany i (prawdopodobnie) łatwiejszy do zinterpretowania, jeśli nie podoba ci się wyjście debugowania Java SSL.
  4. Jest całkowicie możliwe użycie biblioteki httpclient Apache. Jeśli chcesz użyć httpclient, po prostu zamień docelowy adres URL na odpowiednik HTTPS i dodaj następujące argumenty JVM (które są takie same dla każdego innego klienta, niezależnie od biblioteki, której chcesz użyć do wysyłania/odbierania danych 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
 200
Author: tmbrggmn,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2009-11-10 20:28:20

Inne odpowiedzi pokazują, jak globalnie konfigurować certyfikaty klientów. Jeśli jednak chcesz programowo zdefiniować klucz klienta dla jednego konkretnego połączenia, zamiast definiować go globalnie dla każdej aplikacji uruchomionej na Twoim JVM, możesz skonfigurować swój własny SSLContext w następujący sposób:

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"));
 41
Author: Magnus,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2015-09-10 23:54:04

Plik JKS jest tylko kontenerem dla certyfikatów i par kluczy. W scenariuszu uwierzytelniania po stronie klienta, różne części kluczy będą znajdować się tutaj:

  • sklep client będzie zawierał parę kluczy klienta private i public . To się nazywa keystore.
  • sklep serwera będzie zawierał klucz klienta public. Nazywa się to truststore .

Rozdzielenie truststore i keystore nie jest obowiązkowe, ale zalecane. Mogą być tym samym plikiem fizycznym.

Aby ustawić lokalizacje systemu plików obu sklepów, użyj następujących właściwości systemu:

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

I na serwerze:

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

Aby wyeksportować certyfikat klienta (klucz publiczny) do pliku, dzięki czemu można go skopiować na serwer, użyj

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

Aby zaimportować klucz publiczny klienta do magazynu kluczy serwera, użyj (jak wspomniano na plakacie, zostało to już zrobione przez administratorów serwera)

keytool -import -file publicclientkey.cer -store serversidestore.jks
 27
Author: mhaller,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2009-11-04 08:24:04

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>

Kod Javy:

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

}
 9
Author: wildloop,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2015-03-10 20:39:10

Dla tych z Was, którzy po prostu chcą skonfigurować uwierzytelnianie dwukierunkowe( certyfikaty serwera i klienta), połączenie tych dwóch łączy zapewni Ci dostęp do tego miejsca:

Dwukierunkowa konfiguracja auth:

Https://linuxconfig.org/apache-web-server-ssl-authentication

Nie musisz używać pliku konfiguracyjnego openssl, o którym wspominają; po prostu użyj

  • $ openssl genrsa-des3-out ca.klucz 4096

  • $ openssl req-new-x509-days 365 - klucz ca.key-out ca.crt

Aby wygenerować własny certyfikat CA, a następnie wygenerować i podpisać klucze serwera i klienta poprzez:

  • $ OpenSSL genrsa-des3-serwer out.klucz 4096

  • $ OpenSSL REQ-new-key client.serwer key-out.csr

  • $ OpenSSL x509-REQ-days 365-in server.csr-CA ca.crt-CAkey ca.key-set_serial 100-out server.crt

I

  • $ OpenSSL genrsa-des3-out client.klucz 4096

  • $ OpenSSL REQ-new-key client.kluczowy klient.csr

  • $ openssl x509-req-days 365-in client.csr-CA ca.crt-CAkey ca.key-set_serial 101-out client.crt

Dla reszty wykonaj kroki w linku. Zarządzanie certyfikatami dla Chrome działa tak samo jak w przykładzie dla Firefoksa, o którym mowa.

Następnie skonfiguruj serwer poprzez:

Https://www.digitalocean.com/community/tutorials/how-to-create-a-ssl-certificate-on-apache-for-ubuntu-14-04

Zauważ, że masz już utworzony serwer .crt i .klucz, więc nie musisz już robić tego kroku.

 6
Author: hans,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2017-05-02 12:52:05

Wydaje mi się, że poprawką był Typ keystore, pkcs12(pfx) zawsze ma klucz prywatny, A Typ JKS może istnieć bez klucza prywatnego. O ile nie określisz w kodzie lub nie wybierzesz certyfikatu przez przeglądarkę, serwer nie ma możliwości poznania, że reprezentuje klienta po drugiej stronie.

 0
Author: ObiWanKenobi,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2015-09-23 17:48:48