앞에서 확인했듯이 SSL 통신은 Client 에서 Server 에 대한 검증을 수행한다.

하지만 Server 측에서의 Client에 대한 검증과정은 별도 존재하지 않는다. 

 

일반적인 웹서비스에는 해당되지 않으나, Server 측에서 제한된 Client 만 

접속을 허용하기 위해서 Client 검증을 해야 한다면 MTLS (Mutual TLS) 를 사용하는 것이다. 

즉, Client 에서 접속하는 Server 에 대한 검증과 Server 에서 Client 에 대한 검증을

서로 하는 것이다. 

 

아래 통신과정을 살펴보자

MTLS 통신과정

SSL 통신과정과 유사하나 몇개 추가된 부분이 보인다.

 

Certificate Request

Client 에게 인증서를 제공해달라고 요청하는 역활이다. 

이 메세지를 받은 Client 는 Server 에 인증서를 제공해야 한다. 해당 메세지는 ㅗ뭉노맏 ㅖ개새채ㅣdp 

속하는 메세지이며 두 가지 필드가 있다. 

1. 서버가 인식 할 수 있는 인증서 타입 리스트

2. 서버가 인식 할 수 있는 DN 타입 리스트

 

Client certificate

Server가 Client에게 보낸것과 동일하며 여기에는 Client 인증서와 인증서 체인에 속한 모든 인증서가

들어 있다. 

 

Certificate verify

Client가 Server에 자신이 보낸 인증서의 주인이 정말 Client 라는것을 입증하는 메세지이다. 

ClientVerify 메세지는 Handshake Protocol 에 속하는 메세지로 이 안에는 서명값이 들어 있다. 

서명값은 클라이언트가 서버에게 보낸 모든 메세지들을 압축한 Hash 값을 Client 개인키로

암호화 하여 생성한 값이다. 

 

이 서명값은 서버에게 클라이언트의 인증서 안에 기입된 공개키와 쌍이되는 개인키를 클라이언트가 

가지고 있다는것을 증명한다. 

즉, 공개키와 쌍이되는 개인키를 가지고 있다는 것은 클라이언트가 그 공개키가 있는 인증서의 

주인임을 증명하는 것이다. 

'Development > Security (보안)' 카테고리의 다른 글

SSL 통신원리  (0) 2020.10.14
SSL 이란?  (0) 2020.09.22
X.509 인증서  (0) 2020.09.18
JAVA 사설인증서 MTLS 통신 ( Server )  (0) 2020.09.16
Java 사설인증서 MTLS 통신 ( Client )  (0) 2020.09.15

SSL 통신은 암호화 통신이다. 

즉, Clinet 와 Server 간의 암호화 알고리즘, 암호화 키, 신뢰할 수 있는 인증서 등이

먼저 확인되어야 하며, 그렇기 위해 Handshake 과정을 거친다. 

아래는 HandShake 순서이다. 

 

[ HandShake 순서 ]

그림에서 다 표현하지 못한 부가적인 설정이다. 

( 그럼에 표기된 번호를 기준으로 설명한다. )

 

2번 단계 : Client 에서 전달된 암호화 알고리즘 리스트중 서버가 사용할 수 있는게 1개 이상이라면, 

              알고리즘 리스트 중 가장 보안이 높은 알고리즘을 선택한다. 

4번 단계 : 선택된 알고리즘에 따라 서버에서 공개키는 달라지며 그림에서 오타인데 '교환' 이 아니라 '전달' 이다. 

9번 단계 : Server 에서는 개인키로 Client 가 보낸 'Pre Master Secret' 데이터를 복호화 하여 sessionKey를 획득할

              수 있다. 

이하 설명은 생략한다. ( 그림을 보면 쉽게 이해가 됨 )

 

위 HandShake 가 완료 되면 Server 와 Client 는 동일한 sessionKey 를 가지고 있다.

 

이후 SSL 통신 시 데이터 암/복호화는 Server 과 Client 가 가지고 있는 sessionKey 를 사용한다. ( 대칭키 사용 )

 

