인디노트

jscep 본문

인증기술/PKI 기술

jscep

인디개발자 2018. 9. 28. 07:54

원문 : https://github.com/jscep/jscep

jscep-2.0.2.jar

jscep-2.0.2-sources.jar

jscep-2.0.2-javadoc.jar

jscep-master.zip


jscep 빌드 상태 메이븐 센트럴

지원 받기

jscep 라이브러리에 대한 질문이 있으면 이메일을 보내 주시기 바랍니다  jscep-support@googlegroups.com .

클라이언트 구성

클라이언트를 구성하려면 두 가지 객체가 필요합니다.

  • URL
  • 콜백 핸들러

URL 결정

URL은 귀하의 시스템 관리자에게 문의하십시오. Microsoft NDES 의 경우 URL 은 다음과 같이 보입니다.

URL url = new URL("http://[host]/certsrv/mscep_admin/mscep.dll");


EJBCA의 경우 다음과 같이 보입니다.

URL url = new URL("http://[host]/ejbca/publicweb/apply/scep/pkiclient.exe");

HTTP 프록시 사용

jscep 는 SCEP 서버에 액세스 하기 위해 프록시를 직접 사용하는 것을 지원하지 않습니다. 왜냐하면 SCEP 에 실제로 의미가 없기 때문입니다. 다만, 프록시를 사용할 필요가 있는 경우는, 다음과 같이 ProxySelector 가 제공하는 방법을 사용할 수 있습니다  .

