带有RAW CERT的Spring REST模板配置SSLContext和用密码
我拥有基于私钥和证书的钥匙材料的自定义实现模板,但是当我通过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 技术交流群。

绑定邮箱获取回复消息
由于您还没有绑定你的真实邮箱,如果其他用户或者作者回复了您的评论,将不能在第一时间通知您!
发布评论