예전에 만든 자료인데, 기억 못할까봐 정리해둔다.

-----------------------------------------------

part 1. 10년짜리 사실 인증서 만들기

개발 서버에서는 공인인증서를 꼭 써야 할 필요는 없다. 만약 공인인증서를 쓰지 않았을 때를 위해서 만들어 놓은 환경을 설명한다. 테스트 환경에서만 작동하는 보안 서버를 구축하면 된다.

CA (Certificate Authority, 인증기관)을 구성하고, 인증기관 자체의 private key와 public key를 생성하고, 결과물로 나온 인증서를 보안서버에 올리고, 보안 서버와 통신하는 모든 개발 PC및 개발 장비에서 사용할 수 있는 방법을 설명한다.

Http 서버 설정을 제외하고는 모두 Root 계정을 이용해야 한다.

자료는 아래 링크를 참조하면 된다.

http://www.ipsec-howto.org/x595.html

http://tldp.org/HOWTO/SSL-Certificates-HOWTO/x195.html

 

<파일 수정>

첫번째, CentOS의 openssl.conf 파일을 수정한다.

Redhat 계통(CentOS포함)에서는 /usr/share/ssl/openssl.cnf 설정파일을 참조해서 조금 수정하면 된다. dev개발환경이라는 가정을 하고, 다음 dev-openssl.conf 파일을 생성한다.

dev-ssl.google.com 이라는 도메인에 대한 인증서를 작성한다. 추가적으로 commonName에 alpha-ssl.google.com, beta-ssl.google.com, dev-id.google.com, dev-member.google.com 와 같이 여러 개의 도메인을 한 번에 등록할 수 있다.

 

#

# OpenSSL example configuration file.

# This is mostly being used for generation of certificate requests.

#

# This definition stops the following lines choking if HOME isn't

# defined.

HOME = .

RANDFILE = $ENV::HOME/.rnd

# Extra OBJECT IDENTIFIER info:

#oid_file = $ENV::HOME/.oid

oid_section = new_oids

# To use this configuration file with the "-extfile" option of the

# "openssl x509" utility, name here the section containing the

# X.509v3 extensions to use:

# extensions =

# (Alternatively, use a configuration file that has only

# X.509v3 extensions in its main [= default] section.)

[ new_oids ]

# We can add new OIDs in here for use by 'ca' and 'req'.

# Add a simple OID like this:

# testoid1=1.2.3.4

# Or use config file substitution like this:

# testoid2=${testoid1}.5.6

####################################################################

[ ca ]

default_ca = CA_default # The default ca section

####################################################################

[ CA_default ]

dir = ./demoCA # Where everything is kept

certs = $dir/certs # Where the issued certs are kept

crl_dir = $dir/crl # Where the issued crl are kept

database = $dir/index.txt # database index file.

new_certs_dir = $dir/newcerts # default place for new certs.

certificate = $dir/cacert.pem # The CA certificate

serial = $dir/serial # The current serial number

crl = $dir/crl.pem # The current CRL

private_key = $dir/private/cakey.pem# The private key

RANDFILE = $dir/private/.rand # private random number file

x509_extensions = usr_cert # The extentions to add to the cert

# Comment out the following two lines for the "traditional"

# (and highly broken) format.

name_opt = ca_default # Subject Name options

cert_opt = ca_default # Certificate field options

# Extension copying option: use with caution.

# copy_extensions = copy

# Extensions to add to a CRL. Note: Netscape communicator chokes on V2 CRLs

# so this is commented out by default to leave a V1 CRL.

# crl_extensions = crl_ext

default_days = 1825 # how long to certify for

default_crl_days= 30 # how long before next CRL

default_md = md5 # which md to use.

preserve = no # keep passed DN ordering

# A few difference way of specifying how similar the request should look

# For type CA, the listed attributes must be the same, and the optional

# and supplied fields are just that :-)

policy = policy_match

# For the CA policy

[ policy_match ]

countryName = match

stateOrProvinceName = match

organizationName = match

organizationalUnitName = optional

commonName = supplied

emailAddress = optional

# For the 'anything' policy

# At this point in time, you must list all acceptable 'object'

# types.

[ policy_anything ]

countryName = optional

stateOrProvinceName = optional

localityName = optional

organizationName = optional

organizationalUnitName = optional

commonName = supplied

emailAddress = optional

####################################################################

[ req ]

default_bits = 1024

default_keyfile = privkey.pem

distinguished_name = req_distinguished_name

attributes = req_attributes

x509_extensions = v3_ca # The extentions to add to the self signed cert

# Passwords for private keys if not present they will be prompted for

# input_password = secret

# output_password = secret

# This sets a mask for permitted string types. There are several options.

# default: PrintableString, T61String, BMPString.

# pkix : PrintableString, BMPString.

# utf8only: only UTF8Strings.

# nombstr : PrintableString, T61String (no BMPStrings or UTF8Strings).

# MASK:XXXX a literal mask value.

# WARNING: current versions of Netscape crash on BMPStrings or UTF8Strings

# so use this option with caution!

string_mask = nombstr

# req_extensions = v3_req # The extensions to add to a certificate request

[ req_distinguished_name ]

countryName = Country Name (2 letter code)

countryName_default =

countryName_min = 2

countryName_max = 2

stateOrProvinceName = State or Province Name (full name)

stateOrProvinceName_default =

localityName = Locality Name (eg, city)

localityName_default =

0.organizationName = Organization Name (eg, company)

0.organizationName_default =

