保护您的 J2ME/MIDP 应用程序(3) 椭圆曲线 DSA 签名示例 在 ECDSASigUtil 类中,首先定义您计划使用的椭圆曲线模型,如清单 7 所示: 清单 7. 定义椭圆曲线模型 private static BigInteger q = new 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 中的模型生成随机的密钥对。正如前面提到的那样,这个步骤通常由中央认证中心在脱机状态下完成。 清单 8. 使用清单 7 中的模型生成随机的密钥对 // Get a secure random source. SecureRandom sr = new SecureRandom(); ECCurve.Fp curve = new ECCurve.Fp(q, a, b); ECDomainParameters ECDomPara = new ECDomainParameters(curve, curve.decodePoint(G), n ); ECKeyGenerationParameters ECKeyGenPara = new ECKeyGenerationParameters(ECDomPara, sr); ECKeyPairGenerator ECKeyPairGen = new ECKeyPairGenerator(); ECKeyPairGen.init( ECKeyGenPara ); AsymmetricCipherKeyPair keyPair = ECKeyPairGen.generateKeyPair();
privKey = (ECPrivateKeyParameters) keyPair.getPrivate(); pubKey = (ECPublicKeyParameters) keyPair.getPublic(); | 公钥以参数 Q 来描述,并且用 pubKey.getQ() 方法来检索它。为了避免与模型参数 q 产生混淆,在方法中使用 QQ ,XML 元素名使用大写的 Q 。清单 9 显示了 ECDSAUtil 类中的方法。这些方法检索模型和密钥参数,它们是重新构造公钥对象所必需的。 清单 9. 用于检索模型和密钥参数的 ECDSAUtil 方法 // public key specific field public static String getQQ() throws Exception { return (new String(Base64.encode(pubKey.getQ().getEncoded()))); } // Key parameter fields. Could also be retrieved from pubKey. public static String getQ() throws Exception { return (new String(Base64.encode(q.toByteArray()))); } public static String getA() throws Exception { return (new String(Base64.encode(a.toByteArray()))); } public static String getB() throws Exception { return (new String(Base64.encode(b.toByteArray()))); } public static String getN() throws Exception { return (new String(Base64.encode(n.toByteArray()))); } public static String getG() throws Exception { return (new String(Base64.encode(G))); } | 通过使用生成的私钥,实用程序类 ECDSASigUtil 可以从摘要获取两部分 DSA 签名 R 和 S : 清单 10. 检索 DSA 签名 static public String [] getSignature (String digest) throws Exception { // Sign ECDSASigner signer = new ECDSASigner(); 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 数字签名格式嵌入该文本。和检索方法名中一样,公钥参数 Q 被记录为 QQ ,以将它与相应 XML 元素中的密钥参数 q 区分开来,如清单 11 所示: 清单 11. 编码并以数字签名格式嵌入 <SignedMesg> <mesg>Hello World</mesg> <Signature> <SignedInfo> <SignatureMethod Algorithm="http://www.w3.org/2000/09/xmldsig#dsa-sha1" /> <DigestValue>Ck1VqNd45QIvq3AZd8XYQLvEhtA=</DigestValue> </SignedInfo> <SignatureValue> <R>NK/EIL2lrbFFCThnEuYlUWzh6IEfMsts</R> <S>AMeJDecKWrQO6Eeehl3het+FlDDL4IedCA==</S> </SignatureValue> <KeyInfo> <KeyValue> <ECKeyValue> <QQ>AwCiF5uG+DII/x1XTq84fLm4eGN2fED1PYc=</QQ> <Q>AP////////////////////7//////////w==</Q> <A>AP////////////////////7//////////A==</A> <B>ZCEFGeWcgOcPp+mrciQwSf643uzBRrmx</B> <N>AP///////////////5ne+DYUa8mxtNIoMQ==</N> <G>AxiNqA6wMJD2fL8g60OhiAD0/wr9gv8QEg==</G> </ECKeyValue> </KeyValue> </KeyInfo> </Signature> </SignedMesg> | 验证 MIDP 应用程序从 XML 文档解析出摘要、密钥参数和签名,重新构造公钥并使用清单 12 中显示的方法来验证签名: 清单 12. 验证签名 static public boolean verify (String digest, String sig_r, String sig_s, String key_q, String key_a, String key_b, String key_n, String key_G, String key_Q ) {
BigInteger q = new BigInteger( Base64.decode(key_q) ); BigInteger a = new BigInteger( Base64.decode(key_a) ); BigInteger b = new BigInteger( Base64.decode(key_b) ); BigInteger n = new BigInteger( Base64.decode(key_n) );
byte [] G = Base64.decode(key_G); byte [] Q = Base64.decode(key_Q);
BigInteger r = new BigInteger( Base64.decode(sig_r) ); BigInteger s = new BigInteger( Base64.decode(sig_s) );
ECCurve.Fp curve = new ECCurve.Fp(q, a, b); ECDomainParameters ECDomPara = new ECDomainParameters( curve, curve.decodePoint(G), n ); ECPublicKeyParameters pubKey = new ECPublicKeyParameters( curve.decodePoint(Q), ECDomPara ); // Verify ECDSASigner signer = new ECDSASigner(); signer.init( false, pubKey ); boolean result = signer.verifySignature( digest.getBytes(), r, s ); return result; } | RSA 签名示例 RSA 算法只有一个模型参数 Exponent : | private static BigInteger pubExp = new BigInteger("11", 16); | RSASigUtil.generateKeys() 方法使用 Exponent 生成随机的密钥对。同样,这个步骤通常由中央认证中心在脱机状态下完成。 清单 13. 生成随机的密钥对 SecureRandom sr = new SecureRandom(); RSAKeyGenerationParameters RSAKeyGenPara = new RSAKeyGenerationParameters(pubExp, sr, 1024, 80); RSAKeyPairGenerator RSAKeyPairGen = new RSAKeyPairGenerator(); RSAKeyPairGen.init(RSAKeyGenPara); AsymmetricCipherKeyPair keyPair = RSAKeyPairGen.generateKeyPair();
privKey = (RSAPrivateCrtKeyParameters) keyPair.getPrivate(); pubKey = (RSAKeyParameters) keyPair.getPublic(); | (未完待续)
|