IT/ETC

Certificate for OpenSSL & KeyTool

유가엘 2019. 7. 23. 13:20

SSL

정의 :

웹서버와 웹브라우저 사이에 모든 정보를 암호화 해주는 방식

HTTPS라는 통신채널을 사용하며, 모든 웹서버와 웹브라우저가 SSL을 지원

 

특징 :

상호 인증 : 클라이언트와 서버간의 상호 인증(RSA, DSS, X.509)
기밀성 : 대칭키 암호화 알고리즘을 통한 데이터의 암호화(DES, 3DES, RC4 등)
데이터 무결성 : MAC 기법을 이용해 데이터 변조 여부 확인(HMAC-md5, HMAC-SHA-1)

 

작동 방법 :

HandShake 라는 루틴을 사용

1.  " Client Hello " : 클라이언트가 CipherSuites 및 랜덤 값[해시 기반 난수]을 Server에 전달

CipherSuites란?인증, 키 계약, 암호화 및 무결성 보호에 사용되는 보안 알고리즘 및 키 크기를 정의하는 암호화 매개 변수의 조합

2.  " Server Hello  " :  클라이언트가 제공한 정보 중 사용 될 항목을 선택 후, 랜덤 값[해시 기반 난수]과 서버의 인증서를 보낸다.

3.  " Authorization " :  서버의 인증서를 통해 인증기관을 확인하고 전달받은 랜덤값을 합하여 Pre-Master-Secret을 생성한다. 

4.  " Client Key Exhange " : 3번에서 생성한 Pre-Master-Secret 을 암호화 한 뒤 클라이언트의 인증서와 함께 서버에 전달한다.

5.  " Authorization " : 전달받은 클라이언트의 인증서를 검증한다.

6.  " Client Finished " : 클라이언트에게 완료 Alert을 전달한다.

7.  " Server Finished " : 서버 측에 완료 Alert 을 전달한다. 

8.  " Exchange Messages " : 암호화된 메시지를  전달한다.

 

TLS

정의 : SSL 3.0버전에서 키 생성과 MAC의 계산 방법 등을 좀더 안전한 방법으로 개선하고 구현의 모호함 수정

 


OPENSSL

공개 키 기반(PKI)의 ITU-T 표준인 인증서 생성 방법 ( X.509 v3 )를 지원

 

기본적으로 Linux 계열을 지원하며, Window 버전을 다운 받기위해  해당 경로 참조

https://wiki.openssl.org/index.php/Binaries

 

Binaries - OpenSSLWiki

Some people have offered to provide OpenSSL binary distributions for selected operating systems. The condition to get a link here is that the link is stable and can provide continued support for OpenSSL for a while. Note: many Linux distributions come with

wiki.openssl.org

X.509 v3 디지털 인증서의 구조

  • Certificate

    • Not Before 유효기간 시작 날짜

    • Not After 유효기간 끝나는 날짜

    • Public Key Algorithm 공개 키 알고리즘

    • Subject Public Key

    • Version 인증서의 버전을 나타냄

    • Serial Number CA가 할당한 정수로 된 고유 번호

    • Signature 서명 알고리즘 식별자

    • Issuer 발행자

    • Validity 유효기간

    • Subject 소유자

    • Subject Public Key Info 소유자 공개 키 정보

    • Issuer Unique Identifier (Optional) 발행자 고유 식별자

    • Subject Unique Identifier (Optional) 소유자 고유 식별자

    • Extensions (Optional) 확장

  • Certificate Signature Algorithm

  • Certificate Signature

PEM 인코딩 형식 

 

Privacy Enhanced Mail (PEM)

ASCII (Base64)형식의 데이터가 포함되었으며 , -----BEGIN… 으로 시작하며 인증서 파일에 사용

참고 : .key는 PEM 포맷의 파일이지만 키 값이 있는 것을 명시하기 위해 사용

서버 PrivateKey 생성 [ PEM ]
OpenSSL> genrsa -out server.pem
Loading 'screen' into random state - done
Generating RSA private key, 512 bit long modulus
.++++++++++++
..................++++++++++++
unable to write 'random state'
e is 65537 (0x10001)

 

Private Key 를 이용한 인증서 생성 

CERTIFICATE REQUEST (CSR)

인증기관으로 보내 인증서를 발급받게 하는 일종의 신청서

