# @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))

  1. 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 프로토콜로 작동합니다.

results matching ""

    No results matching ""