# @SafeVarargs는 확장이 된다.
제너릭 같은 가변인자 매개변수를 사용할 때 경고를 무시합니다. (자바7 이상)
이 메서드의 가변인자는 타입 안정성이 있다는 것을 알려 컴파일러의 경고 발생을 저지하는 역할을 하는 애너테이션으로 현재 오버라이드 될 수 없는 constructor, static methods, final method 만을 지원합니다. JDK9에서는 subclass에 의해 오버라이드 될 수 없는 private method에 대해서도 이 애너테이션을 사용할 수 있습니다.
javax.net.ssl.SSLContext 인스턴스를 만들고 초기화 한 다음 이를 HttpClient 빌더에 연결된 sslContext 메소드에 인수로 전달해야합니다. (sslContext 메소드는 SSL 컨텍스트가 아닌 TLS 컨텍스트를 구성합니다. 예제: TLS 버전 1.2 (TLSv1.2))
- Bouncy Castle 라이브러리를 사용하여 인증서를 쉽게 로드 할 수 있습니다. SSLSontext 인스턴스가 TLSv1.2에 대해 생성되도록 생성합니다. Bouncy Castle 라이브러리는 개발자가 Java에서 TLS로 작업 할 때 매우 유명합니다. 이를 사용하여 새로운 HTTP 클라이언트와 Bouncy Castle 라이브러리를 결합하여 JShell에서 h2와 작업합니다. 이 기사를 쓰는 과정에서 Bouncy Castle 라이브러리의 최신 버전은 1.57 이었으므로 이 버전 번호가 포함 된 JAR 파일의 이름을 사용합니다.
Bouncy Castle 사이트에서 다음 JAR 파일을 다운로드하여 폴더에 저장하십시오.
■ bcmail-jdk15on-157.jar
■ bcpkix-jdk15on-157.jar
■ bcprov-jdk15on-157.jar
다음 줄은 --addmodules 옵션의 값으로 지정된 jdk.incubator.httpclient 모듈과 --class-path 옵션의 값으로 지정된 이전에 열거 된 JAR 파일로 JShell을 시작합니다. 이렇게하면 JShell은 jdk.incubator.httpclient를 해석하고 Bouncy Castle 라이브러리로 작업 할 수 있도록 지정된 클래스를로드합니다. JAR 파일을 저장 한 경로에서 JShell을 시작해야합니다.
jshell --add-modules=jdk.incubator.httpclient
--classpath=bcpkix-jdk15on-157.jar;bcmail-jdk15on-157.jar;bcprov-jdk15on-157.jar
다음 코드는 모든 import 문을 포함합니다. 또한 JShell을 사용하지 않을 때 코드를 쉽게 실행할 수 있도록 JShell이 기본적으로 실행하는 많은 import 문을 포함합니다. JShell이 자동으로 가져 오는 구문을 가져 오는 구문은 오류를 생성하지 않습니다.
import jdk.incubator.http.*;
import java.util.concurrent.CompletableFuture;
import java.lang.*;
import java.net.URI;
import java.net.URISyntaxException;
import jdk.incubator.http.*;
import java.security.KeyFactory;
import java.security.KeyStore;
import java.security.KeyStoreException;
import java.security.NoSuchAlgorithmException;
import java.security.PrivateKey;
import java.security.SecureRandom;
import java.security.Security;
import java.security.UnrecoverableKeyException;
import java.security.cert.Certificate;
import java.security.cert.CertificateException;
import java.security.cert.CertificateFactory;
import java.security.cert.X509Certificate;
import java.security.spec.InvalidKeySpecException;
import java.security.spec.PKCS8EncodedKeySpec;
import java.io.File;
import java.io.FileInputStream;
import java.io.FileReader;
import java.io.IOException;
import java.io.InputStream;
import javax.net.ssl.KeyManager;
import javax.net.ssl.KeyManagerFactory;
import javax.net.ssl.SSLContext;
import javax.net.ssl.SSLSocketFactory;
import javax.net.ssl.TrustManager;
import javax.net.ssl.TrustManagerFactory;
import org.bouncycastle.jce.provider.BouncyCastleProvider;
import org.bouncycastle.util.io.pem.PemObject;
import org.bouncycastle.util.io.pem.PemReader;
TLSContextHelper 클래스는 TLSv1.2에서 작업 할 수있는 많은 정적 메서드를 선언합니다. 이 코드에서 중요한 메서드는 다음과 같습니다.
■ getKeyFactoryInstance : RSA 알고리즘의 공개 키와 개인 키를 변환하는 java.key.KeyFactory 인스턴스를 리턴합니다.
■ createX509CertificateFromFile : 인증서 파일을 통해 509Certificate 인스턴스를 생성 한 다음 인스턴스를 반환합니다.
■ createPrivateKeyFromPemFile : PEM 형식의 key 파일을 통해 java.Security.PrivateKey의 인스턴스를 생성 한 다음 인스턴스를 반환합니다.
■ createKeyManagerFactory: 인증서 파일, 클라이언트 키 파일 및 클라이언트 키 암호를 사용합니다. createX509CertificateFromFile을 호출하고 PrivateKeyFromPemFile 메소드를 작성합니다. 그 후, 코드는이 메소드에 의해 리턴된 인스턴스를 사용하여 java.net.ssl.KeyManagerFactory 클래스의 인스턴스를 반환합니다.
■ createTrustManagerFactory : Certiicate authority 인증서 파일을 사용하고 createX509Certificate FromFile 메서드를 호출합니다. 이 코드는 X509Certiicate 인스턴스를 사용하고 java.net.ssl.TrustManagerFactory 클래스의 인스턴스를 생성하고 반환합니다.
■ createAndInitTLS12Context : 인증 기관 인증서 파일, 클라이언트 인증서 파일 및 클라이언트 키 파일을 생성하고 원하는 TLS 버전 (TLSv1.2)을 사용하여 SSLContext 인스턴스를 만들고 초기화합니다. 이 코드는 BouncyCastleProvider를 사용하고 이전에 설명한 createKeyManagerFactory 및 createTrustManagerFactory 메소드를 호출합니다.
다음 코드의 certificatesPath 변수는 인증서의 기본 경로를 선언합니다. 이 문자열의 내용을 인증 기관 인증서 파일, 클라이언트 인증서 파일 및 클라이언트 키 파일이 있는 경로로 바꿔야합니다. 처리할 HTTP 요청과 호환 될 파일을 사용해야합니다. Windows 경로 인 C:\JavaMagazine\http2를 예제로 사용합니다. 이 코드는 String.join을 호출하고 java.io.File.separator를 사용하여 앞에서 설명한 경로와 파일 이름을 결합하여 파일 이름을 만듭니다. ca.crt, server.crt 및 server.key를 적절한 파일 이름으로 대체하십시오. 마지막 줄은 HttpClient와 함께 h2와 함께 사용할 SSLContext를 생성하고 초기화합니다.
String certificatesPath = "C:\\JavaMagazine\\http2";
String caCertificateFileName = String.join(java.io.File.separator,
certificatesPath, "ca.crt");
String clientCertificateFileName =
String.join(java.io.File.separator,
certificatesPath,
"server.crt");
String clientKeyFileName =
String.join(java.io.File.separator,
certificatesPath,
"server.key");
SSLContext sslContext =
SecurityHelper.createAndInitSSLContext(
caCertificateFileName,
clientCertificateFileName,
clientKeyFileName);
다음 행은 호출한 후에 많은 메소드 호출을 체인화하여 client라는 HttpClient 인스턴스를 작성합니다. 이 경우 인수로 sslContext라는 이전에 작성된 SSLContext 인스턴스와 함께 sslContext 메소드를 호출하면 TLSv1.2에서 HTTP/2로 작업 할 수 있습니다. 그런 다음 httpClient.Version.HTTP_2를 인자로 사용하는 version 메소드를 호출하면 SSLContext에 대한 호출을 체인 연결했기 때문에 TLSv1.2에서 HTTP/2를 사용해야합니다.
HttpClient client = HttpClient.newBuilder()
.sslContext(sslContext)
.version(HttpClient.Version.HTTP_2)
.followRedirects(HttpClient.Redirect.ALWAYS)
.build();
System.out.println(client.version());
URI uri = new URI("https://your-rest-api-url-for-get-method");
HttpRequest request = HttpRequest
.newBuilder()
.uri(uri)
.GET()
.build();
CompletableFuture<HttpResponse<String>> response =
client.sendAsync(request,
HttpResponse.BodyHandler.asString());
response.whenComplete((HttpResponse<String> response,
Throwable exception) -> {
if (exception == null) {
System.out.println(
String.format("Status code: %d",
response.statusCode()));
System.out.println(String.format(
"Body length: %d",
response.body().length()));
} else {
System.out.println(String.format(
"Something went wrong. %s",
exception.getMessage()));
}
}
);
https://your-rest-api-url-for-get-method를 GET 메소드를 허용하고 TLSv1.2에서 HTTP/2로 응답을 제공하는 REST API URI로 바꿔야합니다. 인증서를 사용 중이므로 잘못된 인증서로 인해 TLS 핸드 셰이크가 실패합니다. 그러나 이 경우 REST API가 TLSv1.2에서 HTTP/2와 호환되지 않으면 HttpClient는 HTTP/1.1 프로토콜로 작동합니다.