인증서 요청서 작성하기 [ cnf 파일을 읽지 못하여 bin 폴더에 이동 후, config 선언 ]
OpenSSL> req -new -key server.pem -out server.csr -config openssl.cnf
Loading 'screen' into random state - done
You are about to be asked to enter information that will be incorporated
into your certificate request.
What you are about to enter is what is called a Distinguished Name or a DN.
There are quite a few fields but you can leave some blank
For some fields there will be a default value,
If you enter '.', the field will be left blank.
-----
Country Name (2 letter code) [AU]:AU
State or Province Name (full name) [Some-State]:SERVER
Locality Name (eg, city) []:CITY
Organization Name (eg, company) [Internet Widgits Pty Ltd]:internet
Organizational Unit Name (eg, section) []:section
Common Name (eg, YOUR name) []:name
Email Address []:email

Please enter the following 'extra' attributes
to be sent with your certificate request
A challenge password []:qwe123
An optional company name []:qwe123
OpenSSL> req -new -key client.der -out client.csr -config openssl.cnf
Loading 'screen' into random state - done
You are about to be asked to enter information that will be incorporated
into your certificate request.
What you are about to enter is what is called a Distinguished Name or a DN.
There are quite a few fields but you can leave some blank
For some fields there will be a default value,
If you enter '.', the field will be left blank.
-----
Country Name (2 letter code) [AU]:AU
State or Province Name (full name) [Some-State]:client
Locality Name (eg, city) []:city
Organization Name (eg, company) [Internet Widgits Pty Ltd]:internet
Organizational Unit Name (eg, section) []:section
Common Name (eg, YOUR name) []:name
Email Address []:email

Please enter the following 'extra' attributes
to be sent with your certificate request
A challenge password []:
An optional company name []:

CERTIFICATE (CRT)

Linux 계열에서 주로 사용, PEM or DER 으로 인코딩 될 수 있다.

x509 양식으로 요청 365일 간 sha256 암호화 방식으로 server.pem[개인키]로 인증합니다.
OpenSSL> x509 -req -in server.csr -days 3650 -sha256 -signkey server.pem -out server.crt
Loading 'screen' into random state - done
Signature ok
subject=/C=AU/ST=client/L=city/O=internet/OU=section/CN=name/emailAddress=email
Getting Private key
unable to write 'random state'

CERTIFICATE (CER)

Microsoft 계열에서 주로 사용, PEM or DER 으로 인코딩 될 수 있다.

 

PRIVATE KEY 생성

Distinguished Encoding Representation (DER)

바이너리 형식으로 인코딩된 인증서.

PKCS8 : 해당 내용은 [ Private-Key Cryptography Standard ] 에 의거하여 개인키에 대한 정보와 속성을 의미

TOPK8 : OPENSSL 내에서 PKCS8과 한쌍으로 쓰이며 PKCS8 file 형태로 나타낸다는 의미이다.

OpenSSL> pkcs8 -topk8 -nocrypt -in server.pem -outform DER -out server.private.der

PUBLIC KEY 생성

X509 구조의 인증서를 넣은 후, 바이너리 형태[DER] PUBLIC.KEY를 추출한다.

OpenSSL> x509 -in server.crt -out  server.public.der -outform DER
writing RSA key

KEYTOOL

Java 는 KeyStore 라는 인터페이스를 통해 Private Key, Public Key 와 Certificate 를 추상화하여 제공

 

Java Key Store (JKS)

자바에서 제공하는 KeyTool을 이용하여 인증서를 생성

 

1. PrivateKey 와 CERTIFICATE 가 포함된 JKS 생성

keytool -genkey -v -keystore server.jks -alias server_private -keyalg RSA -sigalg sha256withRSA -keysize 1024 -validity 10000

keytool -genkey -v -keystore client.jks -alias client_private -keyalg RSA -sigalg sha256withRSA -keysize 1024 -validity 10000

-v          결과를 상세하게 보기 옵션이다. 

-keystore  : 키가 저장될 JKS 파일의 이름

-alias : JKS 안에 저장될 Private Key 값의 별칭을 지정

-sigalg    : 인증서의 서명 알고리즘 [ 기본 개인 키의 유형이 RSA이면 -sigalg옵션의 기본값은 SHA256withRSA ]

-keysize   : 키값의 길이 

-validity    : 인증서의 유효기간 - 10000이 1일로 계산한다.

C:\Users\user>keytool -genkey -v -keystore server.jks -alias server_private -keyalg RSA -sigalg sha256withRSA -keysize 1024 -validity 10000
키 저장소 비밀번호 입력:
새 비밀번호 다시 입력:
이름과 성을 입력하십시오.
  [Unknown]:  이름
조직 단위 이름을 입력하십시오.
  [Unknown]:  부서명
조직 이름을 입력하십시오.
  [Unknown]:  업체명
구/군/시 이름을 입력하십시오?
  [Unknown]:  무슨 동인지 입력