'Development > Security (보안)' 카테고리의 다른 글

MTLS 란 무엇인가? 통신과정은 ?  (0) 2020.10.27
SSL 이란?  (0) 2020.09.22
X.509 인증서  (0) 2020.09.18
JAVA 사설인증서 MTLS 통신 ( Server )  (0) 2020.09.16
Java 사설인증서 MTLS 통신 ( Client )  (0) 2020.09.15

아래 내용 참고 블로그 : m.blog.naver.com/nttkak/20130246203

 

SSL : Secure Socket Layer

TLS : Transport Layer Security 

 

SSL 과 TLS 는 거의 비슷한 기능을 가지고 있고, 몇가지 차이점이 있다.

 

아래는 OSI 7개층 표이다. 

7 Application Layer TELNET / FTP / SMTP / DNS / HTTP
6 Presentation Layer  
5 Session Layer  
4 Transport Layer TCP / UDP
3 Network Layer IP
2 Data Link Layer ARPANET / SATNET / PACKET RADIO / LAN
1 Physical Layer

OSI 7계층은 모두 독립적으로 존재 하며 독립적으로 운영된다. 각 계층의 상호간의 인터페이스만

맞는다면 각 계층이 어떻게 구현 되었던 상관없이 동작하게 된다.

 

SSL 프로토콜은 OSI 어느 한 계층에서 계속 동작하는것이 아닌 독립적인 프로토콜 계층을 

만들어서 동작하게 된다. SSL 프로토콜은 Application Layer 과 TCP 계층 사이에 위치한다

 

HTTP →   SSL  TCP IP

 

Application Layer는 TCP 계층 바로 위에 있는 계층으로 Application, 즉 사용자 프로그램이 

사용하는 계층이다. 

Application Layer 에는 웹이 사용하는 HTTP 프로토콜, FTP 프로토콜 등등 많은 프로토콜이 존재한다.

HTTP / NNTP / FTP / ETC...

                

               SSL 

                

               TCP

                

                IP

이 Application Layer 에 해당되는 프로토콜들은 바로 아래의 TCP 계층을 사용해서 데이터를 외부

네트워크에 전달하고 받는다. 

 

SSL 은 Application Layer 과 TCP Layer 사이에 들어가서 동작 하므로 SSL이 적용 되었을 경우에

Application Layer 층의 프로토콜 들은 외부로 보내는 데이터를 TCP 층이 아닌 SSL 층에 보내게 된다. 

SSL 은 받은 데이터를 암호화 하여 아래의 TCP 계증에 보내고 외부 네트워크로 보낸다. 

그리고 TCP 층으로부터 받은 데이터는 복호화 하여 윗 계층인 Application Layer 의 프로토콜에 전달하는 것이다.

 

SSL 계층이 Application Layer 과 TCP Layer 사이에 끼이더라도 Application Layer 의 프로토콜은 TCP 

프로토콜과 데이터를 주고 받던 방식 그대로 사용한다. 그리고 TCP 프로토콜 역시 Application Layer

와 데이터를 주고 받던 방식 그대로를 사용한다. 

 

결국 SSL층이 적용된다고 해도 원래의 Application Layer 과 TCP Layer 의 프로토콜은 수정하지 않아도 된다.

Application Layer 의 프로토콜은 SSL 을 TCP Layer로 인식하고, TCP Layer 은 SSL을 Application Layer 로 인식하기 때문이다. 

 

[ SSL 프로토콜 ]

 

위에서 언급되었던 프로토콜이란 무엇일까?

- 프로토콜은 간단히 얘기하면 규약이다.

통신규약 즉 통신 프로토콜으 수신자와 송신자가 주고 받는 메세지는 어떤 형식이고,

어떻게 해석하며, 어떻게 주고 받을 지에 대한어떤방식으로 약속인것이다. 

 

SSL은 4가지 프로토콜로 구성되어 있다. 

1. ChangeCipherSpec

2. Alert

3. Handshake

4. Record         

 

* ChangeCipherSpec

