AutoCAD 3DMAX C语言 Pro/E UG JAVA编程 PHP编程 Maya动画 Matlab应用 Android
Photoshop Word Excel flash VB编程 VC编程 Coreldraw SolidWorks A Designer Unity3D
 首页 > JAVA编程

保护您的 J2ME/MIDP 应用程序

51自学网 2015-09-02 http://www.wanshiok.com

验证 MIDP 应用程序从 XML 文档解析出摘要、密钥参数和签名,重新构造公钥并使用下列方法来验证签名:

清单 6. 验证签名

static public boolean verify (String digest,                              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. 定义椭圆曲线模型

  private static BigInteger q = newBigInteger("6277101735386680763835789423207666416083908700390324961279");  private static BigInteger a = newBigInteger("fffffffffffffffffffffffffffffffefffffffffffffffc", 16);  private static BigInteger b = newBigInteger("64210519e59c80e70fa7e9ab72243049feb8deecc146b9b1", 16);  private static BigInteger n = newBigInteger("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 fieldpublic 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 签名 RS

清单 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();

公钥以参数 Modulus 来描述,并且用 pubKey.getModulus() 方法来检索它。清单 14 显示了 RSAUtil 类中的方法。这些方法检索 ExponentModulus、模型以及密钥参数,它们是重新构造公钥对象所必需的。

清单 14. 用于检索模型和密钥参数的 RSAUtil 方法

// Public key specific parameter.public static String getMod() throws Exception {  return (new String(Base64.encode(pubKey.getModulus().toByteArray())));}// General key parameter. pubExp is the same as pubKey.getExponent()public static String getPubExp() throws Exception {  return (new String(Base64.encode(pubExp.toByteArray())));}

通过使用生成的私钥,实用程序类 RSASigUtil 可以从摘要获取一个字节数组 RSA 签名:

清单 15. 获取字节数组 RSA 签名

static public String getSignature (String mesg) throws Exception {  SHA1Digest digEng = new SHA1Digest();  RSAEngine rsaEng = new RSAEngine();   PSSSigner signer = new PSSSigner(rsaEng, digEng, 64);  signer.init(true, privKey);   byte [] sig = signer.generateSignature( mesg.getBytes() );  String result = new String( Base64.encode(sig) );  return result;}

服务器将摘要、签名和密钥参数编码成 ASCII 文本格式并以 XML 数字签名格式嵌入该文本:

清单 16. 编码并以数字签名格式嵌入

<SignedMesg>  <mesg>Hello World</mesg>  <Signature>    <SignedInfo>      <SignatureMethod Algorithm="rsa-sha1" />      <DigestValue>Ck1VqNd45QIvq3AZd8XYQLvEhtA=</DigestValue>    </SignedInfo>    <SignatureValue>      IhJ/UMitJX7sWbzhnG8UKIdDYiZ0mfOUoAwemGiG08C      WcQ3cUszgJXoIclHW/LN7w54w2FQyLStB+hPKASEC6r      OjjgTBs6pwhjHCh2XxWx7hS7fdi9/Qk/ybH6xYGaeaZ      3oHDBjFz3hEDtrvBYcHn3keCavncE22idRX7kBl8Do=    </SignatureValue>    <KeyInfo>      <KeyValue>        <RSAKeyValue>          <Modulus>            AKT1SyxSm4uT1zYWEPY9IaFY7vDhpkIM7FZeIQ            OGnKeSEE5d3sPfONkCiHfO2oe4x6jNCXg/ngRi            tmixBkjfKgHzF4trZZtNQZjfzAgcXGljzp9MD2            ZEWQbHKvMZvZyJVrT2SlxLzusxWLwXdacprIDG            bqDAmldBOBpkmrUdPpF9          </Modulus>          <Exponent>EQ==</Exponent>        </RSAKeyValue>      </KeyValue>    </KeyInfo>  </Signature></SignedMesg>

验证 MIDP 应用程序从 XML 文档解析出摘要、密钥参数和签名,重新构造公钥并使用下列方法来验证签名:

清单 17. 验证签名

static public boolean verify (String mesg, String signature,                              String mod, String pubExp) {   BigInteger modulus = new BigInteger( Base64.decode(mod) );  BigInteger exponent = new BigInteger( Base64.decode(pubExp) );   SHA1Digest digEng = new SHA1Digest();  RSAEngine rsaEng = new RSAEngine();   RSAKeyParameters pubKey = new RSAKeyParameters(false, modulus, exponent);   PSSSigner signer = new PSSSigner(rsaEng, digEng, 64);  signer.init(false, pubKey);  boolean res = signer.verifySignature( mesg.getBytes(),                                        Base64.decode(signature) );  return res;}

性能问题
我的测试说明了无线设备上的 XML 解析和摘要生成都非常快。正如预料的那样,主要的性能瓶颈是公钥算法的速度很慢。

Bouncy Castle Crypto 包提供几个使用 DSA、RSA 和 ECC 算法的签名引擎类来签署和验证消息。但在实际设备中,它们并不都是实用的。因为 Bouncy Castle Crypto 包完全基于Java 语言,所以它在没有特殊优化的情况下依靠很慢的 JVM 来执行甚至是最密集的大整数数学运算。

结果,只有 RSA 算法提供了一个合理的性能,而且是勉强接受的。它只要花一分多钟就可以在 16MHz Palm VII 设备上验证具有 1024 位公钥的简单数字签名。通过选择较弱的密钥可以改进性能。但虽然如此,验证进程在任何实际的应用程序中还是必须作为后台线程运行,以避免用户界面锁定。

DSA 和 ECC 算法性能在其当前实现中是完全不可接受的。具有 1024 位密钥的 DSA 签名和具有 192 位密钥的 ECC 签名要花一个多小时在标准的 Palm VII MIDP 上进行验证。

性能问题强烈暗示我们需要为大整数数学运算和公钥算法对 JVM 进行优化。JVM 还必须利用可用的特殊硬件和底层的 OS 功能来促进与安全性相关的数学运算。公钥算法用于安全连接(如 HTTPS)中的信息交换。许多当前的 MIDP VM 可以用合理的性能来支持 HTTPS 协议。MIDP4Palm VM 可以利用 Palm OS 的底层的 inethttps 协议来建立安全连接。可以想象未来的 VM 和核心语言库不仅优化与安全连接相关的公钥操作,而且还使优化可用于一般安全性功能(如数字签名)。

结束语

在本文中,您学习了安全性在无线 Web 服务中的重要性并说明了在无线和 Web 服务端处理 XML 数字签名的技巧。我使用了 Bouncy Castle Java 密码术包的纯 Java 实现来处理数字签名。在 Bouncy Castle 提供的所有算法中,只有 RSA 算法提供了无线设备上可勉强接受的性能。然而,未来在 MIDP 运行时环境上的进步可以使数字签名更易于移动用户使用。

 
 
说明
:本教程来源互联网或网友上传或出版商,仅为学习研究或媒体推广,wanshiok.com不保证资料的完整性。
 

上一篇:Java学习方法的一点个人见解-完整版  下一篇:熟练掌握JDK1.5枚举类型