模拟CA系统
實(shí)現(xiàn)一個(gè)ca系統(tǒng),可以接受用戶的認(rèn)證請(qǐng)求,安全儲(chǔ)存用戶信息,記錄儲(chǔ)存對(duì)用戶的一些認(rèn)證信息,給用戶頒發(fā)證書,可以吊銷。
(1)接受用戶的提交申請(qǐng),提交時(shí)候讓用戶自己產(chǎn)生公鑰對(duì);
(2)接受用戶的申請(qǐng),包括用戶信息的表單提交,公鑰的提交;
(3)在對(duì)用戶實(shí)施認(rèn)證的過(guò)程中,儲(chǔ)存相應(yīng)的電子文檔,比如證書、營(yíng)業(yè)執(zhí)照的掃描文檔;
(4)通過(guò)驗(yàn)證的給予頒發(fā)證書;
(5)用戶密鑰丟失時(shí),可以吊銷證書,密鑰作廢。
1.前言
1.1實(shí)驗(yàn)?zāi)康?br /> 實(shí)現(xiàn)一個(gè)ca系統(tǒng),可以接受用戶的認(rèn)證請(qǐng)求,安全儲(chǔ)存用戶信息,記錄儲(chǔ)存對(duì)用戶的一些認(rèn)證信息,給用戶頒發(fā)證書,可以吊銷。
(1)接受用戶的提交申請(qǐng),提交時(shí)候讓用戶自己產(chǎn)生公鑰對(duì);
(2)接受用戶的申請(qǐng),包括用戶信息的表單提交,公鑰的提交;
(3)在對(duì)用戶實(shí)施認(rèn)證的過(guò)程中,儲(chǔ)存相應(yīng)的電子文檔,比如證書、營(yíng)業(yè)執(zhí)照的掃描文檔;
(4)通過(guò)驗(yàn)證的給予頒發(fā)證書;
(5)用戶密鑰丟失時(shí),可以吊銷證書,密鑰作廢。
1.2實(shí)驗(yàn)環(huán)境
運(yùn)行系統(tǒng):win10
開(kāi)發(fā)環(huán)境:mysql、navicat、java,eclipse
1.3安裝說(shuō)明
1.3.1使用mysql(見(jiàn)附錄)
1.3.2數(shù)據(jù)庫(kù)連接(使用navicat)
1.3.3建立數(shù)據(jù)表
1.4準(zhǔn)備工作
1.4.1 mysql連接eclipse
(1)關(guān)鍵步驟是下載mysql-connector-java-8.0.18.jar
這步非常關(guān)鍵,否則連接不上數(shù)據(jù)庫(kù)
1-1建立成功
1.4.2新建jdbcutil.java
用于實(shí)現(xiàn)數(shù)據(jù)庫(kù)和程序的連接
其關(guān)鍵代碼如下:
driverClass=“com.mysql.jdbc.Driver”;
url=“jdbc:mysql://localhost:3306/mydb?useUnicode=true&characterEncoding=UTF-8&useJDBCCompliantTimezoneShift=true&useLegacyDatetimeCode=false&serverTimezone=UTC”;
user=“root”;
password="";
Driver是個(gè)實(shí)現(xiàn)類,它由具體的數(shù)據(jù)庫(kù)廠商來(lái)實(shí)現(xiàn)。
它的connect方法可以獲取數(shù)據(jù)庫(kù)連接
1.4.3申請(qǐng)證書(詳見(jiàn)附錄)
(1)設(shè)置密鑰庫(kù)的口令,并填寫信息申請(qǐng)證書,這里密碼為111111
(2)將證書導(dǎo)出,存入文件中
(3)更改密碼(不是必要步驟)設(shè)置密碼為Changeit
2.系統(tǒng)分析
2.1系統(tǒng)需求概述
本次課程設(shè)計(jì)是實(shí)現(xiàn)一個(gè)簡(jiǎn)單的CA頒發(fā)系統(tǒng),該系統(tǒng)主要實(shí)現(xiàn)以下幾個(gè)需求:
(1) CA頒發(fā)機(jī)構(gòu)(服務(wù)器)接受用戶(客戶機(jī))的認(rèn)證請(qǐng)求,(服務(wù)器)通過(guò)用戶提交的信息,判斷申請(qǐng)用戶是否合法,包括用戶的公鑰是否已經(jīng)申請(qǐng)注冊(cè)過(guò),是否注銷過(guò),是否接受用戶的請(qǐng)求。
(2) 用戶認(rèn)證信息通過(guò)后,將認(rèn)證信息存儲(chǔ),接下來(lái)可以接收用戶信息和用戶自己產(chǎn)生的公鑰;
(3) 接受完成后,先通過(guò)獲取CA證書獲得CA頒發(fā)機(jī)構(gòu)的信息以及CA的私鑰,然后對(duì)接收到的用戶信息和公鑰進(jìn)行數(shù)字簽名,生成數(shù)字證書并頒發(fā)給用戶,同時(shí)需要存儲(chǔ)用戶信息,證書
(4) 數(shù)字證書吊銷:掛失操作具體為將用戶需要掛失的公鑰存入到不信任的公鑰數(shù)據(jù)庫(kù)中,這樣,這個(gè)公鑰就會(huì)被作廢,但是在掛失前需要對(duì)用戶進(jìn)行身份驗(yàn)證,防止惡意掛失行為,驗(yàn)證通過(guò)后還要訪問(wèn)不信任公鑰數(shù)據(jù)庫(kù)中這個(gè)公鑰是否已經(jīng)存在,如果存在,彈出錯(cuò)誤提示,如果不存在,則將公鑰進(jìn)行掛失。
(5) 客戶機(jī)能夠接收到服務(wù)器對(duì)自己請(qǐng)求的回復(fù),如果證書申請(qǐng)成功,能將證書在
本地找到。
2.2系統(tǒng)體系結(jié)構(gòu)設(shè)計(jì)
2.2.1系統(tǒng)實(shí)現(xiàn)流程圖
首先從bytes[8]中提取判斷符,判斷用戶的請(qǐng)求(a代表申請(qǐng),b代表注銷)
申請(qǐng)證書調(diào)用函數(shù)camain(bytes,apub),將用戶提供的用戶信息和公鑰傳給函數(shù)。
將處理結(jié)果用i保存。
注銷證書調(diào)用函數(shù)cancel(bytes[6], bytes[7]);將用戶提供的認(rèn)證信息和公鑰傳給函數(shù)。將處理結(jié)果用i保存。
判斷bytes[8]和i,判斷給用戶返回什么信息。
2-1總體功能流程圖
2.2.2網(wǎng)絡(luò)通信流程圖
serverSocket = new ServerSocket(10086);
創(chuàng)建一個(gè)socket對(duì)象,監(jiān)聽(tīng)10086接口。
in = new ObjectInputStream(socket.getInputStream());
將客戶機(jī)傳來(lái)的信息用in接收
然后將in中的內(nèi)容用數(shù)組bytes[]存儲(chǔ)
os = socket.getOutputStream();
將服務(wù)器要傳給客戶機(jī)的內(nèi)容存在pw和newcert中,其中pw是系統(tǒng)消息,newcert是證書和簽名。
圖2-2socket流程圖
3.功能設(shè)計(jì)
3.1判斷用戶
設(shè)計(jì)三個(gè)變量,ss,id,s1,分別用于判斷申請(qǐng)人是否合法,用戶信息表是否注冊(cè),是否掛失過(guò),并對(duì)于結(jié)果存在i中,由i得到系統(tǒng)提示。
這三個(gè)功能分別調(diào)用函數(shù)
if_id_exists(userID) ,
userinfo_id_exist(userID)
if_pkey_exsit(apub);
判斷過(guò)程如下:
3.2證書生成
函數(shù)Mycertificate(String []Personinfo,String Apub),用于證書生成,
我們傳遞了用戶信息和公鑰兩個(gè)變量。
getSignCertInfo();
//獲取簽名證書信息
signCertificate(Personinfo,Apub);
這個(gè)函數(shù)用來(lái)用簽名證書信息簽發(fā)代簽名證書,我們傳遞給它用戶信息和公鑰
boolean test=createNewCertificate(Personinfo) ;
創(chuàng)建并保存簽發(fā)后的新證書,這時(shí)我們只需要將用戶信息發(fā)給它。并用布爾型變量表示,證書是否申請(qǐng)成功。
用函數(shù)createNewCertificate(String []info) 取得并保存證書,用變量nerwcert保存證書,sig保存簽名。
newcert=new X509CertImpl(ClientInfo);
newcert.sign(CAPrivateKey, “sha256WithRSA”);
取得這些信息后我們用文件保存函數(shù)將證書保存,用返回函數(shù),將證書信息交給socket類,由它發(fā)給客戶機(jī)。
3.4界面詳細(xì)設(shè)計(jì)
程序的界面設(shè)計(jì)主要是顯示數(shù)字簽名、保存的結(jié)果,以及用戶信息的設(shè)置輸入等功能。申請(qǐng)證書之前,該程序要求用戶輸入信息,然后才能單擊申請(qǐng)證書按鈕,既可以進(jìn)行證書申請(qǐng),最后要在文本框中顯示申請(qǐng)數(shù)字簽名最后的結(jié)果,簽名完成之后用戶可以繼續(xù)進(jìn)行掛失申請(qǐng)或者是直接退出界面。
我們覺(jué)得采用Swing常用組件之文本區(qū):JTextArea、按鈕:button、、標(biāo)簽組:JLabel以及文本字段:JTextFileld來(lái)實(shí)現(xiàn)。
(1) 文本顯示框:
具體位置:main2.java121行
private JButton getC2ConnJButton() {
if (canceljbutton==null) {
canceljbutton=new JButton();
canceljbutton.setBounds(new Rectangle(300,145,97,26));
canceljbutton.setText(“掛失”);
canceljbutton.addActionListener(this);
}
return canceljbutton;
}
(2) 名字編輯框
具體位置:main2.java35行
jusertextfield=new JTextField(“root”);
jusertextfield.setBounds(new Rectangle(70,10,70,26));
}
(3) 文本區(qū)域信息添加函數(shù)
具體位置:main2.java200行
jtextarea.append("\n"+s+"\n");
(4) 掛失按鈕
具體位置:main2.java110行
canceljbutton=new JButton();
canceljbutton.setBounds(new Rectangle(300,145,97,26));
canceljbutton.setText(“掛失”);
canceljbutton.addActionListener(this);
3.5socket通信設(shè)計(jì)
上圖體現(xiàn)的是,利用socket通信時(shí),一段數(shù)據(jù)流的標(biāo)準(zhǔn)開(kāi)銷,也成為我們通信的報(bào)文頭,其中id用來(lái)做身份認(rèn)證,公鑰作為客戶簽發(fā)自定義的公鑰,請(qǐng)求類型用于我們判斷對(duì)方的數(shù)據(jù)流的函數(shù)流向。
4.解決系統(tǒng)特色及關(guān)鍵技術(shù)
4.1將字符串轉(zhuǎn)化為公鑰
具體位置:myc.java220行
X509EncodedKeySpec apubkeyspec=new X509EncodedKeySpec(new BASE64Decoder().decodeBuffer(pubkey));
//RSA對(duì)稱加密算法
KeyFactory keyFactory;
keyFactory= KeyFactory.getInstance(“RSA”);
//取公鑰匙對(duì)象
publicKey=keyFactory.generatePublic(apubkeyspec);
在接收用戶信息的時(shí)候,都是字符串,所以不能直接數(shù)字簽用于數(shù)字簽名,否則數(shù)字簽名會(huì)失敗,所以需要將字符串先轉(zhuǎn)化為公鑰,再將公鑰返回才可以用
4.2驗(yàn)證簽名
具體位置:main2.java246行
//獲得簽名實(shí)例
signature=Signature.getInstance(certificate.getSigAlgName());
//用證書公鑰進(jìn)行初始化
signature.initVerify(certificate.getPublicKey());
//更新資源
signature.update(original);
//驗(yàn)證數(shù)字簽名
return signature.verify(sign);
首先,先獲得簽名實(shí)例,用證書公鑰進(jìn)行初始化,之后需要更新元數(shù)據(jù),然后驗(yàn)證數(shù)字簽名,并返回?cái)?shù)字簽名驗(yàn)證結(jié)果,驗(yàn)證時(shí)需要知道CA證書數(shù)字簽名的算法,必須根據(jù)CA證書的算法采用相同的算法來(lái)確定驗(yàn)證數(shù)字簽名的算法才能夠保證驗(yàn)證簽名成功。
4.3進(jìn)入數(shù)據(jù)庫(kù)
Myc.java L.152
KeyStore ks=KeyStore.getInstance(“JKS”);
ks.load(in,storePwd);//得到路徑和密碼,可進(jìn)入
我們想要進(jìn)入數(shù)據(jù)庫(kù),必須得到數(shù)據(jù)庫(kù)路徑,以及數(shù)據(jù)庫(kù)密碼,用函數(shù)load()就可以進(jìn)入數(shù)據(jù)庫(kù)了。
5.所遇到的問(wèn)題及分析解決
5.1警告:SerialVersionUid
The serializable class main2 does not declare a static final serialVersionUID field of type long
5.1.1問(wèn)題出現(xiàn)的原因:
只要任何類別實(shí)作了Serializable這個(gè)介面,如果沒(méi)有加入serialVersionUID,Eclipse都會(huì)給你warning提示,這個(gè)serialVersionUID為了讓該類別Serializable後兼容.(其實(shí)有這個(gè)功能是好的.)
5.1.2分析問(wèn)題:
如果不顯式設(shè)置SerialVersionUid,有什么后果?
jdk文檔中有解釋,建議我們顯式聲明,因?yàn)槿绻宦暶?#xff0c;JVM會(huì)為我們自動(dòng)產(chǎn)生一個(gè)值,但這個(gè)值和編譯器的實(shí)現(xiàn)相關(guān),并不穩(wěn)定,這樣就可能在不同JVM環(huán)境下出現(xiàn)反序列化時(shí)報(bào)InvalidClassException異常。
①兩種SerialVersionUid有什么區(qū)別?
一種就是1L,一種是生成一個(gè)很大的數(shù),這兩種有什么區(qū)別呢?
看上去,好像每個(gè)類的這個(gè)類不同,似乎這個(gè)SerialVersionUid在類之間有某種關(guān)聯(lián)。其實(shí)不然,兩種都可以,從JDK文檔也看不出這一點(diǎn)。我們只要保證在同一個(gè)類中,不同版本根據(jù)兼容需要,是否更改SerialVersionUid即可。
對(duì)于第一種,需要了解哪些情況是可兼容的,哪些根本就不兼容。
在可兼容的前提下,可以保留舊版本號(hào),如果不兼容,或者想讓它不兼容,就手工遞增版本號(hào)。
1->2->3…
第二種方式,是根據(jù)類的結(jié)構(gòu)產(chǎn)生的hash值。增減一個(gè)屬性、方法等,都可能導(dǎo)致這個(gè)值產(chǎn)生變化。我想這種方式適用于這樣的場(chǎng)景:
開(kāi)發(fā)者認(rèn)為每次修改類后就需要生成新的版本號(hào),不想向下兼容,操作就是刪除原有serialVesionUid聲明語(yǔ)句,再自動(dòng)生成一下。
個(gè)人認(rèn)為,一般采用第一種就行了,簡(jiǎn)單。第二種能夠保證每次更改類結(jié)構(gòu)后改變版本號(hào),但還是要手工去生成,并不是修改了類,會(huì)提示你要去更新這個(gè)SerialVersionUid
, 所以,實(shí)際上讓人很迷惑。
5.2 警告:靜態(tài)方法
This method has a constructor name :
第一:main方法是個(gè)靜態(tài)方法如果你想調(diào)用本類中的其他方法要么將其他方法設(shè)置為靜態(tài)的
要么創(chuàng)建一個(gè)該類的對(duì)象,調(diào)用之;
第二:盡量不要將你的方法的名字和類的名字相同。
5.3錯(cuò)誤:驅(qū)動(dòng)
5-2錯(cuò)誤界面
5.3.1No suitable driver found for jdbc:mysql://localhost:3306/mydb
出現(xiàn)這樣的情況,一般有四種原因:
①連接URL格式出現(xiàn)了問(wèn)題(Connection conn=DriverManager.getConnection(“jdbc:mysql://localhost:3306/XX”,“root”,“XXXX”)
②驅(qū)動(dòng)字符串出錯(cuò)(com.mysql.jdbc.Driver)
③Classpath中沒(méi)有加入合適的mysql_jdbc驅(qū)動(dòng)(驅(qū)動(dòng)要和你的數(shù)據(jù)庫(kù)版本一致)
④驅(qū)動(dòng)jar包放的位置不對(duì)
首先,解決版本不一致的問(wèn)題,因?yàn)槲业膍ysql為mysql server 8.0 而我用的connector 為5.0 必須重新下載
5-3解決錯(cuò)誤
然后,我的驅(qū)動(dòng)復(fù)制錯(cuò)了,不應(yīng)復(fù)制壓縮包,應(yīng)該復(fù)制以.jar結(jié)尾的connector
再然后,系統(tǒng)提示,應(yīng)該將驅(qū)動(dòng)字符串改為:driverClass=“com.mysql.cj.jdbc.Driver”;
5.3.2問(wèn)題三解決了之后,出現(xiàn)了新的問(wèn)題
The server time zone value ‘?й???’ is unrecognized or represents more than one time zone. You must configure either the server or JDBC driver (via the serverTimezone configuration property) to use a more specifc time zone value if you want to utilize time zone support.
為URL添加參數(shù)serverTimezone=UTC即可,這里的時(shí)區(qū)可以根據(jù)自己數(shù)據(jù)庫(kù)的設(shè)定來(lái)設(shè)置(GMT/UTC )。
jdbc:mysql://localhost:3306/dbname?useUnicode=true&characterEncoding=UTF-8&useJDBCCompliantTimezoneShift=true&useLegacyDatetimeCode=false&serverTimezone=UTC
5.3.3新的問(wèn)題又出現(xiàn)了:Parameter index out of range (1 > number of parameters, which is 0).
String sql=“select pkey form mypub where pkey=?”;原來(lái)是?是中文的?,改成英文的就好了
5.4 錯(cuò)誤:keystore入口
Keytool存儲(chǔ)keystore位置不對(duì)
通過(guò)CMD命令創(chuàng)建的keystore存儲(chǔ)在c:\user下
而我們的程序執(zhí)行命令時(shí),是在iava下,必須指定好具體詳細(xì)的位置要不然找不到
5.5 錯(cuò)誤:Premature EOF
屬于IOException異常中,在解決這個(gè)問(wèn)題中查閱了很多資料,甚至源碼。目前在網(wǎng)絡(luò)里的說(shuō)法是:EOF是end of file的縮寫,premature eof指的是讀取文件時(shí)非正常的提前進(jìn)入EOF狀態(tài)
問(wèn)題解決:用戶輸入的密鑰不符合算法規(guī)定
6.測(cè)試
6.1 首次申請(qǐng)
6.1.1服務(wù)器接受用戶的提交申請(qǐng),提交時(shí)候讓用戶自己產(chǎn)生公鑰對(duì). 該界面提供了用戶輸入的界面,其中認(rèn)證信息用于驗(yàn)證用戶的合法性,公鑰為用戶自己的公鑰,其他信息根據(jù)用戶情況進(jìn)行填寫。
6-1服務(wù)器接收申請(qǐng)界面
6.1.2系統(tǒng)提示:申請(qǐng)成功
6-2申請(qǐng)成功
圖6-3 用戶證書文檔
6.1.3以下為用戶得到的證書與簽名(圖6-3)
6.1.4保存成功
此時(shí)我們可以看到數(shù)據(jù)庫(kù)中保存了用戶信息
6-4數(shù)據(jù)庫(kù)成功保存信息
6.2重復(fù)申請(qǐng)
該用戶已經(jīng)得到證書后,再次申請(qǐng)
此時(shí),系統(tǒng)則會(huì)提示,該用戶已經(jīng)注冊(cè)過(guò)
6-4再次申請(qǐng)
6.3掛失
該用戶密鑰丟失需要掛失
系統(tǒng)提示,掛失成功
6-5掛失成功
6.4掛失后重復(fù)申請(qǐng)
此時(shí)如果我們?cè)儆脪焓н^(guò)的公鑰,再次申請(qǐng)
系統(tǒng)則會(huì)顯示失敗
6-6掛失失敗
最后,附上源碼鏈接:客戶端 https://download.csdn.net/download/luckism/12652274
主機(jī)端 https://download.csdn.net/download/luckism/12652278
大概地給大家展示一下證書
總結(jié)
- 上一篇: CImage类 from http://
- 下一篇: ca系统包括ca服务器,ca认证中心有哪