: SSL 통신을 하는 송/수신 양쪽에 암호화 통신을 할 때 사용할 암호화 알고리즘 즉, 비밀키 알고리즘과

  메세지 다이제스트 알고리즘을 정하자는 신호

 

* Alert

: SSL 통신을 하는 송/수진 양쪽에 Alert 신호를 알리기 위해 사용. 이 신호는 SSL 통신을 하는 양 쪽

 또는 어는 한쪽에서 에러나 비정상적인 상황이 발생 했을 때 이를 상대방에게 알리기 위한 신호.

                            

* Handshake

: SSL 통신의 대부분의 메세지이다.

 Handshake Protocol의 종류는 다음과 같다. 

필드 값 Handshake 프로토콜 타입
0 HelloRequest
1 ClientHello
2 ServerHello
11 Certificate
12 ServerKeyExchange
13 CertificateRequest
14 ServerHelloDone
15 CertificateVerify
16 ClientKeyExchange
20 Finished

'Development > Security (보안)' 카테고리의 다른 글

MTLS 란 무엇인가? 통신과정은 ?  (0) 2020.10.27
SSL 통신원리  (0) 2020.10.14
X.509 인증서  (0) 2020.09.18
JAVA 사설인증서 MTLS 통신 ( Server )  (0) 2020.09.16
Java 사설인증서 MTLS 통신 ( Client )  (0) 2020.09.15

출처 : m.blog.naver.com/nttkak/20130245553 사이트에서 참고하였습니다. 

 

CA가 인증서를 발급 할 때, 인증서에 들어가는 항목의 종류와 항목의 값들을 CA 나름대로 기입 한다면, 인증서를 사용하는 사용자마다 다른 형식의 인증서를 가지고 있을 것이고, 인증서의 내용을 이해하는데 문제가 생긴다. 

 

따라서 인증서를 작성 함에 있어서 표준이 필요하다. 

현재 가장 널리 사용되고 있는 공개키 인증서의 표준은 X.509 형식이다. 

X.509 라는 것은 표준번호이며, 그래서 이 형식 대로 작성된 인증서를 X.509 라고 부르는 것이다. 

 

[ X.509 인증서 항목 ]

 

X.509 인증서는 현재 (2020 년 9월 기준) 버전 3까지 발표된 상태이다. 

버전 3 인증서와 이전의 X.509 인증서와 다른 점은 버전 3에는 Extension 이라는 섹션이 추가되었다. 

 

이 Extension 섹션은 필수 항목이 아닌 추가적인 사항을 나타내는 항목들이다. 

다음은 X.509 형식의 필수/추가 항목이다.  ( Extension 은 중요한 것만 설명됨 )

항목명 필수/옵션 설명
필수 정보
Version 필수 인증서의 버전으로 V3 가 들어간다.
SerialNumber 필수 인증서 고유의 일련번호
Signature 필수 발급자의 서명
Issuer 필수 발급자의 정보. DN( Distinguished Name) 형식
Validity 필수 인증서의 유효기간. 시작 날자와 종료날자
Subject 필수 주체의 정보. DN( Distinguished Name ) 형식
SubjectPublicKeyInfo 필수 주체의 공개키
항목명 필수/옵션 설명
Extension
SubjectAltName 필수/옵션 주체의 다른 이름을 나타냄.
DN 형식이 아니라 여러 종류의 값이 들어갈 수 있다. 주로 주체의 도메인 네임이 들어간다
PolicyMappings 옵션 정책 정보를 다른 정책들과 연결 할 수 있는 정보를 제공
NameConstraints 옵션  
PolicyConstraints 옵션 인증서 경로의 제약 사항을 정함
IssuerAltName 옵션 발급자의 다른 이름. DN 형식이 다른 값들이 들어갈 수 있다. 주로 발급자의 도메인 네임이 들어감
AuthorityKeyIdentifier 옵션 발급자의 키를 나타낼 수 있는 키의 이름
SubjectKeyIdentifier 옵션 주체의 키를 나타낼 수 있는 키 이름
BasicConstraints 필수/옵션 제약 사항. 주로 이 인증서가 다른 인증서를 발급 할 수 있는 권한이 있는지 없는지를 나타낸다. 
CRLDistributionPoints 옵션 이 인증서의 CRL을 얻을 수 있는 곳을 정한다.
KeyUsage 옵션 인증서에 기입된 공개키가 사용되는 보안 서비스 종류를 결정.
보안 서비스의 종류는 서명, 부인방지, 전자 서명, 키교환 등이 있다. 

