日韩黑丝制服一区视频播放|日韩欧美人妻丝袜视频在线观看|九九影院一级蜜桃|亚洲中文在线导航|青草草视频在线观看|婷婷五月色伊人网站|日本一区二区在线|国产AV一二三四区毛片|正在播放久草视频|亚洲色图精品一区

分享

論Java加密技術與Windows的結合

 douli 2005-08-24

  公共鑰匙加密技術需要一個空間來存儲數(shù)字證書和私鑰。通過將鑰匙和證書存儲到一個文件中(稱為keystore),Java Security Architecture實現(xiàn)了獨立于平臺的加密技術。

  Microsoft Windows把鑰匙和證書存儲到Windows注冊表和文件系統(tǒng)中。這就是說,在Windows系統(tǒng)上運行安全的Java程序的用戶必須在Java和Microsoft的鑰匙和證書庫之間輸入和輸出鑰匙和證書。好消息是,你可以“哄騙”Java應用程序通過Microsoft本地函數(shù)來運用Microsoft的證書和鑰匙庫。

  通過將你的Java應用程序同Windows 鑰匙/證書庫結合起來,你雖然犧牲了平臺獨立性,但你得到了四個好處:減少了管理和支持的成本、更方便用戶使用、更好的證書撤消校驗、以及更好的鑰匙和證書管理工具。

  一個Java程序必須通過四個不同的類實現(xiàn)與Windows加密術的集成: · TrustManager Provider:用這個類來實現(xiàn)與Windows證書庫的集成并實現(xiàn)安全策略。

  · KeyManager Provider:用這個類來實現(xiàn)與Windows私鑰庫的集成。
  · RSA Signature Provider:數(shù)字簽名需要訪問私鑰庫。如果Java程序不能讀取私鑰(比如,如果私鑰存在一個加密了的智能卡上了),那么簽名操作就必須在Windows中進行。
  · RSA Cipher Provider:解密RSA加密的數(shù)據(jù)(如加密套接字協(xié)議層(SSL)對稱的鑰匙)需要訪問私鑰庫。如果Java程序不能讀取私鑰(比如,如果私鑰存在一個加密了的智能卡上了),那么RSA解密操作就必須在Windows中進行。

  我將講述與Windows平臺集成的TrustManager Provider、KeyManager Provider、RSA Signature Provider和RSA Cipher Provider的用法。TrustManager和KeyManager可以讓你構建可運行的Windows支持的Java Secure Socket Extension(JSSE)應用程序。JSSE范例程序——EchoServer和EchoClient可以證明這一點。你不能覆蓋JSSE的內置的RSA Cipher Provider,所以,只有當私鑰可以從Windows鑰匙庫中輸出時,JSSE應用程序才可以運行。

  如果你在編寫一個運用RSA簽名或RSA加密的Java應用程序,那么你可以運用Windows支持的RSA Signature Provider和Cipher Provider。這不需要從Windows鑰匙庫中輸出私鑰。對于其它三個提供者(provider),你可以單獨使用每一個。

  該代碼是用 beta版JDK 1.4.0-rc開發(fā)的,很穩(wěn)定。不過,我們打算將該代碼作為一個框架,進行進一步的開發(fā)。在將該代碼用于生產環(huán)境前,你應該改進異常處理,確信在本地代碼中沒有內存泄露,并使密鑰的暴露降低到最小。為了測試代碼,你需要一個RSA數(shù)字證書。你可以從VeriSign網(wǎng)站www./client/enrollment得到一個臨時證書,有效期是60天。具體操作請遵循該站點上的指南。不要選定標為“Protect your Private Key”的框。因為沒有選定這個框,你的私鑰就可以輸出。

  下面的代碼初試化了四個提供者:

