數字簽名、數字證書等技術,是現代資訊安全的核心技術,可謂使用面十分廣泛。其基本理論本身并不複雜,本文希望通過深入淺出的介紹,能夠讓大家有一些基本了解。
對稱加密、非對稱加密
讓我們通過一個例子開始:我們的主角分别是Alice和Bob。現在假設Alice要給Bob發送一份檔案,檔案内容非常機密。Alice不希望檔案在發送的過程中被人截取而洩密。
這個時候,自然想到的方法就是對檔案進行加密。當然除了加密外,我們還需要讓Bob能夠解密。就像Alice對檔案上了鎖,為了讓Bob能夠解開,則Bob必須有鑰匙來對檔案解鎖。在資訊安全或密碼學中,我們将這種鑰匙稱為密鑰。密鑰一般分為兩種,對稱密鑰與非對稱密鑰:
對稱密鑰很容易了解,如同Alice用一把鑰匙将檔案上鎖,而Bob使用相同的鑰匙就可以将檔案解鎖,即加密使用的密鑰與解密使用的密鑰是相同的。目前的對稱密鑰算法有DES、3DES、AES等,而密鑰則一般是一串固定長度的字元。
如下,Bob和Alice事先已經約定,将使用DES算法,并且已經約定好使用的密鑰。于是Alice使用這份密鑰對檔案進行了加密,并發送給Bob。Bob使用相同的密鑰對檔案解密即可:
對稱密鑰算法的安全性還是非常有保障的。拿DES算法舉例,到目前為止,除了用窮舉搜尋法對DES算法進行攻擊外,還沒有發現更有效的辦法。而56位長的密鑰的窮舉空間為256,這意味着如果一台計算機的速度是每一秒鐘檢測一百萬個密鑰,則它搜尋完全部密鑰就需要将近2285年的時間。而3DES(3次DES操作)、AES算法的安全性則更高。由此可見,使用對稱密鑰對檔案進行了加密,基本上不用太擔心檔案可能洩密。
目前大部分的開發語言都有對應的數字加密子產品,例如JAVA的JCE子產品就可以調用簡單的實作加解密。以下是一份JAVA代碼例子:
[java] view plain copy print ?
- package com.test.chiper;
- import javax.crypto.Cipher;
- import javax.crypto.spec.SecretKeySpec;
- import sun.misc.BASE64Decoder;
- import sun.misc.BASE64Encoder;
- public class TestChiper {
- private static final BASE64Encoder base64En = new sun.misc.BASE64Encoder();
- private static final BASE64Decoder base64De = new sun.misc.BASE64Decoder();
- private static String AESKey="1234567890123456";
- public static String encrypt(String mText) throws Exception {
- SecretKeySpec secrekeysp = new SecretKeySpec(
- AESKey.getBytes(),"AES");
- java.security.Key key = (java.security.Key) secrekeysp;
- Cipher cipher = Cipher.getInstance("AES/ECB/PKCS5Padding");
- cipher.init(javax.crypto.Cipher.ENCRYPT_MODE, key);
- byte[] b = cipher.doFinal(mText.getBytes());
- return base64En.encode(b);
- }
- public static String decrypt(String mText) throws Exception {
- SecretKeySpec secrekeysp = new SecretKeySpec(
- AESKey.getBytes(),"AES");
- java.security.Key key = (java.security.Key) secrekeysp;
- Cipher cipher = Cipher.getInstance("AES/ECB/PKCS5Padding");
- cipher.init(javax.crypto.Cipher.DECRYPT_MODE, key);
- byte[] b = cipher.doFinal(base64De.decodeBuffer(mText));
- return new String(b);
- }
- }
package com.test.chiper;
import javax.crypto.Cipher;
import javax.crypto.spec.SecretKeySpec;
import sun.misc.BASE64Decoder;
import sun.misc.BASE64Encoder;
public class TestChiper {
private static final BASE64Encoder base64En = new sun.misc.BASE64Encoder();
private static final BASE64Decoder base64De = new sun.misc.BASE64Decoder();
private static String AESKey="1234567890123456";
public static String encrypt(String mText) throws Exception {
SecretKeySpec secrekeysp = new SecretKeySpec(
AESKey.getBytes(),"AES");
java.security.Key key = (java.security.Key) secrekeysp;
Cipher cipher = Cipher.getInstance("AES/ECB/PKCS5Padding");
cipher.init(javax.crypto.Cipher.ENCRYPT_MODE, key);
byte[] b = cipher.doFinal(mText.getBytes());
return base64En.encode(b);
}
public static String decrypt(String mText) throws Exception {
SecretKeySpec secrekeysp = new SecretKeySpec(
AESKey.getBytes(),"AES");
java.security.Key key = (java.security.Key) secrekeysp;
Cipher cipher = Cipher.getInstance("AES/ECB/PKCS5Padding");
cipher.init(javax.crypto.Cipher.DECRYPT_MODE, key);
byte[] b = cipher.doFinal(base64De.decodeBuffer(mText));
return new String(b);
}
}
前文提到的對稱密鑰算法,已經基本滿足了Alice對于檔案機密性的要求。然而還是存在兩個問題:
1、Alice與Bob彼此之間必須約定将使用的密鑰,而這個約定的過程本身就可能存在洩密的風險;
2、如果除了Alice以外,還有Jessica、Eva、Mary等100位女士也需要向Bob發送檔案。那麼,Bob可能需要有100次約定密鑰的過程。
由此可見,無論是安全性還是可用性上,對稱密鑰都是存在問題的。而兩個問題則是必須解決的。
1976年,美國斯坦福大學的研究所學生Diffie和教授Hellman發表一個基于非對稱密鑰加密的想法,這個想法開創了密碼學的變革。他們的想法其實非常簡單,将密鑰分為公鑰(publicKey)和私鑰(privateKey)兩種。公鑰加密的内容,使用私鑰可以解開;而私鑰加密的内容,公鑰可以解開。然而,單獨的知道公鑰或私鑰,卻沒有辦法推出另一份密鑰。
繼續我們的例子:仍然是Alice需要與Bob發送一份絕密檔案。在此之前,Bob生成了一對密鑰:公鑰和私鑰。Bob将公鑰釋出在了一個公共的密鑰庫中,而私鑰則不對外公開,僅Bob本人持有。如下圖所示,Alice從公鑰庫中取出Bob的公鑰,對檔案進行加密,再發送給Bob。而Bob通過自己持有的私鑰,即可将檔案解密:
通過非對稱密鑰,Bob隻是将公鑰公布,并沒有和Alice約定密鑰。僅僅知道公鑰是沒有辦法推出私鑰的,是以不用擔心私鑰洩密的問題。另一反面,如果有jessica也想向Bob發送檔案,僅需要從公鑰庫中取到Bob的公鑰即可,也不需要更多的密鑰。
非對稱密鑰算法很有效的解決了安全性和可用性的問題,非對稱密鑰算法又稱作“公開密鑰加密算法”,我們常說的PKI(Public Key Infrastructure)則是建立在此技術之上的安全基礎體系。目前使用最為廣泛的非對稱密鑰為RSA算法,該算法是由三位算法發明者的姓氏開頭字母命名而來。
由于非對稱加密算法的複雜度更高,是以非對稱加密的速度遠沒有對稱加密算法快,甚至可能比對稱加密慢上1000倍。
資訊摘要、數字簽名
基于上文的非對稱密鑰算法,我們可以繼續我們的場景:
假設有一天,Alice收到了一份署名為Bob的檔案。Alice希望能夠确認這份檔案一定是來自Bob;另外Alice希望能夠确信,這份檔案在傳輸過程中并沒有被它人篡改。那麼基于非對稱密鑰算法我們應該怎麼做?
确認檔案一定來自于Bob,其實就是Bob無法否認自己發送過這份檔案。資訊安全中稱作不可抵賴性;另一方面,确信檔案并沒有中途被篡改,則稱作不可篡改性。
在非對稱密鑰算法中提到,公鑰加密的内容使用私鑰可以解密。同樣的,基于私鑰加密的内容使用公鑰也可以解密,兩者一一對應。是以我們可以很容易想到。如果Bob利用自己手裡的私鑰對檔案進行加密後,傳輸給Alice。Alice再通過公鑰庫中Bob的公鑰進行解密,則可以證明檔案一定是由Bob發出(由于隻有Bob持有私鑰)。另外,因為傳輸的是密文,如果能夠使用公鑰解密,同時也證明了檔案并沒有中途被篡改。這樣的做法其實已經同時滿足了不可抵賴性和不可篡改性。
然而,由于傳輸的檔案可能很大,為了證明檔案的不可抵賴性和不可篡改性,需要對整個檔案進行加密,由于非對稱算法效率較低,這樣做的代價太大。是以正常的做法是用到資訊摘要和數字簽名的方式。
所謂資訊摘要,其實就是某種HASH算法。将資訊明文轉化為固定長度的字元,它具有如下特點:
①無論輸入的消息有多長,計算出來的消息摘要的長度總是固定的;
②用相同的摘要算法對相同的消息求兩次摘要,其結果必然相同;
③一般地,隻要輸入的消息不同,對其進行摘要以後産生的摘要消息也幾乎不可能相同;
④消息摘要函數是單向函數,即隻能進行正向的資訊摘要,而無法從摘要中恢複出任何的消息;
⑤好的摘要算法,沒有人能從中找到“碰撞”,雖然“碰撞”是肯定存在的。即對于給定的一個摘要,不可能找到一條資訊使其摘要正好是給定的。或者說,無法找到兩條消息,是它們的摘要相同。
一般的,我們将資訊的摘要也稱作資訊的指紋。如同指紋的含義,相同的資訊一定會得相同的指紋,而僅通過指紋又無法還原出原始資訊。目前主要的摘要算法有MD5和SHA1。
當有了資訊摘要技術以後,基于Bob向Alice發送檔案的場景,我們可以進行如下的操作:
第一步:
① Bob将原始的資訊進行一次資訊摘要算法,得到原始資訊的摘要值;
② Bob使用自己的私鑰,對該摘要值進行加密。得到資訊摘要的密文;
③ Bob将原始檔案和摘要值的密文一起發送給Alice。
④ 一般的,我們将原始檔案和摘要密文稱作Bob對原始檔案的簽名結果。
第二步:
① 當Alice接收到Bob傳輸的資訊(原始檔案,資訊摘要密文)後,使用Bob的公鑰将摘要密文解密,得到資訊摘要明文;
② 使用資訊摘要算法,取原文的摘要資訊,擷取原始檔案摘要資訊;
③ Alice比較解密後的摘要資訊和取得的摘要資訊。如果相同,則可以證明檔案一定由Bob發送,并且中途并沒有經過任何篡改。一般将這個過程稱作驗簽。
所謂數字簽名,就是對原始檔案的“指紋”進行了私鑰加密。這樣,即可保證檔案的特征(摘要值)一定經過了私鑰的加密。同時由于資訊摘要的長度普遍不長(MD5為128位,SHA1主要為256位),也并沒有帶來太大的開銷。
如同對稱密鑰算法,在大部分開發語言中,基于非對稱算法的數字簽名,數字加密算法。也都進行了一定的封裝。如下連結就比較詳細的描述了基于JCE如何實作數字簽名、加密、驗證等:
http://blog.csdn.net/centralperk/article/details/8538697
數字證書
基于非對稱密鑰算法,Bob生成了一對公私鑰。Bob将公鑰釋出在公開的密鑰庫中。而Alice在向Bob發送加密檔案或者驗證Bob簽名的檔案時,均要從公鑰庫取到Bob的公鑰。我們已經知道,一般來說公鑰就是一段固定長度的字元串,并沒有特定的含義。
為了讓Alice能夠友善的辨識公鑰,我們可以考慮對給公鑰附加一些資訊,例如該公鑰使用的算法,該公鑰的所有者(主題),該公鑰的有效期等一系列屬性。這樣的資料結構我們稱作PKCS10資料包
公鑰的主題我們采用唯一标示符(或稱DN-distinguished name),以盡量唯一的标示公鑰所有者。以下是基于抽象文法表示法所定義的PKCS10資料結構:
[plain] view plain copy print ?
- CertificationRequestInfo ::= SEQUENCE {
- version INTEGER { v1(0) } (v1,...),
- subject Name,
- subjectPKInfo SubjectPublicKeyInfo{{ PKInfoAlgorithms }},
- attributes [0] Attributes{{ CRIAttributes }}
- }
- SubjectPublicKeyInfo { ALGORITHM : IOSet} ::= SEQUENCE {
- algorithm AlgorithmIdentifier {{IOSet}},
- subjectPublicKey BIT STRING
- }
- PKInfoAlgorithms ALGORITHM ::= {
- ... -- add any locally defined algorithms here -- }
- Attributes { ATTRIBUTE:IOSet } ::= SET OF Attribute{{ IOSet }}
- CRIAttributes ATTRIBUTE ::= {
- ... -- add any locally defined attributes here -- }
- Attribute { ATTRIBUTE:IOSet } ::= SEQUENCE {
- type ATTRIBUTE.&id({IOSet}),
- values SET SIZE(1..MAX) OF ATTRIBUTE.&Type({IOSet}{@type})
- }
CertificationRequestInfo ::= SEQUENCE {
version INTEGER { v1(0) } (v1,...),
subject Name,
subjectPKInfo SubjectPublicKeyInfo{{ PKInfoAlgorithms }},
attributes [0] Attributes{{ CRIAttributes }}
}
SubjectPublicKeyInfo { ALGORITHM : IOSet} ::= SEQUENCE {
algorithm AlgorithmIdentifier {{IOSet}},
subjectPublicKey BIT STRING
}
PKInfoAlgorithms ALGORITHM ::= {
... -- add any locally defined algorithms here -- }
Attributes { ATTRIBUTE:IOSet } ::= SET OF Attribute{{ IOSet }}
CRIAttributes ATTRIBUTE ::= {
... -- add any locally defined attributes here -- }
Attribute { ATTRIBUTE:IOSet } ::= SEQUENCE {
type ATTRIBUTE.&id({IOSet}),
values SET SIZE(1..MAX) OF ATTRIBUTE.&Type({IOSet}{@type})
}
我們已經有了PKCS10資料包,除了公鑰資訊外,還有公鑰的持有者,公鑰的版本号等資訊。然而這樣的資料結構其實并沒有任何權威性。例如有一天一個叫做Richard的人想冒充Bob,也生成一對公私鑰,并且使用了相同的公鑰主題封裝為P10資料結構。Alice其實并沒有辦法分辨哪個是真實Bob的公鑰。
為了解決這個問題,就需要一個權威的第三方機構,對P10結構的資料進行認證。就如同對P10檔案蓋上一個權威的章,防止仿照。這樣的權威機構,我們稱作CA(Certificate Authority)數字證書認證中心。而CA如何為P10資料蓋章呢?非常簡單,就是我們前文已經提到的數字簽名技術:
① 如上圖所示,CA機構其實也持有一張私鑰。一般來說,CA會對這份私鑰進行特别的保護,嚴禁洩漏和盜用。
② Bob将自己的公鑰附加上一系列資訊後,形成了P10資料包(請求包),并發送給CA。
③ CA機構通過其他一些手段,例如檢視Bob的身份資訊等方式,認可了Bob的身份。于是使用自己的私鑰對P10請求進行簽名。(也可能會先對資料進行一些簡單修改,如修改有效期或主題等)
④ 這樣的簽名結果,我們就稱作數字證書。
數字證書同樣遵循一個格式标準,我們稱作X509标準,我們一般提到的X509證書就是如此。以下是X509的格式:
[plain] view plain copy print ?
- [Certificate ::= SEQUENCE {
- tbsCertificate TBSCertificate,
- signatureAlgorithm AlgorithmIdentifier,
- signature BIT STRING
- }
- TBSCertificate ::= SEQUENCE {
- version [0] EXPLICIT Version DEFAULT v1,
- serialNumber CertificateSerialNumber,
- signature AlgorithmIdentifier,
- issuer Name,
- validity Validity,
- subject Name,
- subjectPublicKeyInfo SubjectPublicKeyInfo,
- issuerUniqueID [1] IMPLICIT UniqueIdentifier OPTIONAL,
- -- If present, version must be v2or v3
- subjectUniqueID [2] IMPLICIT UniqueIdentifier OPTIONAL,
- -- If present, version must be v2or v3
- extensions [3] EXPLICIT Extensions OPTIONAL
- -- If present, version must be v3
- }
- Version ::= INTEGER {
- v1(0), v2(1), v3(2)
- }
- CertificateSerialNumber ::= INTEGER
- Validity ::= SEQUENCE {
- notBefore CertificateValidityDate,
- notAfter CertificateValidityDate
- }
- CertificateValidityDate ::= CHOICE {
- utcTime UTCTime,
- generalTime GeneralizedTime
- }
- UniqueIdentifier ::= BIT STRING
- SubjectPublicKeyInfo ::= SEQUENCE {
- algorithm AlgorithmIdentifier,
- subjectPublicKey BIT STRING
- }
- Extensions ::= SEQUENCE OF Extension
- Extension ::= SEQUENCE {
- extnID OBJECT IDENTIFIER,
- critical BOOLEAN DEFAULT FALSE,
- extnValue OCTET STRING
- }
[Certificate ::= SEQUENCE {
tbsCertificate TBSCertificate,
signatureAlgorithm AlgorithmIdentifier,
signature BIT STRING
}
TBSCertificate ::= SEQUENCE {
version [0] EXPLICIT Version DEFAULT v1,
serialNumber CertificateSerialNumber,
signature AlgorithmIdentifier,
issuer Name,
validity Validity,
subject Name,
subjectPublicKeyInfo SubjectPublicKeyInfo,
issuerUniqueID [1] IMPLICIT UniqueIdentifier OPTIONAL,
-- If present, version must be v2or v3
subjectUniqueID [2] IMPLICIT UniqueIdentifier OPTIONAL,
-- If present, version must be v2or v3
extensions [3] EXPLICIT Extensions OPTIONAL
-- If present, version must be v3
}
Version ::= INTEGER {
v1(0), v2(1), v3(2)
}
CertificateSerialNumber ::= INTEGER
Validity ::= SEQUENCE {
notBefore CertificateValidityDate,
notAfter CertificateValidityDate
}
CertificateValidityDate ::= CHOICE {
utcTime UTCTime,
generalTime GeneralizedTime
}
UniqueIdentifier ::= BIT STRING
SubjectPublicKeyInfo ::= SEQUENCE {
algorithm AlgorithmIdentifier,
subjectPublicKey BIT STRING
}
Extensions ::= SEQUENCE OF Extension
Extension ::= SEQUENCE {
extnID OBJECT IDENTIFIER,
critical BOOLEAN DEFAULT FALSE,
extnValue OCTET STRING
}
基于數字證書,我們可以再來看看Bob如何給Alice發送一份不可否認、不可篡改的檔案:
第一步:Bob除了對檔案進行簽名操作外,同時附加了自己的數字證書。一同發給Alice。
第二步:Alice首先使用CA的公鑰,對證書進行驗證。如果驗證成功,提驗證書中的公鑰,對Bob發來的檔案進行驗簽。如果驗證成功,則證明檔案的不可否認和不可篡改。
可以看到,基于數字證書後,Alice不在需要一個公鑰庫維護Bob(或其他人)的公鑰證書,隻要持有CA的公鑰即可。數字證書在電子商務,電子認證等方面使用非常廣泛,就如同計算機世界的身份證,可以證明企業、個人、網站等實體的身份。同時基于數字證書,加密算法的技術也可以支援一些安全互動協定(如SSL)。下一篇文章,将為大家介紹SSL協定的原理。