위 표에서 언급된 DN의 항목들과 설명은 다음 표와 같다

항목 설명 DN 항목 Example
CountryName 국가코드 2자리 C KR
StateOrProvinceName 주 이름.
(우리나라 경우 도 / 시 )
ST Kyunggi
LocalityName 시 이름 또는 구 이름 L Pangyo
OrganizationName 소속 기관명 O Naver
OrganizationalUnitName 소속 부서명 OU Dev
CommonName 주체를 나타내는 이름 CN test.naver.com
EmailAddress 이메일 emalAddress test@naver.com

 

[ X.509 인증서가 저장되는 여러 가지 형식들 ]

 

X.509 표준의 인증서 형식은 ASN.1 ( Abstract Syntax Notation One ) 이라는 명명규칙을 따름

ASN1. 은 C 언어 같은 프로그래밍 언어와 비슷하다. 

많은 변수 형식이 있고, 많은 구조체 데이터 저장 형식이 있다. 인증서의 각 항목은 OID 라는 일련

번호로 나타낼 수 있으며 항목의 데이터는 ASN.1 타입의 데이터로 표현된다. 

 

ASN.1 형식은 다음 기회에 설명하며, X.509 인증서의 각 항목의 내용은 ASN.1 의 형식에 맞게 

저장된다는 것만 알아두자.

 

ASN.1 형식으로 되어 있는 X.509 인증서의 내용을 파일로 저장 할 때 주로 두가지 종류의 형식으로 

저장한다. 

첫 번째는 X.509 인증서 내용을 Base64 encoding 으로 인코딩 하여 저장하는 것이다. 

두 번째는 X.509 인증서 내용을 binary 형태로 저장하는 것이다. 

 

Base64 encoding 형식은 PEM 이고, binary 형식은 DER 이다. 

 

 

'Development > Security (보안)' 카테고리의 다른 글

MTLS 란 무엇인가? 통신과정은 ?  (0) 2020.10.27
SSL 통신원리  (0) 2020.10.14
SSL 이란?  (0) 2020.09.22
JAVA 사설인증서 MTLS 통신 ( Server )  (0) 2020.09.16
Java 사설인증서 MTLS 통신 ( Client )  (0) 2020.09.15

Client 에서 서버로 통신 시 사설인증서 + MTLS (상호 인증서 검증) 을 위한 부분을 알아보았다.

웹서버 (Ex: Apache ) 내에서 MTLS 를 위한 설정을 할 수 있다. (일반적으로 그렇게 처리한다.)

 

이번에 작성할 내용은 Application 레벨 (WAS)에서 인증서 검증을 하는 방법이다.

 

만약 웹서비스를 개발/유지보수 하면서 WAS 레벨에서 인증서 검증을 한다면 참고하는게 좋을듯 하다.

단, 웹서비스 ( WEB & WAS ) 를 이용한다면, 

'Development > Security (보안)' 카테고리의 다른 글

MTLS 란 무엇인가? 통신과정은 ?  (0) 2020.10.27
SSL 통신원리  (0) 2020.10.14
SSL 이란?  (0) 2020.09.22
X.509 인증서  (0) 2020.09.18
Java 사설인증서 MTLS 통신 ( Client )  (0) 2020.09.15

[ 사설인증서를 이용하여 상호 인증서 검증 통신 하는 방법 ]

 

1. Java KeyStore 에 사설인증서 RootCA를 저장한다. 

2. Java 에서 KeyStore & TrustManager 객체를 생성 후 소스 레벨에서 등록한다. 

 