MSTrustMgrProvider.install();
MSKeyMgrProvider.install();
MSRSASignProvider.install();
MSRSACipherProvider.install();
kmf = KeyManagerFactory
.getInstance("MSKMF");
tmf = TrustManagerFactory.
getInstance("MSTMF");
Cipher cipher =
Cipher.getInstance(
"RSA/ECB/PKCS1Padding");
Signature rsa =
Signature.getInstance(
"SHA1withRSA");

  所有的四個提供者都調用了10個本地的Microsoft函數(shù):

  · MSgetCACerts()從Microsoft證書庫返回一列認證授權中心(Certificate Authority (CA))簽發(fā)的證書。
  · 如果一個證書沒有被撤消,MSVerifyCertRevocation()返回true。
  · MSgetPrivateKey()為一個特定的別名(alias )返回私鑰。(這里所說的一個別名就是帶有一個RSA私鑰和證書的一個身份。)鑰匙從Microsoft鑰匙庫中輸出。
  · MSgetCert()為一個特定的別名從Microsoft證書庫中返回一個證書。
  · MSgetAliases()返回一組別名(帶有私鑰的一個身份的名字)。Microsoft鑰匙庫中的每個私鑰都有一個別名。
  · MSrsaSignHash()返回哈希數(shù)據(jù)(hashed data)的RSA簽名。
  · MSrsaDecrypt()用RSA算法來解密一個先前加密了的數(shù)據(jù)塊。
  · MSrsaEncrypt()用Microsoft RSA provider來加密一個數(shù)據(jù)塊。
  · MSrsaGetKeysize()返回Microsoft鑰匙庫中一個鑰匙的RSA鑰匙大小。
  · MSgetCRL()將一個證書撤消清單(Certificate Revocation List(CRL))下載到Microsoft Internet緩存中。

  一個約500行的用C語言代碼編寫的源文件mscryptofunctions.c中包含了所有這些函數(shù)。該代碼可以在Windows 98/NT4/2000/XP上運行。

  公共鑰匙加密算法

  公共鑰匙加密有兩個目的:加密和數(shù)字簽名。公共鑰匙加密運用一個包含兩部分的鑰匙(或一對鑰匙):一個私鑰和一個公鑰。公鑰帶有開始和終止日期、一個序號、一個身份(稱為Subject Distinguished Name)、和一個CA的簽名(見列表1)。RSA是最常用的公共鑰匙加密算法。

