HTTPS/SSL을 통한 Java 클라이언트 인증서
6을 .HttpsURLConnection
클라이언트 증명서를 사용하여 리모트서버에 접속합니다.
서버에서 자체 서명된 루트 인증서를 사용하고 있으며 암호로 보호된 클라이언트 인증서를 제공해야 합니다.서버 를 기본 Java 키스토어는 증명서입니다./System/Library/Frameworks/JavaVM.framework/Versions/1.6.0/Home/lib/security/cacerts
(OSX 10.5).keystore 파일의 이름이 클라이언트 증명서가 들어가지 않도록 되어 있는 것 같습니다.
를 이 으로써 악명 높은 루트 증명서가 되었습니다.javax.net.ssl.SSLHandshakeException: sun.security.validator.ValidatorException: PKIX path building failed' problem.
하지만 지금은 클라이언트 증명서를 어떻게 사용하는지 고민하고 있습니다.두 가지 방법을 시도해 봤지만 아무 소용이 없어요.
되는 것은 다음과 같습니다:,, 음, :, 음, :, 음, :, :, :, :, :, 음, 음, 음, 음, 음.
SSLSocketFactory sslsocketfactory = (SSLSocketFactory) SSLSocketFactory.getDefault();
URL url = new URL("https://somehost.dk:3049");
HttpsURLConnection conn = (HttpsURLConnection)url.openConnection();
conn.setSSLSocketFactory(sslsocketfactory);
InputStream inputstream = conn.getInputStream();
// The last line fails, and gives:
// javax.net.ssl.SSLHandshakeException: Received fatal alert: handshake_failure
Https를 건너뛰려고 했습니다.URL Connection 클래스(서버와 HTTP를 통신하고 싶기 때문에 이상적이지 않음)로 대신 다음을 수행합니다.
SSLSocketFactory sslsocketfactory = (SSLSocketFactory) SSLSocketFactory.getDefault();
SSLSocket sslsocket = (SSLSocket) sslsocketfactory.createSocket("somehost.dk", 3049);
InputStream inputstream = sslsocket.getInputStream();
// do anything with the inputstream results in:
// java.net.SocketTimeoutException: Read timed out
고객 증명서가 문제인지도 잘 모르겠습니다.
드디어 해결했다;)여기서 강한 힌트를 얻었다(간달프스의 답변도 약간 건드렸다).누락된 링크는 (대부분) 아래 파라미터의 첫 번째이며, 어느 정도 키스토어와 트러스트스토어의 차이를 간과하고 있었습니다.
자기 서명 서버 증명서를 신뢰 스토어로 Import해야 합니다.
keytool -import -alias gridserver -file gridserver.crt -storepass $PASS -keystore gridserver.키스토어
다음 속성을 설정해야 합니다(명령줄 또는 코드).
-Djavax.net.ssl.keyStoreType=pkcs12
-Djavax.net.ssl.trustStoreType=jks
-Djavax.net.ssl.keyStore=clientcertificate.p12
-Djavax.net.ssl.trustStore=gridserver.keystore
-Djavax.net.debug=ssl # very verbose debug
-Djavax.net.ssl.keyStorePassword=$PASS
-Djavax.net.ssl.trustStorePassword=$PASS
작업 예 코드:
SSLSocketFactory sslsocketfactory = (SSLSocketFactory) SSLSocketFactory.getDefault();
URL url = new URL("https://gridserver:3049/cgi-bin/ls.py");
HttpsURLConnection conn = (HttpsURLConnection)url.openConnection();
conn.setSSLSocketFactory(sslsocketfactory);
InputStream inputstream = conn.getInputStream();
InputStreamReader inputstreamreader = new InputStreamReader(inputstream);
BufferedReader bufferedreader = new BufferedReader(inputstreamreader);
String string = null;
while ((string = bufferedreader.readLine()) != null) {
System.out.println("Received " + string);
}
권장되지 않지만 SSL 증명서 검증을 모두 디세블로 할 수도 있습니다.
import javax.net.ssl.*;
import java.security.SecureRandom;
import java.security.cert.X509Certificate;
public class SSLTool {
public static void disableCertificateValidation() {
// Create a trust manager that does not validate certificate chains
TrustManager[] trustAllCerts = new TrustManager[] {
new X509TrustManager() {
public X509Certificate[] getAcceptedIssuers() {
return new X509Certificate[0];
}
public void checkClientTrusted(X509Certificate[] certs, String authType) {}
public void checkServerTrusted(X509Certificate[] certs, String authType) {}
}};
// Ignore differences between given hostname and certificate hostname
HostnameVerifier hv = new HostnameVerifier() {
public boolean verify(String hostname, SSLSession session) { return true; }
};
// Install the all-trusting trust manager
try {
SSLContext sc = SSLContext.getInstance("SSL");
sc.init(null, trustAllCerts, new SecureRandom());
HttpsURLConnection.setDefaultSSLSocketFactory(sc.getSocketFactory());
HttpsURLConnection.setDefaultHostnameVerifier(hv);
} catch (Exception e) {}
}
}
KeyStore 및/또는 TrustStore 시스템 속성을 설정했습니까?
java -Djavax.net.ssl.keyStore=pathToKeystore -Djavax.net.ssl.keyStorePassword=123456
또는 코드를 사용하여
System.setProperty("javax.net.ssl.keyStore", pathToKeyStore);
javax.net.ssl.trustStore와 동일
Axis 프레임워크를 사용하여 웹 서비스 콜을 처리하는 경우 훨씬 더 간단한 답변이 있습니다.클라이언트가 SSL 웹 서비스를 호출하여 SSL 증명서 오류를 무시할 수 있는 경우 웹 서비스를 호출하기 전에 다음 문구를 입력합니다.
System.setProperty("axis.socketSecureFactory", "org.apache.axis.components.net.SunFakeTrustSocketFactory");
실제 가동 환경에서 매우 나쁜 일이라는 일반적인 면책 사항이 적용됩니다.
Apache Http Components ~HttpClient 4.x 를 사용하면 다음과 같이 동작합니다.
KeyStore keyStore = KeyStore.getInstance("PKCS12");
FileInputStream instream = new FileInputStream(new File("client-p12-keystore.p12"));
try {
keyStore.load(instream, "helloworld".toCharArray());
} finally {
instream.close();
}
// Trust own CA and all self-signed certs
SSLContext sslcontext = SSLContexts.custom()
.loadKeyMaterial(keyStore, "helloworld".toCharArray())
//.loadTrustMaterial(trustStore, new TrustSelfSignedStrategy()) //custom trust store
.build();
// Allow TLSv1 protocol only
SSLConnectionSocketFactory sslsf = new SSLConnectionSocketFactory(
sslcontext,
new String[] { "TLSv1" },
null,
SSLConnectionSocketFactory.ALLOW_ALL_HOSTNAME_VERIFIER); //TODO
CloseableHttpClient httpclient = HttpClients.custom()
.setHostnameVerifier(SSLConnectionSocketFactory.ALLOW_ALL_HOSTNAME_VERIFIER) //TODO
.setSSLSocketFactory(sslsf)
.build();
try {
HttpGet httpget = new HttpGet("https://localhost:8443/secure/index");
System.out.println("executing request" + httpget.getRequestLine());
CloseableHttpResponse response = httpclient.execute(httpget);
try {
HttpEntity entity = response.getEntity();
System.out.println("----------------------------------------");
System.out.println(response.getStatusLine());
if (entity != null) {
System.out.println("Response content length: " + entity.getContentLength());
}
EntityUtils.consume(entity);
} finally {
response.close();
}
} finally {
httpclient.close();
}
P12 파일에는 BouncyCastle로 작성된 클라이언트 인증서와 클라이언트 개인 키가 포함되어 있습니다.
public static byte[] convertPEMToPKCS12(final String keyFile, final String cerFile,
final String password)
throws IOException, CertificateException, KeyStoreException, NoSuchAlgorithmException,
NoSuchProviderException
{
// Get the private key
FileReader reader = new FileReader(keyFile);
PEMParser pem = new PEMParser(reader);
PEMKeyPair pemKeyPair = ((PEMKeyPair)pem.readObject());
JcaPEMKeyConverter jcaPEMKeyConverter = new JcaPEMKeyConverter().setProvider("BC");
KeyPair keyPair = jcaPEMKeyConverter.getKeyPair(pemKeyPair);
PrivateKey key = keyPair.getPrivate();
pem.close();
reader.close();
// Get the certificate
reader = new FileReader(cerFile);
pem = new PEMParser(reader);
X509CertificateHolder certHolder = (X509CertificateHolder) pem.readObject();
java.security.cert.Certificate x509Certificate =
new JcaX509CertificateConverter().setProvider("BC")
.getCertificate(certHolder);
pem.close();
reader.close();
// Put them into a PKCS12 keystore and write it to a byte[]
ByteArrayOutputStream bos = new ByteArrayOutputStream();
KeyStore ks = KeyStore.getInstance("PKCS12", "BC");
ks.load(null);
ks.setKeyEntry("key-alias", (Key) key, password.toCharArray(),
new java.security.cert.Certificate[]{x509Certificate});
ks.store(bos, password.toCharArray());
bos.close();
return bos.toByteArray();
}
현재 프로젝트에서는 Apache Commons HTTP Client 패키지를 사용하고 있으며 SSL과 자기 서명 증명서를 사용하여 정상적으로 동작합니다(당신이 말한 바와 같이 cacerts에 설치한 후).여기를 봐주세요.
http://hc.apache.org/httpclient-3.x/tutorial.html
http://hc.apache.org/httpclient-3.x/sslguide.html
서버 증명서에 문제가 있다고 생각합니다만, 유효한 증명서가 아닙니다(이 경우는 「handshake_failure」라고 하는 것은, 다음과 같습니다).
서버 증명서를 클라이언트의 JRE의 trustcerts keystore로 Import합니다.이것은 키툴로 간단하게 할 수 있습니다.
keytool
-import
-alias <provide_an_alias>
-file <certificate_file>
-keystore <your_path_to_jre>/lib/security/cacerts
아래 코드 사용
-Djavax.net.ssl.keyStoreType=pkcs12
또는
System.setProperty("javax.net.ssl.keyStore", pathToKeyStore);
는 전혀 필요 없습니다.또, 독자적인 커스텀 SSL 팩토리를 작성할 필요도 없습니다.
저도 같은 문제가 발생했는데, 제 경우 완전한 증명서 체인이 트러스트 스토어에 Import되지 않는 문제가 있었습니다.루트 증명서를 사용하여 증명서를 Import합니다.또한 메모장에서 cacerts 파일을 열어 증명서 체인 전체를 Import했는지 여부를 확인할 수 있습니다.증명서를 Import 할 때 지정한 에일리어스 이름과 대조하여 증명서를 열고 몇 개의 증명서가 포함되어 있는지 확인합니다.cacerts 파일에 같은 수의 증명서가 있어야 합니다.
또한 cacerts 파일은 어플리케이션을 실행하고 있는 서버에서 설정해야 합니다.두 서버는 공개/비공개 키로 서로를 인증합니다.
비록 이 질문은 12년 이상 된 질문이고 좋은 답변들이 많이 있지만 나는 대안을 제시하고 싶다.다음은 keystore와 truststore를 로드하여 sslsocketfactory 또는 sslcontext를 가져오는 간단한 예입니다.
SSLFactory sslFactory = SSLFactory.builder()
.withIdentityMaterial("clientcertificate.p12", "password".toCharArray(), "PKCS12")
.withTrustMaterial("gridserver.keystore", "password".toCharArray(), "PKCS12")
.build();
SSLSocketFactory sslSocketFactory = sslFactory.getSslSocketFactory();
SSLContext sslContext = sslFactory.getSslContext();
이 코드 스니펫의 예는 라이브러리에서 가져온 것입니다.GitHub - SSLContext Kickstart 다음 스니펫을 사용하여 추가할 수 있습니다.
<dependency>
<groupId>io.github.hakky54</groupId>
<artifactId>sslcontext-kickstart</artifactId>
<version>7.0.2</version>
</dependency>
언급URL : https://stackoverflow.com/questions/875467/java-client-certificates-over-https-ssl
'programing' 카테고리의 다른 글
PHP의 cURL을 사용하여 리다이렉트할 곳을 찾으려면 어떻게 해야 하나요? (0) | 2022.10.01 |
---|---|
MySQL 5.7을 새로운 MySQL 8.0으로 업데이트하려면 어떻게 해야 합니까? (0) | 2022.10.01 |
MySQL의 JSON 어레이를 행으로 변환 (0) | 2022.10.01 |
Join을 사용한 JPA 업데이트 (0) | 2022.10.01 |
함수에서의 디폴트 인수 사용 (0) | 2022.10.01 |