# we can do this but it is not needed normally :-)

#1.organizationName = Second Organization Name (eg, company)

#1.organizationName_default = World Wide Web Pty Ltd

organizationalUnitName = Organizational Unit Name (eg, section)

organizationalUnitName_default = team

commonName = Common Name (eg, your name or your server\'s hostname)

commonName_default = dev-ssl.google.com

commonName_max = 64

# 만약 하나의 인증서로 다른 서버도 같이 쓸 수 있음. 아래의 예제와 같이 진행하면 됨

#0.commonName = Common Name (eg, your name or your server\'s hostname)

#0.commonName_default = dev-ssl.google.com

#0.commonName_max = 64

#1.commonName = Common Name (eg, your name or your server\'s hostname)

#1.commonName_default = alpha-ssl.google.com

#1.commonName_max = 64

#2.commonName = Common Name (eg, your name or your server\'s hostname)

#2.commonName_default = beta-ssl.google.com

#2.commonName_max = 64

#3.commonName = Common Name (eg, your name or your server\'s hostname)

#3.commonName_default = dev-id.google.com

#3.commonName_max = 64

#4.commonName = Common Name (eg, your name or your server\'s hostname)

#4.commonName_default = dev-member.google.com

#4.commonName_max = 64

emailAddress = Email Address

emailAddress.default = (메일)

emailAddress_max = 64

# SET-ex3 = SET extension number 3

[ req_attributes ]

challengePassword = A challenge password

challengePassword_min = 4

challengePassword_max = 20

unstructuredName = An optional company name

[ usr_cert ]

# These extensions are added when 'ca' signs a request.

# This goes against PKIX guidelines but some CAs do it and some software

# requires this to avoid interpreting an end user certificate as a CA.

basicConstraints=CA:FALSE

# Here are some examples of the usage of nsCertType. If it is omitted

# the certificate can be used for anything *except* object signing.

# This is OK for an SSL server.

# nsCertType = server

# For an object signing certificate this would be used.

# nsCertType = objsign

# For normal client use this is typical

# nsCertType = client, email

# and for everything including object signing:

# nsCertType = client, email, objsign

# This is typical in keyUsage for a client certificate.

# keyUsage = nonRepudiation, digitalSignature, keyEncipherment

# This will be displayed in Netscape's comment listbox.

nsComment = "OpenSSL Generated Certificate"

# PKIX recommendations harmless if included in all certificates.

subjectKeyIdentifier=hash

authorityKeyIdentifier=keyid,issuer:always

# This stuff is for subjectAltName and issuerAltname.

# Import the email address.

# subjectAltName=email:copy

# An alternative to produce certificates that aren't

# deprecated according to PKIX.

# subjectAltName=email:move

# Copy subject details

# issuerAltName=issuer:copy

#nsCaRevocationUrl = http://www.domain.dom/ca-crl.pem

#nsBaseUrl

#nsRevocationUrl

#nsRenewalUrl

#nsCaPolicyUrl

#nsSslServerName

[ v3_req ]

# Extensions to add to a certificate request

basicConstraints = CA:FALSE

keyUsage = nonRepudiation, digitalSignature, keyEncipherment

[ v3_ca ]

# Extensions for a typical CA

# PKIX recommendation.

subjectKeyIdentifier=hash

authorityKeyIdentifier=keyid:always,issuer:always

# This is what PKIX recommends but some broken software chokes on critical

# extensions.

#basicConstraints = critical,CA:true

# So we do this instead.

basicConstraints = CA:true

# Key usage: this is typical for a CA certificate. However since it will

# prevent it being used as an test self-signed certificate it is best

# left out by default.

# keyUsage = cRLSign, keyCertSign

# Some might want this also

# nsCertType = sslCA, emailCA

# Include email address in subject alt name: another PKIX recommendation

# subjectAltName=email:copy

# Copy issuer details

# issuerAltName=issuer:copy

# DER hex encoding of an extension: beware experts only!

# obj=DER:02:03

# Where 'obj' is a standard or added object

# You can even override a supported extension:

# basicConstraints= critical, DER:30:03:01:01:FF

[ crl_ext ]

# CRL extensions.

# Only issuerAltName and authorityKeyIdentifier make any sense in a CRL.

# issuerAltName=issuer:copy

authorityKeyIdentifier=keyid:always,issuer:always

 

두번째 수정할 파일은 /usr/share/ssl/misc/CA 파일이다. CA 파일은 Shell script로 구성되어 있으며, 인증기관 역할을 해준다. 보안(Http) 서버를 리얼 장비에 사용할 때 패스워드에 등록하지만, 개발 장비에 사용될 경우에는 굳이 패스워드를 등록하지 않아도 된다.

이를 위해서 /usr/share/ssl/misc/CA 파일의 일부분을 수정하면 된다. REQ의 내용에 ‘-nodes’를 추가하면 된다.

 

REQ="openssl req $SSLEAY_CONFIG"

->

REQ="openssl req $SSLEAY_CONFIG -nodes"

 

<CA 구성>

CA(Certificate Authority, 인증기관)을 구성한다. 인증기관 자체의 private key와 public key 를 생성한다.

]# mkdir certs

]# cd certs

]# /usr/share/ssl/misc/CA –newca

CA certificate filename (or enter to create) (엔터)

Making CA certificate ...

Generating a 1024 bit RSA private key

.....................++++++

.........................++++++

writing new private key to './demoCA/private/./cakey.pem'

Enter PEM pass phrase:

