最後更新於2017年01月24日html
爲何寫這篇文章?ca是什麼?數字證書是什麼?ejbca又是什麼?java
讓咱們從http與https提及。http是超文本傳輸協議(HyperText Transfer Protocol)的縮寫,也是互聯網的基石,互聯網絕大部分數據傳輸都是基於http協議,可是,http自己有個致命的缺點,一個早期並不引人注目,現在不得不重視的缺點,就是明文傳輸。node
當你訪問一個http站點,例如http://www.login.com/python
輸入用戶名與密碼,例如admin/123456,點擊「Sign in」按鈕登陸,數據將被髮往服務器,期間登陸的我的數據「admin/123456」仍以明文形式在網絡上傳輸,若是有人抓到這個數據包,你的帳號密碼就有泄漏的風險。今天看到一則新聞,google chrome 56將把http站點要求輸入帳戶密碼的狀況標記爲不安全,相信將來http將成爲歷史。mysql
https很好的解決了這個問題,以www.baidu.com爲例,早期使用http://www.biadu.com,現在已更換爲https://www.baidu.com,訪問https://www.baidu.com能夠一把綠色的鎖,點擊就出現了以下信息,此時,與服務器之間的通訊鏈接是加密的,即便被他人截獲數據包,也很難知道傳輸的內容linux
https與http有啥區別?除了多一個「s」其實沒啥區別(開玩笑),這個「s」相當重要,使用https必須在服務器上安裝數字證書,上圖中點擊「查看證書」就能夠看到服務器證書信息nginx
有了數字證書,https還能實現另外一個很是重要的功能:身份驗證。仍是以www.baidu.com爲例,早期的http://www.biadu.com沒有數字證書,因此可能會出現這樣的狀況,你的網關被人劫持,全部你發往www.baidu.com的信息都被人截獲,同時,他對你的請求做出響應,以此冒充真正的www.baidu.com,也就是說,你訪問的www.baidu.com並非真正的www.baidu.com,而是一個代理網站,它的行爲表現與www.baidu.com很像。web
https://www.baidu.com就不會出現這種狀況了,由於它須要數字證書,而數字證書在全球範圍內都是惟一併不可僞造的算法
理解數字證書須要先了解對稱加密與非對稱加密,咱們從普通加密通訊模型開始sql
我向朋友發送一條消息「有時間一塊兒吃飯嗎」,實際在網絡中傳輸的是加密後的數據,不是原文,只是加密和解密過程對我和朋友是透明的
上述通訊過程當中,若是加密與解密使用相同的密鑰,稱爲對稱加密,若是使用不一樣的密鑰,稱爲非對稱加密
對稱加密的特色是速度快,可加密的數據量大;非對稱加密的特色是速度慢,所以只用來加密少許數據,但極難破解
非對稱加密過程當中使用的兩個不一樣的密鑰,一個稱爲「私鑰」,一個稱爲「公鑰」,它們一一對應,稱爲「密鑰對」,「公鑰」能夠給任何人使用,但「私鑰」必須本身保持,一旦「私鑰」泄露,這個密鑰對就應該被拋棄
密鑰對有兩個很是重要的特色:一、「私鑰」能夠導出「公鑰」,但「公鑰」沒法導出「私鑰」;二、經「私鑰」加密的內容只能由「公鑰」解密,經「公鑰」加密的內容也只能由「私鑰」解密。
這兩個特色有兩個重要用途:一、「私鑰」持有者用「私鑰」加密內容,發送給「公鑰」持有者解密,驗證「私鑰」持有者的身份。由於「公鑰」能解密的內容,只能是由「私鑰」加密的;二、「公鑰」持有者用「公鑰」加密內容,發送給「私鑰」持有者解密,保證內容安全。由於只有「私鑰」能解密,即便內容被截獲,截獲者也沒法知道內容是什麼
數字證書是一個文件,包含了使用者的身份信息、以及權威機構(CA)的數字簽名,就向咱們的居民身份證同樣,數字證書是「網絡身份證」,用於驗證互聯網上證書持有者的身份
頒發數字證書的機構叫CA,全世界只有少數權威的CA,由於頒發出去的證書它們是要負法律責任的,因此向它們申請證書也要繳費,咱們本身搭建的ejbca頒發的證書只適合在企業內部使用,這個CA是沒有權利向互聯網上其餘商業公司頒發證書的
有了數字證書,只要配置服務開啓https就可使用數字證書了
https對於數字證書的認證包括「單向認證」與「雙向認證」,「單向認證」只由客戶端驗證服務器,「雙向認證」則二者相互驗證,只要驗證不經過,通訊就自動中斷,下面介紹通訊的流程以及如何在tomcat中配置
那麼,數字證書從何而來?數字證書由專門的機構頒發,這種機構稱爲CA(Certificate Authority),ejbca則是CA的一種java實現
「單向認證」的兩個實體
「單向認證」的流程以下圖
簡要說明以下
tomcat配置https單向認證
<Connector port="8443" protocol="org.apache.coyote.http11.Http11Protocol" maxThreads="150" SSLEnabled="true" scheme="https" secure="true" clientAuth="false" sslProtocol="TLS" keystoreFile="D:\tomcat.jks" keystorePass="123456" />
nginx配置https單向認證
server { listen 443 ssl; server_name localhost; ssl_certificate cert.pem; #服務器證書公鑰部分 ssl_certificate_key cert.key; #服務器證書私鑰 ssl_session_cache shared:SSL:1m; ssl_session_timeout 5m; ssl_ciphers HIGH:!aNULL:!MD5; ssl_prefer_server_ciphers on; location / { proxy_pass http://127.0.0.1:4000; #代理tomcat的http服務 } }
「雙向認證」的兩個實體
「雙向認證」的流程以下圖
簡要說明以下
tomcat配置https雙向認證
<Connector port="8443" protocol="org.apache.coyote.http11.Http11Protocol" maxThreads="150" SSLEnabled="true" scheme="https" secure="true" clientAuth="true" sslProtocol="TLS" keystoreFile="D:\tomcat.jks" keystorePass="123456" truststoreFile="D:\tomcat.jks" truststorePass="123456"/>
nginx配置https雙向認證
server { listen 443 ssl; server_name localhost; ssl_certificate cert.pem; #服務器證書公鑰部分 ssl_certificate_key cert.key; #服務器證書私鑰 ssl_verify_client on; #開啓瀏覽器認證 ssl_client_certificate ca.pem; #CA根證書 ssl_session_cache shared:SSL:1m; ssl_session_timeout 5m; ssl_ciphers HIGH:!aNULL:!MD5; ssl_prefer_server_ciphers on; location / { proxy_pass http://127.0.0.1:4000; #代理tomcat的http服務 proxy_set_header client-cert $ssl_client_cert; #將瀏覽器證書傳遞給tomcat,tomcat經過header拿到證書 } }
檢查已安裝jdk,若是有,先刪除
rpm -qa|grep java rpm -e --nodeps filename
從oracle官方網站下載jdk安裝包:jdk-8u111-linux-x64.rpm,放在/usr/file目錄
rpm -ivh jdk-8u111-linux-x64.rpm
默認安裝在/usr/java目錄,安裝完後配置環境變量
vi /etc/profile
追加以下內容
#java conf export JAVA_HOME=/usr/java/jdk1.8.0_111 export PATH=$PATH:$JAVA_HOME/bin export CLASSPATH=:$JAVA_HOME/lib
使配置當即生效,而後檢查安裝結果
source /etc/profile java -version
從ant官方網站下載ant安裝包:apache-ant-1.9.7-bin.tar.gz,解壓
tar xvf apache-ant-1.9.7-bin.tar.gz -C /usr/java/
配置環境變量
vi /etc/profile
追加以下內容
#ant conf export ANT_HOME=/usr/java/apache-ant-1.9.7 export PATH=$PATH:$ANT_HOME/bin
使配置當即生效,而後檢查安裝結果
source /etc/profile ant -version
安裝前查看已安裝的mysql或mysql庫,並刪除它們
rpm -qa|grep -i mysql rpm -e --nodeps filename
若是重裝mysql,查找安裝mysql產生的文件,並刪除它們
find / -name mysql rm -fr filename
安裝mysql所需的perl依賴
yum install -y perl-Module-Install.noarch
從mysql官方網站下載Red Hat Enterprise Linux 6 / Oracle Linux 6 (x86, 64-bit)對應5.6版本的安裝包:MySQL-client-5.6.33-1.el6.x86_64.rpm、MySQL-server-5.6.33-1.el6.x86_64.rpm,依次安裝
rpm -ivh MySQL-server-5.6.33-1.el6.x86_64.rpm rpm -ivh MySQL-client-5.6.33-1.el6.x86_64.rpm
安裝完成,啓動mysql服務,並設置爲開機啓動
service mysql start chkconfig mysql on
mysql安裝完成後爲root帳戶生成隨機密碼,位於/root目錄下的.mysql_secret文件中,用這個密碼登陸root
cat /root/.mysql_secret mysql -uroot -pLTHVNyKmmg7jwiLk
修改root密碼並配置遠程訪問
set password=password('root'); grant all privileges on *.* to 'root'@'%' identified by 'root';
從jboss官方網站下載jboss安裝包:jboss-as-7.1.1.Final.tar.gz,解壓並配置環境變量
tar xvf jboss-as-7.1.1.Final.tar.gz -C /usr/java vi /etc/profile
追加內容
#jboss conf export JBOSS_HOME=/usr/java/jboss-as-7.1.1.Final
使配置當即生效
source /etc/profile
啓動jboss,注意最後的&符號,待啓動完成再運行「exit」
sh /usr/java/jboss-as-7.1.1.Final/bin/standalone.sh & exit
這樣jboss就運行在後臺了,如下命令查看jboss進程並關閉
ps -ef|grep jboss kill -9 進程號
建立目錄,而後在該目錄下建立module.xml
mkdir -p /usr/java/jboss-as-7.1.1.Final/modules/com/mysql/main cd /usr/java/jboss-as-7.1.1.Final/modules/com/mysql/main vi module.xml
module.xml內容以下
<?xml version="1.0" encoding="UTF-8"?> <module xmlns="urn:jboss:module:1.0" name="com.mysql"> <resources> <resource-root path="mysql-connector-java-5.1.27.jar"/> </resources> <dependencies> <module name="javax.api"/> <module name="javax.transaction.api"/> </dependencies> </module>
下載mysql的驅動包mysql-connector-java-5.1.27.jar,放在/usr/file目錄,而後拷貝到當前目錄
cp /usr/file/mysql-connector-java-5.1.27.jar ./
打開新的shell窗口,運行
sh /usr/java/jboss-as-7.1.1.Final/bin/jboss-cli.sh -c
若是是「disconnect」狀態,先輸入「connect」,多回車幾回後,運行下面命令
/subsystem=datasources/jdbc-driver=com.mysql.jdbc.Driver:add(driver-name=com.mysql.jdbc.Driver,driver-class-name=com.mysql.jdbc.Driver,driver-module-name=com.mysql,driver-xa-datasource-class-name=com.mysql.jdbc.jdbc.jdbc2.optional.MysqlXADataSource) :reload
從ejbca官方網站下載ejbca安裝包:ejbca_ce_6_3_1_1.zip,放在/usr/file目錄,解壓,準備修改配置
unzip /usr/file/ejbca_ce_6_3_1_1.zip -d /usr/java cd /usr/java mv ejbca_ce_6_3_1_1 ejbca-ce-6.3.1.1 cd /usr/java/ejbca-ce-6.3.1.1/conf/
一、修改ejbca.properties
mv ejbca.properties.sample ejbca.properties vi ejbca.properties
修改以下內容
appserver.home=/usr/java/jboss-as-7.1.1.Final appserver.type=jboss
二、修改database.properties
mv database.properties.sample database.properties vi database.properties
修改以下內容
# dataSource datasource.jndi-name=jboss/datasources/MySqlDS # mysql info database.name=mysql database.url=jdbc:mysql://127.0.0.1:3306/ejbca?characterEncoding=UTF-8 database.driver=com.mysql.jdbc.Driver database.username=root database.password=root
三、修改install.properties
mv install.properties.sample install.properties vi install.properties
修改以下內容
#設置ca名稱 ca.name=test #設置ca信息 ca.dn=CN=test,O=test,C=cn
四、修改cesecore.properties、jaxws.properties,不須要修改內容
mv cesecore.properties.sample cesecore.properties mv jaxws.properties.sample jaxws.properties
五、修改web.properties
mv web.properties.sample web.properties vi web.properties
修改以下內容
superadmin.password=ejbca superadmin.cn=superadmin httpsserver.hostname=ca.test.com httpsserver.dn=CN=${httpsserver.hostname},O=test,C=cn
首先,在配置的mysql中建立「ejbca」數據庫,編碼「utf-8」,而後正式用ant構建ejbca並安裝到jboss
cd /usr/java/ejbca-ce-6.3.1.1 ant clean deploy ant install ant deploy-keystore
deploy用ant部署,install生成證書,deploy-keystore將證書部署到jboss,前兩步所需時間較長,過程當中如需輸入,請直接回車
打開新的shell窗口,運行
sh /usr/java/jboss-as-7.1.1.Final/bin/jboss-cli.sh -c
若是是「disconnect」狀態,運行「connect」,多回車幾回,準備運行下面4部分配置
第一部分(配置任意主機可訪問)
/interface=http:add(inet-address="0.0.0.0") /interface=httpspub:add(inet-address="0.0.0.0") /interface=httpspriv:add(inet-address="0.0.0.0") /socket-binding-group=standard-sockets/socket-binding=http:add(port="8080",interface="http") /subsystem=undertow/server=default-server/http-listener=http:add(socket-binding=http) /subsystem=undertow/server=default-server/http-listener=http:write-attribute(name=redirect-socket, value="httpspriv") :reload
第二部分(配置證書)
/core-service=management/security-realm=SSLRealm:add() /core-service=management/security-realm=SSLRealm/server-identity=ssl:add(keystore-path="${jboss.server.config.dir}/keystore/keystore.jks", keystore-password="serverpwd", alias="prod-ica1") /core-service=management/security-realm=SSLRealm/authentication=truststore:add(keystore-path="${jboss.server.config.dir}/keystore/truststore.jks", keystore-password="changeit") /socket-binding-group=standard-sockets/socket-binding=httpspriv:add(port="8443",interface="httpspriv") /socket-binding-group=standard-sockets/socket-binding=httpspub:add(port="8442", interface="httpspub") :reload
第三部分(配置ssl)
/subsystem=undertow/server=default-server/https-listener=httpspriv:add(socket-binding=httpspriv, security-realm="SSLRealm", verify-client=REQUIRED) /subsystem=undertow/server=default-server/https-listener=httpspub:add(socket-binding=httpspub, security-realm="SSLRealm") :reload
第四部分(配置web service)
/system-property=org.apache.tomcat.util.buf.UDecoder.ALLOW_ENCODED_SLASH:add(value=true) /system-property=org.apache.catalina.connector.CoyoteAdapter.ALLOW_BACKSLASH:add(value=true) /system-property=org.apache.catalina.connector.URI_ENCODING:add(value="UTF-8") /system-property=org.apache.catalina.connector.USE_BODY_ENCODING_FOR_QUERY_STRING:add(value=true) /subsystem=webservices:write-attribute(name=wsdl-host, value=jbossws.undefined.host) /subsystem=webservices:write-attribute(name=modify-wsdl-address, value=true) :reload
拷貝ejbca根目錄下的p12目錄下的superadmin.p12文件到windows,推薦xftp工具,或者訪問
http://172.17.210.124:8080/ejbca/doc/superadmin.p12
下載該文件到windows,雙擊開始安裝,密碼「ejbca」,安裝完,訪問
https://172.17.210.124:8443/ejbca/adminweb
也就是ejbca的管理後臺
假設安裝ejbca系統的服務器地址爲
172.17.210.124
編輯windows的「C:\Windows\System32\drivers\etc」下的hosts文件,加入一行
172.17.210.124 ca.xmyself.com
ejbca提供了兩個操做界面
管理員界面(須要superadmin證書)
https://ca.xmyself.com:8443/ejbca/adminweb/
用戶界面(不須要證書)
http://ca.xmyself.com:8080/ejbca/
ejbca管理員界面,打開「RA Functions」—「Add End Entity」菜單,填寫如下「Required」列打勾的項。
用戶模板選擇「EMPTY」
輸入用戶名與密碼
Common name,若是是服務器用證書,這裏請填寫域名
填寫證書信息,證書模板選擇「ENDUSER」,CA選擇「dev」,Token選擇「P12 file」
點擊「Add」註冊
ejbca用戶界面,打開「Enroll」—「Create Browser Certificate」菜單
輸入用戶名和密碼,點擊「OK」按鈕
「Key length」選擇「2048 bits」;「Certificate profile」選擇「ENDUSER」,點擊「Enroll」按鈕下載證書
ejbca管理員界面,打開「RA Functions」—「Search End Entities」菜單。「Search end entities with status」處下拉框選擇「All」,點擊右邊的「Search」按鈕查看用戶信息(下圖省略其餘列)
勾選須要吊銷的用戶,點擊表格下方的「Revoke Selected」按鈕
ejbca管理員界面,打開「RA Functions」—「Search End Entities」菜單。「Search end entities with status」處下拉框選擇「All」,點擊右邊的「Search」按鈕查看用戶信息(下圖省略其餘列)
點擊須要更新證書用戶的最右邊列中的「Edit End Entity」超連接,編輯用戶
設置「Status」爲「New」,點擊右邊的「Save」按鈕。而後輸入新密碼,其餘項保持不變,點擊頁面最下方的「Save」按鈕保存設置
ejbca用戶界面,打開「Retrieve」—「Fetch CA Certificates」菜單,能夠下載不一樣格式的根證書
用戶註冊時,證書模板選擇「SERVER」,CA選擇「dev」,Token選擇「JKS file」,其餘項不變
下載證書時,在ejbca用戶界面中,打開「Enroll」—「Create Keystore」菜單,輸入用戶名與密碼,進入下面的頁面
「Key length」選擇「2048 bits」;「Certificate profile」選擇「SERVER」,點擊「Enroll」按鈕下載證書
ejbca系統裝好了,能夠管理數字證書了,可是,全部操做都在ejbca界面中執行,先不說全是英文,單單它裏面配置項就多的人眼花繚亂,不少配置項要麼是固定的,要麼是不須要的,所以,最合理的作法是在ejbca之上構建中間層,用戶訪問中間層提供的服務,中間層調用ejbca的web service interface
ejbca提供的web service須要證書認證,證書格式:jks。對superadmin用戶執行更新操做,修改「Token」值爲「JKS file」
保存,而後按照下載普通用戶證書的步驟下載superadmin的證書
將web service所需的jar包添加到工程中,這些jar包是下面兩個目錄下的全部jar
/usr/java/ejbca-ce-6.3.1.1/dist/ejbca-ws-cli/lib /usr/java/ejbca-ce-6.3.1.1/dist/ejbca-ws-cli
初始化web service
public void init() { if (!new File(certPath).exists()) return; CryptoProviderTools.installBCProvider(); System.setProperty("javax.net.ssl.trustStore", "d:/superadmin.jks"); System.setProperty("javax.net.ssl.trustStorePassword", "123456"); System.setProperty("javax.net.ssl.keyStore", "d:/superadmin.jks"); System.setProperty("javax.net.ssl.keyStorePassword", "123456"); QName qname = new QName("http://ws.protocol.core.ejbca.org/", "EjbcaWSService"); try { EjbcaWSService service = new EjbcaWSService(new URL("https://ca.xmyself.com:8443/ejbca/ejbcaws/ejbcaws?wsdl"), qname); EjbcaWS ejbcaWS = service.getEjbcaWSPort(); } catch (Exception e) { } }
注意:鏈接地址只能是域名,所以,鏈接ejbca的web service的機器要配置hosts
172.17.210.124 ca.xmyself.com
查看用戶是否已經註冊
private boolean isExist(String username) throws Exception { UserMatch usermatch = new UserMatch(); usermatch.setMatchwith(UserMatch.MATCH_WITH_USERNAME); usermatch.setMatchtype(UserMatch.MATCH_TYPE_EQUALS); usermatch.setMatchvalue(username); try { List<UserDataVOWS> users = ejbcaWS.findUser(usermatch); if (users != null && users.size() > 0) { return true; } else { return false; } } catch (Exception e) { throw new Exception("檢查用戶 " + username() + " 是否存在時出錯:" + e.getMessage()); } }
用戶註冊與更新,用的都是editUser()方法,所以要先判斷是否存在
public void editUser() throws Exception { UserDataVOWS userData = new UserDataVOWS(); userData.setUsername("testname");//用戶名 userData.setPassword("123456");//密碼 userData.setClearPwd(false);//默認 userData.setSubjectDN("CN=" + "testname" + ",OU=" + "testou" + ",O=" + "testo" + ",C=cn" + ",telephoneNumber=" + "1234567890" );//設置惟一甄別名 String pattern = "yyyy-MM-dd HH:mm:ssZZ"; // ISO 8601標準時間格式 userData.setStartTime(DateFormatUtils.format(new Date(),pattern));//證書有效起始日期 userData.setEndTime(DateFormatUtils.format(DateUtils.addDays(new Date(), 100), pattern));//結束日期 userData.setCaName("test");//ca名稱,ejbca的名稱 userData.setSubjectAltName(null); userData.setEmail("test@test.com");//郵件地址 userData.setStatus(UserDataVOWS.STATUS_NEW);//狀態爲new userData.setTokenType(UserDataVOWS.TOKEN_TYPE_P12);//設置p12格式證書 userData.setEndEntityProfileName("user");//終端實體模板 userData.setCertificateProfileName("user");//證書模板 try { ejbcaWS.editUser(userData); } catch (Exception e) { throw new Exception(e.getMessage()); } }
代碼中有幾處值得注意的,終端實體模板「user」和證書模板「user」須要在ejbca管理員界面中配置,而且終端實體模板「user」中要配置開啓「SubjectDN」的屬性如CN、OU、O、C、telephoneNumber等,還要容許修改startTime和endTime
吊銷證書
public void revoke(String username) throws ServiceException { try { ejbcaWS.revokeUser(username, RevokedCertInfo.REVOCATION_REASON_UNSPECIFIED, false); } catch (Exception e) { } }
建立證書
private void createCert(String username, String password, String path) throws Exception { FileOutputStream fileOutputStream = null; try { // 建立證書文件 KeyStore ksenv = ejbcaWS.pkcs12Req(username, password, null, "2048", AlgorithmConstants.KEYALGORITHM_RSA); java.security.KeyStore ks = KeyStoreHelper.getKeyStore(ksenv.getKeystoreData(), "PKCS12", password); fileOutputStream = new FileOutputStream(path + File.separator + username + ".p12"); ks.store(fileOutputStream, password.toCharArray()); // 建立密碼文件 File pwdFile = new File(path + File.separator + username + ".pwd"); pwdFile.createNewFile(); BufferedWriter out = new BufferedWriter(new FileWriter(pwdFile)); out.write(password); out.flush(); out.close(); } catch (Exception e) { throw new Exception("用戶 " + username + " 證書建立失敗:" + e.getMessage()); } finally { if (fileOutputStream != null) { try { fileOutputStream.close(); } catch (IOException e) { } } } }
證書建立在服務器上,用戶調用下載證書的接口服務,返回下載地址,所以,這裏須要一個下載服務器,下面介紹將nginx配置爲下載服務器,文件存放的目錄是/var/tmp + /download/
location ^~ /download/{ root /var/tmp; if ($request_filename ~* ^.*?\.(txt|doc|pdf|rar|gz|zip|docx|exe|xlsx|ppt|pptx)$){ add_header Content-Disposition: 'attachment;'; } }