1. 程式人生 > >java安全之數字簽名及證書

java安全之數字簽名及證書

MD5/SHA的應用

訊息摘要是一種演算法:無論原始資料多長,訊息摘要的結果都是固定長度的;原始資料任意bit位的變化,都會導致訊息摘要的結果有很大的不同,且根據結果推算出原始資料的概率極低。訊息摘要可以看作原始資料的指紋,指紋不同則原始資料不同。

數字摘要與MD5/SHA演算法

ü  通用處理方式:呼叫MessageDigest物件的update和digest方法

ü  流資料的特殊處理: 使用DigestInputStream或DigestOutputStream包裝MessageDigest物件,呼叫DigestInputStream的read方法或DigestOutputStream的write方法讀寫完資料並將流關閉後,再呼叫MessageDigest物件的digest方法,流中的資料就全被摘要處理了。

基於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。

數字簽名

 數字簽名的基礎是公鑰和私鑰的非對稱加密,傳送者使用私鑰加密訊息摘要(簽名),接收者使用公鑰解密訊息摘要以驗證簽名是否是某個人的。

基本步驟:

•    得到keyPairGenerator的例項物件,並呼叫其generateKeyPair()方法建立KeyPair物件。

•    呼叫KeyPair物件的getPrivate和getPublic方法,分別得到PrivateKey物件和PublicKey物件。

•    得到Signature的例項物件,呼叫其initSign()方法和指定PrivateKey物件,然後呼叫update方法和sign方法產生簽名

•    呼叫Signature物件的initVerify()方法和指定PublicKey物件,然後呼叫update方法和verify()方法對原始資料的簽名進行驗證。

擴充套件步驟:

•    把公鑰和私鑰分別儲存為公鑰檔案和私鑰檔案,把公鑰檔案傳遞給對方,用私鑰檔案對其他檔案進行簽名後,再把其他檔案和簽名的結果檔案傳遞給對方,讓對方用公鑰檔案進行簽名驗證。

私鑰就像一個人的筆跡或印章,是每個人獨有的,同時又是人人可以檢驗的。使用私鑰加密訊息摘要,就像在檔案上簽名或蓋章,確認了資料的身份。

程式碼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的預設儲存位置和信任證書的儲存位置。

  1. keytool -genkeypair  (java6修改了以前的一些命令選項,所以,不能完全按tomcat的ssl部分的文件來做,另外,在命令後可以指定選項,也可以不指定選項,一些選項不指定,會採用對應的預設值,有些選項沒有預設值,則會提示輸入)
  2. keytool -genkeypair -alias xxx 後面部分是為證書指定別名,否則採用預設的名稱為mykey。
  3.  keytool -list 和keytool -list -v 看看keystore中有哪些專案。

在演示keytool時,先產生一個key,這時候要為新建立的keystore指定password,給大家看keystore檔案的位置,然後再產生一個alias相同的key,這時候必須先指定keystore的密碼,才能進入該keystore,進入後報告alias重名的錯誤,然後再產生另外一個alias的key。

針對數字證書的程式設計

在java程式中使用Certificate類來描述通用的數字證書,其子類X509Certificate則專用於描述X.509類別的數字證書,這兩個類中最常用的方法如下:

•    getPublicKey()

•    getSignature()

•    getNotBefore()

•    getNotAfter()

•    getSigAlgName()

•    checkValidity(),只是驗證證書的日期是否有效,不驗證簽發者的簽名和權威性。

•    verify(),用證書籤發者的公鑰去驗證證書上的簽名是否有效。

直接根據證書檔案獲取Certificate物件的基本步驟:

•    得到CertificateFactory的例項物件。

•    呼叫CertificateFactory物件的generateCertificate()方法,並傳入指向證書檔案的InputStream流物件作為引數。

•    將得到的Certificate物件強轉為X509Certificate型別,或者呼叫其toString方法()。

從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的其他應用

刪除指定alias名稱的證書。

修改keystore的密碼。

改變某個證書的alias名稱。

產生CSR(Certificate Signing Request)檔案

•    介紹如何通過證書鏈來確保證書的可靠性

匯入數字證書檔案

•    增加受信任的證書項

•    匯入被CA簽過名的數字證書

對於證書鏈,必須先匯入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的程式設計

KeyStore類中最常用的方法如下:

•    aliases()

•    store(),可用於修改整個keystore的口令

•    setKeyEntry(alias,privatekey,pass,chain),可用於修改原有key的密碼

•    setCertificateEntry,用於儲存trustedCertEntry

•    containsAlias()

•    deleteAlias()

•    getCertificate()

•    getCertificateChain()

數字證書的簽發與校驗:

•    屬簡單瞭解的知識