公共鑰匙加密運用一個公鑰和一個私鑰。一個數(shù)字證書(如下所示)包含公鑰、開始和結束日期、一個序號、一個身份和一個證書授權中心(CA)的簽名。
Serial number:
6822 3C33 7945 3AC8 F8C5 398B 7469 94E1
Signature algorithm: md5RSA
Issuer: CN = VeriSign Class 1 CA Individual
Subscriber-Persona Not Validated,
OU = www./repository/RPA Incorp.
By Ref.,LIAB.LTD(c)98,
OU = VeriSign Trust Network, O = VeriSign, Inc.
Valid from: Wednesday, May 30, 2001 7:00:00 PM
Valid to: Monday, July 30, 2001 6:59:59 PM
Subject: E = boyter@txdirect.net,
CN = Brian Boyter,
OU = Digital ID Class 1 <\? Microsoft,
OU = Persona Not Validated,
OU = www./repository/RPA Incorp.
by ref.,LIAB.LTD(c)98,
OU = VeriSign Trust Network, O = VeriSign, Inc.
Public key: 3081 8902 8181 00BA B459 0F39 156E
C69E C238 BFD0 401D DBB9 D207 DFA4 5DBD 09F3
5CE6 B5E6 C357 88DD 808B 0699 5F68 A2A4 6A8A
3B21 6D3D D0A1 1E5F DAB1 FB8E F835 F84F 849B
29A4 6943 8D59 0669 7C81 1D00 03B7 1A02 4E7A
8596 11BD 7CC4 07A3 D7E5 9FF6 5684 B853 04F0
0938 A11E 5218 F9AB F034 070D C8C4 6652 C19B
4C57 E435 EFDC 85D4 B269 07B7 0102 0301 0001
Basic constraints: Subject Type=End Entity,
Path Length Constraint=None
Certificate policy:
Policy Qualifier Id=CPS Qualifier:
https://www./CPS
Policy Qualifier Info:
Organization=VeriSign, Inc.,
Notice Number=1
CRL Distribution Point Distribution Point Name:
Full Name:
URL=http://crl./class1.crl
Thumbprint algorithm: sha1
Thumbprint: 74A8 9F07 43AA 8FFC C4D5 AB09 3773 3AFF F7E7 DFFC

  公共鑰匙加密中的加密是用公鑰來完成的,解密是用私鑰完成的。公共鑰匙加密對于大量的加密來說運算很復雜,但它卻被廣泛用來分配密鑰。密鑰,或對稱加密算法,如DES和RC4,通常用于大量的加密,但是密鑰加密算法需要一些保密的方法來交換用于大量加密的鑰匙。一種技術是生成一個隨機數(shù),用公共鑰匙加密算法來加密那個隨機數(shù),然后將加密了的隨機數(shù)發(fā)送給遠端的同伴。發(fā)送者用遠端同伴的公鑰來加密隨機數(shù)。接收者用它自己的私鑰來解密這個隨機數(shù)。任何截取了加密的隨機數(shù)的第三方都不能解密那個隨機數(shù),因為他沒有私鑰。

  在數(shù)字簽名中,用私鑰來完成簽名,用公鑰來完成確認。被簽名了的文件通常是經過哈希算法處理過的。哈希算法是一個單向算法,它可以減小文件的大小。運用MD5哈希算法,文件被簡小到16字節(jié)。運用SHA1哈希算法,文件被簡小到20字節(jié)。然后,就用簽名人的私鑰對經哈希算法處理過的文件進行加密。任何人都可以用簽名人的公鑰來解密哈希文件。

  你必須非常小心地保護私鑰。Windows將私鑰以有些令人迷惑的形式存儲在文件系統(tǒng)中。一個惡意的侵犯者可以進入到你的計算機并找到你的私鑰。任何得到了你的私鑰的人都可以化裝成你,在你不知道的情況下做出簽署文件等行為。一種保護私鑰的方法就是用一個加密的智能卡,該卡上存儲了私鑰。運用一個加密的智能卡,用戶仍然可以進行公鑰加密和簽名活動,但沒有人——甚至用戶——可以讀取私鑰。智能卡有一個RSA加密和簽名處理器,只有這個RSA處理器有權使用私鑰。

  當一個SSL服務器向一個SSL客戶端確認身份時,客戶端必須根據(jù)下面這些標準來確定服務器的證書是否有效:
  · 證書必須有一個信任鏈,其根CA必須是客戶端信任的。
  · 服務器證書,和信任鏈中所有的CA證書必須有有效的簽名。每個證書都是由下面更高級的CA來簽署的,除了根CA外,它簽名自己的證書。
  · 當前的日期和時間必須在服務器證書的有效期內,而且也在信任鏈中所有證書的有效期內。每個證書都有一個有效期(證書可以有效使用的一個開始日期和時間以及結束日期和時間)。
  · 每個CA應該管理和公布一個CRL。客戶端必須可以從信任鏈中的每個CA得到CRLs,來查看服務器證書或下屬CA的一個證書是否已被其下面更高級的CA撤消了。
  · 證書必須可以有效用于其目的。鑰匙的用途定義在證書中。例如,CA批準的僅用于數(shù)字簽名的一個鑰匙就不能用于SSL鑰匙交換。

  Java安全實現(xiàn)環(huán)境中不進行證書撤消確認,就是說,它不進行CRL處理。我將向你展示如何運用Microsoft Windows的本地加密函數(shù)來檢查證書信任鏈中的CRLs,從而為Java實現(xiàn)一個TrustManager和KeyManager。

  TrustManager

  javax.net.ssl.X509TrustManager有三個方法,你可以在MSTrustManagerlmpl.java中找到: · getAcceptedIssuers()為Microsoft證書庫中的所有CAs返回一組證書。
  · checkClientTrusted()執(zhí)行服務器的安全策略。
  · checkServerTrusted()執(zhí)行客戶端的安全策略。

  一個典型的TCP網(wǎng)絡安全策略是:

  · 客戶端開始連接。假設客戶端只連接到“安全的”服務器??蛻舳藨撘蠓掌饔靡粋€數(shù)字證書向客戶端證明身份。通過確認服務器的證書(信任鏈、簽名是有效的,有效期、證書沒有被撤消,而且證書是批準用于RSA鑰匙交換的),客戶端確認服務器的真實性。客戶端通過有效的證書來信任服務器。
  · 服務器接收來自所有客戶端的TCP連接,有些客戶端可能是惡意的。服務器可以要求客戶端用一個數(shù)字證書向服務器證明身份。那樣的話,客戶端的身份就可以被確認,而且多種信任級別也可以實現(xiàn)了。如果服務器不要求客戶端證明身份,服務器應該假設所有的客戶端都是惡意的。

  你可以在checkServerTrusted()中看到,實現(xiàn)客戶端安全策略是很容易的。CheckServerTrusted()檢查簽名、信任鏈中證書的有效日期和CRLs。(我在后面會探討證書撤消處理。)checkClientTrusted()方法與checkServerTrusted()是一樣的。一般來說,這個安全策略對服務器來說并不夠。一種增強服務器安全狀態(tài)的方法就是要求客戶端用數(shù)字證書來證明身份,只接受由一個特定的CA(如VeriSign CA)發(fā)布的證書,并且檢驗證書的Subject Distinguished Name中的特殊字段(如0=sun.com)。只需要幾行Java代碼就可以把這個過程添加到checkClientTrusted()中了。你需要定制checkClientTrusted()來實現(xiàn)你的安全策略(見列表2)。