시/도 이름을 입력하십시오.
  [Unknown]:  어느 지역인지 입력
이 조직의 두 자리 국가 코드를 입력하십시오.
  [Unknown]:  대한민국
CN=1, OU=q, O=w, L=e, ST=r, C=t이(가) 맞습니까?
  [아니오]:  정보값 확인 Y or N

다음에 대해 유효 기간이 10,000일인 1,024비트 RSA 키 쌍 및 자체 서명된 인증서(SHA256withRSA)를 생성하는 중
        : CN=1, OU=q, O=w, L=e, ST=r, C=t
<server_private>에 대한 키 비밀번호를 입력하십시오.
        (키 저장소 비밀번호와 동일한 경우 Enter 키를 누름):
[server.jks을(를) 저장하는 중]

Warning:
JKS 키 저장소는 고유 형식을 사용합니다. "keytool -importkeystore -srckeystore server.jks -destkeystore server.jks -deststoretype pkcs12"를 사용하는 산업 표준 형식인 PKCS12로 이전하는 것이 좋습니다.

키를 생성하게 되면 C 드라이브에서 실행 시, 접속해 있는 User에 JKS가 생성되며, D 드라이브에서 실행 시킨다면 해당 폴더에 생성

 

2. 생성 된 JKS 에 대한 리스트 상세 보기

keytool -v -list -keystore server.jks

keytool -v -list -keystore client.jks

keytool -v -list -keystore server.jks
키 저장소 비밀번호 입력:
키 저장소 유형: jks
키 저장소 제공자: SUN

키 저장소에 1개의 항목이 포함되어 있습니다.

별칭 이름: server_private
생성 날짜: 2019. 10. 1
항목 유형: PrivateKeyEntry
인증서 체인 길이: 1
인증서[1]:
소유자: CN=1, OU=q, O=w, L=e, ST=r, C=t
발행자: CN=1, OU=q, O=w, L=e, ST=r, C=t
일련 번호: 5aed18f2
적합한 시작 날짜: Tue Oct 01 10:19:09 KST 2019 종료 날짜: Sat Feb 16 10:19:09 KST 2047
인증서 지문:
         MD5:  CE:70:27:53:CB:B9:B7:C1:E5:9A:14:22:5D:7B:1E:2D
         SHA1: 57:EC:77:C8:E3:B2:05:30:3C:F0:D3:21:F3:35:07:41:F7:75:A6:BB
         SHA256: B7:54:97:AA:D3:1B:40:33:B9:15:6F:21:75:69:7C:6B:1A:5B:5A:4D:72:FC:59:0D:CB:5F:BD:DB:D6:99:25:90
서명 알고리즘 이름: SHA256withRSA
주체 공용 키 알고리즘: 1024비트 RSA 키
버전: 3

확장:

#1: ObjectId: 2.5.29.14 Criticality=false
SubjectKeyIdentifier [
KeyIdentifier [
0000: D2 DC 41 A1 CD 72 65 5E   AC 41 B0 31 E7 44 A4 66  ..A..re^.A.1.D.f
0010: 81 D5 92 E7                                        ....
]
]



*******************************************
*******************************************



Warning:
JKS 키 저장소는 고유 형식을 사용합니다. "keytool -importkeystore -srckeystore server.jks -destkeystore server.jks -deststoretype pkcs12"를 사용하는 산업 표준 형식인 PKCS12로 이전하는 것이 좋습니다.

 

3. 상위에 만들어둔 JKS 에서 자체 서명이 된 Public Key 추출

keytool -export -alias server_private -keystore server.jks -rfc -file Server.cer

-alias      : 지정할 키의 별칭

-keystore  : 지정할 키가 저장된 키 스토어 명

-rfc        : 출력 포맷은 64 비트로 인코딩 된 PEM이고 그렇지 않으면 이진 DER이 만들어진다.

-file        : 저장할 파일 명

C:\Users\user> keytool -export -alias server_private -keystore server.jks -rfc -file Server.cer
키 저장소 비밀번호 입력:
인증서가 <Server.cer> 파일에 저장되었습니다.

