【问题标题】:Java SSL - Connect to secure Rest Service using pkcs12 (.p12) fileJava SSL - 使用 pkcs12 (.p12) 文件连接到安全的 Rest 服务
【发布时间】:2020-04-11 06:43:11
【问题描述】:

我正在为我的 Web 应用程序使用休息服务。并且服务提供商提供了一个带有密码的 .p12 文件以连接到他们的服务。

出于测试目的,我在邮递员中安装了证书文件,它工作正常。现在我必须将它集成到我的 java 代码中。

这是我用于集成的 java 代码。

import java.io.FileInputStream;
import java.security.KeyStore;
import java.security.cert.Certificate;
import java.security.cert.X509Certificate;
import java.util.Enumeration;
import java.util.logging.Level;
import java.util.logging.Logger;
import javax.net.ssl.KeyManagerFactory;
import org.springframework.web.client.RestTemplate;

import javax.net.ssl.SSLContext;
import javax.net.ssl.TrustManagerFactory;
import org.springframework.http.HttpEntity;
import org.springframework.http.HttpHeaders;
import org.springframework.http.HttpMethod;
import org.springframework.http.MediaType;
import org.springframework.stereotype.Service;

@Service
public class DemoIntegration
{

    String key = "XYX";
    String value = "12BN";
    String encVal= "343fhh22343mm90ddfd61lcsert";
    private static String certPw = "44vvxxffx";  //Password to cerfificate file

    public void checkConnection()
    {
        try
        {
            RestTemplate restTemplate = new RestTemplate();
            HttpHeaders httpHeaders = new HttpHeaders();

            String uri = "https://my_demo_uri";

            KeyStore ks = KeyStore.getInstance("pkcs12");
            ks.load(new FileInputStream("C:\\Users\\my_cert.p12"), certPw.toCharArray()); //my_cert.p12 is my cerfificate file 

            KeyStore jks = KeyStore.getInstance("JKS");
            jks.load(null);

            for (Enumeration<String> t = ks.aliases(); t.hasMoreElements();)
            {
                String alias = t.nextElement();
                System.out.println("@:" + alias);
                if (ks.isKeyEntry(alias))
                {
                    Certificate[] a = ks.getCertificateChain(alias);
                    for (int i = 0; i < a.length; i++)
                    {
                        X509Certificate x509 = (X509Certificate) a[i];
                        System.out.println(x509.getSubjectDN().toString());
                        if (i > 0)
                        {
                            jks.setCertificateEntry(x509.getSubjectDN().toString(), x509);
                        }
                        System.out.println(ks.getCertificateAlias(x509));
                        System.out.println("ok");
                    }
                }
            }

            KeyManagerFactory kmf = KeyManagerFactory.getInstance("SunX509");
            kmf.init(ks, certPw.toCharArray());

            TrustManagerFactory tmf = TrustManagerFactory.getInstance("SunX509");
            tmf.init(jks);

            SSLContext ctx = SSLContext.getInstance("TLS");
            ctx.init(kmf.getKeyManagers(), tmf.getTrustManagers(), null);

            httpHeaders.setContentType(MediaType.APPLICATION_JSON);
            httpHeaders.set("API_KEY", api_key);
            httpHeaders.set("Signature", SHA256Val);

            String r2 = restTemplate.exchange(uri, HttpMethod.GET, new HttpEntity<>(httpHeaders), String.class).getBody();
            System.out.println("Result " + r2);
        }
        catch (Exception ex)
        {
            System.out.println("Error " + ex.toString());
            Logger.getLogger(DemoIntegration.class.getName()).log(Level.SEVERE, null, ex);
        }
    }
}

而且我总是得到如下响应。这与我在未添加证书文件的情况下使用 postman 对其进行测试时得到的响应相同。

400 Bad Request:{
  "error": {
    "message": "No SSL cetificate"
  }
}

有人能指出我在这里做错了什么吗?

我是 java 安全领域的新手。老实说,我按照指南编写此代码/尝试连接时是否有要遵循的清单? (就像将文件添加到密钥库或信任库一样。至少有一个指南可以帮助我)

