Java HTTPS 클라이언트 인증서 인증


222

나는 상당히 새롭고 HTTPS/SSL/TLS인증서로 인증 할 때 클라이언트가 정확히 무엇을 제시 해야하는지 약간 혼란 스럽습니다.

POST특정 데이터에 간단한 데이터를 작성 해야하는 Java 클라이언트를 작성 중 URL입니다. 그 부분은 제대로 작동하지만 유일한 문제는 끝났습니다 HTTPS. 이 HTTPS부분은 HTTPclientJava의 내장 HTTPS지원을 사용하거나 사용 하는 것이 상당히 쉽지만 클라이언트 인증서로 인증하는 데 어려움을 겪고 있습니다. 나는 여기에 이미 매우 비슷한 질문이 있다는 것을 알았습니다.이 코드는 아직 시도하지 않았습니다 (곧 그렇게 할 것입니다). 내 현재 문제는 내가하는 일에 관계없이 Java 클라이언트가 인증서를 보내지 않는다는 것입니다 ( PCAP덤프로 확인할 수 있습니다 ).

인증서로 인증 할 때 클라이언트가 서버에 정확히 무엇을 제시 해야하는지 알고 싶습니다 (특히 Java의 경우 전혀 중요하지 않음)? 이 JKS파일 PKCS#12입니까? 그들 안에 있어야 할 것; 클라이언트 인증서 또는 키? 그렇다면 어떤 키입니까? 모든 다른 종류의 파일, 인증서 유형 등에 대해서는 약간의 혼란이 있습니다.

내가 처음에 말했듯이 HTTPS/SSL/TLS배경 지식도 고맙게 생각합니다 (에세이가 될 필요는 없습니다. 좋은 기사에 대한 링크를 결정합니다).


나는 키 스토어에 추가 할 수있는 하나의 요구 식별하는 방법을 클라이언트에서이 인증서를 부여하고 신뢰 당신은 내가 실제로 제기 한이 문제가 할 수있는 어떤 단서도 얻지 못하고, 이미 문제의 비슷한 종류의 겪었으로 도움이 문제를 확인하시기 바랍니다 수 유래를 .com / questions / 61374276 /…
henrycharles

답변:


233

마지막으로 모든 문제를 해결 했으므로 내 질문에 대답하겠습니다. 이들은 특정 문제를 해결하기 위해 관리하는 데 사용한 설정 / 파일입니다.

클라이언트의 키 스토어는 A는 PKCS # 12 형식의 포함 된 파일

  1. 클라이언트의 공개 인증서 (이 경우 자체 서명 된 CA에 의해 서명 됨)
  2. 클라이언트의 개인

이를 생성하기 위해 OpenSSL의 pkcs12명령을 사용했습니다 .

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

팁 : PKCS # 12 파일을 제대로 생성 할 수없는 버그로 인해 버전 0.9.8h가 아닌 최신 OpenSSL을 구 하십시오.

이 PKCS # 12 파일은 서버가 클라이언트에게 인증을 명시 적으로 요청한 경우 서버에 클라이언트 인증서를 제공하기 위해 Java 클라이언트가 사용합니다. 클라이언트 인증서 인증 프로토콜이 실제로 작동하는 방법에 대한 개요는 TLS Wikipedia 기사를 참조하십시오 (여기서 클라이언트 개인 키가 필요한 이유도 설명 함).

고객의 신뢰는 정직이다 JKS 형식 포함하는 파일을 루트 또는 중간 CA 인증서를 . 이러한 CA 인증서는 통신 할 수있는 엔드 포인트를 결정하며,이 경우 클라이언트가 신뢰 저장소의 CA 중 하나에서 서명 한 인증서를 제공하는 서버에 연결할 수 있습니다.

이를 생성하기 위해 예를 들어 표준 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.

위의 파일은 엄격하게 클라이언트 전용입니다. 서버도 설정하려면 서버에 자체 키 및 신뢰 저장소 파일이 필요합니다. Tomcat을 사용하여 Java 클라이언트와 서버 모두에 대한 완전한 예제를 설정하는 데 대한 유용한 정보는 이 웹 사이트 에서 찾을 수 있습니다 .