Verifying - Enter PEM pass phrase:

-----

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) [GB]:(입력)

State or Province Name (full name) [Berkshire]:(입력)

Locality Name (eg, city) [Newbury]:(입력)

Organization Name (eg, company) [My Company Ltd]:(입력)

Organizational Unit Name (eg, section) []:(입력)

Common Name (eg, your name or your server's hostname) [dev-ssl.google.com]: (입력)

Email Address []:(입력)

 

demoCA 디렉토리가 생기고, cacert.pem가 생성된다. 이는 CA 인증서 생성됨을 의미한다.

demoCA/private/cakey.pem 파일은 1024bite private key 이다.

 

]# cd demoCA/

]# openssl x509 -in cacert.pem -days 3650 -out cacert.pem -signkey ./private/cakey.pem

Getting Private key

Enter pass phrase for ./private/cakey.pem:

]# /usr/share/ssl/misc/CA -newreq

Generating a 1024 bit RSA private key

......++++++

..........................++++++

writing new private key to 'newreq.pem'

Enter PEM pass phrase:

Verifying - Enter PEM pass phrase:

-----

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) [GB]:(엔터)

State or Province Name (full name) [Berkshire]:(엔터)

Locality Name (eg, city) [Newbury]:(엔터)

Organization Name (eg, company) [My Company Ltd]:(엔터)

Organizational Unit Name (eg, section) []:(엔터)

Common Name (eg, your name or your server's hostname) [dev-ssl.google.com]:(dev-ssl.google.com 엔터)

Email Address []:(입력)

Please enter the following 'extra' attributes

to be sent with your certificate request

A challenge password []: (엔터)

An optional company name []: (엔터)

Request (and private key) is in newreq.pem

 

만들어진 newreq.pem 파일을 원래 생성했던 위치에 두어 다음 작업을 쉽게 하도록 한다.

]# cp newreq.pem ..

]# cd ..

]# ls -al

drwxr-xr-x 3 root root 4096 11 15:09 .

drwxr-xr-x 8 root root 4096 11 15:06 ..

drwxr-xr-x 6 root root 4096 11 15:07 demoCA

-rw-r--r-- 1 root root 1667 11 15:09 newreq.pem

 

그 후, 만들어진 private key 파일을 가지고 CA로부터 인증을 한다. 이 결과로 newcert.pem 이라는 공개키가 생성된다. CA –sign은 인증서 승인 후, 인증서의 정보를 보여준다.

]# /usr/share/ssl/misc/CA -sign

Using configuration from /usr/share/ssl/openssl.cnf

Enter pass phrase for ./demoCA/private/cakey.pem:

Check that the request matches the signature

Signature ok

Certificate Details:

Serial Number: 1 (0x1)

Validity

Not Before: Mar 11 06:10:07 2011 GMT

Not After : Mar 10 06:10:07 2012 GMT

Subject:

countryName =

stateOrProvinceName =

localityName =

organizationName =

organizationalUnitName =

commonName = dev-ssl.google.com

emailAddress = 메일

X509v3 extensions:

X509v3 Basic Constraints:

CA:FALSE

Netscape Comment:

OpenSSL Generated Certificate

X509v3 Subject Key Identifier:

77:BC:33:80:3E:07:F3:10:40:71:D0:EE:49:F6:53:93:66:DB:6A:20

X509v3 Authority Key Identifier:

keyid:B4:8A:40:59:12:D6:3A:44:FC:27:31:A9:3E:D6:D8:B7:CD:40:08:EB

DirName:/C=/ST=/L=/O=/OU=test/CN=dev-ssl.google.com/emailAddress=

serial:00

Certificate is to be certified until Mar 10 06:10:07 2012 GMT (365 days)

Sign the certificate? [y/n]:(y엔터)

1 out of 1 certificate requests certified, commit? [y/n](y엔터)

Write out database with 1 new entries

Data Base Updated

Certificate:

Data:

Version: 3 (0x2)

Serial Number: 1 (0x1)

Signature Algorithm: md5WithRSAEncryption

Issuer: C=, ST=, L=, O=, OU=, CN=dev-ssl.google.com/emailAddress=메일

Validity

Not Before: Mar 11 06:10:07 2011 GMT

Not After : Mar 10 06:10:07 2012 GMT

Subject: C=, ST=, L=, O=, OU=, CN=dev-ssl.google.com/emailAddress=메일

Subject Public Key Info:

Public Key Algorithm: rsaEncryption

RSA Public Key: (1024 bit)

Modulus (1024 bit):

00:ce:0d:60:77:08:7b:8b:35:6e:97:dd:58:d3:0e:

81:ed:1b:d7:55:f6:c1:49:dc:e2:07:93:08:ca:1d:

27:8e:cd:19:30:fc:98:b7:c3:89:0a:98:43:aa:cf:

7e:8c:24:53:1a:87:1c:c1:13:10:0e:db:a5:09:e4:

c3:79:0f:9b:1a:48:4b:51:50:63:bb:9a:23:c9:8f:

e4:b0:a3:5b:74:3c:a7:fe:98:06:3c:fc:47:0e:ed:

d8:e5:b1:3e:e1:8c:c1:e9:11:6e:ec:d4:84:0a:51:

67:7c:2b:86:ac:7d:16:b6:65:90:77:95:88:d2:29:

e4:60:4d:73:59:2e:82:70:c5

Exponent: 65537 (0x10001)

X509v3 extensions:

X509v3 Basic Constraints:

CA:FALSE

Netscape Comment:

