Uwierzytelnianie certyfikatu klienta Java HTTPS

222

Jestem dość nowy HTTPS/SSL/TLSi jestem trochę zdezorientowany, co dokładnie klienci powinni przedstawić podczas uwierzytelniania za pomocą certyfikatów.

Piszę klienta Java, który musi wykonać proste POSTdane do konkretnego URL. Ta część działa dobrze, jedynym problemem jest to, że należy to zrobić HTTPS. Ta HTTPSczęść jest dość łatwa w obsłudze (z HTTPclientwbudowanym HTTPSwsparciem Java lub z nią ), ale utknąłem na uwierzytelnianiu za pomocą certyfikatów klienta. Zauważyłem, że jest już bardzo podobne pytanie, którego nie wypróbowałem jeszcze z moim kodem (zrobię to wkrótce). Mój obecny problem polega na tym, że - cokolwiek robię - klient Java nigdy nie wysyła wraz z certyfikatem (mogę to sprawdzić za pomocą PCAPzrzutów).

Chciałbym wiedzieć, co dokładnie klient powinien przedstawić serwerowi podczas uwierzytelniania za pomocą certyfikatów (szczególnie dla Javy - jeśli to w ogóle ma znaczenie)? Czy to JKSplik, czy PKCS#12? Co powinno w nich być; tylko certyfikat klienta lub 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 powiedziałem wcześniej, jestem nowy, HTTPS/SSL/TLSwięc doceniłbym również pewne podstawowe informacje (nie musi to być esej; zadowolę się linkami do dobrych artykułów).

tmbrggmn
źródło
dałem dwa zaświadczenia z klientem, jak rozpoznać, które trzeba dodać do kluczy i zaufanych można proszę mi pomóc zidentyfikować ten problem jak już przeszedł podobnego rodzaju emisji, ten problem mam podniesione faktycznie nie coraz pojęcia co robić stackoverflow .pl / pytania / 61374276 /…
henrycharles

Odpowiedzi:

233

W końcu udało mi się rozwiązać wszystkie problemy, więc odpowiem na własne pytanie. Są to ustawienia / pliki, którymi zarządzałem, aby rozwiązać konkretny problem;

The kluczy klienta jest PKCS # 12 Format pliku zawierającego

  1. Publiczny certyfikat klienta (w tym przypadku podpisany przez samopodpisany urząd certyfikacji)
  2. Klucz prywatny klienta

Aby go wygenerować, użyłem pkcs12na przykład polecenia OpenSSL ;

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

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

Ten plik PKCS # 12 zostanie wykorzystany przez klienta Java do przedstawienia certyfikatu klienta na serwerze, gdy serwer wyraźnie zażąda od klienta uwierzytelnienia. Zobacz artykuł Wikipedii na temat TLS, aby zobaczyć, jak działa protokół uwierzytelniania certyfikatu klienta (wyjaśnia również, dlaczego potrzebujemy tutaj klucza prywatnego klienta).

Przez zaufanych klienta jest proste JKS Format pliku zawierającego korzeniowych lub certyfikaty pośredniego urzędu certyfikacji . Te certyfikaty CA określą, z którymi punktami końcowymi będziesz mógł się komunikować, w tym przypadku pozwoli Twojemu klientowi połączyć się z dowolnym serwerem, który przedstawia certyfikat, który został podpisany przez jeden z CA urzędu certyfikacji.

Aby go wygenerować, możesz na przykład użyć standardowego narzędzia 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

Korzystając z tego magazynu zaufanych certyfikatów, Twój klient będzie próbował przeprowadzić pełny uścisk protokołu SSL ze wszystkimi serwerami, które przedstawią certyfikat podpisany przez urząd certyfikacji myca.crt.

Powyższe pliki są przeznaczone wyłącznie dla klienta. Jeśli chcesz również skonfigurować serwer, serwer potrzebuje własnych plików kluczy i magazynu zaufanych certyfikatów. Na tej stronie można znaleźć świetne instrukcje konfiguracji w pełni działającego przykładu zarówno dla klienta Java, jak i serwera (za pomocą Tomcat) .

