Ответ 1
Вам нужно указать SSLSocketFactory (org.apache.http, а не javax) о вашем хранилище ключей и настроить ваш DefaultHTTPClient для использования его для соединений https.
Я хочу использовать взаимную аутентификацию SSL между службами A и B. В настоящее время я реализую передачу клиентского сертификата из службы A на Java. Я использую Apache DefaultHttpClient для выполнения моих запросов. Мне удалось получить сертификат клиента для моей службы A из внутреннего диспетчера учетных данных, и я сохраняю его как массив байтов.
DefaultHttpClient client = new DefaultHttpClient();
byte [] certificate = localCertManager.retrieveCert();
У меня очень мало опыта в этой области, и я буду благодарен за вашу помощь!
Я подумал, что, возможно, это должно быть каким-то образом передано через аргументы в HTTP-клиенте или, возможно, в заголовках.
Как передать сертификат клиента клиенту HTTP?
Вам нужно указать SSLSocketFactory (org.apache.http, а не javax) о вашем хранилище ключей и настроить ваш DefaultHTTPClient для использования его для соединений https.
Сертификат клиента отправляется во время рукопожатия TLS при установлении соединения и не может быть отправлен через HTTP в этом соединении.
Сообщение выглядит следующим образом:
Вам необходимо отправить сертификат клиента во время рукопожатия TLS, прежде чем на HTTP (методы, заголовки, URL, тела запросов) будет оказано влияние. Сервер не примет клиентский сертификат, отправленный позже.
Я рекомендую перейти с DefaultHttpClient (не рекомендуется) на CloseableHttpClient, который более аккуратно работает с try-with-resources.
Apache HttpClient 4.5 делает Mutual TLS достаточно удобным. Этот ответ был протестирован с помощью Apache HttpClient 4.5.3.
Важной отправной точкой является использование loadKeyMaterial для загрузки сертификата клиента и его ключа (пары ключей клиента) в SSLContext:
SSLContext sslContext = SSLContexts.custom().loadKeyMaterial(
MutualHttpsMain.class.getResource(TEST_CLIENT_KEYSTORE_RESOURCE),
password, password,
(aliases, socket) -> aliases.keySet().iterator().next()
).build();
И, наконец, создаем HTTP-клиент с фабрикой сокетов:
CloseableHttpClient httpclient = HttpClients
.custom().setSSLContext(sslContext).build();
С этим клиентом все ваши запросы могут быть выполнены с подразумеваемой взаимной аутентификацией TLS:
CloseableHttpResponse closeableHttpResponse = httpclient.execute(
new HttpGet(URI.create("https://mutual-tls.example.com/")));
Вот полный работающий пример взаимной TLS с Apache HttpClient:
import org.apache.http.HttpEntity;
import org.apache.http.StatusLine;
import org.apache.http.client.methods.CloseableHttpResponse;
import org.apache.http.client.methods.HttpGet;
import org.apache.http.impl.client.CloseableHttpClient;
import org.apache.http.impl.client.HttpClients;
import org.apache.http.ssl.SSLContexts;
import javax.net.ssl.SSLContext;
import java.io.Console;
import java.io.IOException;
import java.io.InputStream;
import java.net.URI;
import java.nio.ByteBuffer;
import java.nio.channels.Channels;
import java.nio.channels.ReadableByteChannel;
import java.nio.channels.WritableByteChannel;
import java.security.GeneralSecurityException;
public class MutualHttpsMain {
private static final String TEST_URL = "https://mutual-tls.example.com/";
private static final String TEST_CLIENT_KEYSTORE_RESOURCE = "/mutual-tls-keystore.p12";
public static void main(String[] args) throws GeneralSecurityException, IOException {
Console console = System.console();
char[] password = console.readPassword("Keystore password: ");
SSLContext sslContext = SSLContexts.custom().loadKeyMaterial(
MutualHttpsMain.class.getResource(TEST_CLIENT_KEYSTORE_RESOURCE),
password, password,
(aliases, socket) -> aliases.keySet().iterator().next()
).build();
try (CloseableHttpClient httpclient = HttpClients
.custom().setSSLContext(sslContext).build();
CloseableHttpResponse closeableHttpResponse = httpclient.execute(
new HttpGet(URI.create(TEST_URL)))) {
console.writer().println(closeableHttpResponse.getStatusLine());
HttpEntity entity = closeableHttpResponse.getEntity();
try (InputStream content = entity.getContent();
ReadableByteChannel src = Channels.newChannel(content);
WritableByteChannel dest = Channels.newChannel(System.out)) {
ByteBuffer buffer = ByteBuffer.allocateDirect(16 * 1024);
while (src.read(buffer) != -1) {
buffer.flip();
dest.write(buffer);
buffer.compact();
}
buffer.flip();
while (buffer.hasRemaining())
dest.write(buffer);
}
}
}
}
Как правило, лучше использовать Gradle или Maven для запуска чего-то подобного, но в целях обеспечения минимального бритья Яка я предоставляю базовые инструкции JDK для создания и запуска этого.
Загрузите файлы JAR со следующих страниц:
Сохраните приведенный выше полный пример как MutualHttpsMain.java.
Скопируйте ваш PKCS # 12 в joint-tls-keystore.p12 в том же каталоге.
Скомпилируйте его следующим образом (в macOS/Linux/* nix-likes):
javac MutualHttpsMain.java -cp httpclient-4.5.3.jar:httpcore-4.4.8.jar
или в Windows:
javac MutualHttpsMain.java -cp httpclient-4.5.3.jar;httpcore-4.4.8.jar
Выполните следующим образом (в macOS/Linux/* nix-likes):
java -cp httpclient-4.5.3.jar:commons-codec-1.10.jar:commons-logging-1.2.jar:httpcore-4.4.8.jar:. MutualHttpsMain
Запустите (в Windows) следующим образом:
java -cp httpclient-4.5.3.jar;commons-codec-1.10.jar;commons-logging-1.2.jar;httpcore-4.4.8.jar;. MutualHttpsMain