OpenSSL Generated Certificate

X509v3 Subject Key Identifier:

77:BC:33:80:3E:07:F3:10:40:71:D0:EE:49:F6:53:93:66:DB:6A:20

X509v3 Authority Key Identifier:

keyid:B4:8A:40:59:12:D6:3A:44:FC:27:31:A9:3E:D6:D8:B7:CD:40:08:EB

DirName:/C=/ST=/L=/O=/OU=/CN=dev-ssl.google.com/emailAddress=메일

serial:00

Signature Algorithm: md5WithRSAEncryption

3c:d4:9b:79:9c:e3:eb:45:52:26:6d:12:61:27:a0:e6:4e:dc:

c3:3a:59:6d:6a:d0:a5:62:e3:88:f5:c9:26:e2:35:15:01:8d:

65:2f:1d:b6:2e:be:62:83:db:cf:80:0b:c6:cc:3c:00:b3:7f:

9a:ce:b0:35:fd:a5:c1:b7:aa:fb:5d:40:2c:4f:39:64:ba:f1:

c3:f8:10:aa:26:d0:5c:30:a1:3b:81:00:a6:77:6e:c5:33:ef:

25:c8:be:c1:c5:2b:40:e2:83:32:a1:a4:e2:99:91:95:d2:da:

83:3e:64:66:45:d3:86:25:db:aa:67:98:d4:fb:2f:cd:31:24:

0a:d7

-----BEGIN CERTIFICATE-----

MIIDqzCCAxSgAwIBAgIBATANBgkqhkiG9w0BAQQFADCBizELMAkGA1UEBhMCS1Ix

EDAOBgNVBAgTB0t5dW5nZ2kxEDAOBgNVBAcTB0J1bmRhbmcxDDAKBgNVBAoTA05I

TjENMAsGA1UECxMEdGVzdDEcMBoGA1UEAxMTZGV2LXNzbC5uaG5jb3JwLmNvbTEd

MBsGCSqGSIb3DQEJARYOa25pZ2h0QG5obi5jb20wHhcNMTEwMzExMDYxMDA3WhcN

MTIwMzEwMDYxMDA3WjCBjzELMAkGA1UEBhMCS1IxEDAOBgNVBAgTB0t5dW5nZ2kx

EDAOBgNVBAcTB0J1bmRhbmcxDDAKBgNVBAoTA05ITjENMAsGA1UECxMEdGVhbTEc

MBoGA1UEAxMTZGV2LXNzbC5uaG5jb3JwLmNvbTEhMB8GCSqGSIb3DQEJARYSa25p

Z2h0QG5obmNvcnAuY29tMIGfMA0GCSqGSIb3DQEBAQUAA4GNADCBiQKBgQDODWB3

CHuLNW6X3VjTDoHtG9dV9sFJ3OIHkwjKHSeOzRkw/Ji3w4kKmEOqz36MJFMahxzB

ExAO26UJ5MN5D5saSEtRUGO7miPJj+Swo1t0PKf+mAY8/EcO7djlsT7hjMHpEW7s

1IQKUWd8K4asfRa2ZZB3lYjSKeRgTXNZLoJwxQIDAQABo4IBFzCCARMwCQYDVR0T

BAIwADAsBglghkgBhvhCAQ0EHxYdT3BlblNTTCBHZW5lcmF0ZWQgQ2VydGlmaWNh

dGUwHQYDVR0OBBYEFHe8M4A+B/MQQHHQ7kn2U5Nm22ogMIG4BgNVHSMEgbAwga2A

FLSKQFkS1jpE/CcxqT7W2LfNQAjroYGRpIGOMIGLMQswCQYDVQQGEwJLUjEQMA4G

A1UECBMHS3l1bmdnaTEQMA4GA1UEBxMHQnVuZGFuZzEMMAoGA1UEChMDTkhOMQ0w

CwYDVQQLEwR0ZXN0MRwwGgYDVQQDExNkZXYtc3NsLm5obmNvcnAuY29tMR0wGwYJ

KoZIhvcNAQkBFg5rbmlnaHRAbmhuLmNvbYIBADANBgkqhkiG9w0BAQQFAAOBgQA8

1Jt5nOPrRVImbRJhJ6DmTtzDOlltatClYuOI9ckm4jUVAY1lLx22Lr5ig9vPgAvG

zDwAs3+azrA1/aXBt6r7XUAsTzlkuvHD+BCqJtBcMKE7gQCmd27FM+8lyL7BxStA

4oMyoaTimZGV0tqDPmRmRdOGJduqZ5jU+y/NMSQK1w==

-----END CERTIFICATE-----

Signed certificate is in newcert.pem

]# ls -al

drwxr-xr-x 3 root root 4096 11 15:09 .

drwxr-xr-x 8 root root 4096 11 15:06 ..

drwxr-xr-x 6 root root 4096 11 15:10 demoCA

-rw-r--r-- 1 root root 3616 11 15:10 newcert.pem

-rw-r--r-- 1 root root 1667 11 15:09 newreq.pem

 

이렇게 만든 newreq.pem는 키이고, newcert.pem은 인증서이므로, 의미 있는 파일이름으로 각각 변경한다.

]# mv newreq.pem dev-ssl.node.key

]# mv newcert.pem dev-ssl.node.crt

 

<10년짜리 인증서 만들기>

 