지금 작업할 내용은 Java 소스 레벨에서 사설인증서를 이용한 상호인증서 검증 (이하 MTLS) 통신 소스 이다. 

 

[ 참고 자료 : Openssl 로 사설인증서 만들기. ]

 

* 준비물

-- RSA & SHA256 사설인증서 생성.

1. 사설인증서 RootCA ( 테스트로 생성 : TestRootCA.pem )

2. 사설인증서 CertCA  ( 테스트로 생성한 Client 인증서 : TestClientCert.pem )

3. 사설인증서 CertCA 의 개인키 ( TestClientCert.pem 파일 생성을 위하 만든 TestClientPrikey.der )

(* 참고: Java 에서는 .pem 형식의 key 는 인식하지 못한다. 때문에 .der 형식의 개인키를 생성 )

 

자 이제 개발 작업을 시작하자.

Java HttpsUrlConnection 통신 시 사설인증서를 적용하기 위해 다음과 같은 객체가 필요하다.

1. javax.net.ssl.KeyManager : 용도~~

2. javax.net.ssl.TrustManager : 용도 ~~

3. javax.net.ssl.HostnameVerifier : 용도 ~~

 

 

[ 인증서 파일을 Certificate 객체로 리턴 함수 ]

- loadCertificate( .... )

/*****************************
[ Title : 인증서 파일을 읽어 Certificate 객체 리턴 ]
- certPath : 생성된 TestClientCert.pem 파일 경로
*****************************/
public Certificate loadCertificate( String certPath )
{
    Collection<? extends Certificate> collCert = null;
    FileInputStream                        fisCert   = null;
    Certificate                               rtnCert   = null;

    try
    {
        fisCert = new FileInputStream( certPath );

        collCert = Certificate.getInstance( "X509" ).generateCertificates( fisCert );

        rtnCert = collCert.iterator().next();
    }
    catch( Exception e)
    {
        e.printStackTrace();
    }
    finally
    {
        if( fisCert != null ) try { fisCert.close(); } catch ( IOException ex ) {}
    }
    
    return rtnCert;
}

 

[ PrivateKey 객체 추출 (Key 파일은 decrypt Key 이어야함) ]

- loadDecryptPrivateKey( .... )

/*****************************
[ Title : 사설인증서 키 TestClientPrikey.der 의 PrivateKey 추출 ]
- certKeyPath : 생성된 TestClientPrikey.der 파일 경로 ( Decrypt Key 이어야 함 )
*****************************/
public PrivateKey loadDecryptPrivateKey( String certKeyPath )
{
    PrivateKey priKey = null;

    FileInputStream fisKey = null;
    DataInputStream disKey = null;
    ByteArrayInputStream bisKey = null;

    try
    {
        fisKey = new FileInputStream( certKeyPath );
        disKey = new DataInputStream( fisKey );
        byte[] btKeyData = new byte[ disKey.available() ];

        disKey.readFully( btKeyData );
        disKey.close();

        bisKey = new ByteArrayInputStream( btKeydata );
        byte[] baKey = new byte[ bisKey.available() ];

        bisKey.read( baKey, 0, bisKey.available() );
        bisKey.close();

        PKCS8EncodedKeySpec pkcs8Spec = new PKCS8EncodedKeySpec( baKey );
        KeyFactory keyFac = KeyFactory.getInstance("RSA");

        priKey = keyFac.generatePrivate( pkcs8Spec);        
    }
    catch( Exception e )
    {
        e.printStackTrace();
        return null;
    }
    finally
    {
        if( bisKey != null ) try{ bisKey.close(); } catch( Exception e) {}
        if( disKey != null ) try{ disKey.close(); } catch( Exception e) {}
        if( fisKey != null ) try{ fisKey.close(); } catch( Exception e) {}
    }

    return priKey;
}

 

[ KeyManager 을 리턴하는 함수 ]

- getKeyManager( ..... )