ProxySelector.setDefault(new ProxySelector() {
    @Override
    public List<Proxy> select(URI uri) {
      Proxy proxy = new Proxy(Proxy.Type.HTTP, new InetSocketAddress("squid", 3128);
    	return Collections.singletonList(proxy);
    }
    
    @Override
    public void connectFailed(URI uri, SocketAddress sa, IOException ioe) {
    	// Do nothing
    } 
});

HTTPS 사용

jscep 는 HttpURLConnection  을 사용하며 HTTPS 연결이 필요하지 않지만 HTTPS 를 사용하는 SCEP 서버도 완벽하게 지원합니다.

SCEP 서버에서 SSL을 사용하여 연결을 설정해야 하는 경우 setDefaultHostnameVerifier 및 setDefaultSSLSocketFactory 메서드를 사용하여 HttpsURLConnection 을 구성 할 수 있습니다. SSL 서버가 SCEP URL 의 호스트 이름과 일치하지 않는 인증서를 제공하는 경우에만 HostnameVerifier를 지정하면 됩니다.

기본적으로 HttpsURLConnection 은 JSSE 에 지정된 대로 SSLSocketFactory 를 사용 하므로 직접 구성 할 필요는 없습니다. 그러나, 소켓 팩토리를 사용자 정의 하려면 전송을 사용자 정의하여 이 작업을 수행 할 수 있습니다 (아래 참조).

상세한것에 대하여는, JSSE 레퍼런스 가이드 , 특히 커스터마이즈 섹션을  읽어주세요 .

전송 장치 사용자 정의

자신의 전송 구현 (예 : HttpURLConnection 이외의것을 사용)을 제공하려면 TransportFactory 인터페이스를 살펴보십시오. 일단 인스턴스화 되면 Client.setTransportFactory() 를 사용하여 사용자 정의 TransportFactory 를 주입 할 수 있습니다.

UrlConnectionTransportFactory 가 사용하는 기본 SSLSocketFactory 를 사용자 정의 하려는 경우 고유한 사용자 정의 TransportFactory 를 작성하지 않아도 됩니다. 대신 UrlConnectionTransportFactory 를 직접 인스턴스화 하고 사용자 정의 된 SSLSocketFactory 를 다음과 같이 전달하십시오.

SSLSocketFactory factory = new CustomisedSSLSocketFactory();

Client client = new Client(url, handler);
client.setTransportFactory(new UrlConnectionTransportFactory(factory));


Ernst-Georg Schmid 는 HTTP 기본 인증을 사용하는 전송을 만들었습니다. ergo70/jscep-basic-auth 에서 그의 repo 를 찾을 수 있습니다.


콜백 핸들러 만들기

콜백 핸들러는 SCEP 서버에 의해 보내지는 CA 인증서가 정확한 인증서인지 확인하는데 사용됩니다. jscep 를 사용하면 인증서 검증자 중 선택하여 기본 콜백 메커니즘을 사용하거나 자체 콜백 처리기를 제공하도록 선택할 수 있습니다.

기본 콜백 메커니즘

디폴트 콜백 메카니즘은, CertificateVerifier 구현에 검증을 위임하는 DefaultCallbackHandler 를 제공합니다. jscep 는 사전 제공된 인증서 또는 다이제스트 및 대화형 콘솔 확인 프로그램을 포함하여 인증서를 확인하기 위한 몇 가지 전략을 지원합니다. 다음 예제 에서는 콘솔 확인 프로그램을 구성하는데 필요한 단계를 보여줍니다.

CertificateVerifier verifier = new ConsoleCertificateVerifier(); CallbackHandler handler = new DefaultCallbackHandler(verifier);


기본적으로 jscep 는 각각의 작업 전에 확인을 요청합니다. 동일한 SCEP 서버에 대해 여러가지 작업을 수행하는 경우 다음과 같이 인증서 확인 프로그램을 구성하면 사용자 응답을 캐시 할 수 있습니다.

CertificateVerifier consoleVerifier = new ConsoleCertificateVerifier(); CertificateVerifier verifier = new CachingCertificateVerifier(consoleVerifier); CallbackHandler handler = new DefaultCallbackHandler(verifier);


나만의 콜백 핸들러 제공하기

자신의 CallbackHandler 를 사용 하려면 CertificateVerificationCallback 을 처리해야 합니다.


클라이언트 만들기

클라이언트를 만들려면 두 매개 변수를 결합하면됩니다.

Client client = new Client(url, handler);


클라이언트는 스레드로 부터 안전하므로 동일한 CA 를 사용하는 경우 여러 엔티티를 병렬로 등록 할 수 있습니다.

프로필

SCEP 서버가 여러 CA 를 지원하는 경우 CA 관리자는 사용할 발급자를 식별하는 문자열을 제공해야합니다. jscep 가 지원하는 각 작업은 선택적 형태의 프로필 매개 변수를 문자열 형식으로 허용합니다.

jscep 클라이언트는 스레드로부터 안전하므로 응용 프로그램은 새로운 SCEP 클라이언트를 생성할 필요없이 여러 CA 프로필에 대해 작업을 호출 할 수 있습니다.

참고 : Microsoft NDES 에는 항상 프로필이 필요합니다.


요청자 초기화

각 SCEP 메시지 교환 에는 요청자 (특정 엔티티를 PKI 에 등록하는 사람) 와 발급기관 (CA)을 나타내는 SCEP 서버가 있습니다.

대부분의 작업에서 SCEP 서버는 요청자가 서명하고 요청을 암호화 하도록 요구합니다. 차례로 서버는 응답을 서명하고 암호화 합니다. 이를 처리 하려면 양 당사자가 인증서와 키 쌍을 가져야 합니다.

요청자가 CA 에 의해 인증서를 발급받은 경우 요청자는 해당 인증서와 관련 키 쌍을 사용해야 합니다. 마찬가지로 요청자가 현재 CA 가 신뢰하는 다른 CA 에서 인증서를 발급한 경우 요청자가 해당 인증서와 키 쌍을 사용해야 합니다. 그렇지 않은 경우 -이는 대다수의 경우에 해당- 요청자가 자체 서명 인증서를 생성해야합니다.

키 쌍 생성하기

인증서를 생성하기 전에 먼저 키 쌍을 생성해야합니다. SCEP 사양은 RSA 만 지원 하므로 우리는 RSA 를 사용할 것입니다. JCA 에서는 1024 및 2048 비트 키를 지원하는 Java 구현이 필요합니다.

KeyPairGenerator keyPairGenerator = KeyPairGenerator.getInstance("RSA");
keyPairGenerator.initialize(1024);
KeyPair requesterKeyPair = keyPairGenerator.genKeyPair();

자체 서명 된 인증서 생성

키 쌍이 있으면 다음 단계는 X509 인증서를 생성하는 것입니다. JCA 는 프로그래밍 방식으로 인증서를 작성하는 메커니즘을 제공하지 않습니다. 그러나 Bouncy Castle 을 사용하여 JcaX509v1CertificateBuilder 또는 JcaX509v3CertificateBuilder 클래스를 사용하여 수행 할 수 있습니다.

다음 예제는 X509 확장을 지원하기 때문에 JcaX509v3CertificateBuilder 를 사용 합니다. Bouncy Castle 은 org.bouncyca stle.asn1.x509 패키지를 통해 확장 기능 사용을 단순화하는 클래스와 인터페이스를 제공합니다.

확장 기능이 필요하지 않은 경우 JcaX509v1CertificateBuilder 를 사용 할 수 있습니다. JucX509v1CertificateBuilder 는 JCA 호환 생성자에서 JcaX509v3CertificateBuilder 와 동일한 인수를 사용합니다. 두 경우 모두 JcaContentSignerBuilder 를 사용하여 만들 수 있는 ContentSigner 를 제공해야합니다.

SCEP 는 다음과 같은 서명 알고리즘을 지원합니다.

  • MD5withRSA
  • SHA1withRSA
  • SHA256withRSA
  • SHA512withRSA

다음 예제를 사용하여 SCEP 서버가 지원하는 가장 강력한 서명 알고리즘을 찾을 수 있습니다.

Capabilities caps = client.getCaCapabilities();
String sigAlg = caps.getStrongestSignatureAlgorithm();


참고 : 자체 서명 된 인증서를 사용하는 경우 인증서 제목 X500 이름이 인증서 서명 요청의 제목과 같아야합니다.

// Mandatory
X500Principal requesterIssuer = new X500Principal("CN=jscep.org, L=Cardiff, ST=Wales, C=UK");
BigInteger serial = BigInteger.ONE;
Calendar calendar = Calendar.getInstance();
calendar.add(Calendar.DATE, -1); // yesterday
Date notBefore = calendar.getTime();
calendar.add(Calendar.DATE, +2); // tomorrow
Date notAfter = calendar.getTime();
X500Principal requesterSubject = new X500Principal("CN=jscep.org, L=Cardiff, ST=Wales, C=UK"); // doesn't need to be the same as issuer
PublicKey requesterPubKey = requesterKeyPair.getPublic(); // from generated key pair
JcaX509v3CertificateBuilder certBuilder = new JcaX509v3CertificateBuilder(requesterIssuer, serial, notBefore, notAfter, requesterSubject, requesterPubKey);

// Optional extensions
certBuilder.addExtension(X509Extension.keyUsage, false, new KeyUsage(KeyUsage.digitalSignature));

// Signing
PrivateKey requesterPrivKey = requesterKeyPair.getPrivate(); // from generated key pair
JcaContentSignerBuilder certSignerBuilder = new JcaContentSignerBuilder(sigAlg); // from above
ContentSigner certSigner = signerBuilder.build(requesterPrivKey);

X509CertificateHolder certHolder = certBuilder.build(certSigner);


JcaX509CertificateConverter 를 사용하여 JCA 호환 인증서를 추출 할 수 있습니다.

JcaX509CertificateConverter converter = new JcaX509CertificateConverter();
X509Certificate requesterCert = converter.getCertificate(certHolder);


축하합니다! 이제 SCEP 서버에 대한 작업을 호출하는 데 필요한 모든 것이 있습니다.


인증서 등록

인증서 등록은 SCEP 프로토콜을 사용하는 주요한 이유 중 하나 입니다.

인증서 등록

엔터티를 PKI 에 등록 할 때 다음 코드와 같이 엔터티를 나타내는 새 키 쌍을 생성해야합니다. KeyPairGenerator 를 이전 단계에서 재사용 하지 않을 이유는 없지만 여기서는 단순화를 위해 다른 항목을 만듭니다.

KeyPairGenerator keyPairGenerator = KeyPairGenerator.getInstance("RSA");
keyPairGenerator.initialize(1024);
KeyPair entityKeyPair = keyPairGenerator.genKeyPair();


이 키 쌍의 이름을 entityKeyPair 로 지정하여 SCEP 클라이언트를 나타내는데 사용 되는 키 쌍 requesterKeyPair 와 구별 합니다. 키 쌍이 만들어지면 서명 요청을 만들어 CA 에 보내야 합니다. JCA 는 CSR 작성을 지원하지 않으므로 Bouncy Castle 을 다시 사용합니다.

X500Principal entitySubject = requesterSubject; // use the same subject as the self-signed certificate
PublicKey entityPubKey = entityKeyPair.getPublic();
PKCS10CertificationRequestBuilder csrBuilder = new JcaPKCS10CertificationRequestBuilder(entitySubject, entityPubKey); 


이제 PKCS10CertificationRequestBuilder 를 사용하여 특성을 추가 할 수 있습니다. SCEP 서버에 따라 추가 확장 기능을 제공해야 하지만 대부분의 경우 PKCS#9 challengePassword 를 추가합니다.

DERPrintableString password = new DERPrintableString("password");
csrBuilder.addAttribute(PKCSObjectIdentifiers.pkcs_9_at_challengePassword, password);


인증서를 갱신하는 중이면 다음 예제에 따라 빈 암호를 보내야 하지만 SCEP 서버는 요청자 인증서인 requesterCert 에 대해 요청의 유효성을 검사해야 합니다.

DERPrintableString password = new DERPrintableString("");
csrBuilder.addAttribute(PKCSObjectIdentifiers.pkcs_9_at_challengePassword, password);

확장 프로그램

CSR 에 확장을 추가 하려면 extensionRequest OID 를 추가 하십시오. BC는 일반적인 사용 사례를 단순화 하기 위해 ExtensionsGenerator 를 제공합니다.

ExtensionsGenerator extGen = new ExtensionsGenerator();
extGen.addExtension(Extension.extendedKeyUsage, false, new ExtendedKeyUsage(KeyPurposeId.id_kp_eapOverLAN));
csrBuilder.addAttribute(PKCSObjectIdentifiers.pkcs_9_at_extensionRequest, extGen.generate());


경우에 따라 BC 는 특정 개체를 제공하지 않으므로 직접 작성해야 합니다.

ASN1EncodableVector otherName = new ASN1EncodableVector(); 
otherName.add(new DERObjectIdentifier("1.3.6.1.4.1.311.20.2.3")); 
otherName.add(new DERTaggedObject(true, 0, new DERUTF8String( "devuser@dvam.local"))); 
ASN1Object genName = new DERTaggedObject(false, 0, new DERSequence(otherName)); 

ASN1EncodableVector genNames = new ASN1EncodableVector();
genNames.add(genName);

ExtensionsGenerator extGen = new ExtensionsGenerator();
extGen.addExtension(Extension.subjectAlternativeName, true, new DERSequence(genNames));

CSR 서명

속성 추가가 끝나면 엔티티의 개인 키로 CSR 에 서명해야 합니다.

PrivateKey entityPrivKey = entityKeyPair.getPrivate(); JcaContentSignerBuilder csrSignerBuilder = new JcaContentSignerBuilder("SHA1withRSA"); ContentSigner csrSigner = csrSignerBuilder.build(entityPrivKey); PKCS10CertificationRequest csr = csrBuilder.build(csrSigner);


이제 등록해야 할 모든 것이 준비 되었습니다. 애플리케이션의 다음 줄은 일반적으로 CSR 을 SCEP 서버로 보내고 응답을 받는 것입니다.

EnrollmentResponse res = client.enrol(requesterCert, requesterPrivKey, csr);


서버 응답을 이해하는 것은 다음에 해야 할 일을 아는 데 중요합니다.


등록 응답

Client.enrol() 및 Client.poll() 이 반환하는 EnrollmentResponse 는 응용 프로그램에서 검사하여 다음에 수행 할 작업을 결정해야합니다. EnrollmentResponse 에는 응답 상태를 결정 하는데 사용할 수 있는 세 가지 메소드가 있습니다.

  • isSuccess()
  • isPending()
  • isFailure()


isSuccess() 가 true 를 돌려주는 경우, 어플리케이션은 getCertStore() 를 호출해, 등록된 증명서를 취득할 필요가 있습니다. 많은 응용 프로그램의 경우 jscep 클라이언트와 마지막으로 상호 작용합니다.

isFailure() 가 true 를 돌려주는 경우, 어플리케이션은 getFailInfo() 를 호출해 실패의 이유를 확인 할 필요가 있습니다. 응용 프로그램은 이것을 영구 오류로 처리해야 합니다. 불행하게도, SCEP 프로토콜은 실패 이유의 자세한 세부 정보를 제공하지 않으므로 SCEP 실패에 대한 애플리케이션의 탄력성을 유지하는 것이 중요하지 않습니다.

마지막 메서드인 isPending() 은 다른 두 메서드가 false 를 반환하면 true 가 됩니다. 보류중인 응답의 경우 응용 프로그램은 getTransactionId()를 호출하고 Client.poll() 을 호출 할 때 반환된 TransactionId 를 사용해야 합니다 (아래 설명 참조).


응용 프로그램은 서버에 폴링 요청을 보내기 위해 여러 가지 다른 접근법을 사용할 수 있으며 jscep 는 응용 프로그램이 이 상황에 어떻게 접근하려고 하는지 추측하지 않습니다. 그러나 폴링 할 인수로 사용 되는 모든 클래스는 Serializable 을 구현하며 변경 불가능하므로 다른 스레드 및 다른 JVM 에서도 안전하게 사용할 수 있습니다.

요청자 PrivateKey 를 명확하게 전달하지 않도록 응용 프로그램을 강력히 권장합니다. JCA 는 키를 안전하게 저장하기 위한 KeyStore 클래스를 제공하며 다음과 같이 요청자 인증서와 쌍을 저장 할 수 있습니다.

KeyStore store = KeyStore.getInstance("JKS");
store.load(null, null);
store.setKeyEntry("requester", requesterPrivKey, "secret".toCharArray(), requesterCert);

ByteArrayOutputStream bOut = new ByteArrayOutputStream();
store.store(bOut, "secret".toCharArray());


또는 응용 프로그램에서 SealedObject 를 사용하여 직렬화를 단순화 할 수는 있지만 이는 확실히 복잡합니다.


보류중인 등록을 위한 폴링

응용 프로그램이 이전에 보류중인 응답을 받은 경우 응용 프로그램은 SCEP 서버를 폴링 하여 등록의 현재 상태를 확인해야 합니다. poll() 메소드는 enroll() 메소드와 동일한 유형을 리턴 하므로 응용 프로그램은 동일한 단계를 따라 등록의 현재 상태를 판별해야합니다.

EnrollmentResponse res = client.poll(requesterCert, requesterPrivKey, subject, transId);


인증서를 발행하는데 오랜 시간이 걸리는 수동 프로세스가 필요할 수 있으므로 응용 프로그램에서 수많은 폴링 요청을 해야 할 수 있습니다.


비 등록 운영

CRL 액세스

특정 인증서에 대한 CRL 을 검색해야 하는 경우.

X509CRL crl = client.getRevocationList(cert, keyPair.getPrivate(), issuer, serial);

인증서 액세스

이전에 발급 된 인증서에 액세스 해야 하는 경우 인증서의 일련 번호만 전달하면 됩니다.

CertStore store = client.getCertificate(cert, keyPair.getPrivate(), serial);

CA 역량

SCEP 서버의 기능은 내부 jscep 조작, pkcsPkiEnvelope 구조의 키 랩핑에 사용할 암호 판별 및 pkiMessage 구조 서명에 사용할 서명을 광범위 하게 사용합니다.

기본적으로 jscep 는 이 작업을 호출하여 보안 메시지 객체를 구성 할 때 사용할 알고리즘을 결정합니다.

Capabilities capabilities = client.getCaCapabilities();

참고 : AES-192 및 AES-256에는 무제한 정책 JAR 이 필요합니다.

CA 키 롤오버

CertStore store = client.getRolloverCertificate();


참조 :  http://tools.ietf.org/html/draft-nourse-scep-23#appendix-E

RA / CA 인증서 배포

SCEP 서버에서 CA 및 RA 인증서를 수신하는 것은 중요한 작업입니다.

CertStore store = client.getCaCertificate();



로깅

jscep 에서 로깅을 사용하려면 classpath 에 SLF4J 바인딩 (예 : log4j, jcl) 을 제공한 다음 바인딩에 대한 구성을 제공해야 합니다. 예를 들어 jscep 프로젝트는 pom.xml 에서 다음 종속성을 사용하여 빌드 프로세스 중 log4j 를 사용하여 로깅 합니다.

<dependency>
	<groupId>org.slf4j</groupId>
	<artifactId>slf4j-log4j12</artifactId>
	<version>1.7.1</version>
	<scope>test</scope>
</dependency>
<dependency>
	<groupId>log4j</groupId>
	<artifactId>log4j</artifactId>
	<version>1.2.17</version>
	<scope>test</scope>
</dependency>


이 구성 파일을 사용하는 방법은 다음과 같습니다.  

https://github.com/jscep/jscep/blob/master/src/test/resources/log4j.properties


크레딧

Ryan Schipper 와 Danny deSousa 에게 감사의 말을 전합니다.


참고 문헌



반응형
Comments