checkClientTrusted()方法檢查簽名、信任鏈中證書的有效日期和CRLs。但是,對服務器來說,這個安全函數(shù)并不夠。你可以通過定制checkClientTrusted()來增強安全策略。
public void checkClientTrusted(
X509Certificate chain[]) {
// DontKnowFlag indicates what to do if we‘re
// not sure if the certificate is revoked
// int DontKnowFlag=0; // reject the cert
// int DontKnowFlag=1; // accept the cert
int DontKnowFlag=2; // ask the user

// check for revoked certs in the cert chain
if (com.boyter.mscrypto.MSValidCertificate.
isCertChainValid(chain, DontKnowFlag))
return;

// client cert is not trusted
System.out.println("Client Certificate is not Trusted - aborting");
System.exit(2);
}

  Java提供了幾個與證書鏈處理相關的類,在Java Certification Path API Programmer‘s Guide中有進一步說明。我對它們做過實驗,最后決定不用它們,因為我認為它們太復雜了。

  TrustManager有第三個方法getAcceptedIssuers()。該方法為Microsoft證書庫中所有CAs返回一組證書。Microsoft將這些證書存儲在Registry中;你可以通過啟動REGEDIT程序并查看HKEY_LOCAL_MACHINE\SOFTWARE\Microsoft\SystemCertificates\Root\Certificates找到它們。GetAcceptedIssuers()方法執(zhí)行了Microsoft的本地函數(shù)CertEnumCertificateslnStore()。CA證書被作為一組base64位編碼的字符串傳回到Java方法。Java.security.cert.CertificateFactory將base64編碼的證書轉換成Java證書。

  證書撤消

  有兩種撤消證書的方法:Online Certificate Status Protocol(OCSP)和CRL。OCSP(見RFC2560)并沒有得到廣泛的支持,所以我只探討用來確認一個證書的撤消狀況的CRLs。CAs定期公布一個CRL(見RFC2459)。CRL是一列證書序號,由CA簽署。如果一個證書的序號列在CRL中,那個證書就被撤消了,不再有效了。大多數(shù)證書在發(fā)布時,都有一個稱為CRL Distribution Point(CDP)的擴展名。CDP通常是一個HTTP或Lightweight Directory Access Protocol(LDAP)URL,它指明該CRL存儲在哪里。我的VeriSign證書的CDP是http://crl./class1.crl。該CRL有500多K,所以需要花些時間來下載。

  Java的確提供了類java.security.cert.LDAPCertStoreParameters,它可以用來從一個LDAP目錄獲取一個CRL,但這并不適用于VeriSign CRL(因為那個CRL存在一個Web服務器上,而不是一個LDAP服務器上)。Microsoft有一個比Java更豐富的API用來管理證書撤消。如果你瀏覽一個安全的網(wǎng)站(HTTPS),IE將會檢驗服務器的證書是否已被撤消(假設服務器的證書有一個CDP)。IE將從證書鏈中的CAs獲取CRLs并把它們緩存到IE的Temporary Internet Files(臨時Internet文件)目錄中。在下一次需要來自那個CA的CRL時,IE將首先檢查緩存。如果它在緩存中找到該CRL,IE會檢查該CRL是否過期。(CRLs也有有效的from/to期限。VeriSign運用一個10天的有效期限。)如果CRL已經過期,IE將獲取一個新的CRL。

  我考慮開發(fā)一個只用于Java的CRL獲取、緩存和撤消確認過程——但是那樣會需要大量的Java代碼,需要支持幾個URL協(xié)議(HTTP、LDAP、FTP、文件),需要用一個文件來存儲CRL緩存,而且需要復制Microsoft提供的底層框架。目的是運用IE的證書撤消底層框架。

  Microsoft提供了一個函數(shù)CertVerifyRevocation(),它與IE緩存結合起來了并按需要下載和處理CRLs。我琢磨了好幾周撤消過程,最后得到了一個可靠的算法。當CertVerifyRecocation()下載一個CRL時,下載有時會中斷。這就使CertVerifyRevocation()返回假的、很難恢復的錯誤狀況。我實現(xiàn)的過程顯示在圖1中。