문제 / 발언 / 팁

  1. 클라이언트 인증서 인증 은 서버에서만 시행 할 수 있습니다.
  2. ( 중요! ) 서버가 클라이언트 인증서를 요청하면 (TLS 핸드 셰이크의 일부로) 인증서 요청의 일부로 신뢰할 수있는 CA 목록도 제공합니다. 당신이 인증을 위해 존재하고자하는 클라이언트 인증서가되면 하지 이러한 CA의 중 하나에 의해 서명, (내 의견으로는,이 이상한 행동을하지만, 나는 확실히 그것을위한 이유가있어) 모두에서 발표되지 않습니다. 상대방이 내 자체 서명 클라이언트 인증서를 수락하도록 서버를 올바르게 구성하지 않았기 때문에 이것이 문제의 주요 원인이었으며 요청에서 클라이언트 인증서를 올바르게 제공하지 않아 문제가 발생한 것으로 가정했습니다.
  3. Wireshark를 받으십시오. SSL / HTTPS 패킷 분석 기능이 뛰어나며 디버깅 및 문제 발견에 큰 도움이됩니다. 그것은 비슷 -Djavax.net.debug=ssl하지만, 더 구조화되고 (틀림없이) 당신은 자바 SSL 디버그 출력 불편이 있다면 쉽게 해석 할 수.
  4. Apache httpclient 라이브러리를 완벽하게 사용할 수 있습니다. 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

6
"인증을 위해 제시하려는 클라이언트 인증서가 이러한 CA 중 하나에 의해 서명되지 않은 경우 전혀 표시되지 않습니다". 클라이언트가 서버에서 인증서를 수락하지 않기 때문에 인증서가 제공되지 않습니다. 또한 인증서는 중간 CA "ICA"로 서명 될 수 있으며 서버는 클라이언트에게 루트 CA "RCA"를 제시 할 수 있으며 웹 브라우저는 RCA가 아닌 ICA가 서명하더라도 인증서를 계속 선택할 수 있습니다.
KyleM

2
위 주석의 예로, 하나의 루트 CA (RCA1)와 두 개의 중간 CA (ICA1 및 ICA2)가있는 상황을 고려하십시오. Apache Tomcat에서 RCA1을 신뢰 저장소로 가져 오면 웹 브라우저는 신뢰 저장소에 있지 않더라도 ICA1 및 ICA2가 서명 한 모든 인증서를 제공합니다. 개별 인증서가 중요하지 않은 체인이기 때문입니다.
KyleM

2
"제 생각에 이것은 이상한 행동이지만 그 이유가 있다고 확신합니다." 그 이유는 그것이 RFC 2246에서 말하는 것입니다. 그것에 대해 이상한 것은 없습니다. 클라이언트가 서버에서 허용하지 않는 인증서를 제시하도록 허용하는 것은 이상하고 시간과 공간을 완전히 낭비하는 것입니다.
Lorne의 후작

1
위의 예에서와 같이 단일 JVM 전체의 키 저장소 (및 신뢰 저장소!)를 사용하지 않는 것이 좋습니다. 단일 연결을 사용자 정의하는 것이 더 안전하고 유연하지만 조금 더 코드를 작성해야합니다. SSLContext@ Magnus의 답변에서 와 같이 사용자 정의해야합니다 .
크리스토퍼 슐츠

63

다른 답변은 클라이언트 인증서를 전체적으로 구성하는 방법을 보여줍니다. 그러나 하나의 특정 연결에 대해 클라이언트 방식으로 클라이언트 키를 프로그래밍 방식으로 정의하려는 경우 JVM에서 실행되는 모든 응용 프로그램에서 클라이언트 키를 전역 적으로 정의하지 않고 다음과 같이 자체 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"));

나는을 사용해야했다 sslContext = SSLContexts.custom().loadTrustMaterial(keyFile, PASSWORD).build();. 나는 그것을 사용할 수 없었다 loadKeyMaterial(...).
코너 스벤손