非常感谢。

【问题讨论】:

  • 有什么帮助吗?
  • 在你的代码中,我没有看到 SslContext 和 RestTemplate 是如何连接的,可能还缺少一块。

标签: java rest ssl keystore pkcs#12


【解决方案1】:

试试这个方法。因为我没有证书文件,所以我无法测试它,但我希望做一些小的修改,它会起作用。

请将您的证书文件包含在资源文件夹中。

@Service
public class DemoIntegration
{
    String key = "XYX";
    String value = "12BN";
    String encVal= "343fhh22343mm90ddfd61lcsert";
    private static String certPw = "44vvxxffx";

    public void checkConnection()
    {
        HttpHeaders httpHeaders = new HttpHeaders();
        httpHeaders.setContentType(MediaType.APPLICATION_JSON);
        httpHeaders.set("API_KEY", api_key);
        httpHeaders.set("Signature", SHA256Val);

        String uri = "https://my_demo_uri";

        try
        {
            RestTemplate restTemplate = getRestTemplateClientAuthentication();

            httpHeaders.setContentType(MediaType.APPLICATION_JSON);
            httpHeaders.set("API_KEY", api_key);
            httpHeaders.set("Signature", SHA256Val);

            String r2 = restTemplate.exchange(uri, HttpMethod.GET, new HttpEntity<>(httpHeaders), String.class).getBody();
            System.out.println("Result " + r2);
        }
        catch (Exception ex)
        {
            System.out.println("Error " + ex.toString());
            Logger.getLogger(PesonetService.class.getName()).log(Level.SEVERE, null, ex);
        }
    }

    private RestTemplate getRestTemplateClientAuthentication() throws Exception
    {
        TrustStrategy acceptingTrustStrategy = (X509Certificate[] chain, String authType) -> true;

        SSLContext sslContext = SSLContextBuilder
                .create()
                .loadKeyMaterial(ResourceUtils.getFile("classpath:my_cert.p12"),
                 certPw.toCharArray(), certPw.toCharArray())
                .build();

        CloseableHttpClient client = HttpClients.custom()
                .setSSLContext(sslContext)
                .build();

        HttpComponentsClientHttpRequestFactory requestFactory = new HttpComponentsClientHttpRequestFactory();
        requestFactory.setHttpClient(client);

        RestTemplate restTemplate = new RestTemplate(requestFactory);
        return restTemplate;
    }
}

让我知道它是否有效。

