带有RAW CERT的Spring REST模板配置SSLContext和用密码

发布于 2025-02-03 14:55:05 字数 6597 浏览 2 评论 0原文

我拥有基于私钥和证书的钥匙材料的自定义实现模板,但是当我通过ret模板重现请求时,我在卷发上进行了并面对成功的结果时,我面临错误,如果我没有附上相同的错误curl命令cert.crt

-----BEGIN CERTIFICATE-----
// something
-----END CERTIFICATE-----

key.key: key.key:

-----BEGIN ENCRYPTED PRIVATE KEY-----
// something
-----END ENCRYPTED PRIVATE KEY-----

我也有密码字符串密码,并且当我起诉curl时,它没有错误

curl -x post -v -h'content -'content -type: application/json'https:// some_url -d'{“ some_key”:“ some_value”}'}' - cert cert.crt

key.key

curl -X POST -v -H 'Content-type: application/json' https://some_url -d'{"some_key":"some_value"}'

-key 使用400

,这是我的REST模板实现,我将证书,PrivateKey和KeyStore Oject置于> .load> .loadekymaterial

import lombok.extern.slf4j.Slf4j;
import org.apache.http.client.HttpClient;
import org.apache.http.conn.ssl.SSLConnectionSocketFactory;
import org.apache.http.impl.client.HttpClients;
import org.apache.http.ssl.SSLContextBuilder;
import org.apache.http.ssl.TrustStrategy;
import org.bouncycastle.asn1.pkcs.PrivateKeyInfo;
import org.bouncycastle.jce.provider.BouncyCastleProvider;
import org.bouncycastle.openssl.PEMEncryptedKeyPair;
import org.bouncycastle.openssl.PEMKeyPair;
import org.bouncycastle.openssl.PEMParser;
import org.bouncycastle.openssl.bc.BcPEMDecryptorProvider;
import org.bouncycastle.openssl.jcajce.JcaPEMKeyConverter;
import org.bouncycastle.operator.InputDecryptorProvider;
import org.bouncycastle.pkcs.PKCS8EncryptedPrivateKeyInfo;
import org.bouncycastle.pkcs.PKCSException;
import org.bouncycastle.pkcs.jcajce.JcePKCSPBEInputDecryptorProviderBuilder;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.context.annotation.Profile;
import org.springframework.core.io.Resource;
import org.springframework.http.client.HttpComponentsClientHttpRequestFactory;
import org.springframework.stereotype.Service;
import org.springframework.util.ResourceUtils;
import org.springframework.web.client.RestTemplate;

import javax.net.ssl.SSLContext;
import java.io.*;
import java.nio.file.Files;
import java.nio.file.Path;
import java.security.KeyStore;
import java.security.PrivateKey;
import java.security.Security;
import java.security.cert.Certificate;
import java.security.cert.CertificateFactory;
import java.security.cert.X509Certificate;
import java.util.ArrayList;
import java.util.List;
import java.util.stream.Collectors;
import java.util.stream.Stream;


