通过使用 Java 的证书通过 https 使用 RESTful 服务

Posted

技术标签:

【中文标题】通过使用 Java 的证书通过 https 使用 RESTful 服务【英文标题】:Consuming RESTful service over https with certificate using Java 【发布时间】:2012-07-10 21:40:39 【问题描述】:

我是 REST 服务的新用户。我需要使用 Jersey 生成的 RESTful API 服务。问题出现是因为该服务托管在远程主机上,并且需要使用证书进行 https 访问。

我从组织获得了证书,并且可以使用我的任何浏览器(设置了证书)访问该 REST API 服务。

我在这里阅读了很多帖子,并遵循了有关此主题的答案: Using HTTPS with REST in Java

现在我已经在我的 Java 密钥库上设置了我的证书。但我不知道如何在我的 Java 程序上使用它,所以它使用的正是我进行 https 连接所需的证书。

这是我用于本地测试的简单连接代码。

package rest.test.first;
import java.net.URI;
import javax.ws.rs.core.MediaType;
import javax.ws.rs.core.UriBuilder;

import com.sun.jersey.api.client.Client;
import com.sun.jersey.api.client.ClientResponse;
import com.sun.jersey.api.client.WebResource;
import com.sun.jersey.api.client.config.ClientConfig;
import com.sun.jersey.api.client.config.DefaultClientConfig;

public class TestClient
public static void main(String[]args)
    ClientConfig config= new DefaultClientConfig();
    Client client=Client.create(config);
    WebResource service=client.resource(getBaseURI());
    //Fluentinterfaces
    System.out.println(service.path("rest").path("hello").accept(MediaType.TEXT_PLAIN).get(ClientResponse.class).toString());
    //Getplaintext
    System.out.println(service.path("rest").path("hello").accept(MediaType.TEXT_PLAIN).get(String.class));
    //GetXML
    System.out.println(service.path("rest").path("hello").accept(MediaType.TEXT_XML).get(String.class));
    //Thehtml
    System.out.println(service.path("rest").path("hello").accept(MediaType.TEXT_HTML).get(String.class));


private static URI getBaseURI()
    return UriBuilder.fromUri("http://localhost:8080/rest.test").build();


我已经阅读了有关使用系统集属性通过以下代码指定密钥库路径的信息:

System.setProperty("javax.net.ssl.keyStore", "/path/to/keystore.jks");
System.setProperty("javax.net.ssl.keyStorePassword", "password");

我在连接到远程服务器时仍然收到 401 错误。

但是我不知道如何使用我在密钥库上的证书进行 SSL 连接。 我也一直在阅读有关为此目的使用 sslSocketFactory 的信息,但我无法按照这篇文章中的说明使其工作:How can I use different certificates on specific connections?

我已经设法使用此代码从密钥库中检索我的证书。现在我只需要知道如何在连接中使用它:

package rest.test.first;

    import java.io.FileInputStream;
    import java.security.KeyStore;
    import java.security.cert.Certificate;

    public class keystore 

      public static void main(String[] args) throws Exception 
        String keystoreFilename = "/usr/lib/jvm/jdk1.6.0_32/jre/lib/security/cacerts";

        char[] password = "changeit".toCharArray();
        String alias = "remote_https_server";

        FileInputStream fIn = new FileInputStream(keystoreFilename);
        KeyStore keystore = KeyStore.getInstance("JKS");

        keystore.load(fIn, password);

        Certificate cert = keystore.getCertificate(alias);

        System.out.println(cert);
      
    

好的,这是我写的最后一个脚本。我可以连接到 https 站点,但我仍然无法连接到需要发送我的证书进行身份验证的 https 站点。

package rest.test.first;


import java.io.*;
import java.net.*;
import java.security.*;

import javax.net.ssl.HttpsURLConnection;
import javax.net.ssl.SSLContext;
import javax.net.ssl.SSLSocketFactory;
import javax.net.ssl.TrustManager;
import javax.net.ssl.TrustManagerFactory;


public class urlConnection
  public static void main(String args[]) throws Exception 

     System.setProperty("javax.net.ssl.trustStore", "/usr/lib/jvm/jdk1.6.0_32/jre/lib/security/cacerts"); 
  System.setProperty("javax.net.ssl.trustStorePassword", "changeit");
  Security.addProvider(new com.sun.net.ssl.internal.ssl.Provider());

      //TrustStore..
      char[] passphrase = "changeit".toCharArray(); //password
      KeyStore keystore = KeyStore.getInstance("JKS");
      //KeyStore keyStore = KeyStore.getInstance(KeyStore.getDefaultType());
      keystore.load(new FileInputStream("/usr/lib/jvm/jdk1.6.0_32/jre/lib/security/cacerts"), passphrase); //path

      //TrustManagerFactory tmf = TrustManagerFactory.getInstance("SunX509"); //instance
      TrustManagerFactory tmf = TrustManagerFactory.getInstance(TrustManagerFactory.getDefaultAlgorithm());
      tmf.init(keystore);


      SSLContext context = SSLContext.getInstance("TLS");
      TrustManager[] trustManagers = tmf.getTrustManagers();
      context.init(null, trustManagers, null);
      SSLSocketFactory sf = context.getSocketFactory();
      URL url = new URL("https://www.google.es");

      HttpsURLConnection httpsCon = (HttpsURLConnection) url.openConnection();
      httpsCon.setSSLSocketFactory(sf);
      httpsCon.setRequestMethod("GET");

      /*InputStream inStrm = httpsCon.getInputStream();
      System.out.println("\nContent at " + url);
      int ch;
        while (((ch = inStrm.read()) != -1))
          System.out.print((char) ch);
        inStrm.close();
      */

      System.out.println("Response Message is " + httpsCon.getResponseMessage());

 

【问题讨论】:

您是如何生成密钥库的?在您发布的代码中,为什么使用 cacerts 信任库?我建议将您的证书导入到它自己的 JKS 中,并在对 System.setProperty("javax.net.ssl.keyStore", "...") 的调用中使用它。 【参考方案1】:

假设您在服务器上部署此代码并且您已正确完成其他所有操作(例如正确生成密钥库核心并将其放置在您的服务器可以访问的位置,使用与您的代码相同的 java 版本生成密钥库)那么我认为您需要做的是添加以下内容

<Connector SSLEnabled="true" clientAuth="false" keystoreFile="pathToKeystore" keystorePass="password" maxThreads="150" port="443" protocol="HTTP/1.1" scheme="https" secure="true" sslProtocol="TLS"/>

在您运行客户端的服务器实例的 server.xml 中,并且

<Connector connectionTimeout="20000" port="80" protocol="HTTP/1.1" redirectPort="8443"/>

IMP:如果您在此代码旁边使用 Skype,请务必(取消选中)更改默认值,因为它还使用相同的端口(80 和 443)进行其他连接

【讨论】:

以上是关于通过使用 Java 的证书通过 https 使用 RESTful 服务的主要内容,如果未能解决你的问题,请参考以下文章

解决 https 证书验证不通过的问题

JAVA - SSL - 客户端证书

如何通过 HTTPS 使用客户端证书?

https SSL主流数字证书有哪些格式

[web建站]-简单通过letsencrypt配置HTTPS证书

使用自签名证书通过 HTTPS 进行 CORS 请求的 Phonegap 应用程序