圖1. 證書撤消流程表

  其訣竅就是調用函數(shù)CryptRetrieveObjectByUrl()來從LDAP、HTTP或FTP服務器預取CRL到IE緩存中,然后調用CertVerifyRevocation()來檢查這個證書是否已被撤消。雖然CryptRetrieveObjectByUrl()下載仍然要受到網(wǎng)絡的影響,造成連接中斷,但運用CryptRetrieveObjectByUrl()的好處就是你可以識別問題,報告一個有意義的錯誤信息,并且(如果你需要)可以提示用戶接受或拒絕該證書。不幸的是,只有Windows 2000或更高版本中有CryptRetrieveObjectByUrl()。如果程序要在Windows 98或Windows NT4上運行,你就不能預取CRL。

  我也嘗試運用Microsoft函數(shù)來下載CRLs,然后在Java中處理CRL。對于一個大的CRL(比如VeriSign CRL),這種方式很慢。

  列表3顯示關于isCertRevoked()方法的Java代碼片段。IsCertRevoked()方法調用了兩個本地函數(shù),MSgetCRL()和MSVerifyCertRevocation()。MSgetCRL()調用Microsoft函數(shù)CryptRetrieveObjectByURL(),如果CRL不在緩存中,它可以使CRL被下載。你不需要把CRL傳遞到Java中,因為它從來不用在Java中。函數(shù)MSCertVerifyRevocation()調用Microsoft函數(shù)CertVerifyRevocatoin()來確定一個證書是否被撤消了。列表4顯示關于MSgetCRL()和MSVerifyCertRevocation()的代碼片段。警告:當我第一次在Windows 2000 Server PC上測試CertVerifyRevocation()時,它沒有運行。在我將PC升級到Service Pack 2后,重新測試就很成功。

我們的確認程序中的isCertRevoked()方法調用了兩個本地函數(shù):MSgetCRL()和MSVerifyCertRevocation() (如列表4所示)。
boolean isCertRevoked(X509Certificate cert,
int DontKnowFlag) {
byte[] certblob = cert.getEncoded();

// Does the cert have a CDP (
// CRL distribution point)???
byte[] CDPblob = cert.getExtensionValue(
"2.5.29.31");

// yes there is a CDP - ASN parse the CDP
String[] URLarray = MSF.MSparseCDP(CDPblob);
for (int i=0; i<URLARRAY.LENGTH; i++) String URL = URLarray[i];
// go fetch that CRL
if (MSF.MSgetCRL(URL)) {
// url was fetched correctly
break;
}

// is the cert revoked???
int revocationStatus =
MSF.MSVerifyCertRevocation(certblob);
switch (revocationStatus) {
case 0: // cert is revoked
return AskUserWhatHeWantsToDo(DontKnowFlag);
case 1: // cert is not revoked
return false;
default:
}

// processing error - cannot determine
// if cert is revoked
return AskUserWhatHeWantsToDo(DontKnowFlag);
}


函數(shù)MSgetCRL()和MSVerifyCertRevocation()由方法isCertRevoked()調用(如列表3所示)。MSgetCRL()調用Microsoft函數(shù)CryptRetrieveObjectByURL(),如果在緩存中沒有CRL,可以用該函數(shù)來下載它。函數(shù)MSVerifyCertRevocation()調用Microsoft函數(shù)CertVerifyRevocation()來查看一個證書是否已被撤消。
MSgetCRL(jstring jurl)
{
if (!CryptRetrieveObjectByUrl(
url, CONTEXT_OID_CRL, 0, timeout*1000,
(LPVOID)&crl, NULL, NULL, NULL, NULL)) {
printf("CryptRetrieveObjectByUrl failed\n");
// cached url is corrupted
DeleteUrlCacheEntry(url);
return JNI_FALSE;
}
return JNI_TRUE;
}


MSVerifyCertRevocation (jbyteArray jCert)
{
rgpvContext[0] = (PVOID)pCertContext;
if (CertVerifyRevocation(X509_ASN_ENCODING,
CERT_CONTEXT_REVOCATION_TYPE, 1, rgpvContext,
0, NULL, &status)) {
return 1; // cert is not revoked
}

if (status.dwError == CRYPT_E_REVOKED)
return 0; // cert is revoked

return -2; // processing error
}


  KeyManager

  javax.net.ssl.X509KeyManager有六個方法:
  · getClientAliases()返回一組客戶端別名。(這里的一個別名就是帶有一個RSA私鑰和證書的一個身份。)
  · getServerAliases()返回一組服務器別名。
  · chooseClientAlias()從一組別名中選擇一個客戶端別名。
  · chooseServerAlias()從一組別名中選擇一個服務器別名。
  · getCertificateChain()為一個證書返回有序的證書鏈。
  · getPrivateKey()為一個別名返回私鑰。

  Microsoft將私鑰和它們相關的證書存儲在文件系統(tǒng)中。我的私鑰和證書存儲在目錄C:\Documents和Settings\Administrator\Application Data\Microsoft中。你不需要知道鑰匙存儲在哪里,因為Microsoft提供了一個API用來訪問鑰匙和證書庫。

  KeyManager的getClientAliases()和getServerAliases()方法執(zhí)行Microsoft本地的函數(shù)CertEnumCertificateslnStore(),查看Microsoft的“My”證書庫中的所有證書。“My”證書庫中的證書應該有一個與它們相關連的私鑰。每個證書/私鑰組合都有一個特殊的標識符,稱為CONTAINER;這就相當于Java中的“別名”(見列表5)。

