example - httpclient post method java
¿Cómo uso un certificado de cliente SSL con Apache HttpClient? (2)
En Python estaba usando peticiones como esta:
requests.put(
webdavURL,
auth=(tUsername, tPassword),
data=webdavFpb,
verify=False,
cert=("/path/to/file.pem", "/path/to/file.key"))
Muy fácil.
Ahora necesito implementar lo mismo en Java usando Apache HttpClient. ¿Cómo puedo pasar un certificado de cliente cuando hago solicitudes usando HttpClient?
Creo que la principal diferencia es que, en java, normalmente se coloca la clave y el certificado en un almacén de claves y se usa desde allí. Como mencionó a menudo, la gente desea usar una biblioteca separada para él, como el cliente httpcomponents mencionado (como si estuviera utilizando la biblioteca de solicitudes en su ejemplo de Python).
Este es un ejemplo del uso de un certificado de cliente de un almacén de claves, utilizando la biblioteca mencionada anteriormente:
import org.apache.http.HttpEntity;
import org.apache.http.HttpResponse;
import org.apache.http.client.HttpClient;
import org.apache.http.client.methods.HttpGet;
import org.apache.http.impl.client.HttpClients;
import org.apache.http.ssl.SSLContexts;
import org.apache.http.util.EntityUtils;
import org.junit.Test;
import javax.net.ssl.SSLContext;
import java.io.InputStream;
import java.security.KeyStore;
import static org.junit.Assert.assertEquals;
import static org.junit.Assert.assertNotNull;
public class MyClientCertTest {
private static final String KEYSTOREPATH = "/clientkeystore.jks"; // or .p12
private static final String KEYSTOREPASS = "keystorepass";
private static final String KEYPASS = "keypass";
KeyStore readStore() throws Exception {
try (InputStream keyStoreStream = this.getClass().getResourceAsStream(KEYSTOREPATH)) {
KeyStore keyStore = KeyStore.getInstance("JKS"); // or "PKCS12"
keyStore.load(keyStoreStream, KEYSTOREPASS.toCharArray());
return keyStore;
}
}
@Test
public void readKeyStore() throws Exception {
assertNotNull(readStore());
}
@Test
public void performClientRequest() throws Exception {
SSLContext sslContext = SSLContexts.custom()
.loadKeyMaterial(readStore(), KEYPASS.toCharArray()) // use null as second param if you don''t have a separate key password
.build();
HttpClient httpClient = HttpClients.custom().setSSLContext(sslContext).build();
HttpResponse response = httpClient.execute(new HttpGet("https://slsh.iki.fi/client-certificate/protected/"));
assertEquals(200, response.getStatusLine().getStatusCode());
HttpEntity entity = response.getEntity();
System.out.println("----------------------------------------");
System.out.println(response.getStatusLine());
EntityUtils.consume(entity);
}
}
Maven pom para versiones de dependencia:
<?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>com.acme</groupId>
<artifactId>httptests</artifactId>
<version>0.0.1-SNAPSHOT</version>
<properties>
<maven.compiler.source>1.8</maven.compiler.source>
<maven.compiler.target>1.8</maven.compiler.target>
</properties>
<dependencies>
<dependency>
<groupId>org.apache.httpcomponents</groupId>
<artifactId>httpclient</artifactId>
<version>4.5.3</version>
</dependency>
<dependency>
<groupId>junit</groupId>
<artifactId>junit</artifactId>
<version>4.12</version>
<scope>test</scope>
</dependency>
</dependencies>
<build>
<plugins>
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-surefire-plugin</artifactId>
<version>2.9</version>
<!-- this is not needed, but useful if you want to debug what''s going
on with your connection -->
<configuration>
<argLine>-Djavax.net.debug=all</argLine>
</configuration>
</plugin>
</plugins>
</build>
</project>
También he publicado una página de prueba simple para probar un certificado de cliente.
Solo para demostrar que se puede hacer, a continuación se muestra un ejemplo de uso del certificado de cliente usando solo la API java estándar, sin bibliotecas adicionales.
import org.junit.Test;
import javax.net.ssl.HttpsURLConnection;
import javax.net.ssl.KeyManager;
import javax.net.ssl.KeyManagerFactory;
import javax.net.ssl.SSLContext;
import java.io.BufferedReader;
import java.io.InputStream;
import java.io.InputStreamReader;
import java.net.URL;
import java.security.KeyStore;
public class PlainJavaHTTPS2Test {
@Test
public void testJKSKeyStore() throws Exception {
final String KEYSTOREPATH = "clientkeystore.jks";
final char[] KEYSTOREPASS = "keystorepass".toCharArray();
final char[] KEYPASS = "keypass".toCharArray();
try (InputStream storeStream = this.getClass().getResourceAsStream(KEYSTOREPATH)) {
setSSLFactories(storeStream, "JKS", KEYSTOREPASS, KEYPASS);
}
testPlainJavaHTTPS();
}
@Test
public void testP12KeyStore() throws Exception {
final String KEYSTOREPATH = "clientkeystore.p12";
final char[] KEYSTOREPASS = "keystorepass".toCharArray();
final char[] KEYPASS = "keypass".toCharArray();
try (InputStream storeStream = this.getClass().getResourceAsStream(KEYSTOREPATH)) {
setSSLFactories(storeStream, "PKCS12", KEYSTOREPASS, KEYPASS);
}
testPlainJavaHTTPS();
}
private static void setSSLFactories(InputStream keyStream, String keystoreType, char[] keyStorePassword, char[] keyPassword) throws Exception
{
KeyStore keyStore = KeyStore.getInstance(keystoreType);
keyStore.load(keyStream, keyStorePassword);
KeyManagerFactory keyFactory =
KeyManagerFactory.getInstance(KeyManagerFactory.getDefaultAlgorithm());
keyFactory.init(keyStore, keyPassword);
KeyManager[] keyManagers = keyFactory.getKeyManagers();
SSLContext sslContext = SSLContext.getInstance("SSL");
sslContext.init(keyManagers, null, null);
SSLContext.setDefault(sslContext);
}
public void testPlainJavaHTTPS() throws Exception {
String httpsURL = "https://slsh.iki.fi/client-certificate/protected/";
URL myUrl = new URL(httpsURL);
HttpsURLConnection conn = (HttpsURLConnection)myUrl.openConnection();
try (InputStream is = conn.getInputStream()) {
InputStreamReader isr = new InputStreamReader(is);
BufferedReader br = new BufferedReader(isr);
String inputLine;
while ((inputLine = br.readLine()) != null) {
System.out.println(inputLine);
}
}
}
}
Y aquí hay una tercera versión con la menor cantidad de código, pero que se basa en el hecho de que a) el almacén de claves es un archivo en el disco, no en el archivo jar, yb) la contraseña de la clave debe ser idéntica a la contraseña del almacén de claves.
import org.junit.BeforeClass;
import org.junit.Test;
import java.net.URL;
import java.io.*;
import javax.net.ssl.HttpsURLConnection;
public class PlainJavaHTTPSTest {
@BeforeClass
public static void setUp() {
System.setProperty("javax.net.ssl.keyStore", "/full/path/to/clientkeystore-samepassword.jks");
System.setProperty("javax.net.ssl.keyStorePassword", "keystorepass");
}
@Test
public void testPlainJavaHTTPS() throws Exception {
String httpsURL = "https://slsh.iki.fi/client-certificate/protected/";
URL myUrl = new URL(httpsURL);
HttpsURLConnection conn = (HttpsURLConnection)myUrl.openConnection();
try (InputStream is = conn.getInputStream()) {
InputStreamReader isr = new InputStreamReader(is);
BufferedReader br = new BufferedReader(isr);
String inputLine;
while ((inputLine = br.readLine()) != null) {
System.out.println(inputLine);
}
}
}
}
Por supuesto, las propiedades establecidas anteriormente en el código también se pueden proporcionar como parámetros de inicio, -Djavax.net.ssl.keyStore=/full/path/to/clientkeystore-samepassword.jks
y -Djavax.net.ssl.keyStorePassword=keystorepass
.
Si desea utilizar el cliente HTTP de Apache en lugar del cliente HTTP de Java, debe proporcionar a SSLFactory su almacén de claves y configurar DefaultHTTPClient para usarlo en el protocolo HTTPS.
Puedes encontrar un ejemplo de trabajo here .
Espero que eso ayude.