Problemy / uwagi / wskazówki

  1. Uwierzytelnianie certyfikatu klienta może być wymuszone tylko przez serwer.
  2. ( Ważne! ) Gdy serwer zażąda certyfikatu klienta (w ramach uzgadniania TLS), dostarczy również listę zaufanych urzędów certyfikacji jako część żądania certyfikatu. Kiedy certyfikat klienta, który chcesz przedstawić do uwierzytelnienia, nie jest podpisany przez jeden z tych urzędów certyfikacji, w ogóle nie zostanie przedstawiony (moim zdaniem jest to dziwne zachowanie, ale jestem pewien, że istnieje ku temu powód). To była główna przyczyna moich problemów, ponieważ druga strona nie skonfigurowała odpowiednio swojego serwera, aby akceptował mój samopodpisany certyfikat klienta i założyliśmy, że problem leży po mojej stronie z powodu niepoprawnego podania certyfikatu klienta w żądaniu.
  3. Uzyskaj Wireshark. Ma świetną analizę pakietów SSL / HTTPS i będzie ogromną pomocą przy debugowaniu i znajdowaniu problemu. Jest podobny do, -Djavax.net.debug=sslale jest bardziej uporządkowany i (prawdopodobnie) łatwiejszy do interpretacji, jeśli nie czujesz się komfortowo z wynikiem debugowania Java SSL.
  4. Korzystanie z biblioteki httpclient Apache jest całkowicie możliwe. Jeśli chcesz użyć klienta httpclient, po prostu zastąp docelowy adres URL odpowiednikiem HTTPS i dodaj następujące argumenty JVM (takie same dla każdego innego klienta, niezależnie od biblioteki, której chcesz używać do wysyłania / odbierania danych przez 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
tmbrggmn
źródło
6
„Gdy certyfikat klienta, który chcesz przedstawić do uwierzytelnienia, nie jest podpisany przez jeden z tych urzędów certyfikacji, w ogóle nie zostanie przedstawiony”. Certyfikaty nie są prezentowane, ponieważ klient wie, że nie zostaną zaakceptowane przez serwer. Ponadto certyfikat może być podpisany przez pośredni urząd certyfikacji „ICA”, a serwer może przedstawić klientowi główny urząd certyfikacji „RCA”, a przeglądarka internetowa nadal pozwala wybrać certyfikat, mimo że jest podpisany przez ICA, a nie RCA.
KyleM
2
Jako przykład powyższego komentarza rozważ sytuację, w której masz jeden główny urząd certyfikacji (RCA1) i dwa pośrednie urzędy certyfikacji (ICA1 i ICA2). Jeśli w Apache Tomcat zaimportujesz RCA1 do magazynu zaufania, Twoja przeglądarka internetowa wyświetli WSZYSTKIE certyfikaty podpisane przez ICA1 i ICA2, nawet jeśli nie znajdują się w twoim magazynie zaufania. Jest tak, ponieważ to łańcuch nie ma znaczenia dla poszczególnych certyfikatów.
KyleM
2
„moim zdaniem jest to dziwne zachowanie, ale jestem pewien, że istnieje ku temu powód”. Wynika to z tego, że tak mówi RFC 2246. Nie ma w tym nic dziwnego. Dziwne byłoby pozwalanie klientom na prezentowanie certyfikatów, które nie zostaną zaakceptowane przez serwer, i całkowite marnowanie czasu i miejsca.
Markiz Lorne
1
Zdecydowanie odradzam używanie pojedynczego magazynu kluczy JVM (i truststore!) Jak w powyższym przykładzie. Dostosowywanie pojedynczego połączenia jest bezpieczniejsze i bardziej elastyczne, ale wymaga napisania nieco więcej kodu. Musisz dostosować SSLContextjak w odpowiedzi @ Magnus.
Christopher Schultz
63

Inne odpowiedzi pokazują, jak globalnie skonfigurować certyfikaty klienta. Jeśli jednak chcesz programowo zdefiniować klucz klienta dla jednego konkretnego połączenia, zamiast globalnie zdefiniować go dla każdej aplikacji uruchomionej na JVM, możesz skonfigurować własny tekst 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"));
Magnus
źródło
Musiałem użyć sslContext = SSLContexts.custom().loadTrustMaterial(keyFile, PASSWORD).build();. Nie mogłem z tym pracować loadKeyMaterial(...).
Conor Svensson,
4
@ConorSvensson Materiał zaufania jest dla klienta ufającego certyfikatowi zdalnego serwera, materiał klucza jest dla serwera ufającego klientowi.
Magnus,
1
Bardzo podoba mi się ta zwięzła i trafna odpowiedź. Jeśli ludzie są zainteresowani, podam tutaj działający przykład z instrukcjami kompilacji. stackoverflow.com/a/46821977/154527
Alain O'Dea
3
Uratowałeś mi dzień! Jeszcze jedna dodatkowa zmiana, którą musiałem zrobić, wyślij też hasło w loadKeyMaterial(keystore, keyPassphrase.toCharArray())kodzie ”!
AnandShanbhag
1
@peterh Tak, jest to specyficzne dla Apache http. Każda biblioteka HTTP będzie miała swój własny sposób konfiguracji, ale większość z nich powinna jakoś użyć SSLContext.
Magnus
30

Plik JKS to tylko kontener certyfikatów i par kluczy. W scenariuszu uwierzytelnienia po stronie klienta różne części kluczy będą znajdować się tutaj:

  • Przez klienta sklep „s będzie zawierać w kliencie prywatnego i publicznego parę kluczy. Nazywa się to magazynem kluczy .
  • Przez serwer Store „s będzie zawierać klienta publicznej klucza. Nazywa się to truststore .

Rozdzielenie magazynu zaufanych certyfikatów i magazynu kluczy nie jest obowiązkowe, ale zalecane. Mogą być tym samym plikiem fizycznym.

Aby ustawić lokalizacje systemu plików dwóch 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, aby móc 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 w plakacie, zostało to już zrobione przez administratorów serwera)