【讨论】:

  • 这非常适合 .p12 (PKCS12) 格式的 x.509 证书。我必须做的唯一改变是使用 .loadKeyMaterial(new ClassPathResource("/keystore/somepackage/certificatename.p12").getFile(), 因为 ResourceUtils.getFile 出于某种原因在我的项目上不起​​作用。(其中 keystore 是一个文件夹内部资源)
  • TrustStrategy acceptingTrustStrategy = (X509Certificate[] chain, String authType) -> true;此行没有在构建 restTemplate 的方法中的任何地方使用?
【解决方案2】:

你能试试这个代码吗?我对负载信任库进行了一些更改。确保从您的代码中传递系统信任库的路径。

package com.jerry;
import java.io.FileInputStream;
import java.security.KeyStore;
import java.security.SecureRandom;
import javax.net.ssl.*;

import org.apache.http.client.HttpClient;
import org.apache.http.conn.ssl.SSLSocketFactory;
import org.apache.http.impl.client.HttpClients;
import org.springframework.http.client.ClientHttpRequestFactory;
import org.springframework.http.client.HttpComponentsClientHttpRequestFactory;
import org.springframework.web.client.RestTemplate;

import org.springframework.http.HttpEntity;
import org.springframework.http.HttpHeaders;
import org.springframework.http.HttpMethod;
import org.springframework.http.MediaType;


public class SSLTest {
    private String api_key = "XYX";
    private  String api_secret = "12BN";
    private String SHA256Val = "343fhh633343mm90ddfd61lcsert";
    private static String certPw = "44vvxxffx";  //

    String trustStorePath = "C:/Program Files/Java/jre1.8.0_91/lib/security/cacerts";
    String trustStorePassword = "changeit"; // default trust store password

    public void checkConnection()
    {
        try
        {
//            RestTemplate restTemplate = new RestTemplate();
            HttpHeaders httpHeaders = new HttpHeaders();

            String uri = "https://my_demo_uri";

            KeyStore ks = KeyStore.getInstance("PKCS12");
            ks.load(new FileInputStream("C:\\Users\\IB\\Downloads\\my_cert.p12"), certPw.toCharArray()); //my_cert.p12 is my cerfificate file

            KeyManagerFactory kmf = KeyManagerFactory.getInstance(KeyManagerFactory.getDefaultAlgorithm());
            kmf.init(ks, certPw.toCharArray());
            KeyManager[] kms = kmf.getKeyManagers();

            KeyStore jks = KeyStore.getInstance("JKS");
            jks.load(new FileInputStream(trustStorePath), trustStorePassword.toCharArray());
//
//            for (Enumeration<String> t = ks.aliases(); t.hasMoreElements();)
//            {
//                String alias = t.nextElement();
//                System.out.println("@:" + alias);
//                if (ks.isKeyEntry(alias))
//                {
//                    Certificate[] a = ks.getCertificateChain(alias);
//                    for (int i = 0; i < a.length; i++)
//                    {
//                        X509Certificate x509 = (X509Certificate) a[i];
//                        System.out.println(x509.getSubjectDN().toString());
//                        if (i > 0)
//                        {
//                            jks.setCertificateEntry(x509.getSubjectDN().toString(), x509);
//                        }
//                        System.out.println(ks.getCertificateAlias(x509));
//                        System.out.println("ok");
//                    }
//                }
//            }


            TrustManagerFactory tmf = TrustManagerFactory.getInstance(TrustManagerFactory.getDefaultAlgorithm());
            tmf.init(jks);
            TrustManager[] tms = tmf.getTrustManagers();


            SSLContext ctx = SSLContext.getInstance("TLS");
            ctx.init(kmf.getKeyManagers(), tmf.getTrustManagers(), new SecureRandom());
            SSLSocketFactory socketFactory = new SSLSocketFactory(ks, certPw, jks);

            HttpClient httpClient = HttpClients.custom().setSSLSocketFactory(
                    socketFactory).build();
            ClientHttpRequestFactory requestFactory = new HttpComponentsClientHttpRequestFactory(
                    httpClient);
            RestTemplate restTemplate = new RestTemplate(requestFactory);
            httpHeaders.setContentType(MediaType.APPLICATION_JSON);
            httpHeaders.set("API_KEY", api_key);
            httpHeaders.set("Signature", SHA256Val);

            HttpEntity<String> entity = new HttpEntity<>("body", httpHeaders);

            String r2 = restTemplate.exchange(uri, HttpMethod.GET, entity, String.class).getBody();
            System.out.println("Result " + r2);
        }
        catch (Exception ex)
        {
            System.out.println("Error " + ex.toString());
        }
    }
}

PS:我无法验证此代码,因为我没有证书和密钥。 :)

【讨论】:

    【解决方案3】:

    你不应该硬编码任何东西来让它工作,只需为 Java 添加一些启动参数,比如:

    -Djavax.net.ssl.keyStore=C:\Users\my_cert.p12
    

    。 (尽管它们随后被应用程序中运行的所有线程和代码共享,这可能是不可接受的)查看这个问题和提供的答案:

    java SSL and cert keystore

    此外,了解幕后发生的事情可能非常有用,您可以通过启用一些额外的日志记录来查看,例如:

    -Djavax.net.debug=all
    

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 1970-01-01
      • 2021-10-21
      • 1970-01-01
      • 2018-08-31
      • 1970-01-01
      • 1970-01-01
      • 2011-08-29
      • 2018-05-05
      相关资源
      最近更新 更多