self signed certificate with custom CA and apache SSLRequire
建立自簽 CA 憑證
先建立一把 CA key
# openssl genrsa -des3 -out rootCA.key 4096
然後產生 root CA 憑證
# openssl req -x509 -new -nodes -key rootCA.key -sha256 -days 18250 -out rootCA.crt
可以進去 /etc/pki/tls/openssl.cnf 編輯預設值,沒編輯也沒關係。我是會編輯下列幾個項目:countryName_default, localityName_default, 0.organizationName_default, organizationalUnitName_default
也會改下列的設定
[ policy_match ]
countryName = match
stateOrProvinceName = optional
再來把建立好的 CA key 與憑證 放到預設的位置,還有初始設定
# cp rootCA.key /etc/pki/CA/private/cakey.pem
# cp rootCA.crt /etc/pki/CA/cacert.pem
# touch /etc/pki/CA/index.txt
# echo ‘1000’ > /etc/pki/CA/serial
# echo “1000” > /etc/pki/CA/crlnumber
在 client 端產生 csr
$ openssl genrsa -out user1.key 2048
$ openssl req -new -key user1.key -out user1.csr
用下列指令簽發 client 端產生的 csr 然後就可以把憑證給使用者
# openssl ca -out user1.crt -infiles user1.csr
# openssl ca -days 720 -out user2.crt -infiles user2.csr
使用者可以用下列指令可以產生 p12
$ openssl pkcs12 -export -out user1.p12 -inkey user1.key -in user1.crt
補充說明,使用者可以透過下列指令產生 ssh keys:
$ openssl pkcs12 -in user1.p12 -clcerts -nodes -nocerts | openssl rsa > ~/.ssh/id_rsa
$ chmod 0600 ~/.ssh/id_rsa
$ ssh-keygen -y -f ~/.ssh/id_rsa >> ~/.ssh/authorized_keys
伺服器端 可以把建立好的 rootCA.crt 加入 /etc/pki/tls/certs/ca-bundle.crt 檔案
# cat rootCA.crt >> /etc/pki/tls/certs/ca-bundle.crt
然後編輯 apache ssl 設定檔案
# vi /etc/httpd/conf.d/ssl.conf
SSLVerifyClient require SSLVerifyDepth 10 SSLOptions +FakeBasicAuth SSLRequireSSL AuthName "Private Authentication" AuthType Basic SSLRequire %{SSL_CLIENT_S_DN_O} eq "XXXX" \ and %{SSL_CLIENT_S_DN_OU} in {"YYYY", "ZZZZ"}
然後重新啟動網頁伺服器
在 client 端匯入 p12 檔後 開瀏覽器瀏覽 https://xxx.xx/private 就可以用憑證認證使用者了 在網頁端 可以用下列變數取得使用者 CN
$user_cn = $_SERVER[“SSL_CLIENT_S_DN_CN”];
假設日後要 revoke 那要產生 crl.pem
# openssl ca -revoke user1.crt
# openssl ca -gencrl -out /etc/pki/CA/crl.pem
# openssl crl -inform PEM -in /etc/pki/CA/crl.pem -outform DER -out root.crl
# ### cp crl.pem and root.crl to your web server
在預設的openssl.cnf 要改變一下你的 crl 主機位址及路徑
# vi /etc/pki/tls/openssl.cnf
[ usr_cert ] basicConstraints=CA:FALSE # 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 authorityKeyIdentifier=keyid,issuer:always crlDistributionPoints = URI:https://crl.liho.tw/root.crl nsCaRevocationUrl = https://crl.liho.tw/crl.pem
# openssl crl -in crl.pem -text -noout
# openssl crl -inform DER -text -noout -in root.crl
但是crl我不知道要怎麼讓瀏覽器自己知道已經被 revoked 了,後來用下列 php 程式暫時解決了
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 | $ssl_client_verify = $_SERVER['SSL_CLIENT_VERIFY']; if ($ssl_client_verify == "SUCCESS") { $ssl_serial = $_SERVER['SSL_CLIENT_M_SERIAL']; $ssl_crl_contents = file_get_contents("https://crl.liho.tw/root.crl"); file_put_contents("root.crl", $ssl_crl_contents); $output = shell_exec('/usr/bin/openssl crl -inform DER -text -noout -in root.crl'); if (strpos($output, "Serial Number: ".$ssl_serial) !== false) { echo "<h1>Your certificate has been revoked!!</h1>\n"; echo "Please contact your system administrator. Thanks!\n"; exit(); } else { // do something else XD } } |