keytool -import -file publicclientkey.cer -store serversidestore.jks
mhaller
źródło
Powinienem chyba wspomnieć, że nie mam kontroli nad serwerem. Serwer zaimportował nasz certyfikat publiczny. Administratorzy tego systemu powiedzieli mi, że muszę jawnie dostarczyć certyfikat, aby można go było przesłać podczas uzgadniania (ich serwer wyraźnie tego wymaga).
tmbrggmn
Będziesz potrzebował kluczy publicznych i prywatnych do swojego certyfikatu publicznego (tego, który jest znany serwerowi) jako pliku JKS.
sfussenegger
Dzięki za przykładowy kod. W powyższym kodzie czym dokładnie jest „mykey-public.cer”? Czy to jest publiczny certyfikat klienta (używamy certyfikatów z podpisem własnym)?
tmbrggmn
Tak, zmieniłem nazwy plików we fragmentach kodu. Mam nadzieję, że to wyjaśnia.
mhaller
Cześć, dzięki. Ciągle się mylę, ponieważ najwyraźniej „klucz” i „certyfikat” są używane zamiennie.
tmbrggmn
10

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

}
kinjelom
źródło
Jeśli wolisz, aby certyfikat był dostępny dla wszystkich aplikacji korzystających z konkretnej instalacji JVM, zastosuj tę odpowiedź .
ADTC
czy metoda configureRequest()ustawiania proxy projektu klienta jest prawidłowa?
shareef
tak, jest to konfiguracja klienta http i w tym przypadku konfiguracja proxy
kinjelom
może mam głupie pytanie, ale jak mogę utworzyć plik cacert.jks? Mam wyjątek Wyjątek w wątku „main” java.io.FileNotFoundException:. \ Cacert.jks (System nie może znaleźć określonego pliku)
Patlatus
6

Dla tych z Was, którzy po prostu chcą skonfigurować uwierzytelnianie dwukierunkowe (certyfikaty serwera i klienta), połączenie tych dwóch linków doprowadzi Cię tam:

Konfiguracja dwukierunkowego uwierzytelniania:

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

Nie musisz używać wspomnianego pliku konfiguracyjnego openssl; po prostu użyj

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

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

wygenerować własny certyfikat CA, a następnie wygenerować i podpisać klucze serwera i klienta za pomocą:

  • $ openssl genrsa -des3 -out server.key 4096

  • $ openssl req -new -key server.key -out server.csr

  • $ openssl x509 -req -dni 365 -in server.csr -CA ca.crt -CAkey ca.key -set_serial 100 -out server.crt

i

  • $ openssl genrsa -des3 -out client.key 4096

  • $ openssl req -new -key client.key -out client.csr

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

Przez resztę postępuj zgodnie z instrukcjami w linku. Zarządzanie certyfikatami dla Chrome działa tak samo, jak w przykładzie dla wspomnianego Firefoxa.

Następnie skonfiguruj serwer za pomocą:

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

Zauważ, że już utworzyłeś serwer .crt i .key, więc nie musisz już robić tego kroku.

hans
źródło
1
Myślisz, że masz literówkę w kroku generowania CSR serwera: server.keynie używajclient.key
Legend
0