CA 기본 shell script에서 디폴트로 1년로 만들기 때문에 매년 연장해야 하는 불편함이 존재할 수 있다. 이를 운영에서 편리하게 하기 위해서 ‘/usr/share/ssl/misc/CA –sign’의 결과의 내용을 살펴보면, 아래와 같이 1년으로 되어 있는 것으로 확인할 수 있다.

Validity

Not Before: Mar 11 06:10:07 2011 GMT

Not After : Mar 10 06:10:07 2012 GMT

Sign할 때의 인증기간을 수정하면 된다. /usr/share/ssl/misc/CA 파일을 수정한다.

첫번째 수정사항은 기간을 수정한다.

DAYS=”-days 365”

->

DAYS="-days 1280"

두번째 수정사항은 Sign 파라미터에 들어가는 스크립트를 수정한다.

$CA -policy policy_anything -out newcert.pem -infiles newreq.pem

->

$CA -policy policy_anything -out newcert.pem -in newreq.pem $DAYS

그리고, 인증서를 만드는 작업을 다시 진행한다.

Sign 작업후에 콘솔로 나오는 화면에 10년 짜리 인증서가 만들어진 것을 확인할 수 있다.

Validity

Not Before: Mar 11 10:46:21 2011 GMT

Not After : Mar 8 10:46:21 2021 GMT

 

 

-------------------------

part 2. 사설 인증서를 로딩한 웹 서버 사용하기

 

http서버에 10년 짜리 인증서를 설치한다는 것을 가정한다.

새로 만든 인증서와 키파일을 모두 /home/www/httpd/conf 파일에 위치한다. 암호를 설정하지 않았기 때문에 SSLPassPhraseDialog지시자에 builtin을 지정한다. 만약 암호를 설정하였을 때는 SSLPassPhraseDialog 지시자에 암호를 출력하는 스크립트를 넣을 수 있다.

<IfModule mod_ssl.c>

SSLRandomSeed startup builtin

SSLRandomSeed connect builtin

Listen 443

AddType application/x-x509-ca-cert .crt

AddType application/x-pkcs7-crl .crl

SSLPassPhraseDialog builtin

SSLSessionCache dbm:/home/www/httpd/logs/ssl_scache

SSLSessionCacheTimeout 300

SSLMutex file: /home/www/httpd/logs/ssl_mutex

</IfModule>

..

<VirtualHost *:443>

DocumentRoot /home/www/deploy

ServerName dev-ssl.google.com

<Directory "/home/www/deploy/service">

Options -Indexes FollowSymLinks MultiViews

AllowOverride None

Order allow,deny

Allow from all

FileETag None

</Directory>

<IfModule mod_ssl.c>

SSLEngine on

SSLCipherSuite ALL:!ADH:!EXPORT56:RC4+RSA:+HIGH:+MEDIUM:+LOW:!SSLv2:+EXP:+eNULL

SSLCertificateFile /home/www/httpd/conf/dev-ssl.node.crt

SSLCertificateKeyFile /home/www/httpd/conf/dev-ssl.node.key

SetEnvIf User-Agent ".*MSIE.*" \

nokeepalive ssl-unclean-shutdown \

downgrade-1.0 force-response-1.0

</IfModule>

위와 같이 설정하고, http 서버를 재시작한다.

 

<로컬 pc 에서 인증서 설치>

로컬 PC에서 웹 브라우져를 이용해서 dev-ssl.google.com으로 접속하면 다음과 같이 보안 인증서에 대한 경고 화면이 뜬다. (Window 7, IE8 기준) 바로 신뢰할 수 있는 루트 인증기관이 아닌 서버에서 인증한 보안 인증서이기 때문이다.

웹 브라우져에서 이런 화면이 나오지 않게 하기 위해서는 해당 서버의 인증서를 신뢰할 수 있는 서버로 등록하면 된다. 인증서를 다운받는 법은 2가지이다. 첫번째는 서버에서 인증서를 파일로 다운받는다. 두번째는 웹브라우져에서 ‘인증서 오류’ 라 나오는 버튼을 클릭하고 ‘인증서 보기’로 해서 창을 띄우는 방법을 사용한다.

인증서를 띄우면 아래와 같이 확인할 수 있다. 10년짜리 인증서이지만, 확인할 수 없는 인증서라고 문구가 나온다. 인증서 설치를 선택한다.

‘인증서 설치’ 버튼을 선택하면 인증서 가져오기 마법사 시작 팝업창이 뜬다. 다음을 선택한다.

인증서 가져오기 마법사에서 ‘모든 인증서를 다음 저장소에 저장’을 선택한다.

인증 저장서를 선택하는 팝업창에서 ‘신뢰할 수 있는 루트 인증 기관’을 선택하고 확인 버튼을 선택한다.

‘인증서 가져오기 마법사’에 대한 완료 팝업창이 뜬다. 마침 버튼을 선택한다.

인증서 설치에 대한 화면이 발생하고 인증서를 설치할 것인지 묻는 팝업창이 발생한다. ‘예’ 버튼을 선택한다.

인증서 설치가 완료되었다는 팝업창이 열린다.

웹 브라우져를 이용해서 dev-ssl.google.com을 접속하면 아래와 같이 ‘보안 인증서에 문제가 있습니다’라는 내용이 뜨지 않는다.

 

<리늑서 서버 설치>

리눅스 서버에서 wget으로 접근시에는 인증시에 대한 검증에서 실패가 되어 에러를 발생시킨다.

]# wget https://dev-ssl.google.com/

[root@e61127 certs2]# wget https://dev-ssl.google.com/

--14:51:58-- https://dev-ssl.google.com/

