etCRL()调用Microsoft函数CryptRetrieveObjectByURL(),如果CRL不在缓存中,它可以使CRL被下载。你不需要把CRL传递到Java中,因为它从来不用在Java中。函数MSCertVerifyRevocation()调用Microsoft函数CertVerifyRevocatoin()来确定一个证书是否被撤消了。列表4显示关于MSgetCRL()和MSVerifyCertRevocation()的代码片段。警告:当我第一次在Windows
2000 Server PC上测试CertVerifyRevocation()时,它没有运行。在我将PC升级到Service Pack
2后,重新测试就很成功。
我们的确认程序中的isCertRevoked()方法调用了两个本地函数: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); }
函数MSgetCRL()和MSVerifyCertRevocation()由方法isCertRevoked()调用(如列表3所示)。MSgetCRL()调用Microsoft函数CryptRetrieveObjectByURL(),如果在缓存中没有CRL,可以用该函数来下载它。函数MSVerifyCertRevocation()调用Microsoft函数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将私钥和它们相关的证书存储在文件系统中。我的私钥和证书存储在目录C:\Documents和Settings\Administrator\Application
Data\Microsoft中。你不需要知道钥匙存储在哪里,因为Microsoft提供了一个API用来访问钥匙和证书库。
KeyManager的getClientAliases()和getServerAliases()方法执行Microsoft本地的函数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的创始人对于选择运用哪个别名并没有提供任何特殊的指导,我就选择了清单中第一个别名。另一个选择合适的客户端别名的方法就是提示用户从一列可能的别名中选择一个别名。(在SSL客户端,你通常可以这么做,但在服务器上不行。)
KeyManager的getCertificateChain()方法为一个证书返回有序的证书链。该方法通过调用getAcceptedIssuers()方法得到一列可信任的证书签发者。首先我们找到证书签发者的Distinguished
Name(DN),然后我们查看是否有哪个可信任的签发者有那个DN。几个签发者可以有同一个DN。对于具有签发者DN的每个证书,我们提取公钥并尝试在原始证书上确认签名。如果没有一个签发者有正确的DN和正确的公钥,证书链就被破坏了,出现一个异常。如果我们找到了正确的签发者签发的证书,我们就重复上述过程来查找和确认那个证书的签发者。重复该过程,直到我们达到根CA。对于一个根CA,Subject
DN和签发者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程序试图执行一个RSA数字签名函数时,我们就会知道运用哪个私钥了(缓存的别名),而且Microsoft加密提供者就可以执行我们想要的RSA签名或解密函数了(见列表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<keysize/8;
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()增加数据到签名或确认操作。 ·
engineSign()完成签名操作并返回数字签名。 · engineVerify()完成签名-确认过程,如果签名是正确的,返回true。
记住,数字签名需要私钥,确认一个数字签名需要公钥。如果我们有权使用Microsoft的私钥——即,私钥是可输出的——就没必要在Microsoft本地代码中执行RSA签名操作了。但是在有些情况下(例如,如果我们运用一个加密了的智能卡),我们无权使用私钥。如果私钥是不能输出的,我们必须用Microsoft本地代码进行数字签名。
如果Java程序运用KeyManager的方法getPrivateKey()来获取私钥,私钥的别名就被缓存起来。当RSA Signature
Provider进行签名时,我们就重用缓存的别名,并调用Microsoft本地函数来执行签名操作而不用暴露私钥。(这听起来有些虚假,但确实可行。)注意,在engineInitSign()中没有用私钥。我用Java
JCE哈希函数来进行运算,然后用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本地代码中实现签名-确认,但这么做没有优势。在实现过程中,我们运用了JSSE提供者在Java中执行确认:
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()继续一个由多个部分组成的加密或解密操作。 ·
engineDoFinal()加密或解密一个单一操作中的数据,或完成一个由多个部分组成的操作。 ·
engineGetBlockSize()返回字区大小(以字节形式)。 · engineGetIV()返回初试化向量。它不用于RSA密码。
· engineGetKeySize()返回一个特定的钥匙对象的钥匙大小。 ·
engineGetOutputSize()以字节形式返回输出长度,输出缓冲器需要这个长度来保存下一个update或doFinal操作的结果,输入长度已假定。
· engineGetParameters()返回这个密码运用的参数。 · engineSetMode()设置密码的模式(加密或解密)。
· engineSetPadding()设置这个密码的填充机制(当前只支持PKCS1填充)。 ·
engineWrap()封装一个钥匙(未实现)。 · engineUnwrap()解开一个 上一页 [1] [2] [3] 下一页 没有相关教程
|