public class ClientCertificateRestTemplate {
private final RestTemplate crt;

public ClientCertificateRestTemplate(
        @Value("${pass_phrase}") String passPhrase,
        @Value("${client-certificate-file}") Resource clientCertificateFile,
        @Value("${client-certificate-private-key-file}") Resource clientCertificatePrivateKeyFile
) throws Exception {

    Certificate certificate = getCertificate(clientCertificateFile);
    PrivateKey privateKey = stringToPrivateKey(clientCertificatePrivateKeyFile, passPhrase);
    KeyStore keyStore = keyStore(privateKey, certificate, passPhrase);

    TrustStrategy acceptingTrustStrategy = (X509Certificate[] chain, String authType) -> true;

    SSLContext sslContext = new SSLContextBuilder()
            .loadTrustMaterial(null, acceptingTrustStrategy)
            .loadKeyMaterial(keyStore, passPhrase.toCharArray())
            .build();
    SSLConnectionSocketFactory socketFactory = new SSLConnectionSocketFactory(sslContext);
    HttpClient httpClient = HttpClients.custom()
            .setSSLSocketFactory(socketFactory)
            .build();
    HttpComponentsClientHttpRequestFactory factory =
            new HttpComponentsClientHttpRequestFactory(httpClient);

    crt = new RestTemplate(factory);

}

private KeyStore keyStore(PrivateKey privateKey, Certificate certificate, String psw) throws Exception {
    final KeyStore ks = KeyStore.getInstance(KeyStore.getDefaultType());
    ks.load(null);

    final List<Certificate> certs = new ArrayList<>();
    ks.setCertificateEntry("certificate", certificate);
    certs.add(certificate);

    ks.setKeyEntry("key", privateKey, psw.toCharArray(), certs.toArray(new Certificate[]{}));

    return ks;
}

private PrivateKey stringToPrivateKey(Resource file, String password)
        throws IOException, PKCSException {
    try (Stream<String> lines = Files.lines(Path.of(file.getURL().getPath()))) {
        String s = lines.collect(Collectors.joining("\n"));

        // For JcaPEMKeyConverter().setProvider("BC")
        BouncyCastleProvider provider = new BouncyCastleProvider();
        Security.addProvider(provider);
        PrivateKeyInfo pki;

        try (PEMParser pemParser = new PEMParser(new StringReader(s))) {

            Object o = pemParser.readObject();

            if (o instanceof PKCS8EncryptedPrivateKeyInfo) {

                PKCS8EncryptedPrivateKeyInfo epki = (PKCS8EncryptedPrivateKeyInfo) o;

                JcePKCSPBEInputDecryptorProviderBuilder builder =
                        new JcePKCSPBEInputDecryptorProviderBuilder().setProvider(provider);

                InputDecryptorProvider idp = builder.build(password.toCharArray());

                pki = epki.decryptPrivateKeyInfo(idp);
            } else if (o instanceof PEMEncryptedKeyPair) {

                PEMEncryptedKeyPair epki = (PEMEncryptedKeyPair) o;
                PEMKeyPair pkp = epki.decryptKeyPair(new BcPEMDecryptorProvider(password.toCharArray()));

                pki = pkp.getPrivateKeyInfo();
            } else {
                throw new PKCSException("Invalid encrypted private key class: " + o.getClass().getName());
            }

            JcaPEMKeyConverter converter = new JcaPEMKeyConverter().setProvider(provider);
            return converter.getPrivateKey(pki);
        }
    }
}

private Certificate getCertificate(Resource file) throws Exception {
    File key = ResourceUtils.getFile(file.getURL());
    InputStream stream = new FileInputStream(key);

    // CertificateFactory
    CertificateFactory cf = CertificateFactory.getInstance("X.509");
    // certificate
    Certificate ca;
    try {
        ca = cf.generateCertificate(stream);
    } finally {
        stream.close();
    }

    return ca;
}

public RestTemplate getCrt() {
    return crt;
}

}

然后在我的测试中

    ResponseEntity<String> response = crt.exchange(
            "https://some_url",
            HttpMethod.POST,
            entity,
            String.class
    );

使用,然后失败了,面对400。有正确的配置休息模板?

I have custom implementation rest template with key material based on private key and cert, but when I reproduced request by rest template, which I did on curl and faced with successful result, I faced with error, looks the same error if I did not attached key and cert to the curl command

cert.crt:

-----BEGIN CERTIFICATE-----
// something
-----END CERTIFICATE-----

key.key:

-----BEGIN ENCRYPTED PRIVATE KEY-----
// something
-----END ENCRYPTED PRIVATE KEY-----

also I have passphrase String passPhrase and when I sue curl it works without error

curl -X POST -v -H 'Content-type: application/json' https://some_url -d'{"some_key":"some_value"}' --cert cert.crt --key key.key

then I enetered passPhrase and got 200 with some body

curl without cert and key

curl -X POST -v -H 'Content-type: application/json' https://some_url -d'{"some_key":"some_value"}'

faced with 400

and this is my rest template implementation where I cretaed Certificate, PrivateKey and KeyStore oject and then used it on .loadKeyMaterial

import lombok.extern.slf4j.Slf4j;
import org.apache.http.client.HttpClient;
import org.apache.http.conn.ssl.SSLConnectionSocketFactory;
import org.apache.http.impl.client.HttpClients;
import org.apache.http.ssl.SSLContextBuilder;
import org.apache.http.ssl.TrustStrategy;
import org.bouncycastle.asn1.pkcs.PrivateKeyInfo;
import org.bouncycastle.jce.provider.BouncyCastleProvider;
import org.bouncycastle.openssl.PEMEncryptedKeyPair;
import org.bouncycastle.openssl.PEMKeyPair;
import org.bouncycastle.openssl.PEMParser;
import org.bouncycastle.openssl.bc.BcPEMDecryptorProvider;
import org.bouncycastle.openssl.jcajce.JcaPEMKeyConverter;
import org.bouncycastle.operator.InputDecryptorProvider;
import org.bouncycastle.pkcs.PKCS8EncryptedPrivateKeyInfo;
import org.bouncycastle.pkcs.PKCSException;
import org.bouncycastle.pkcs.jcajce.JcePKCSPBEInputDecryptorProviderBuilder;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.context.annotation.Profile;
import org.springframework.core.io.Resource;
import org.springframework.http.client.HttpComponentsClientHttpRequestFactory;
import org.springframework.stereotype.Service;
import org.springframework.util.ResourceUtils;
import org.springframework.web.client.RestTemplate;

