2014-01-19 2 views
9

В Python я использовал запросы как это:Как использовать сертификат клиента SSL с Apache HttpClient?

requests.put(
       webdavURL, 
       auth=(tUsername, tPassword), 
       data=webdavFpb, 
       verify=False, 
       cert=("/path/to/file.pem", "/path/to/file.key")) 

Легкий как пирог.

Теперь мне нужно реализовать ту же самую вещь в Java, используя Apache HttpClient. Как передать сертификат клиента при выполнении запросов с использованием HttpClient?

ответ

7

Я думаю, что основное отличие состоит в том, что в java вы обычно кладете ключ и сертификат на key store и используете его оттуда. Как вы говорите, часто люди хотят использовать для этого отдельную библиотеку, например, упомянутый httpcomponents client (как и в примере на вашем примере python) requests library.

Вот пример использования клиентского сертификата из хранилища ключей, используя ранее упомянутой библиотеки:

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 для версий зависимостей:

<?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> 

Я также опубликовал простой test page для тестирования сертификата клиента.


Просто чтобы показать, что это может быть сделано, ниже приведен пример использования клиентского сертификата только с помощью стандартного Java API, без дополнительных библиотек.

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

А вот третий вариант с наименьшим количеством кода, но опирается на тот факт, что а) хранилище ключей является файлом на диске, а не в банке, и б) пароль ключа должен быть идентичным хранилище пароль.

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

свойство, установленное выше в коде, конечно, может быть также приведено в качестве параметров запуска, -Djavax.net.ssl.keyStore=/full/path/to/clientkeystore-samepassword.jks и -Djavax.net.ssl.keyStorePassword=keystorepass.

11

Если вы хотите использовать HTTP-клиент Apache вместо HTTP-клиента Java, вы должны предоставить SSLFactory свое хранилище ключей и настроить DefaultHTTPClient, чтобы использовать его в протоколе HTTPS.

Рабочий стол here.

Я надеюсь, что это поможет.

+0

Можете ли вы дать некоторые указатели на использование Java HTTP Client? У меня проблемы с Apache. – Ravi

+5

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

+0

Это не отвечает на вопрос. Это добавит только заказную доверенность, а не сертификат клиента. – sstendal

Смежные вопросы