4
@ConorSvensson 신뢰 자료는 원격 서버 인증서를 신뢰하는 클라이언트를위한 것이고 핵심 자료는 클라이언트를 신뢰하는 서버를위한 것입니다.
Magnus

1
나는이 간결하고 정확한 답변을 정말 좋아합니다. 사람들이 관심이 있다면 여기에 빌드 지침이있는 예제를 제공합니다. stackoverflow.com/a/46821977/154527
Alain O'Dea

3
당신은 내 하루를 구했다! 내가해야 할 한 가지 추가 변경 사항은 암호를 loadKeyMaterial(keystore, keyPassphrase.toCharArray())코드로 보내십시오 !
AnandShanbhag

1
@peterh 예, Apache http에만 해당됩니다. 모든 HTTP 라이브러리에는 자체 구성 방식이 있지만 대부분은을 사용해야합니다 SSLContext.
Magnus

30

JKS 파일은 인증서 및 키 쌍의 컨테이너 일뿐입니다. 클라이언트 측 인증 시나리오에서 키의 다양한 부분이 여기에 있습니다.

  • 클라이언트 의 저장소는 클라이언트의 포함 개인 및 공용 키 쌍을. 이것을 키 스토어 라고합니다 .
  • 서버 의 저장소는 클라이언트가 포함됩니다 공개 키를. 이를 신뢰 저장소 라고합니다 .

신뢰 저장소와 키 저장소 분리는 필수는 아니지만 권장됩니다. 이들은 동일한 실제 파일 일 수 있습니다.

두 상점의 파일 시스템 위치를 설정하려면 다음 시스템 특성을 사용하십시오.

-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

서버를 제어 할 수 없다는 것을 언급해야 할 것입니다. 서버가 공개 인증서를 가져 왔습니다. 해당 시스템의 관리자에게 핸드 셰이크 중에 인증서를 보낼 수 있도록 인증서를 명시 적으로 제공해야한다고 들었습니다 (서버에서 명시 적으로 요청).
tmbrggmn 2009

공개 인증서 (서버에 알려진 키)를 JKS 파일로 사용하려면 공개 및 개인 키가 필요합니다.
sfussenegger 2009

예제 코드에 감사드립니다. 위의 코드에서 "mykey-public.cer"는 정확히 무엇입니까? 이것이 클라이언트 공개 인증서입니까 (자체 서명 된 인증서를 사용합니까)?
tmbrggmn 2009

예, 코드 스 니펫의 파일 이름을 적절하게 변경했습니다. 이것이 분명해지기를 바랍니다.
mhaller 2009

알았어, 고마워 분명히 "키"와 "인증서"가 서로 바꿔서 사용되기 때문에 혼란스러워합니다.
tmbrggmn 2009

10

메이븐 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>

자바 코드 :

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

}

특정 JVM 설치를 사용하는 모든 응용 프로그램에서 인증서를 사용할 수있게하려면 대신 이 답변을 따르십시오 .
ADTC

configureRequest()클라이언트 프로젝트의 프록시를 설정 하는 방법 이 올바른가요?
shareef

예, HTTP 클라이언트 구성이며이 경우 프록시 설정입니다
kinjelom

어리석은 질문이 있지만 cacert.jks 파일을 어떻게 만듭니 까? "main"스레드에 예외 예외가 있습니다. java.io.FileNotFoundException :. \ cacert.jks (시스템이 지정된 파일을 찾을 수 없음)
Patlatus

6

양방향 인증 (서버 및 클라이언트 인증서)을 설정하려는 사용자를 위해이 두 링크를 조합하여 사용할 수 있습니다.

양방향 인증 설정 :

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

그들이 언급 한 openssl 설정 파일을 사용할 필요는 없습니다. 그냥 사용

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

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

자체 CA 인증서를 생성 한 후 다음을 통해 서버 및 클라이언트 키를 생성하고 서명하십시오.

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

  • $ openssl 요청 -new -key server.key -out server.csr

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

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

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

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

