验证Java在C#中生成的DSA签名

时间:2011-02-03 14:21:50

标签: c# java encryption digital-signature

我知道这是一个包含很多现有问题的主题,但我没有找到任何涵盖我确切案例的现有答案。

我需要在某些Java代码中签署一个字符串(URL),并将该字符串与签名一起发送到C#程序。

我运行以下Java代码一次以生成DSA密钥对:

KeyPairGenerator keyGen = KeyPairGenerator.getInstance("DSA", "SUN");
SecureRandom random = SecureRandom.getInstance("SHA1PRNG", "SUN");
keyGen.initialize(1024, random);

KeyPair pair = keyGen.generateKeyPair();
PrivateKey priv = pair.getPrivate();
PublicKey pub = pair.getPublic();

/* save the private key in a file */
byte[] privkey = priv.getEncoded();
FileOutputStream privkeyfos = new FileOutputStream("key.priv");
privkeyfos.write(privkey);
privkeyfos.close();

/* save the public key in a file */
byte[] pubkey = pub.getEncoded();
FileOutputStream pubkeyfos = new FileOutputStream("key.public");
pubkeyfos.write(pubkey);
pubkeyfos.close();

然后我使用以下代码生成签名。

public static String Sign(String keyPath, byte[] data)
{
  FileInputStream keyfis = new FileInputStream(new File(keyPath, "key.priv"));
  byte[] encKey = new byte[keyfis.available()];
  keyfis.read(encKey);
  keyfis.close();

  PKCS8EncodedKeySpec privKeySpec = new PKCS8EncodedKeySpec(encKey);
  KeyFactory keyFactory = KeyFactory.getInstance("DSA");
  PrivateKey privKey = keyFactory.generatePrivate(privKeySpec);

  Signature dsa = Signature.getInstance("SHA1withDSA", "SUN");

  dsa.initSign(privKey);

  ByteArrayInputStream in = new ByteArrayInputStream(data);
  BufferedInputStream bufin = new BufferedInputStream(in);
  byte[] buffer = new byte[1024];
  int len;
  while ((len = bufin.read(buffer)) >= 0)
  {
    dsa.update(buffer, 0, len);
  }
  bufin.close();

  byte[] realSig = dsa.sign();

  return new String(Base64.encodeBase64(realSig), "UTF-8");
}

在我的C#代码中,我可以从第一步访问字符串,Base64编码签名和“key.public”文件。

任何人都可以提供一个代码块,它将这些元素与一个合适的库结合起来,以确定该字符串是否已被篡改?

1 个答案:

答案 0 :(得分:1)

我现在已经通过本文中的一些关键输入解决了这个问题:http://www.codeproject.com/KB/security/CryptoInteropSign.aspx

使用以下C#函数完成主要验证。

private static Boolean isValid(String xiString, String xiSig)
{
  AsnKeyParser keyParser = new AsnKeyParser("path/to/key.public");
  DSAParameters publicKey = keyParser.ParseDSAPublicKey();

  DSACryptoServiceProvider DSA = new DSACryptoServiceProvider();
  DSA.ImportParameters(publicKey);
  DSASignatureDeformatter DSADeformatter = new DSASignatureDeformatter(DSA);
  UTF8Encoding UTF8 = new UTF8Encoding();
  byte[] plainBytes = UTF8.GetBytes(xiString);      
  var sha1 = new SHA1Managed();
  var hash = sha1.ComputeHash(plainBytes);
  byte[] asn1SigBytes = Convert.FromBase64String(xiSig);
  byte[] sigBytes = ConvertToP1363Signature(asn1SigBytes);
  Boolean retVal = DSADeformatter.VerifySignature(hash, sigBytes);
  return retVal;
}

这依赖于两种辅助方法。

1)AsnKeyParser是附加到链接文章的类。本文提供了一个C#下载,我使用了两个文件:AsnKeyParser.cs和BerDecodeError.cs。我从AsnKeyParser中删除了RSA函数,以删除对BigInteger文件的依赖。

此类处理解析由我的Java代码创建的“key.public”文件。

2)用于将Java生成的46-48字节DER编码签名转换为C#将接受的DSA签名的函数。

此功能基于链接文章评论中的代码。

private static byte[] ConvertToP1363Signature(byte[] ASN1Sig)
{
  AsnParser asn = new AsnParser(ASN1Sig);
  asn.NextSequence();
  byte[] r = asn.NextInteger();
  byte[] s = asn.NextInteger();

  // Returned to caller
  byte[] p1363Signature = new byte[40];

  if (r.Length > 21 || (r.Length == 21 && r[0] != 0))
  {
    // WTF???
    // Reject - signature verification failed
  }
  else if (r.Length == 21)
  {
    // r[0] = 0
    // r[1]'s high bit *should* be set
    Array.Copy(r, 1, p1363Signature, 0, 20);
  }
  else if (r.Length == 20)
  {
    // r[0]'s high bit *should not* be set
    Array.Copy(r, 0, p1363Signature, 0, 20);
  }
  else
  {
    // fewer than 20 bytes
    int len = r.Length;
    int off = 20 - len;
    Array.Copy(r, 0, p1363Signature, off, len);
  }

  if (s.Length > 21 || (s.Length == 21 && s[0] != 0))
  {
    // WTF???
    // Reject - signature verification failed
  }
  else if (s.Length == 21)
  {
    // s[0] = 0
    // s[1]'s high bit *should* be set
    Array.Copy(s, 1, p1363Signature, 20, 20);
  }
  else if (s.Length == 20)
  {
    // s[0]'s high bit *should not* be set
    Array.Copy(s, 0, p1363Signature, 20, 20);
  }
  else
  {
    // fewer than 20 bytes
    int len = s.Length;
    int off = 40 - len;
    Array.Copy(s, 0, p1363Signature, off, len);
  }

  return p1363Signature;
}