=> `index.html'

Resolving dev-ssl.google.com... 1.1.1.1

Connecting to dev-ssl.google.com|1.1.1.1|:443... connected.

ERROR: Certificate verification error for dev-ssl.google.com: unable to get local issuer certificate

To connect to dev-ssl.google.com insecurely, use `--no-check-certificate'.

Unable to establish SSL connection.

이 문제를 해결하기 위해서는 간단하게 ‘--no-check-certificate’ 파라미터를 추가하여 접근한다.

]# wget --no-check-certificate https://dev-ssl.google.com/

--14:55:21-- https://dev-ssl.google.com/

=> `index.html.1'

Resolving dev-ssl.google.com... 10.25.149.50

Connecting to dev-ssl.google.com|10.25.149.50|:443... connected.

WARNING: Certificate verification error for dev-ssl.google.com: unable to get local issuer certificate

HTTP request sent, awaiting response... 200 OK

Length: 11 [text/html]

100%[===============================>] 11 --.--K/s

14:55:21 (565.38 KB/s) - `index.html' saved [11/11]

 

<Java에 인증서 설치>

Java를 이용하여 개발 보안 서버에 접근시 에러가 발생한다. Validation Check에서 실패하기 때문이다.

Java의 KeyStore에 해당 보안 개발 서버에 대한 접근이 되도록 할 수 있다. 보안 개발 서버를 사용하는 Linux 또는 PC 서버의 java에 인증서를 설치한다.

참고 웹은 다음 싸이트이다.

http://blogs.sun.com/andreas/entry/no_more_unable_to_find

예제는 다음과 같다. Apache Common의 httpclient 3.0.1를 가지고 dev-ssl.google.com 접속하는 코드이다.

import org.apache.commons.httpclient.HttpClient;

import org.apache.commons.httpclient.HttpStatus;

import org.apache.commons.httpclient.methods.GetMethod;

public class Test {

public static void main(String[] args) throws Exception {

byte[] responseBody = null;

HttpClient client = new HttpClient();

GetMethod method = new GetMethod("https://dev-ssl.google.com");

int responseCode = client.executeMethod(method);

if (responseCode != HttpStatus.SC_OK) {

System.out.println("Method failed: " + method.getStatusLine());

} else {

responseBody = method.getResponseBody();

System.out.println(new String(responseBody));

}

}

}

테스트 코드를 실행하면 SSL handshake를 하면서 인증서에 대한 valid 체크에서 발생했다는 Exception이 발생한다.

Exception in thread "main" javax.net.ssl.SSLHandshakeException: sun.security.validator.ValidatorException: PKIX path building failed: sun.security.provider.certpath.SunCertPathBuilderException: unable to find valid certification path to requested target

at com.sun.net.ssl.internal.ssl.Alerts.getSSLException(Alerts.java:174)

at com.sun.net.ssl.internal.ssl.SSLSocketImpl.fatal(SSLSocketImpl.java:1649)

at com.sun.net.ssl.internal.ssl.Handshaker.fatalSE(Handshaker.java:241)

at com.sun.net.ssl.internal.ssl.Handshaker.fatalSE(Handshaker.java:235)

at com.sun.net.ssl.internal.ssl.ClientHandshaker.serverCertificate(ClientHandshaker.java:1206)

at com.sun.net.ssl.internal.ssl.ClientHandshaker.processMessage(ClientHandshaker.java:136)

at com.sun.net.ssl.internal.ssl.Handshaker.processLoop(Handshaker.java:593)

at com.sun.net.ssl.internal.ssl.Handshaker.process_record(Handshaker.java:529)

at com.sun.net.ssl.internal.ssl.SSLSocketImpl.readRecord(SSLSocketImpl.java:893)

at com.sun.net.ssl.internal.ssl.SSLSocketImpl.performInitialHandshake(SSLSocketImpl.java:1138)

at com.sun.net.ssl.internal.ssl.SSLSocketImpl.writeRecord(SSLSocketImpl.java:632)

at com.sun.net.ssl.internal.ssl.AppOutputStream.write(AppOutputStream.java:59)

at java.io.BufferedOutputStream.flushBuffer(BufferedOutputStream.java:65)

at java.io.BufferedOutputStream.flush(BufferedOutputStream.java:123)

at org.apache.commons.httpclient.HttpConnection.flushRequestOutputStream(HttpConnection.java:827)

at org.apache.commons.httpclient.HttpMethodBase.writeRequest(HttpMethodBase.java:1975)

at org.apache.commons.httpclient.HttpMethodBase.execute(HttpMethodBase.java:993)

at org.apache.commons.httpclient.HttpMethodDirector.executeWithRetry(HttpMethodDirector.java:397)

at org.apache.commons.httpclient.HttpMethodDirector.executeMethod(HttpMethodDirector.java:170)

at org.apache.commons.httpclient.HttpClient.executeMethod(HttpClient.java:396)

at org.apache.commons.httpclient.HttpClient.executeMethod(HttpClient.java:324)

at org.Test.main(Test.java:13)

Caused by: sun.security.validator.ValidatorException: PKIX path building failed: sun.security.provider.certpath.SunCertPathBuilderException: unable to find valid certification path to requested target

at sun.security.validator.PKIXValidator.doBuild(PKIXValidator.java:323)

at sun.security.validator.PKIXValidator.engineValidate(PKIXValidator.java:217)

at sun.security.validator.Validator.validate(Validator.java:218)