Połączyłem się z bankiem za pomocą dwukierunkowego protokołu SSL (certyfikat klienta i serwera) z Spring Boot. Opisz więc tutaj wszystkie moje kroki, mam nadzieję, że to komuś pomoże (najprostsze działające rozwiązanie, jakie znalazłem):

  1. Wygeneruj żądanie sertertyfikatu:

    • Wygeneruj klucz prywatny:

      openssl genrsa -des3 -passout pass:MY_PASSWORD -out user.key 2048
    • Wygeneruj żądanie certyfikatu:

      openssl req -new -key user.key -out user.csr -passin pass:MY_PASSWORD

    Zachowaj user.key(i hasło) i wyślij prośbę o certyfikat user.csrdo banku w celu uzyskania mojego certyfikatu

  2. Otrzymaj 2 certyfikat: mój główny certyfikat klienta i główny certyfikat clientId.crtbanku:bank.crt

  3. Utwórz magazyn kluczy Java (wprowadź hasło klucza i ustaw hasło magazynu kluczy):

    openssl pkcs12 -export -in clientId.crt -inkey user.key -out keystore.p12 -name clientId -CAfile ca.crt -caname root

    Nie zwracaj uwagi na wyjściu: unable to write 'random state'. Java PKCS12 keystore.p12utworzona.

  4. Dodaj do magazynu kluczy bank.crt(dla uproszczenia użyłem jednego magazynu kluczy):

    keytool -import -alias banktestca -file banktestca.crt -keystore keystore.p12 -storepass javaops

    Sprawdź certyfikaty magazynu kluczy przez:

    keytool -list -keystore keystore.p12
  5. Gotowy na kod Java :) Użyłem Spring Boot RestTemplatez dodatkową org.apache.httpcomponents.httpcorezależnością:

    @Bean("sslRestTemplate")
    public RestTemplate sslRestTemplate() throws Exception {
      char[] storePassword = appProperties.getSslStorePassword().toCharArray();
      URL keyStore = new URL(appProperties.getSslStore());
    
      SSLContext sslContext = new SSLContextBuilder()
            .loadTrustMaterial(keyStore, storePassword)
      // use storePassword twice (with key password do not work)!!
            .loadKeyMaterial(keyStore, storePassword, storePassword) 
            .build();
    
      // Solve "Certificate doesn't match any of the subject alternative names"
      SSLConnectionSocketFactory socketFactory = new SSLConnectionSocketFactory(sslContext, NoopHostnameVerifier.INSTANCE);
    
      CloseableHttpClient client = HttpClients.custom().setSSLSocketFactory(socketFactory).build();
      HttpComponentsClientHttpRequestFactory factory = new HttpComponentsClientHttpRequestFactory(client);
      RestTemplate restTemplate = new RestTemplate(factory);
      // restTemplate.setMessageConverters(List.of(new Jaxb2RootElementHttpMessageConverter()));
      return restTemplate;
    }
Grigorij Kislin
źródło
Możesz to wszystko zrobić za pomocą klucza. W tym przypadku nie ma potrzeby używania OpenSSL.
Markiz Lorne
0

Biorąc pod uwagę plik p12 z certyfikatem i kluczem prywatnym (na przykład wygenerowanym przez openssl), następujący kod użyje go do określonego połączenia HttpsURLConnection:

    KeyStore keyStore = KeyStore.getInstance("pkcs12");
    keyStore.load(new FileInputStream(keyStorePath), keystorePassword.toCharArray());
    KeyManagerFactory kmf = KeyManagerFactory.getInstance(KeyManagerFactory.getDefaultAlgorithm());
    kmf.init(keyStore, keystorePassword.toCharArray());
    SSLContext ctx = SSLContext.getInstance("TLS");
    ctx.init(kmf.getKeyManagers(), null, null);
    SSLSocketFactory sslSocketFactory = ctx.getSocketFactory();

    HttpsURLConnection connection = (HttpsURLConnection) url.openConnection();
    connection.setSSLSocketFactory(sslSocketFactory);

SSLContextZajmuje trochę czasu, aby zainicjować, więc warto ją buforować.

Johannes Brodwall
źródło
-1

Myślę, że poprawką tutaj był typ magazynu kluczy, pkcs12 (pfx) zawsze mają klucz prywatny, a typ JKS może istnieć bez klucza prywatnego. O ile nie określisz w swoim kodzie lub nie wybierzesz certyfikatu za pomocą przeglądarki, serwer nie będzie wiedział, że reprezentuje klienta na drugim końcu.

Obi-Wan Kenobi
źródło
1
Format PKCS12 był tradycyjnie używany dla klucza prywatnego-i-certyfikatu, ale Java od 8 w 2014 r. (Ponad rok przed tą odpowiedzią) obsługuje certyfikaty zawierające PKCS12 bez klucza prywatnego (prywatnych). Niezależnie od formatu magazynu kluczy uwierzytelnianie klienta wymaga klucza prywatnego i certyfikatu. Nie rozumiem twojego drugiego zdania, ale klient Java może automatycznie wybrać certyfikat klienta i klucz, jeśli dostępny jest co najmniej jeden odpowiedni wpis lub menedżer kluczy może zostać skonfigurowany do używania określonego.
dave_thompson_085
Twoje drugie zdanie jest całkowicie niepoprawne. Serwer udostępnia zaufanym sygnatariuszom, a klient albo umiera, albo nie dostarcza certyfikatu spełniającego to ograniczenie. Automatycznie, nie poprzez „w kodzie”. To jest „sposób, aby serwer wiedział, że reprezentuje klienta”.
Markiz Lorne