我知道这是一个包含很多现有问题的主题,但我没有找到任何涵盖我确切案例的现有答案。
我需要在某些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”文件。
任何人都可以提供一个代码块,它将这些元素与一个合适的库结合起来,以确定该字符串是否已被篡改?
答案 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;
}