CertEnumCertificatesInStore()方法查看Microsoft中“My”證書庫中所有的證書;每個證書都有一個相關的私鑰。每個證書/私鑰組合都有一個標識符,稱為CONTAINER,這就相當于Java中的別名。
JobjectArray MSgetAliases (jstring jcertStore) {

// open Microsoft certificate store
hSystemStore = CertOpenStore(
CERT_STORE_PROV_SYSTEM,
0, 0, CERT_SYSTEM_STORE_CURRENT_USER,
certStore);

// read all the certificates
while(pCertContext=
CertEnumCertificatesInStore(
hSystemStore, pCertContext)) {

// get the cert key container name
CertGetCertificateContextProperty(
pCertContext, CERT_KEY_PROV_INFO_PROP_ID,
alias, &propLen);

// add to list of aliases
AddDataToList(&list, alias, strlen(alias)+1);
}
return jaliases;
}


列表5

  方法chooseClientAlias()和chooseServerAlias()從一列別名中返回一個客戶端(或服務器)別名。如果只有一個別名,在選擇別名時就不會有歧義。因為Java的創(chuàng)始人對于選擇運用哪個別名并沒有提供任何特殊的指導,我就選擇了清單中第一個別名。另一個選擇合適的客戶端別名的方法就是提示用戶從一列可能的別名中選擇一個別名。(在SSL客戶端,你通??梢赃@么做,但在服務器上不行。)

  KeyManager的getCertificateChain()方法為一個證書返回有序的證書鏈。該方法通過調用getAcceptedIssuers()方法得到一列可信任的證書簽發(fā)者。首先我們找到證書簽發(fā)者的Distinguished Name(DN),然后我們查看是否有哪個可信任的簽發(fā)者有那個DN。幾個簽發(fā)者可以有同一個DN。對于具有簽發(fā)者DN的每個證書,我們提取公鑰并嘗試在原始證書上確認簽名。如果沒有一個簽發(fā)者有正確的DN和正確的公鑰,證書鏈就被破壞了,出現(xiàn)一個異常。如果我們找到了正確的簽發(fā)者簽發(fā)的證書,我們就重復上述過程來查找和確認那個證書的簽發(fā)者。重復該過程,直到我們達到根CA。對于一個根CA,Subject DN和簽發(fā)者DN是一樣的(見列表6)。

方法getCertChain()為一個證書返回有序的證書鏈。
MSCryptoFunctions MSF = new MSCryptoFunctions();
X509Certificate[] getCertChain(
X509Certificate cert) {

try {
getCACerts();

Principal subject = cert.getSubjectDN();
Principal issuer = cert.getIssuerDN();
CertChainList.add(cert);

// stop if issuer==subject (root CA)
while (!(issuer.equals(subject))) {

match = false;
X509CertSelector xcs =
new X509CertSelector();
xcs.setCertificateValid(new Date());

Collection certcollection =
CACerts.getCertificates(xcs);

//
// the next 7 lines are inserted to work
// around a problem with X509CertSelector.
// we should be able to do this with
// xcs.setSubject(issuer.toString());
//
Iterator iter = certcollection.iterator();
while ( iter.hasNext() ) {
X509Certificate cacert =
(X509Certificate) (iter.next());
if (!cacert.getSubjectDN().equals(issuer))
iter.remove();
}

issuerArray =
new X509Certificate[
certcollection.size()];
issuerArray = (X509Certificate[])
certcollection.toArray(issuerArray);

for (int i=0; i<\<>issuerArray.length; i++)
if (verifySignature(issuerArray[i], cert)){
match = true;
cert = issuerArray[i];
subject = cert.getSubjectDN();
issuer = cert.getIssuerDN();
CertChainList.add(cert);
break;
}
if (!match) {
return null; // cert chain broken
}
}
} catch (Exception e) {
e.printStackTrace();
}

X509Certificate[] CertChain =
new X509Certificate[CertChainList.size()];
CertChainList.toArray(CertChain);

return CertChain;
}

  getPrivateKey()方法為一個別名返回私鑰,假設私鑰可以從Microsoft鑰匙庫中輸出。記住,有時私鑰是不能輸出的。(例如,如果你用了一個加密了的智能卡,那么就沒人可以從智能卡上讀取私鑰了。)如果不能輸出私鑰,getPrivateKey()就返回一個虛擬的私鑰。所以,如果getPrivateKey()不能得到私鑰,我們就騙Java,讓它認為得到了私鑰。getPrivateKey()也緩存別名,所以,當一個Java程序試圖執(zhí)行一個RSA數(shù)字簽名函數(shù)時,我們就會知道運用哪個私鑰了(緩存的別名),而且Microsoft加密提供者就可以執(zhí)行我們想要的RSA簽名或解密函數(shù)了(見列表7)。