-----BEGIN CERTIFICATE-----
MIICKjCCAZOgAwIBAgIEWu0Y8jANBgkqhkiG9w0BAQsFADBIMQowCAYDVQQGEwF0
MQowCAYDVQQIEwFyMQowCAYDVQQHEwFlMQowCAYDVQQKEwF3MQowCAYDVQQLEwFx
MQowCAYDVQQDEwExMB4XDTE5MTAwMTAxMTkwOVoXDTQ3MDIxNjAxMTkwOVowSDEK
MAgGA1UEBhMBdDEKMAgGA1UECBMBcjEKMAgGA1UEBxMBZTEKMAgGA1UEChMBdzEK
MAgGA1UECxMBcTEKMAgGA1UEAxMBMTCBnzANBgkqhkiG9w0BAQEFAAOBjQAwgYkC
gYEAkT9fXOcX7vfSGBv5nEfwou4wMXQAx9b+rhPsH81oXCs3ud7+r/Lrq8fIyC6k
tLImB4/5KiCtIEYymYOK11aRtZ4EGkOxNHAxeV3Lvo1aWUHWZhLR4kcNQM1GRjZf
TtMNDSH3I+Ai8lSIzR3b0ZUxOme7g178tggAlFWJfKvvkdcCAwEAAaMhMB8wHQYD
VR0OBBYEFNLcQaHNcmVerEGwMedEpGaB1ZLnMA0GCSqGSIb3DQEBCwUAA4GBAFdG
WZus7/Ryh5+Xg9nIaWtLdXQxArm8swew5Cjfo3jpivO5OjfoJXmOgESOJxkxgAqF
/4me7Xh7IwlglaOeYGG/X7Ie0T97GzRV29PgwOUymXOsy0ukk5/cVPxsfNdSh3L1
HQfs1dtK6j+vK0+U57fRmkIOj0ERTHiJkcLNyjta
-----END CERTIFICATE-----

3. TrustStore 생성 [ 나의 PrivateKey와 서명 + 상대방의 Public Key 와 서명이 담긴 JKS ]

keytool -import -alias trustClient -file Client.cer -keystore server.jks

keytool -import -alias trustServer -file Server.cer -keystore client.jks

C:\Users\user>keytool -v -list -keystore server.jks
키 저장소 비밀번호 입력:
키 저장소 유형: jks
키 저장소 제공자: SUN

키 저장소에 2개의 항목이 포함되어 있습니다.

별칭 이름: trustclient
생성 날짜: 2019. 10. 1
항목 유형: trustedCertEntry

소유자: CN=1, OU=q, O=w, L=e, ST=r, C=t
발행자: CN=1, OU=q, O=w, L=e, ST=r, C=t
일련 번호: 758d8625
적합한 시작 날짜: Tue Oct 01 10:50:46 KST 2019 종료 날짜: Sat Feb 16 10:50:46 KST 2047
인증서 지문:
         MD5:  0A:09:9B:3B:23:F6:33:F8:C5:0E:79:D0:D2:8E:E7:52
         SHA1: CC:C1:A9:7F:34:E6:12:89:56:43:2D:C2:70:ED:B3:76:8D:32:FC:98
         SHA256: B7:1F:1E:DC:32:8C:FC:24:91:F8:D7:8B:CD:46:D2:BE:63:B1:27:EC:E0:F1:93:B5:AB:06:29:98:A8:A9:FF:22
서명 알고리즘 이름: SHA256withRSA
주체 공용 키 알고리즘: 1024비트 RSA 키
버전: 3

확장:

#1: ObjectId: 2.5.29.14 Criticality=false
SubjectKeyIdentifier [
KeyIdentifier [
0000: F4 29 4F 32 E4 9A 13 3E   71 4F 9B FA 36 B6 64 C3  .)O2...>qO..6.d.
0010: 04 BE EC 59                                        ...Y
]
]



*******************************************
*******************************************


별칭 이름: server_private
생성 날짜: 2019. 10. 1
항목 유형: PrivateKeyEntry
인증서 체인 길이: 1
인증서[1]:
소유자: CN=1, OU=q, O=w, L=e, ST=r, C=t
발행자: CN=1, OU=q, O=w, L=e, ST=r, C=t
일련 번호: 5aed18f2
적합한 시작 날짜: Tue Oct 01 10:19:09 KST 2019 종료 날짜: Sat Feb 16 10:19:09 KST 2047
인증서 지문:
         MD5:  CE:70:27:53:CB:B9:B7:C1:E5:9A:14:22:5D:7B:1E:2D
         SHA1: 57:EC:77:C8:E3:B2:05:30:3C:F0:D3:21:F3:35:07:41:F7:75:A6:BB
         SHA256: B7:54:97:AA:D3:1B:40:33:B9:15:6F:21:75:69:7C:6B:1A:5B:5A:4D:72:FC:59:0D:CB:5F:BD:DB:D6:99:25:90
서명 알고리즘 이름: SHA256withRSA
주체 공용 키 알고리즘: 1024비트 RSA 키
버전: 3

확장:

#1: ObjectId: 2.5.29.14 Criticality=false
SubjectKeyIdentifier [
KeyIdentifier [
0000: D2 DC 41 A1 CD 72 65 5E   AC 41 B0 31 E7 44 A4 66  ..A..re^.A.1.D.f
0010: 81 D5 92 E7                                        ....
]
]



*******************************************
*******************************************



Warning:
JKS 키 저장소는 고유 형식을 사용합니다. "keytool -importkeystore -srckeystore server.jks -destkeystore server.jks -deststoretype pkcs12"를 사용하는 산업 표준 형식인 PKCS12로 이전하는 것이 좋습니다.

1. 만약 PrivateKey 및 인증서가 존재하며 JKS를 만들어야 할 경우, 아래 첨부된 ImportKey.java 내에서 저장될 명칭을 기재한다.

import java.security.*;
import java.io.IOException;
import java.io.InputStream;
import java.io.FileInputStream;
import java.io.DataInputStream;
import java.io.ByteArrayInputStream;
import java.io.FileOutputStream;
import java.security.spec.*;
import java.security.cert.Certificate;
import java.security.cert.CertificateFactory;
import java.util.Collection;
import java.util.Iterator;


public class ImportKey  {

    private static InputStream fullStream ( String fname ) throws IOException {
        FileInputStream fis = new FileInputStream(fname);
        DataInputStream dis = new DataInputStream(fis);
        byte[] bytes = new byte[dis.available()];
        dis.readFully(bytes);
        ByteArrayInputStream bais = new ByteArrayInputStream(bytes);
        return bais;
    }
        
    public static void main ( String args[]) {
        
        // 해당 JKS 에 대한 password
        String keypass = "password"; 
        
        // 해당 JKS 에 대한 alias
        String defaultalias = "alias";

        String keystorename = System.getProperty("keystore");

        if (keystorename == null)
            keystorename = "keystore.ImportKey";----------------------------저장될 Keystore 명칭 기재

        // parsing command line input
        String keyfile = "";
        String certfile = "";
        if (args.length < 2 || args.length>3) {
            System.out.println("Usage: java comu.ImportKey keyfile certfile [alias]");
            System.exit(0);
        } else {
            keyfile = args[0];
            certfile = args[1];
            if (args.length>2)
                defaultalias = args[2];
        }

        try {
            // initializing and clearing keystore 
            KeyStore ks = KeyStore.getInstance("JKS", "SUN");
            ks.load( null , keypass.toCharArray());
            System.out.println("Using keystore-file : "+keystorename);
            ks.store(new FileOutputStream ( keystorename  ),
                    keypass.toCharArray());
            ks.load(new FileInputStream ( keystorename ),
                    keypass.toCharArray());

            // loading Key
            InputStream fl = fullStream (keyfile);
            byte[] key = new byte[fl.available()];
            KeyFactory kf = KeyFactory.getInstance("RSA");
            fl.read ( key, 0, fl.available() );
            fl.close();
            PKCS8EncodedKeySpec keysp = new PKCS8EncodedKeySpec ( key );
            PrivateKey ff = kf.generatePrivate (keysp);

            // loading CertificateChain
            CertificateFactory cf = CertificateFactory.getInstance("X.509");
            InputStream certstream = fullStream (certfile);

            Collection c = cf.generateCertificates(certstream) ;
            Certificate[] certs = new Certificate[c.toArray().length];

            if (c.size() == 1) {
                certstream = fullStream (certfile);
                System.out.println("One certificate, no chain.");
                Certificate cert = cf.generateCertificate(certstream) ;
                certs[0] = cert;
            } else {
                System.out.println("Certificate chain length: "+c.size());
                /**
                 * Run for through chained certificates
                 **/
                // certs = (Certificate[])c.toArray();
                int i = 0;
                    Iterator it = c.iterator();
                    while (it.hasNext()) {
                        certs[i] = (Certificate)it.next();
                    i++;
                }
            }

            // storing keystore
            ks.setKeyEntry(defaultalias, ff, 
                           keypass.toCharArray(),
                           certs );
            System.out.println ("Key and certificate stored.");
            System.out.println ("Alias:"+defaultalias+"  Password:"+keypass);
            ks.store(new FileOutputStream ( keystorename ),
                     keypass.toCharArray());
        } catch (Exception ex) {
            ex.printStackTrace();
        }
    }
}

 

3. 해당 클래스안에  인증이 완료된 Private Key 와 Public Key를 넣는다.

C:\openssl\bin>java ImportKey server.private.der server.public.der
Using keystore-file : C:\Users\user\openssl_server.jks
One certificate, no chain.
Key and certificate stored.
Alias:NsH_keystore  Password:nsh