at com.sun.net.ssl.internal.ssl.X509TrustManagerImpl.validate(X509TrustManagerImpl.java:126)

at com.sun.net.ssl.internal.ssl.X509TrustManagerImpl.checkServerTrusted(X509TrustManagerImpl.java:209)

at com.sun.net.ssl.internal.ssl.X509TrustManagerImpl.checkServerTrusted(X509TrustManagerImpl.java:249)

at com.sun.net.ssl.internal.ssl.ClientHandshaker.serverCertificate(ClientHandshaker.java:1185)

... 17 more

Caused by: sun.security.provider.certpath.SunCertPathBuilderException: unable to find valid certification path to requested target

at sun.security.provider.certpath.SunCertPathBuilder.engineBuild(SunCertPathBuilder.java:174)

at java.security.cert.CertPathBuilder.build(CertPathBuilder.java:238)

at sun.security.validator.PKIXValidator.doBuild(PKIXValidator.java:318)

... 23 more

Java 에서 신뢰할만한 웹 사이트를 등록하여 인증서가 발생되지 않게 할 수 있다.

Andreas Sterbenz’s Blogs(http://blogs.sun.com/andreas/resource/InstallCert.java)에서 소스를 다운받고 컴파일한다.

]# javac InstallCert.java

보안 개발 서버인 dev-ssl.google.com:443 을 파라미터로 넣고 java를 실행한다. 중간에 “

Enter certificate to add to trusted keystore”를 묻는 질문에 ‘1’을 입력한다.

]# java InstallCert dev-ssl.google.com:443

Loading KeyStore C:\Program Files\Java\jre6\lib\security\cacerts...

Opening connection to dev-ssl.google.com:443...

Starting SSL handshake...

javax.net.ssl.SSLHandshakeException: sun.security.validator.ValidatorException:

PKIX path building failed: sun.security.provider.certpath.SunCertPathBuilderExce

ption: unable to find valid certification path to requested target

at com.sun.net.ssl.internal.ssl.Alerts.getSSLException(Unknown Source)

at com.sun.net.ssl.internal.ssl.SSLSocketImpl.fatal(Unknown Source)

at com.sun.net.ssl.internal.ssl.Handshaker.fatalSE(Unknown Source)

at com.sun.net.ssl.internal.ssl.Handshaker.fatalSE(Unknown Source)

at com.sun.net.ssl.internal.ssl.ClientHandshaker.serverCertificate(Unkno

wn Source)

at com.sun.net.ssl.internal.ssl.ClientHandshaker.processMessage(Unknown

Source)

at com.sun.net.ssl.internal.ssl.Handshaker.processLoop(Unknown Source)

at com.sun.net.ssl.internal.ssl.Handshaker.process_record(Unknown Source

)

at com.sun.net.ssl.internal.ssl.SSLSocketImpl.readRecord(Unknown Source)

at com.sun.net.ssl.internal.ssl.SSLSocketImpl.performInitialHandshake(Un

known Source)

at com.sun.net.ssl.internal.ssl.SSLSocketImpl.startHandshake(Unknown Sou

rce)

at com.sun.net.ssl.internal.ssl.SSLSocketImpl.startHandshake(Unknown Sou

rce)

at org.InstallCert.main(InstallCert.java:88)

Caused by: sun.security.validator.ValidatorException: PKIX path building failed:

sun.security.provider.certpath.SunCertPathBuilderException: unable to find vali

d certification path to requested target

at sun.security.validator.PKIXValidator.doBuild(Unknown Source)

at sun.security.validator.PKIXValidator.engineValidate(Unknown Source)

at sun.security.validator.Validator.validate(Unknown Source)

at com.sun.net.ssl.internal.ssl.X509TrustManagerImpl.validate(Unknown So

urce)

at com.sun.net.ssl.internal.ssl.X509TrustManagerImpl.checkServerTrusted(

Unknown Source)

at org.InstallCert$SavingTrustManager.checkServerTrusted(InstallCert.jav

a:183)

... 9 more

Caused by: sun.security.provider.certpath.SunCertPathBuilderException: unable to

find valid certification path to requested target

at sun.security.provider.certpath.SunCertPathBuilder.engineBuild(Unknown

Source)

at java.security.cert.CertPathBuilder.build(Unknown Source)

... 15 more

Server sent 1 certificate(s):

1 Subject CN=dev-ssl.google.com, O=My Company Ltd, L=Newbury, ST=Berkshire, C=

GB

Issuer CN=dev-ssl.google.com, O=My Company Ltd, L=Newbury, ST=Berkshire, C=

GB

sha1 e6 69 34 03 ec f2 b8 2e eb 57 ad 96 9f 25 a9 69 16 87 fc 90

md5 f0 a8 d1 63 65 8f 5c c6 a0 9b ea 4f 48 58 85 3d

Enter certificate to add to trusted keystore or 'q' to quit: [1]

1

