2014/03/07

Just a Note:Sun JCE 驗章問題

驗章時須使用 Public Key,一般來說,在 JCE 下使用已知的 Key 值建立 Public Key 的方式有兩種:

X509EncodedKeySpec keySpec = new X509EncodedKeySpec(pubKey.getEncoded());
KeyFactory keyFactory = KeyFactory.getInstance("RSA");
PublicKey pubKey = keyFactory.generatePublic(keySpec);

RSAPublicKeySpec keySpec = new RSAPublicKeySpec(new BigInteger(modulus), new BigInteger(exponent));
KeyFactory keyFactory = KeyFactory.getInstance("RSA");
RSAPublicKey pubKey = (RSAPublicKey) keyFactory.generatePublic(keySpec);

差別在於 Key 值的保存型態,
第一種存的是 Key.getEncoded() 的值 (http://docs.oracle.com/javase/7/docs/api/java/security/Key.html),
第二種保存的是 Public Key 的 modulus 和 exponent。而使用第二種方式時,通常我們亦會把 modulus 和 exponent 用 byte[] 保存。

最近遇到的問題是,
如果 Public Key 是透過 RSAPublicKeySpec 建立的話,會驗章失敗;使用 X509EncodedKeySpec 建立的話,則會成功。
但是把兩種方式建出來的 Public Key dump 出來 (getEncoded()),卻會得到一模一樣的值。

Debug 後發現,
RSAPublicKeySpec 建出來的 Key,其 modulus 是負數,
X509EncodedKeySpec 建出來的 Key,其 modulus 則是正數。

解決方式:
當 modulus 的第一個 byte 大於等於 0x80 時,需在最前面多放一個 byte 0x00,再轉成 BigInteger,即能正確取得 modulus 的值。

String modulusStr = "C868EEE8D1D300207EA65DB1DBBFD52552AE1814E07FDF23DE040EC96E6F0F35C8ECF922C11D7F39B3E3162B96881447D16B0FCC75FB0378F2B19AFB22884B8F2E2840C74FE73DBCDBF653E26070955BB9A4011C1EDD9E1DBA482F02091DBBFAD77D13F91A06FF76C8FDE1E4F475A2DFF8A9567C115F50ACCA573315F7723D333218E266984953D0642057C46D6F952CFC7CFAEAA8945FAEDE441ADA8497F0895C1AC18CE6679467F6EFEF5C741879487E94B8DAF87FA45E87FD011BB0A794B4F433B655959B6D1D8164793421A360A39E17A2287EE5CFE7200FF6FA3F407DD7FEC2EAA6C6A89BE5A3CE3073B758E56654C17CE17A3657E76E219E1BECA9115D";
byte[] modulusTmp = Util.hexStringToByte(modulusStr);
byte[] modulus = null;
if (modulusTmp[0] < 0) {
    modulus = new byte[modulusTmp.length + 1];
    modulus[0] = 0x00;
    System.arraycopy(modulusTmp, 0, modulus, 1, modulusTmp.length);
} else {
    modulus = new byte[modulusTmp.length];
    System.arraycopy(modulusTmp, 0, modulus, 0, modulusTmp.length);
}