import javax.net.ssl.SSLContext;
import java.io.*;
import java.nio.file.Files;
import java.nio.file.Path;
import java.security.KeyStore;
import java.security.PrivateKey;
import java.security.Security;
import java.security.cert.Certificate;
import java.security.cert.CertificateFactory;
import java.security.cert.X509Certificate;
import java.util.ArrayList;
import java.util.List;
import java.util.stream.Collectors;
import java.util.stream.Stream;


public class ClientCertificateRestTemplate {
private final RestTemplate crt;

public ClientCertificateRestTemplate(
        @Value("${pass_phrase}") String passPhrase,
        @Value("${client-certificate-file}") Resource clientCertificateFile,
        @Value("${client-certificate-private-key-file}") Resource clientCertificatePrivateKeyFile
) throws Exception {

    Certificate certificate = getCertificate(clientCertificateFile);
    PrivateKey privateKey = stringToPrivateKey(clientCertificatePrivateKeyFile, passPhrase);
    KeyStore keyStore = keyStore(privateKey, certificate, passPhrase);

    TrustStrategy acceptingTrustStrategy = (X509Certificate[] chain, String authType) -> true;

    SSLContext sslContext = new SSLContextBuilder()
            .loadTrustMaterial(null, acceptingTrustStrategy)
            .loadKeyMaterial(keyStore, passPhrase.toCharArray())
            .build();
    SSLConnectionSocketFactory socketFactory = new SSLConnectionSocketFactory(sslContext);
    HttpClient httpClient = HttpClients.custom()
            .setSSLSocketFactory(socketFactory)
            .build();
    HttpComponentsClientHttpRequestFactory factory =
            new HttpComponentsClientHttpRequestFactory(httpClient);

    crt = new RestTemplate(factory);

}

private KeyStore keyStore(PrivateKey privateKey, Certificate certificate, String psw) throws Exception {
    final KeyStore ks = KeyStore.getInstance(KeyStore.getDefaultType());
    ks.load(null);

    final List<Certificate> certs = new ArrayList<>();
    ks.setCertificateEntry("certificate", certificate);
    certs.add(certificate);

    ks.setKeyEntry("key", privateKey, psw.toCharArray(), certs.toArray(new Certificate[]{}));

    return ks;
}

private PrivateKey stringToPrivateKey(Resource file, String password)
        throws IOException, PKCSException {
    try (Stream<String> lines = Files.lines(Path.of(file.getURL().getPath()))) {
        String s = lines.collect(Collectors.joining("\n"));

        // For JcaPEMKeyConverter().setProvider("BC")
        BouncyCastleProvider provider = new BouncyCastleProvider();
        Security.addProvider(provider);
        PrivateKeyInfo pki;

        try (PEMParser pemParser = new PEMParser(new StringReader(s))) {

            Object o = pemParser.readObject();

            if (o instanceof PKCS8EncryptedPrivateKeyInfo) {

                PKCS8EncryptedPrivateKeyInfo epki = (PKCS8EncryptedPrivateKeyInfo) o;

                JcePKCSPBEInputDecryptorProviderBuilder builder =
                        new JcePKCSPBEInputDecryptorProviderBuilder().setProvider(provider);

                InputDecryptorProvider idp = builder.build(password.toCharArray());

                pki = epki.decryptPrivateKeyInfo(idp);
            } else if (o instanceof PEMEncryptedKeyPair) {

                PEMEncryptedKeyPair epki = (PEMEncryptedKeyPair) o;
                PEMKeyPair pkp = epki.decryptKeyPair(new BcPEMDecryptorProvider(password.toCharArray()));

                pki = pkp.getPrivateKeyInfo();
            } else {
                throw new PKCSException("Invalid encrypted private key class: " + o.getClass().getName());
            }

            JcaPEMKeyConverter converter = new JcaPEMKeyConverter().setProvider(provider);
            return converter.getPrivateKey(pki);
        }
    }
}

private Certificate getCertificate(Resource file) throws Exception {
    File key = ResourceUtils.getFile(file.getURL());
    InputStream stream = new FileInputStream(key);

    // CertificateFactory
    CertificateFactory cf = CertificateFactory.getInstance("X.509");
    // certificate
    Certificate ca;
    try {
        ca = cf.generateCertificate(stream);
    } finally {
        stream.close();
    }

    return ca;
}

public RestTemplate getCrt() {
    return crt;
}

}

then in my test

    ResponseEntity<String> response = crt.exchange(
            "https://some_url",
            HttpMethod.POST,
            entity,
            String.class
    );

and failed, faced with 400. There were correct configuration rest template ?

如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。
列表为空,暂无数据
我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
原文