[

[

Version: V3

Subject: CN=dev-ssl.google.com, O=My Company Ltd, L=Newbury, ST=Berkshire, C=

GB

Signature Algorithm: MD5withRSA, OID = 1.2.840.113549.1.1.4

Key: Sun RSA public key, 1024 bits

modulus: 164347762420350693911866588325014644788520374516295193084948890924394

29081792855076590616988805837259808518029679131404902167803255295786539153611119

63396590676780565399712003063159189376410163690837891366179294614852352512484317

28761333574986219672551265326558528460114938587517180908714459949305597793224899

public exponent: 65537

Validity: [From: Sat Mar 12 14:05:07 KST 2011,

To: Tue Mar 09 14:05:07 KST 2021]

Issuer: CN=dev-ssl.google.com, O=, L=, ST=, C=

SerialNumber: [ 01]

Certificate Extensions: 4

[1]: ObjectId: 2.16.840.1.113730.1.13 Criticality=false

Extension unknown: DER encoded OCTET string =

0000: 04 1F 16 1D 4F 70 65 6E 53 53 4C 20 47 65 6E 65 ....OpenSSL Gene

0010: 72 61 74 65 64 20 43 65 72 74 69 66 69 63 61 74 rated Certificat

0020: 65 e

[2]: ObjectId: 2.5.29.14 Criticality=false

SubjectKeyIdentifier [

KeyIdentifier [

0000: 9E 6F 1F 00 01 A4 3F 8E 8D 03 B8 6C 0B B5 57 27 .o....?....l..W'

0010: 3A 11 0C 70 :..p

]

]

[3]: ObjectId: 2.5.29.35 Criticality=false

AuthorityKeyIdentifier [

KeyIdentifier [

0000: F4 8D D1 BC BA 8F B4 17 3E 98 93 E9 04 59 5A 32 ........>....YZ2

0010: 41 3D DF 36 A=.6

]

[CN=dev-ssl.google.com, O=, L=, ST=, C=]

SerialNumber: [ 00]

]

[4]: ObjectId: 2.5.29.19 Criticality=false

BasicConstraints:[

CA:false

PathLen: undefined

]

]

Algorithm: [MD5withRSA]

Signature:

0000: 2A E4 E5 14 91 58 17 84 39 DE BA 8F 41 61 92 F3 *....X..9...Aa..

0010: FA 9C 01 70 D9 F9 44 DD B1 02 E4 D9 1D 7C 58 2A ...p..D.......X*

0020: 5A D9 39 E2 24 F7 DD 09 DC 3C 62 C7 09 3D 87 43 Z.9.$....<b..=.C

0030: 44 DC 68 FE 12 89 8E 54 62 94 3D B7 F5 2E 4E 30 D.h....Tb.=...N0

0040: 99 B8 CB 8A 6D 8D 3A 47 0A E3 5C 81 5F AE 4B CF ....m.:G..\._.K.

0050: 8E 3D 70 64 EA E2 72 C5 E7 21 27 74 19 80 91 95 .=pd..r..!'t....

0060: E8 D7 07 E1 9E 53 06 66 D0 4C 79 92 DC D7 4E 30 .....S.f.Ly...N0

0070: 8F 21 37 15 38 01 9F F1 E9 C1 E9 E5 99 03 FB F8 .!7.8...........

]

Added certificate to keystore 'jssecacerts' using alias 'dev-ssl.google.com-1'

실행 이후에 자바 인증서인 jssecacerts 파일이 생성된다.

이 파일을 $JAVA_HOME/jre/lib/security 에 복사하고 Http 연결 테스트 코드를 실행하면 기대하는 결과값이 출력되는 것을 확인할 수 있다. 이렇게 보안 개발 서버와 통신하는 서버들은 이렇게 사용하여 공인 인증서 없이 테스트 환경을 구축할 수 있다.

 

<Keytool 이용>

자바에서는 인증서 파일에 대한 정보를 keystore라고 불리며, cacerts, jssecacerts 두 개의 파일을 사용한다. 읽는 순서는 첫번째가 jssecacerts 파일이고, 그 다음은 cacerts파일이다. 관리가 잘 되어, overwrite되지 않도록 잘 관리되어야 한다.

추가적인 보안 개발 서버를 추가할 때마다 keytool 명령어를 이용해서 관리할 수 있다.

keytool에 대한 자세한 내용은 문서를 참조한다.

http://download.oracle.com/javase/6/docs/technotes/tools/solaris/keytool.html

http://download.oracle.com/javase/6/docs/technotes/tools/windows/keytool.html

리눅스를 이용하는 경우에 jssecacerts keystore 파일에 보안 개발 서버에 대한 인증서가 있는지 확인할 수 있다. 아래 싦행 명령어의 ‘실제 위치’는 ‘$JAVA_HOME/jre/lib/security’를 의미한다.

]# keytool -list –keystore 실제위치/jssecacerts -storepass changeit | grep dev-ssl

dev-ssl.google.com-1, 2011. 3. 14, trustedCertEntry,

더 이상 사용하지 않는다면 keystore 파일에서 보안 개발 서버를 삭제할 수 있다.

]# keytool -delete -alias dev-ssl.google.com-1 -keystore 실제위치/jssecacerts -storepass changeit

]# keytool -list -keystore 실제위치/jssecacerts -storepass changeit | grep dev

(결과 없음)

자바에서 http 서버로 보안 개발 서버로 접근하면, Exception이 발생되는 것을 확인할 수 있다.

윈도우를 사용하는 경우, cmd창을 실행하고, 창안에서 다음과 같이 확인 및 삭제가 가능하다.

]# keytool.exe -list -keystore 실제위치/jssecacerts -storepass changeit | findstr dev-ssl

dev-ssl.google.com-1, 2011. 3. 14, trustedCertEntry,

]# keytool -delete -alias dev-ssl.google.com-1 -keystore 실제위치/jssecacerts -storepass changeit

]# keytool.exe -list -keystore 실제위치/jssecacerts -storepass changeit | findstr dev-ssl

(결과 없음)

Posted by 김용환 '김용환'

댓글을 달아 주세요