나머지는 링크의 단계를 따르십시오. Chrome 용 인증서 관리는 언급 된 firefox의 예와 동일하게 작동합니다.

다음을 통해 서버를 설정하십시오.

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

서버 .crt 및 .key를 이미 생성 했으므로 더 이상 해당 단계를 수행 할 필요가 없습니다.


1
당신은 서버 CSR 생성 단계에서 오타를 줄 생각 사용해야 server.key하지client.key
the.Legend

0

Spring Boot가있는 양방향 SSL (클라이언트 및 서버 인증서)을 사용하여 은행에 연결했습니다. 그래서 여기에 모든 단계를 설명하고 누군가에게 도움이되기를 바랍니다 (가장 간단한 작업 솔루션).

  1. 적극적인 요청을 생성하십시오.

    • 개인 키 생성 :

      openssl genrsa -des3 -passout pass:MY_PASSWORD -out user.key 2048
    • 인증서 요청을 생성하십시오.

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

    내 송금을 위해 user.key(및 비밀번호)를 보관 user.csr하고 은행에 인증서 요청 을 보냅니다.

  2. 2 개의 인증서 수신 : 내 클라이언트 루트 인증서 clientId.crt및 은행 루트 인증서 :bank.crt

  3. Java 키 저장소를 작성하십시오 (키 비밀번호를 입력하고 키 저장소 비밀번호를 설정하십시오).

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

    출력에주의하지 마십시오 : unable to write 'random state'. Java PKCS12가 keystore.p12작성되었습니다.

  4. 키 저장소에 추가하십시오 bank.crt(간단하게하기 위해 하나의 키 저장소를 사용했습니다).

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

    다음을 통해 키 저장소 인증서를 확인하십시오.

    keytool -list -keystore keystore.p12
  5. Java 코드 준비 :) RestTemplate추가 org.apache.httpcomponents.httpcore종속성으로 Spring Boot 를 사용했습니다 .

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

keytool로 모든 것을 할 수 있습니다. 이것에 OpenSSL이 전혀 필요하지 않습니다.
Lorne의 후작

0

인증서와 개인 키가 모두있는 p12 파일 (예 : openssl에 의해 생성됨)이 제공된 경우 다음 코드는 특정 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);

SSLContext초기화 하는 데 시간 이 걸리므로 캐시하는 것이 좋습니다.


-1

여기 수정은 키 저장소 유형이라고 생각합니다. pkcs12 (pfx)에는 항상 개인 키가 있으며 JKS 유형은 개인 키없이 존재할 수 있습니다. 코드를 지정하거나 브라우저를 통해 인증서를 선택하지 않으면 서버는 상대방이 클라이언트를 나타내는 지 알 수 없습니다.


1
PKCS12 형식은 전통적으로 privatekey-AND-cert에 사용되었지만 2014 년 8 월 이후 (이 답변 전 1 년 이상) Java는 개인 키없이 인증서를 포함하는 PKCS12를 지원했습니다. 키 저장소 형식에 관계없이 클라이언트 인증에는 privatekey-AND-cert가 필요합니다. 두 번째 문장을 이해하지 못하지만 Java 클라이언트 하나 이상의 적합한 항목을 사용할 수 있거나 지정된 항목을 사용하도록 키 관리자를 구성 할 수 있는 경우 클라이언트 인증서 및 키를 자동으로 선택할 있습니다.
dave_thompson_085

두 번째 문장이 완전히 틀 렸습니다. 서버는 신뢰할 수있는 서명자를 제공하고 클라이언트는 해당 제약 조건을 만족하는 인증서를 죽거나 제공하지 않습니다. '코드에서'를 통하지 않고 자동으로. 즉 입니다 서버의 '가 클라이언트를 대표한다 아는 방법'.
Lorne의 후작
당사 사이트를 사용함과 동시에 당사의 쿠키 정책개인정보 보호정책을 읽고 이해하였음을 인정하는 것으로 간주합니다.
Licensed under cc by-sa 3.0 with attribution required.