在web開發中,采用RSA公鑰密鑰體系自制ukey,文件證書登陸時,普遍的做法為:在瀏覽器端采用c++ activex控件,使用 c++的第三庫openssl進行RAS加簽操作,在服務器端采用java對客戶端的簽名進行驗簽操作。這就涉及到c++ openssl和java之間交互加簽驗簽對客戶端身份進行驗證的過程。
如果你通過搜索查到我這邊文章,相信你一定發現,采用openssl加簽后的 數據,在java端卻驗簽不成功,使用openssl驗簽可以通過。問題在於openssl的公鑰發在服務端轉換成java RSA 公鑰時有問題,openssl的公鑰格式里附加了它自己的一些額外信息。所以在服務端java構造自己的pubkey時必須先剔除openssl的特有信息。
例如如果我么采用openssl生成 modulus size 為1024, exponent為65537 的公鑰秘鑰對---RSA_generate_key(1024, 65537, NULL, NULL);那么我們在java端通過openssl的公鑰構造java格式的公鑰時,我們就必須采用如下方式獲得modulus ,然后采用java的方式構造公鑰,進行驗簽操作。
private static byte[] getModulus(byte[] pkData, int begin){
byte[] modData = new byte[128] ;
byte[] ss = {pkData[0],pkData[1],pkData[2],pkData[3]};
for(int i = 0, y = begin; i < 128; i++,y++){
modData[i] = pkData[y];
}
return modData;
}
/**
* 從openssl中提起相關的128為的公鑰數據
* @param b64Str
* @return
*/
private static byte[] get128PkData(String b64Str){
String pk = b64Str.replace("-----BEGIN RSA PUBLIC KEY-----", "");//剔除前面的信息
pk = pk.replace("-----END RSA PUBLIC KEY-----", "");// 剔除后面的信息
byte[] pkData = Base64.decode(pk);
return getModulus(pkData, 7);//下標從7開始,獲得128 bytes的modulus
}
/** 如果你問我里邊到底加了一些什么信息,我也不知道,我是從下標1開始不斷測試,才得出應該從下標7開始獲取modulus值 ***/
/**
* 由1024位的公鑰轉換成x509格式的公鑰
* @param modData
* @return
* @throws Exception
*/
private static PublicKey getPublicKey(byte[] modData) throws Exception{
KeyFactory keyf = KeyFactory.getInstance("RSA");
RSAPublicKeySpec pubKeySpec = new RSAPublicKeySpec(new BigInteger(modData), new BigInteger("65537"));
RSAPublicKey pk = (RSAPublicKey) keyf.generatePublic(pubKeySpec);
// 解密由base64編碼的公鑰,並構造X509EncodedKeySpec對象
java.security.spec.X509EncodedKeySpec bobPubKeySpec = new java.security.spec.X509EncodedKeySpec(
pk.getEncoded());
return keyf.generatePublic(bobPubKeySpec);
}
至於openssl我就不再做介紹,網上有很多資料,而且它本身的文檔也很齊全
在后面附上一些相關的類, SignProvider.java 端負責公鑰轉換和加簽驗簽 Base64.java負責64為編碼 fcOpenSslRef.rar---Openssl相關類
注:本人對c++也不熟,所以上面的一些c++代碼只做參考。
原文:http://www.chlusoft.com/tech/347.jhtml