方法getPrivateKey()為一個別名返回私鑰,假設私鑰可以從Windows鑰匙庫中輸出。
MSCryptoFunctions MSF = new MSCryptoFunctions();
public PrivateKey getPrivateKey(String alias) {

// get the private key from MS Windows for
// this alias
byte[] keyblob = MSF.MSgetPrivateKey(alias);

if (keyblob == null) { // generate a dummy key
byte[] modblob = new byte[128];
for(i=0; i<128; i++)
modblob[i] = 127;
mod = new BigInteger(modblob);
exp = mod;

} else { // use the key that got exported
for(i=0; i
modblob[i] = keyblob[19-i+(keysize/16)*2];
expblob[i] = keyblob[19-i+(keysize/16)*9];
}
mod = new BigInteger(1, modblob);
exp = new BigInteger(1, expblob);
}
RSAPrivateKeySpec privKeySpec =
new RSAPrivateKeySpec(mod, exp);
KeyFactory kf = KeyFactory.getInstance("RSA");
privkey = kf.generatePrivate(privKeySpec);
return privkey;
}


  RSA Signature Provider

  java.security.SignatureSpi類有五個方法:
  · engineInitSign()為簽名初試化RSA簽名引擎。
  · engineInitVerify()為確認一個簽名初試化RSA簽名引擎。
  · engineUpdate()增加數(shù)據(jù)到簽名或確認操作。
  · engineSign()完成簽名操作并返回數(shù)字簽名。
  · engineVerify()完成簽名-確認過程,如果簽名是正確的,返回true。

  記住,數(shù)字簽名需要私鑰,確認一個數(shù)字簽名需要公鑰。如果我們有權使用Microsoft的私鑰——即,私鑰是可輸出的——就沒必要在Microsoft本地代碼中執(zhí)行RSA簽名操作了。但是在有些情況下(例如,如果我們運用一個加密了的智能卡),我們無權使用私鑰。如果私鑰是不能輸出的,我們必須用Microsoft本地代碼進行數(shù)字簽名。

  如果Java程序運用KeyManager的方法getPrivateKey()來獲取私鑰,私鑰的別名就被緩存起來。當RSA Signature Provider進行簽名時,我們就重用緩存的別名,并調用Microsoft本地函數(shù)來執(zhí)行簽名操作而不用暴露私鑰。(這聽起來有些虛假,但確實可行。)注意,在engineInitSign()中沒有用私鑰。我用Java JCE哈希函數(shù)來進行運算,然后用Microsoft Cryptographic Provider從哈希文件中生成RSA簽名。

  通過添加一個engineInitSign(字符串別名)方法,可以改進Java Signature類:

MSCryptoFunctions MSF =
new MSCryptoFunctions();
protected void engineInitSign(
PrivateKey privateKey) {
MSF.MSrsaSignInit((byte[])null,
"MD5");
}

protected byte[] engineSign() {
byte[] hash = MD5.digest();
byte[] mssig =
MSF.MSrsaSignHash(hash,
(byte[])null, "MD5");
return mssig;
}

  我們可以在Microsoft本地代碼中實現(xiàn)簽名-確認,但這么做沒有優(yōu)勢。在實現(xiàn)過程中,我們運用了JSSE提供者在Java中執(zhí)行確認:

protected void engineInitVerify(
PublicKey publicKey) {
jsse = Signature.getInstance(
"MD5withRSA", "SunJSSE");
jsse.initVerify(publicKey);
}

