转至繁体中文版     | 网站首页 | 图文教程 | 资源下载 | 站长博客 | 图片素材 | 武汉seo | 武汉网站优化 | 
最新公告:     敏韬网|教学资源学习资料永久免费分享站!  [mintao  2008年9月2日]        
您现在的位置: 学习笔记 >> 图文教程 >> 软件开发 >> JAVA开发 >> 正文
将Java加密技术同Windows结合起来         ★★★★

将Java加密技术同Windows结合起来

作者:闵涛 文章来源:闵涛的学习笔记 点击数:2026 更新时间:2009/4/22 23:27:43
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]  下一页


没有相关教程
教程录入:mintao    责任编辑:mintao 
  • 上一篇教程:

  • 下一篇教程:
  • 【字体: 】【发表评论】【加入收藏】【告诉好友】【打印此文】【关闭窗口
      注:本站部分文章源于互联网,版权归原作者所有!如有侵权,请原作者与本站联系,本站将立即删除! 本站文章除特别注明外均可转载,但需注明出处! [MinTao学以致用网]
      网友评论:(只显示最新10条。评论内容只代表网友观点,与本站立场无关!)

    同类栏目
    · C语言系列  · VB.NET程序
    · JAVA开发  · Delphi程序
    · 脚本语言
    更多内容
    热门推荐 更多内容
  • 没有教程
  • 赞助链接
    更多内容
    闵涛博文 更多关于武汉SEO的内容
    500 - 内部服务器错误。

    500 - 内部服务器错误。

    您查找的资源存在问题,因而无法显示。

    | 设为首页 |加入收藏 | 联系站长 | 友情链接 | 版权申明 | 广告服务
    MinTao学以致用网

    Copyright @ 2007-2012 敏韬网(敏而好学,文韬武略--MinTao.Net)(学习笔记) Inc All Rights Reserved.
    闵涛 投放广告、内容合作请Q我! E_mail:admin@mintao.net(欢迎提供学习资源)

    站长:MinTao ICP备案号:鄂ICP备11006601号-18

    闵涛站盟:医药大全-武穴网A打造BCD……
    咸宁网络警察报警平台