Проверка подлинности сертификата клиента JAVA HTTPS



Я довольно новичок в HTTPS/SSL / TLS, и я немного смущен тем, что именно клиенты должны представлять при аутентификации с сертификатами.



Я пишу Java-клиент, который должен сделать простой пост данных на определенный URL. Эта часть работает нормально, единственная проблема заключается в том, что это должно быть сделано по HTTPS. Часть HTTPS довольно проста в обращении (либо с HTTPclient, либо с помощью встроенной поддержки HTTPS Java), но я застрял на аутентификации с клиентом сертификаты. Я заметил, что здесь уже есть очень похожий вопрос, который я еще не пробовал с моим кодом (сделаю это достаточно скоро). Моя текущая проблема заключается в том, что - что бы я ни делал-клиент Java никогда не отправляет сертификат (я могу проверить это с помощью дампов PCAP).



Я хотел бы знать, что именно клиент должен представить серверу при аутентификации с помощью сертификатов (в частности, для Java - если это вообще имеет значение)? Это файл JKS или PKCS#12? Что должно быть в них; просто сертификат клиента или ключ? Если да, то какой ключ? Существует довольно много путаницы в отношении всех различных типов файлов, типов сертификатов и т. д.



Как я уже говорил, Я новичок в HTTPS/SSL/TLS, поэтому я был бы признателен за некоторую справочную информацию (не обязательно эссе; я соглашусь на ссылки на хорошие статьи).

957   6  

6 ответов:

наконец-то удалось решить все вопросы, поэтому я отвечу на свой собственный вопрос. Это настройки / файлы, которые я использовал, чтобы решить мою конкретную проблему(ы);

The хранилище ключей клиента это формат PKCS#12 содержащих

  1. клиента общественные сертификат (в данном случае подписанный самозаверяющим CA)
  2. клиента частная ключ

To создать его я использовал в OpenSSL , например:

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

Совет: убедитесь, что вы получаете последние OpenSSL,не версия 0.9.8 h, потому что это, кажется, страдает от ошибки, которая не позволяет вам правильно генерировать файлы PKCS#12.

этот файл PKCS#12 будет использоваться клиентом Java для представления сертификата клиента серверу, когда сервер явно запросил клиента для аутентификации. Смотрите Википедия статья о TLS для обзора того, как на самом деле работает протокол аутентификации сертификата клиента (также объясняет, почему нам нужен закрытый ключ клиента здесь).

The магазин доверия клиента прямой формат JKS содержащий root или промежуточные сертификаты CA. Эти сертификаты CA определят, с какими конечными точками вам будет разрешено взаимодействовать, в этом случае это позволит вам клиент для подключения к любому серверу представляет сертификат, подписанный одним из центров сертификации truststore.

для его создания можно использовать стандартный Java keytool, например;

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

используя этот магазин доверия, ваш клиент попытается выполнить полное SSL-квитирование со всеми серверами, которые представляют сертификат, подписанный CA, идентифицированным myca.crt.

файлы выше предназначены только для клиента. Если вы также хотите настроить сервер, то сервер нуждается в собственных файлах ключей и доверенных хранилищ. Отличный проход для настройки полностью рабочего примера как для клиента Java, так и для сервера (используя Tomcat) можно найти на этот сайт.

Вопросы/Замечания/Советы

  1. проверка подлинности клиента сертификат может применяться только сервером.
  2. (важно!) когда сервер запрашивает сертификат клиента (как часть протокола TLS рукопожатие), он также предоставит список доверенных ЦС как часть запроса сертификата. Когда сертификат клиента, который вы хотите представить для аутентификации,не подписанный одним из этих CA, он вообще не будет представлен (на мой взгляд, это странное поведение, но я уверен, что для этого есть причина). Это было основной причиной моих проблем, так как другая сторона не настроила свой сервер должным образом, чтобы принять мой самозаверяющий сертификат клиента, и мы предположили, что проблема была на моем конце за неправильное предоставление сертификата клиента в запросе.
  3. Получить Wireshark. Он имеет большой анализ пакетов SSL / HTTPS и будет огромной помощью отладки и поиска проблемы. Это похоже на -Djavax.net.debug=ssl но более структурирован и (возможно) легче интерпретировать, если вам неудобно с выводом отладки JAVA 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

другие ответы показывают, как глобально настроить сертификаты клиента. Однако если вы хотите программно определить клиентский ключ для одного конкретного соединения, а не глобально определить его в каждом приложении, работающем на вашем 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"));

они JKS файл-это просто контейнер для сертификатов и пар ключей. В сценарии проверки подлинности на стороне клиента различные части ключей будут расположены здесь:

  • The клиент's магазин будет содержать клиента частное и публичное пара ключей. Это называется keystore.
  • The сервер's магазин будет содержать клиента общественные ключ. Это называется truststore.

разделение truststore и keystore не является обязательным, но рекомендуется. Они могут быть одним и тем же физическим файлом.

чтобы задать расположение файловой системы двух хранилищ, используйте следующие системные свойства:

-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

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

}

для тех из вас, кто просто хочет настроить двустороннюю аутентификацию (серверные и клиентские сертификаты), сочетание этих двух ссылок можно получить здесь :

двухсторонняя настройка auth:

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

вам не нужно использовать конфигурационный файл openssl, который они упоминают; просто используйте

  • $ openssl genrsa-des3-out ca.ключ 4096

  • $ openssl req-new-x509-days 365-key ca.key-out ca.ЭЛТ

чтобы создать свой собственный сертификат CA, а затем создать и подписать ключи сервера и клиента с помощью:

  • $ openssl genrsa-des3-out server.ключевые 4096

  • $ openssl req-new-key client.сервер выдачи ключей.КСО

  • $ openssl x509-req-days 365-in server.КСО -ца-ца.crt-CAkey ca.ключ - set_serial 100-out сервер.ЭЛТ

и

  • $ openssl genrsa-des3-out клиент.ключевые 4096

  • $ openssl req-new-key client.ключ -клиента.КСО

  • $ openssl x509-req-days 365-in client.КСО -ца-ца.crt-CAkey ca.ключ-set_serial 101-out клиент.ЭЛТ

для остальных следуйте инструкциям в ссылке. Управление сертификатами для Chrome работает так же, как в примере для firefox, который упоминается.

Далее, установите сервер через:

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

обратите внимание, что вы уже создали сервер .ЭЛТ И.ключ, поэтому вам больше не нужно делать этот шаг.

Я думаю, что исправление здесь было типом хранилища ключей, pkcs12 (pfx) всегда имеет закрытый ключ, а тип JKS может существовать без закрытого ключа. Если вы не укажете в своем коде или не выберете сертификат через браузер, сервер не сможет узнать, что он представляет клиента на другом конце.

Comments

    Ничего не найдено.