protected boolean engineVerify(
byte[] sigBytes) {
boolean verifyresult=false;
verifyresult =
jsse.verify(sigBytes);
return verifyresult;
}

  RSA Cipher Provider

  javax.Crypto.CipherSpi類有12個方法:
  · engineInit()初試化密碼提供者(cipher provider)。
  · engineUpdate()繼續(xù)一個由多個部分組成的加密或解密操作。
  · engineDoFinal()加密或解密一個單一操作中的數(shù)據(jù),或完成一個由多個部分組成的操作。
  · engineGetBlockSize()返回字區(qū)大小(以字節(jié)形式)。
  · engineGetIV()返回初試化向量。它不用于RSA密碼。
  · engineGetKeySize()返回一個特定的鑰匙對象的鑰匙大小。
  · engineGetOutputSize()以字節(jié)形式返回輸出長度,輸出緩沖器需要這個長度來保存下一個update或doFinal操作的結果,輸入長度已假定。
  · engineGetParameters()返回這個密碼運用的參數(shù)。
  · engineSetMode()設置密碼的模式(加密或解密)。
  · engineSetPadding()設置這個密碼的填充機制(當前只支持PKCS1填充)。
  · engineWrap()封裝一個鑰匙(未實現(xiàn))。
  · engineUnwrap()解開一個鑰匙(未實現(xiàn))。

  RSA加密過程需要公鑰;RSA解密需要私鑰。如果我們有權使用Microsoft鑰匙庫中的RSA私鑰——即,私鑰是可以輸出的——那么就沒必要在Microsoft本地代碼中執(zhí)行RSA密碼操作。但在有些情況下(例如,如果我們運用一個加密了的智能卡),我們就無權使用私鑰。如果私鑰是不能輸出的,我們必須用Microsoft本地代碼進行RSA解密。

  RSA密碼在Windows本地代碼中的實現(xiàn)很簡單。本質的解密過程如下:

MSrsaDecrypt (jstring jpadalg,
jbyteArray jdata) {
CryptAcquireContext(
&hDecryptProv, alias, NULL,
PROV_RSA_FULL,0);
CryptGetUserKey(hDecryptProv,
AT_KEYEXCHANGE, &hDecryptKey);
CryptDecrypt(hDecryptKey, 0,
TRUE, 0, encryptblob, &ndata);
return decryptblob;
}

  加密幾乎是一樣的。因為RSA加密只需要公鑰,加密模式可以在Java中執(zhí)行。我選擇在Windows本地代碼中實現(xiàn)RSA加密和解密,因為這很容易。

  RSA密碼是一個美國政府控制的加密算法。如果你想將RSA cipher provider同Sun JCE結合起來運用,你必須創(chuàng)建一個JAR文件,用一個DSA鑰匙簽署它,然后添加一個由Sun Microsystems簽發(fā)的證書。為了方便,我已經把所有的mscrypto-class文件放在了一個單一的包中(com.boyter.mscrypto)。在簽署的JAR文件中,只需要有MSRSACipherProvider.class和MSRSACipherFactoryImpl.class。用來創(chuàng)建和簽名JAR文件的Windows命令是:

jar cvf mscrypto.jar com
jarsigner -keystore keystore
-storepass foobar mscrypto.jar
jcesigner

  你可以從“How to Implement a Provider for the Java Cryptography Extension 1.2.1.”(見資源)的第五步找到關于從Sun獲取一個JCE代碼簽署的證書的說明。如果你想避免JCE的局限性(如美國政府的輸出控制),你可以用一個clean-room式的實現(xiàn)環(huán)境(如BeeJCE)來代替JCE。我提供了一個稱為msrsatest.java的程序,可以用來測試Microsoft 加密的RSA簽名和RSA密碼提供者。為此,你必須在Microsoft Windows中安裝一個RSA私鑰和證書。

  有根據(jù)的運用理由

  將Java安全特征同本地Microsoft Windows安全平臺結合起來有許多好的理由,包括減少了的管理費用、CRL確認和與智能卡的兼容。將Java JCE同Windows證書和鑰匙庫結合起來的另一個理由是用來管理Java證書和鑰匙庫的Java工具很麻煩。Microsoft平臺有一個更好的圖形用戶界面(GUI)用來管理Windows鑰匙和證書庫。你可以通過運行CERTMGR.EXT程序(與Windows平臺SDK在一起)來啟動GUI,或者你可以從IE窗口來啟動:運用下拉菜單Tools | Internet Option,選擇Content鍵,然后選擇Certificates。

  在將Microsoft 加密支持的KeyManager和TrustManager用于JSSE時,你不會有什么問題。Microsoft 加密支持的RSA Signature Provider和RSA Cipher Provider也可以運行,但不能用于JSSE。

    本站是提供個人知識管理的網(wǎng)絡存儲空間,所有內容均由用戶發(fā)布,不代表本站觀點。請注意甄別內容中的聯(lián)系方式、誘導購買等信息,謹防詐騙。如發(fā)現(xiàn)有害或侵權內容,請點擊一鍵舉報。
    轉藏 分享 獻花(0

    0條評論

    發(fā)表

    請遵守用戶 評論公約

    類似文章 更多