/*****************************
[ Title : 사설인증서를 KeyManager 에 등록 ]
- certPath: 생성된 TestClientCert.pem 파일 경로
- certKeyPath : 생성된 TestClientPrikey.der 파일 경로
- certPasswd  TestClientPrikey.der 의 비밀번호 (ex: 1234 )
*****************************/
public KeyManager[] getKeyManager( String certPath, String certKeyPath , String certPasswd )
{
    KeyManager[] keyMgr = null;

    try
    {
        KeyStore ks = KeyStore.getInstance( "JKS" );

        ks.load( null );

        // 위에서 생성했던 loadCertificate 함수
        Certificate clientCert= loadCertificate( certPath );

       // 개인키 파일에서 PrivateKey 객체 추출
        PrivateKey keyPri = loadDecryptPrivateKey( certKeyPath, certPasswd );

        ks.setCertificateEntry( "ClientCERT", clientCert );
        ks.setKeyEntry( "ClientCertKey", keyPri, certPasswd.toCharArray(), new Certificate[]{clientCert} );

        KeyManagerFactory kmFactory = KeyManagerFactory.getInstance( "SunX509" );
        kmFactory.init( ks, certPasswd.toCharArray() );

        keyMgr = kmFactory.getKeyManagers();
    }
    catch( KeyStoreException e)
    {
        e.printStackTrace();
    }
    catch( NoSuchAlgorithmException e)
    {
        e.printStackTrace();
    }
    catch( CertificationException e)
    {
        e.printStackTrace();
    }
    catch( IOException e)
    {
        e.printStackTrace();
    }
    catch( UnrecoverableKeyException e)
    {
        e.printStackTrace();
    }

    return keyManager;
}

 

[ TrustManager 리턴 함수 ]

- getTrustManager( .... )

