java安全之數字簽名及證書
MD5/SHA的應用
l 訊息摘要是一種演算法:無論原始資料多長,訊息摘要的結果都是固定長度的;原始資料任意bit位的變化,都會導致訊息摘要的結果有很大的不同,且根據結果推算出原始資料的概率極低。訊息摘要可以看作原始資料的指紋,指紋不同則原始資料不同。
l 數字摘要與MD5/SHA演算法
ü 通用處理方式:呼叫MessageDigest物件的update和digest方法
ü 流資料的特殊處理: 使用DigestInputStream或DigestOutputStream包裝MessageDigest物件,呼叫DigestInputStream的read方法或DigestOutputStream的write方法讀寫完資料並將流關閉後,再呼叫MessageDigest物件的digest方法,流中的資料就全被摘要處理了。
l 基於MAC(訊息驗證碼)的數字摘要
ü MAC,Message Authentication Code ,金鑰被用作訊息摘要生成過程的一部分。
ü 防止內容和摘要同時被篡改,在一定成都上起到了驗證傳送者身份。
ü 步驟: KeyGenerator.getInstanceàKeyGeneraotr.generateKey àMac.getInstance à Mac.init(secretyKey) à Mac.update à Mac.doFinal
JDK 1.4 支援 HMAC/SHA-1 和 HMAC/MD5 訊息認證碼演算法。
MAC摘要的程式碼:
privatestatic void MacDigest() throws Exception{
PBEKeySpeckeySpec = new PBEKeySpec("abcde".toCharArray());
SecretKeyFactorykeyFactory = SecretKeyFactory.getInstance("PBEWithMD5AndDES");
SecretKeykey = keyFactory.generateSecret(keySpec);
Macmac = Mac.getInstance("HmacMD5");
mac.init(key);
byte[]source = new byte[]{1,2,3,5};
byte[]dest = mac.doFinal(source);
System.out.println(dest.length);
for(inti=0;i<dest.length;i++){
System.out.println(dest[i]+ ",");
}
}
由於由於加密時所涉及的secretkey不好記憶和交換,上面的程式碼也演示了基於口令的加密方案,即使用口令來生成secretkey。
數字簽名
l 數字簽名的基礎是公鑰和私鑰的非對稱加密,傳送者使用私鑰加密訊息摘要(簽名),接收者使用公鑰解密訊息摘要以驗證簽名是否是某個人的。
l 基本步驟:
• 得到keyPairGenerator的例項物件,並呼叫其generateKeyPair()方法建立KeyPair物件。
• 呼叫KeyPair物件的getPrivate和getPublic方法,分別得到PrivateKey物件和PublicKey物件。
• 得到Signature的例項物件,呼叫其initSign()方法和指定PrivateKey物件,然後呼叫update方法和sign方法產生簽名
• 呼叫Signature物件的initVerify()方法和指定PublicKey物件,然後呼叫update方法和verify()方法對原始資料的簽名進行驗證。
l 擴充套件步驟:
• 把公鑰和私鑰分別儲存為公鑰檔案和私鑰檔案,把公鑰檔案傳遞給對方,用私鑰檔案對其他檔案進行簽名後,再把其他檔案和簽名的結果檔案傳遞給對方,讓對方用公鑰檔案進行簽名驗證。
私鑰就像一個人的筆跡或印章,是每個人獨有的,同時又是人人可以檢驗的。使用私鑰加密訊息摘要,就像在檔案上簽名或蓋章,確認了資料的身份。
程式碼1:
KeyPairGeneratorkeyPairGenerator = KeyPairGenerator.getInstance("RSA");
KeyPairkeyPair = keyPairGenerator.generateKeyPair();
PrivateKeyprivateKey = keyPair.getPrivate();
PublicKeypublicKey = keyPair.getPublic();
Signaturesignature = Signature.getInstance("MD5withRSA");
signature.initSign(privateKey);
byte[]src = new byte[]{1,2,3,4,5,6,7,};
signature.update(src);
byte[]signed = signature.sign();
signature.initVerify(publicKey);
signature.update(src);
System.out.println(
signature.verify(signed));
程式碼2:
privatestatic void sign() throws Exception{
FileInputStreamfisPrivateKey = new FileInputStream("private.key");
ObjectInputStreamoisPrivateKey = new ObjectInputStream(fisPrivateKey);
PrivateKeyprivateKey = (PrivateKey)oisPrivateKey.readObject();
oisPrivateKey.close();
fisPrivateKey.close();
Signaturesignature = Signature.getInstance("MD5withRSA");
signature.initSign(privateKey);
FileInputStreamfis = new FileInputStream("src.txt");
byte[]src = new byte[1024];
intlen = 0;
while((len=fis.read(src))!=-1){
signature.update(src,0,len);
}
fis.close();
byte[]signed = signature.sign();
System.out.println(signed.length);
FileOutputStreamfos = new FileOutputStream("signed.dat");
fos.write(signed);
fos.close();
}
privatestatic void verify() throws Exception{
FileInputStreamfisPublicKey = new FileInputStream("public.key");
ObjectInputStreamoisPublicKey = new ObjectInputStream(fisPublicKey);
PublicKeypublicKey = (PublicKey)oisPublicKey.readObject();
oisPublicKey.close();
fisPublicKey.close();
Signaturesignature = Signature.getInstance("MD5withRSA");
signature.initVerify(publicKey);
FileInputStreamfis = new FileInputStream("src.txt");
byte[]src = new byte[1024];
intlen = 0;
while((len=fis.read(src))!=-1){
signature.update(src,0,len);
}
fis.close();
FileInputStreamfisSigned = new FileInputStream("signed.dat");
byte[]signed = new byte[fisSigned.available()];
intreaded =0;
inttotal = 0;
System.out.println(signed.length);
while(total<signed.length){
//此方法的第三個引數len長度不能大於b.length- off,並且len等於0時直接返回,所以不能用while(readed!=-1)進行判斷
readed= fisSigned.read(signed,total,signed.length-total);
total+= readed;
}
fisSigned.close();
System.out.println(
signature.verify(signed));
}
數字證書的建立與檢視
l 按照預設方式建立證書
• 預設建立的keystore檔案為使用者主目錄下的.keystore。
• 預設的key和證書的alias為mykey。
l 在預設儲存位置建立指定alias名稱的證書。
l 在指定的儲存位置建立指定alias名稱的證書。
l 顯示keystore中的證書資訊
• 顯示所有證書的條目列表:keytool–list
• 顯示某個證書的詳細資訊:keytool-list -v -alias zxx
l 將數字證書匯出為檔案
• keytool -exportcert -alias zxx -file zxx.cer
• 生成可列印的證書:keytool-exportcert -alias zxx -file zxx.cer –rfc
l 顯示數字證書檔案中的證書資訊
• keytool -printcert -file zxx.cer
• 直接雙擊zxx.cer,用window系統的內建程式開啟zxx.cer
在jdk的keytool工具幫助頁面中,搜尋“keystore”和”cacerts”就可以知道key的預設儲存位置和信任證書的儲存位置。
- keytool -genkeypair (java6修改了以前的一些命令選項,所以,不能完全按tomcat的ssl部分的文件來做,另外,在命令後可以指定選項,也可以不指定選項,一些選項不指定,會採用對應的預設值,有些選項沒有預設值,則會提示輸入)
- keytool -genkeypair -alias xxx 後面部分是為證書指定別名,否則採用預設的名稱為mykey。
- keytool -list 和keytool -list -v 看看keystore中有哪些專案。
在演示keytool時,先產生一個key,這時候要為新建立的keystore指定password,給大家看keystore檔案的位置,然後再產生一個alias相同的key,這時候必須先指定keystore的密碼,才能進入該keystore,進入後報告alias重名的錯誤,然後再產生另外一個alias的key。
針對數字證書的程式設計
l 在java程式中使用Certificate類來描述通用的數字證書,其子類X509Certificate則專用於描述X.509類別的數字證書,這兩個類中最常用的方法如下:
• getPublicKey()
• getSignature()
• getNotBefore()
• getNotAfter()
• getSigAlgName()
• checkValidity(),只是驗證證書的日期是否有效,不驗證簽發者的簽名和權威性。
• verify(),用證書籤發者的公鑰去驗證證書上的簽名是否有效。
l 直接根據證書檔案獲取Certificate物件的基本步驟:
• 得到CertificateFactory的例項物件。
• 呼叫CertificateFactory物件的generateCertificate()方法,並傳入指向證書檔案的InputStream流物件作為引數。
• 將得到的Certificate物件強轉為X509Certificate型別,或者呼叫其toString方法()。
l 從keystore中提取Certificate物件的基本步驟:
• 得到KeyStore物件。
• 呼叫KeyStore物件的load()方法,並傳入指向金鑰庫檔案的InputStream流物件和代表密碼的字元陣列。
• 呼叫KeyStore物件的getCertificate()方法,並傳入要獲取的證書的alias名稱。
• 將得到的Certificate物件強轉為X509Certificate型別,或者呼叫其toString方法()。
看看KeyStore類的幫助文件,就知道怎樣使用KeyStore物件了。
程式碼1:
privatestatic void loadCertificateFromStore() throws Exception{
KeyStorekeyStore = KeyStore.getInstance("jks");
FileInputStreamfis = new FileInputStream("C:\\Documents andSettings\\IBM\\.keystore");
keyStore.load(fis,"123456".toCharArray());
fis.close();
Certificatecert = keyStore.getCertificate("mykey");
System.out.println(cert.toString());
}
privatestatic void loadCertificateFromFile() throws Exception{
CertificateFactoryfactory = CertificateFactory.getInstance("X.509");
FileInputStreamfis = new FileInputStream("C:\\Java\\jdk1.6.0_21\\bin\\zxx1.cer");
Certificatecert = factory.generateCertificate(fis);
fis.close();
X509Certificatex509cert = (X509Certificate)cert;
System.out.println("公鑰:" +x509cert.getPublicKey());
System.out.println("簽名:" +x509cert.getSignature());
System.out.println("簽名演算法:" +x509cert.getSigAlgName());
System.out.println("型別:" +x509cert.getType());
System.out.println("證書所有者:"+ x509cert.getSubjectDN());
System.out.println("證書釋出者:"+ x509cert.getIssuerDN());
System.out.println("證書起始有效日期:"+ x509cert.getNotBefore());
System.out.println("證書終止有效日期:"+ x509cert.getNotAfter());
}
keytool的其他應用
l 刪除指定alias名稱的證書。
l 修改keystore的密碼。
l 改變某個證書的alias名稱。
l 產生CSR(Certificate Signing Request)檔案
• 介紹如何通過證書鏈來確保證書的可靠性
l 匯入數字證書檔案
• 增加受信任的證書項
• 匯入被CA簽過名的數字證書
l 對於證書鏈,必須先匯入CA的證書後,才能匯入被簽名的證書。
和現實生活中一樣,要有權威的機構檢查證書中內容的真實性,然後再簽發證書(在證書上蓋章)。即權威機構(CA)用自己的私鑰對證書進行數字簽名,而這些CA 的公鑰已經以證書的形式包含在許多作業系統中。
直接執行keytool命令,就會顯示出所有選項的提示資訊,在jdk文件中看keytool的幫助資訊更全面。
匯入數字證書:
1.to add it to the list of trustedcertificates, or
2. to import a certificate reply receivedfrom a CA as the result of submitting a Certificate Signing Request (see the-certreq command) to that CA.
命令列表:
針對keystore的程式設計
l KeyStore類中最常用的方法如下:
• aliases()
• store(),可用於修改整個keystore的口令
• setKeyEntry(alias,privatekey,pass,chain),可用於修改原有key的密碼
• setCertificateEntry,用於儲存trustedCertEntry
• containsAlias()
• deleteAlias()
• getCertificate()
• getCertificateChain()
l 數字證書的簽發與校驗:
• 屬簡單瞭解的知識