处理摘要
正如我早先提到的那样,为了改进性能并避免攻击造成堵塞,您实际上签署的是消息摘要而不是消息本身。清单 1 说明了如何使用 SHA1Digest 摘要引擎来计算来自一段文本消息的已编码的摘要。
清单 1. 创建已编码的摘要
SHA1Digest digEng = new SHA1Digest();
byte [] mesgBytes = mesg.getBytes();
digEng.update( mesgBytes, 0, mesgBytes.length );
byte [] digest = new byte[digEng.getDigestSize()];
digEng.doFinal(digest, 0);
// Encode the digest into ASCII format for XML
return (new String(Base64.encode(digest)));
}
在下几节中,我们将了解如何使用 Bouncy Castle 的 DSA、ECC 和 RSA 签名引擎来签署并验证数字签名。那些签名引擎使用不同的算法和不同的密钥,并且需要不同的参数。我们还将讨论如何将安全性信息(签名、摘要和公钥)嵌入 XML 文档。最后,我将比较三个签名引擎并提出今后的改进建议。
DSA 签名示例
方法 DSASigUtil.generateKeys() 生成密钥对。正如我讨论过的那样,这个步骤通常由中央认证中心在脱机状态下完成,如清单 2 所示:
清单 2. 生成密钥对
SecureRandom sr = new SecureRandom();
// Generate DSA parameters.
DSAParametersGenerator DSAParaGen = new DSAParametersGenerator();
DSAParaGen.init(1024, 80, sr);
DSAPara = DSAParaGen.generateParameters();
// Get DSA key generation parameters.
DSAKeyGenerationParameters DSAKeyGenPara =
new DSAKeyGenerationParameters(sr, DSAPara);
// Generate keys.
DSAKeyPairGenerator DSAKeyPairGen = new DSAKeyPairGenerator();
DSAKeyPairGen.init( DSAKeyGenPara );
AsymmetricCipherKeyPair keyPair = DSAKeyPairGen.generateKeyPair();
privKey = (DSAPrivateKeyParameters) keyPair.getPrivate();
pubKey = (DSAPublicKeyParameters) keyPair.getPublic();
生成的公钥以参数 Y 来描述,并且用 pubKey.getY() 方法来检索它。参数 G、P 和 Q 描述模型。类 DSAUtil 中的下列方法检索模型和密钥参数,它们是重新构造公钥对象所必需的:
清单 3. 检索模型和密钥参数
return (new String(Base64.encode(DSAPara.getG().toByteArray())));
}
public static String getP() throws Exception {
return (new String(Base64.encode(DSAPara.getP().toByteArray())));
}
public static String getQ() throws Exception {
return (new String(Base64.encode(DSAPara.getQ().toByteArray())));
}
public static String getY() throws Exception {
return (new String(Base64.encode(pubKey.getY().toByteArray())));
}
通过使用生成的私钥,实用程序类 DSASigUtil 可以从摘要获取两部分 DSA 签名 R 和 S:
清单 4. 检索 DSA 签名
// Sign
DSASigner signer = new DSASigner();
signer.init( true, privKey );
BigInteger [] sigArray = signer.generateSignature( digest.getBytes());
String [] result = new String [2];
// Signature R
result[0] = new String(Base64.encode(sigArray[0].toByteArray()));
// Signature S
result[1] = new String(Base64.encode(sigArray[1].toByteArray()));
return result;
}
服务器将摘要、签名和密钥参数编码成 ASCII 文本格式并以 XML 数字签名格式嵌入该文本,如清单 5 所示:
清单 5. 编码并以数字签名格式嵌入
<mesg>Hello World</mesg>
<Signature>
<SignedInfo>
<SignatureMethod
Algorithm="http://www.w3.org/2000/09/xmldsig#dsa-sha1" />
<DigestValue>Ck1VqNd45QIvq3AZd8XYQLvEhtA=</DigestValue>
</SignedInfo>
<SignatureValue>
<R>AMfVKyIUyPGdeUCtJxU+N9kQJc2x</R>
<S>RwGahqpopPx//bMYXzH8dtY0lhA=</S>
</SignatureValue>
<KeyInfo>
<KeyValue>
<DSAKeyValue>
<G>
FgLTXVdxKAmDQtQHkDdFF5zthKSpQhUCzRgXxz7yzxM
OLYrRoj5D8AXdGLS+5CzT4gu55MbO62dBfyEWKbWTIO
6E+CuOfa53wvqjMl67tGxc8szgWWA6ZvRwVVVmJ6wqB
m5hNLr7q1X2eJKQ+u3XYpFflJktOjV8O3zeEPOtsTQ=
</G>
<P>
AOAu2WqVEKGTF8Zcxgde4vxc8f/Z+hk8A10M0AtY2lU
8CX54dz2MuD6hOmhqGXJxIVlV9085d9D0yHcMv2wl9V
Vt0/ww+aqFukCKZj9fHgZzq26nOBXMqibDo67J2vfQw
EZMvCnyBXdS665whjzl5i7ubXu2Su+AqsodnvG9pyYB
</P>
<Q>AMjJUZy1RnQRqe/22BS83k2Hk8VR</Q>
<Y>
AM/9leouAW7nyON24xeqibMUpVOW8RyzcdNjp9NiPdfm
HT42BvB4JL/cXx0tCbyHtcR5G+vALoOo7Mh3JJ+/gjx7
sS8uHNngqx6O6dADrc9VdPvyllNDR0szLja1RTRCIy9M
8p0dKe/U8iotAj2zctjfbrroMu/fTOBhkvb2gVvR
</Y>
</DSAKeyValue>
</KeyValue>
</KeyInfo>
</Signature>
</SignedMesg>
验证 MIDP 应用程序从 XML 文档解析出摘要、密钥参数和签名,重新构造公钥并使用下列方法来验证签名:
清单 6. 验证签名
String sig_r, String sig_s,
String key_g, String key_p,
String key_q, String key_y ) {
BigInteger g = new BigInteger( Base64.decode(key_g) );
BigInteger p = new BigInteger( Base64.decode(key_p) );
BigInteger q = new BigInteger( Base64.decode(key_q) );
BigInteger y = new BigInteger( Base64.decode(key_y) );
BigInteger r = new BigInteger( Base64.decode(sig_r) );
BigInteger s = new BigInteger( Base64.decode(sig_s) );
DSAParameters DSAPara = new DSAParameters(p, q, g);
DSAPublicKeyParameters DSAPubKeyPara = new DSAPublicKeyParameters(y,
DSAPara);
// Verify
DSASigner signer = new DSASigner();
signer.init( false, DSAPubKeyPara );
boolean result = signer.verifySignature( digest.getBytes(), r, s );
return result;
}
椭圆曲线 DSA 签名示例
在 ECDSASigUtil 类中,首先定义您计划使用的椭圆曲线模型,如清单 7 所示:
清单 7. 定义椭圆曲线模型
BigInteger("6277101735386680763835789423207666416083908700390324961279");
private static BigInteger a = new
BigInteger("fffffffffffffffffffffffffffffffefffffffffffffffc", 16);
private static BigInteger b = new
BigInteger("64210519e59c80e70fa7e9ab72243049feb8deecc146b9b1", 16);
private static BigInteger n = new
BigInteger("6277101735386680763835789423176059013767194773182842284081");
private static byte [] G =
Hex.decode("03188da80eb03090f67cbf20eb43a18800f4ff0afd82ff1012");
ECDSASigUtil.generateKeys() 方法使用清单 7 中的模型生成随机的密钥对。正如前面提到的那样,这个步骤通常由中央认证中心在脱机状态下完成。