/*****************************
[ Title : 사설인증서 RootCA를 TrustManager 에 등록 ]
- rootCaPath : 생성된 TestRootCA.pem.pem 파일 경로
*****************************/
public TrustManager[] getTrustManager ( String rootCaPath )
{
    TrustManager[] trustMgr = null;

    try
    {
        KeyStore ksJKS = KeyStore.getInstance("JKS");

        Certificate certCA = loadCertificate( rootCaPath );

        KeyStore ksTrust = KeyStore.getInstance ( KeyStore.getDefaultType() );

        // changeit : java keystore 의 Default password
        ksTrust.load( new FileInputStream( System.getProperty("java.home") + "/lib/security/cacerts"), "changeit".toCharArray();

        ksJKS.setCertificateEntry( "TestRootCA" certCA );
        ksTrust.setCertificateEntry( "TestRootCA", certCA );

        TrustManagerFactory trustFactory = TrustManagerFactory.getInstance( TrustManagerFactory.getDefaultAlgorithm());

        trustFactory.init( ksTrust );
 
        trustMgr = trustFactory.getTrustManagers();

    }
    catch (KeyStoreException e)
    {
        e.printStackTrace();
    }
    catch (NoSuchAlgorithmException e)
    {
        e.printStackTrace();
    }
    catch (CertificateException e)
    {
        e.printStackTrace();
    }
    catch (IOException e)
    {
        e.printStackTrace();
    }

    return trustMgr;
}

 

위와 같이 사설인증서를 이용한 상호 검증 TLS 통신 ( 이하 MTLS ) 를 위한 통신함수를 만든다.

[ MTLS 통신 함수 ]

- mtlsConnTest( .... )

/*****************************
[ Title : MTLS 연동 테스트 ]
- targetURL: MTLS 통신 대상 도메인
- sendData : 전달할 데이터

*****************************/
public String mtlsConnTest( String targetURL, String sendData )
{
    String rootCaPath = "...."; // 사설인증서 ROOT CA 파일 경로.
    String certCaPath  = "...."; // 사설인증서 Client 인증서 파일 경로. 
    String certCaPriKeyPath = "..."; // 사설인증서 Client 인증서 개인키 파일 경로. 
    String certCaPriKeyPasswd = "...."; // 사설인증서 Client 인증서 개인키 암호

    // KeyManager 생성
    KeyManager[] keyMgr = getKeyManager( certCaPath, certCaPriKeyPath, certCaPriKeyPasswd );

    // TrustManager 생성
    TrustManager[] trustMgr = getTrustManager( rootCaPath );

    // Read data 변수 선언
    OutputStreamWriter osw = null;
    OutputStream os = null;

    // Connection 객체 선언
    URL url = null;
    HttpsURLConnection conn = null;

    // Return 변수 선언
    StringBuilder sb = new StringBuilder();

    try
    {
        // TLS 1.2 통신만 허용
        SSLContext sslCtx = SSLContext.getInstance( "TLSv1.2" );
        sslCtx.init( keyMgr, trustMgr, null );

        HostnameVerifier hostVerifier = new HostnameVerifier()
        {
            @Override
            public boolean verify( String hostname, SSLSession session)
            {
                try
                {
                    // ROOT CA 검증
                    X509Certificate cert = (X509Certificate) session.getPeerCertificates()[0];
                   
                    // 인증서 유효기간 검증
                    cert.checkValidity();                    

                   /***************************************
                   - 인증서 전체 내용확인 ( RootCA / Chain (branch) / Cert (leaf)
                   int idx = 0;

                   for( Certificate tmpCert : session.getPeerCertificates();
                   {
                       X509Certificate xCert = (X509Certificate) tmpCert;

                       // SubjectAlternativeName 확인 ( 인증서 마다 값이 없을 수 있음 )
                       Collection<List<?>> colSubAltName = xCert.getSubjectAlternativeNames();

                       if( colSubAltName != null )
                       {
                           for( List<?> tmp : colSubAltName )
                           {
                               System.out.println( "[" + idx + "] colSubAltName : " + tmp );
                           }
                       }
                       
                       // SubjectDN-CN ( 인증서 DN 값에 따라 정규식 변경 필요 )
                       Pattern pattern = Pattern.compile( "(?<=CN=)([^,]+)" );
                       Matcher matcher = pattern.matcher( xCert.getSubjectDN().toString() );

                       if( matcher.find() ) System.out.println( "[" + idx + "] matcher.group(1) : " + matcher.group(1) );

                       // 인증서 Subject, Issuer 정보 확인
                      System.out.println( "[" + idx + "] SubjectND : " + xCert.getSubjectDN() ); // 소유자 정보
                      System.out.println( "[" + idx + "] IssuerDN : " + xCert.getIssuerDN() );     // 발행자 정보
                      System.out.println( "[" + idx + "] SigAlgName : " + xCert.SigAlgName() ); // 서명 알고리즘
                      System.out.println( "[" + idx + "] SerialNum : " + xCert.getSerialNumber() );
                      System.out.println( "[" + idx + "] SubX500Principal : " + xCert.SubjectX500Principal.getName() );
                      System.out.println( "[" + idx + "] Type : " + xCert.getType() );

                   }


                    ***************************************/
                }
                catch (SSLPeerUnverifiedException e)
                {
                    e.printStackTrace();
                    return false;
                }
                catch (CertificateExpiredException e)
                {
                    e.printStackTrace();
                    return false;

                }
                catch (CertificateNotYetValidException e)
                {
                    e.printStackTrace();
                    return false;

                }

                return true;                
            }
        };    // HostnameVerifier hostVerifier

        HttpsURLConnection.setDefaultSSLSocketFactory( sslCtx.getSocketFactory() );
        HttpsURLConnection.setDefaultHostnameVerifier( hostVerifier );

        url = new URL( targetURL )
        conn = (HttpsURLConnection) url.openConnection();

        // Set header
        conn.setRequestMethod( "POST" );
        conn.setRequestProperty( "Accept-Charset", "UTF-8" );
        conn.setRequestProperty( "Content-Type", "application/json" );
        conn.setConnectionTimeout( 2000 );
        conn.setReadTimeout ( 5000 );
        conn.setUseCaches( false );
        conn.setDoInput( true );
        conn.setDoOutput ( true );

        osw = new OutputStreamWriter( conn.getOutputStream(), "UTF-8" );
        osw.write( sendData );
        osw.flush();
        osw.close();

        BufferedReader br = null;
        String line = "";
        
        if( conn.getResponseCode() == 200 )
        {
            br = new BufferedReader( new InputStreamReader( conn.getInputStream(), "UTF-8") );

            while( (line = br.readLine()) != null )
            {
                if( !line.trim().equals( "" ) sb.append( line );
            }

            br.close();
        }

    }
    catch( NoSuchAlgorithmException e)
    {
        e.printStackTrace();
    }
    catch( KeyManagementException e)
    {
        e.printStackTrace();
    }
    catch( MalformedURLException e)
    {
        e.printStackTrace();
    }

    catch( IOException e)
    {
        e.printStackTrace();
    }
    finally
    {
        conn.disconnect();
    }

    sb.toString();
}

위 처럼 연결하면 된다.

 

* 추가적으로 PrivateKey 값이 Encrytpt 되어있을 경우, PrivateKey 값을 얻어오는 함수이다. 

  위 예재는 .der ( Encrypt 되어 있지 않는 Key ) 이다. 

[ PrivateKey 객체 추출 Encrypt PrivateKey file ]

- loadEncryptPrivateKey( .... )

/*****************************
[ Title : 사설인증서 키 TestClientPrikey.der 의 PrivateKey 추출 ]
- certKeyPath : 생성된 TestClientPrikey.der 파일 경로 ( Encrypt Key 이어야 함 )
- certKeyPasswd : 생성된 TestClientPrikey.der 비밀번호

*****************************/
public PrivateKey loadEncryptPrivateKEy( String certKeyPath, String certKeyPasswd )
{
    PrivateKey priKey = null;

    FileInputStream fisKey = null;
    DataInputStream disKey = null;
    ByteArrayInputStream bisKey = null;

    try
    {
        fisKey = new FileInputStream( certKeyPath );
        disKey = new DataInputStream( fisKey );
        byte[] btKeyData = new byte[ disKey.available() ];

        disKey.readFully( btKeyData );
        disKey.close();

        bisKey = new ByteArrayInputStream( btKeydata );
        byte[] baKey = new byte[ bisKey.available() ];

        bisKey.read( baKey, 0, bisKey.available() );
        bisKey.close();

        /************************************************************************
          암호화된 개인키 파일에서 PrivateKey 생성 
        ************************************************************************/
        EncryptedPrivateKeyInfo encKeyInfo = new EncryptedPrivateKeyInfo( baKey );
        String                         encKeyAlg = encKeyInfo.getAlgName();                    

        SecretKeyFactory          skFactory   = SecretKeyFactory.getInstance( encKeyAlg  );

        PBEKeySpec                pbeKeySpec = new PBEKeySpec( certKeyPasswd.toCharArray() );
        SecretKey                   sk              = skFactory.generateSecret( pbeKeySpec );

        Cipher                       cipher        = Cipher.getInstance( encKeyAlg );
        cipher.init( Cipher.DECRYPT_MODE, sk, encKeyInfo.getAlgParameters() ); 

        PKCS8EncodedKeySpec pkcs8Spec    = encKeyInfo.getKeySpec( cipher );
        KeyFactory keyFac                         = KeyFactory.getInstance("RSA");

        priKey = keyFac.generatePrivate( pkcs8Spec );        
    }
    catch( Exception e )
    {
        e.printStackTrace();
        return null;
    }
    finally
    {
        if( bisKey != null ) try{ bisKey.close(); } catch( Exception e) {}
        if( disKey != null ) try{ disKey.close(); } catch( Exception e) {}
        if( fisKey != null ) try{ fisKey.close(); } catch( Exception e) {}
    }

    return priKey;
}

 

'Development > Security (보안)' 카테고리의 다른 글

MTLS 란 무엇인가? 통신과정은 ?  (0) 2020.10.27
SSL 통신원리  (0) 2020.10.14
SSL 이란?  (0) 2020.09.22
X.509 인증서  (0) 2020.09.18
JAVA 사설인증서 MTLS 통신 ( Server )  (0) 2020.09.16

+ Recent posts