无法验证由 openssl 使用 dsa 密钥签名的字符串
unable to verify string signed by openssl with dsa key
调整 Creating a DSA Signature from the Linux command line 的说明 我创建了 DSA 签名消息:
echo "foobar" > foo.txt
openssl dgst -dss1 -sign dsa_priv.pem foo.txt > sigfile.bin
说明实际上使用了 foo.sha1 而不是 foo.txt,其中 foo.sha1 是由 sha1sum
生成的,但是签署散列似乎有点多余,因为 DSA 本身应该进行散列.
所以,无论如何,我做到了。这是我使用的私钥(我专门为测试目的生成的):
-----BEGIN DSA PRIVATE KEY-----
MIIBvAIBAAKBgQDsGAHAM16bsPlwl7jaec4QMynYa0YLiLiOZC4mvH4UW/tRJxTz
aV7eH1EtnP9D9J78x/07wKYs8zJEWCXmuq0UluQfjA47+pb68b/ucQTNeZHboNN9
5oEi+8BCSK0y8G3uf3Y89qHvqa9Si6rP374MinEMrbVFm+UpsGflFcd83wIVALtJ
ANi+lYG7xMKQ/bE4+bS8gemNAoGBAORowvirD7AB9x2SpdiME41+O4jVR8rs6+GX
Ml3Hif6Yt1kem0CeraX9SNoyBNAzjD5TVMGIdGlgRr6GNreHeXMGWlvdDkvCACER
ZEEtMsKZicm+yl6kR8AGHTCA/PBltHfyrFQd4n9I//UDqI4RjqzvpCXGQcVEsSDY
CCBGBQJRAoGBALnHTAZlpoLJZuSBVtnMuRM3cSX43IkE9w9FveDV1jX5mmfK7yBV
pQFV8eVJfk91ERQ4Dn6ePLUv2dRIt4a0S0qHqadgzyoFyqkmmUi1kNLyixtRqh+m
2gXx0t63HEpZDbEPppdpnlppZquVQh7TyrKSXW9MTzUkQjFI9UY7kZeKAhQXiJgI
kBniZHdFBAZBTE14YJUBkw==
-----END DSA PRIVATE KEY-----
这是 sigfile.bin
的十六进制编码输出:
302c021456d7e7da10d1538a6cd45dcb2b0ce15c28bac03402147e973a4de1e92e8a87ed5218c797952a3f854df5
我现在正尝试在 Java 中使用 BouncyCastle 验证这一点,但我无法这样做。这是我的 Java 代码:
import java.io.StringReader;
import org.bouncycastle.openssl.PEMReader;
import java.security.interfaces.DSAPublicKey;
import org.bouncycastle.crypto.params.DSAPublicKeyParameters;
import org.bouncycastle.crypto.signers.DSADigestSigner;
import org.bouncycastle.crypto.signers.DSASigner;
import org.bouncycastle.crypto.digests.SHA1Digest;
import org.bouncycastle.crypto.params.DSAParameters;
public class DSA
{
public static void main(String[] args)
throws Exception
{
byte[] message = "foobar".getBytes();
byte[] signature = hexStringToByteArray("302c021456d7e7da10d1538a6cd45dcb2b0ce15c28bac03402147e973a4de1e92e8a87ed5218c797952a3f854df5");
String key = "-----BEGIN PUBLIC KEY-----\n" +
"MIIBuDCCASwGByqGSM44BAEwggEfAoGBAOwYAcAzXpuw+XCXuNp5zhAzKdhrRguI\n" +
"uI5kLia8fhRb+1EnFPNpXt4fUS2c/0P0nvzH/TvApizzMkRYJea6rRSW5B+MDjv6\n" +
"lvrxv+5xBM15kdug033mgSL7wEJIrTLwbe5/djz2oe+pr1KLqs/fvgyKcQyttUWb\n" +
"5SmwZ+UVx3zfAhUAu0kA2L6VgbvEwpD9sTj5tLyB6Y0CgYEA5GjC+KsPsAH3HZKl\n" +
"2IwTjX47iNVHyuzr4ZcyXceJ/pi3WR6bQJ6tpf1I2jIE0DOMPlNUwYh0aWBGvoY2\n" +
"t4d5cwZaW90OS8IAIRFkQS0ywpmJyb7KXqRHwAYdMID88GW0d/KsVB3if0j/9QOo\n" +
"jhGOrO+kJcZBxUSxINgIIEYFAlEDgYUAAoGBALnHTAZlpoLJZuSBVtnMuRM3cSX4\n" +
"3IkE9w9FveDV1jX5mmfK7yBVpQFV8eVJfk91ERQ4Dn6ePLUv2dRIt4a0S0qHqadg\n" +
"zyoFyqkmmUi1kNLyixtRqh+m2gXx0t63HEpZDbEPppdpnlppZquVQh7TyrKSXW9M\n" +
"TzUkQjFI9UY7kZeK\n" +
"-----END PUBLIC KEY-----";
PEMReader reader = new PEMReader(new StringReader(key));
DSAPublicKey decoded = (DSAPublicKey) reader.readObject();
DSADigestSigner dsa = new DSADigestSigner(new DSASigner(), new SHA1Digest());
DSAParameters params = new DSAParameters(
decoded.getParams().getP(),
decoded.getParams().getQ(),
decoded.getParams().getG()
);
DSAPublicKeyParameters publickey = new DSAPublicKeyParameters(decoded.getY(), params);
dsa.init(false, publickey);
dsa.update(message, 0, message.length);
boolean result = dsa.verifySignature(signature);
System.out.println(result ? "good" : "bad");
}
public static byte[] hexStringToByteArray(String s)
{
int len = s.length();
byte[] data = new byte[len / 2];
for (int i = 0; i < len; i += 2)
{
data[i / 2] = (byte) ((Character.digit(s.charAt(i), 16) << 4)
+ Character.digit(s.charAt(i+1), 16));
}
return data;
}
}
签名未通过验证。我的 Java 代码有问题吗?也许 OpenSSL 对 dss1 做了一些奇怪的事情?
我能够使用 OpenSSL 验证签名:
openssl dgst -dss1 -verify dsa_pub.pem -signature sigfile.bin foo.txt
(Unix) echo
输出它的参数,space - 如果超过一个,PLUS A NEWLINE 分隔。使用"foobar\n"
作为验证数据。或者签署 printf '%s' foobar >foo.txt
的结果,其中 可移植地 省略了换行符; 一些版本的echo
为此目的支持-n
,一些较旧的版本使用\c
,还有一些根本不支持。
FYI BouncyCastle 自版本 150 (2013) 起不再具有 org.bouncycastle.openssl.PEMReader
;相反,您需要 PEMParser
其中 returns org.bouncycastle.asn1.x509.SubjectPublicKeyInfo
可以通过 org.bouncycastle.openssl.jcajce.JcaPEMKeyConverter
或 KeyFactory.getInstance(alg).generatePublicKey(new X509EncodedKey(spki.getEncoded()))
转换为关键对象,这正是 JcaPEMKeyConverter
实际所做的。
OTOH 你可以使用 org.bouncycastle.jcajce.provider.asymmetric.dsa.DSAUtil.generatePublicKeyParameter
来替换参数块的摆弄;这就是 BC 提供程序接口(相对于轻量级接口)所做的。或者当然你可以首先使用 JCA 而你根本不需要 BC,因为 OpenSSL 公钥格式(与私钥不同)始终与基本 Java 加密兼容。
顺便说一下,openssl dgst
需要 -dss1
hack 只能通过版本 0.9.8;自 1.0.0 版于 2010 年发布(但由于实际或担心不兼容,许多发行版和产品并未立即升级),您只需要 -sha1
和一个 DSA 公钥。
调整 Creating a DSA Signature from the Linux command line 的说明 我创建了 DSA 签名消息:
echo "foobar" > foo.txt
openssl dgst -dss1 -sign dsa_priv.pem foo.txt > sigfile.bin
说明实际上使用了 foo.sha1 而不是 foo.txt,其中 foo.sha1 是由 sha1sum
生成的,但是签署散列似乎有点多余,因为 DSA 本身应该进行散列.
所以,无论如何,我做到了。这是我使用的私钥(我专门为测试目的生成的):
-----BEGIN DSA PRIVATE KEY-----
MIIBvAIBAAKBgQDsGAHAM16bsPlwl7jaec4QMynYa0YLiLiOZC4mvH4UW/tRJxTz
aV7eH1EtnP9D9J78x/07wKYs8zJEWCXmuq0UluQfjA47+pb68b/ucQTNeZHboNN9
5oEi+8BCSK0y8G3uf3Y89qHvqa9Si6rP374MinEMrbVFm+UpsGflFcd83wIVALtJ
ANi+lYG7xMKQ/bE4+bS8gemNAoGBAORowvirD7AB9x2SpdiME41+O4jVR8rs6+GX
Ml3Hif6Yt1kem0CeraX9SNoyBNAzjD5TVMGIdGlgRr6GNreHeXMGWlvdDkvCACER
ZEEtMsKZicm+yl6kR8AGHTCA/PBltHfyrFQd4n9I//UDqI4RjqzvpCXGQcVEsSDY
CCBGBQJRAoGBALnHTAZlpoLJZuSBVtnMuRM3cSX43IkE9w9FveDV1jX5mmfK7yBV
pQFV8eVJfk91ERQ4Dn6ePLUv2dRIt4a0S0qHqadgzyoFyqkmmUi1kNLyixtRqh+m
2gXx0t63HEpZDbEPppdpnlppZquVQh7TyrKSXW9MTzUkQjFI9UY7kZeKAhQXiJgI
kBniZHdFBAZBTE14YJUBkw==
-----END DSA PRIVATE KEY-----
这是 sigfile.bin
的十六进制编码输出:
302c021456d7e7da10d1538a6cd45dcb2b0ce15c28bac03402147e973a4de1e92e8a87ed5218c797952a3f854df5
我现在正尝试在 Java 中使用 BouncyCastle 验证这一点,但我无法这样做。这是我的 Java 代码:
import java.io.StringReader;
import org.bouncycastle.openssl.PEMReader;
import java.security.interfaces.DSAPublicKey;
import org.bouncycastle.crypto.params.DSAPublicKeyParameters;
import org.bouncycastle.crypto.signers.DSADigestSigner;
import org.bouncycastle.crypto.signers.DSASigner;
import org.bouncycastle.crypto.digests.SHA1Digest;
import org.bouncycastle.crypto.params.DSAParameters;
public class DSA
{
public static void main(String[] args)
throws Exception
{
byte[] message = "foobar".getBytes();
byte[] signature = hexStringToByteArray("302c021456d7e7da10d1538a6cd45dcb2b0ce15c28bac03402147e973a4de1e92e8a87ed5218c797952a3f854df5");
String key = "-----BEGIN PUBLIC KEY-----\n" +
"MIIBuDCCASwGByqGSM44BAEwggEfAoGBAOwYAcAzXpuw+XCXuNp5zhAzKdhrRguI\n" +
"uI5kLia8fhRb+1EnFPNpXt4fUS2c/0P0nvzH/TvApizzMkRYJea6rRSW5B+MDjv6\n" +
"lvrxv+5xBM15kdug033mgSL7wEJIrTLwbe5/djz2oe+pr1KLqs/fvgyKcQyttUWb\n" +
"5SmwZ+UVx3zfAhUAu0kA2L6VgbvEwpD9sTj5tLyB6Y0CgYEA5GjC+KsPsAH3HZKl\n" +
"2IwTjX47iNVHyuzr4ZcyXceJ/pi3WR6bQJ6tpf1I2jIE0DOMPlNUwYh0aWBGvoY2\n" +
"t4d5cwZaW90OS8IAIRFkQS0ywpmJyb7KXqRHwAYdMID88GW0d/KsVB3if0j/9QOo\n" +
"jhGOrO+kJcZBxUSxINgIIEYFAlEDgYUAAoGBALnHTAZlpoLJZuSBVtnMuRM3cSX4\n" +
"3IkE9w9FveDV1jX5mmfK7yBVpQFV8eVJfk91ERQ4Dn6ePLUv2dRIt4a0S0qHqadg\n" +
"zyoFyqkmmUi1kNLyixtRqh+m2gXx0t63HEpZDbEPppdpnlppZquVQh7TyrKSXW9M\n" +
"TzUkQjFI9UY7kZeK\n" +
"-----END PUBLIC KEY-----";
PEMReader reader = new PEMReader(new StringReader(key));
DSAPublicKey decoded = (DSAPublicKey) reader.readObject();
DSADigestSigner dsa = new DSADigestSigner(new DSASigner(), new SHA1Digest());
DSAParameters params = new DSAParameters(
decoded.getParams().getP(),
decoded.getParams().getQ(),
decoded.getParams().getG()
);
DSAPublicKeyParameters publickey = new DSAPublicKeyParameters(decoded.getY(), params);
dsa.init(false, publickey);
dsa.update(message, 0, message.length);
boolean result = dsa.verifySignature(signature);
System.out.println(result ? "good" : "bad");
}
public static byte[] hexStringToByteArray(String s)
{
int len = s.length();
byte[] data = new byte[len / 2];
for (int i = 0; i < len; i += 2)
{
data[i / 2] = (byte) ((Character.digit(s.charAt(i), 16) << 4)
+ Character.digit(s.charAt(i+1), 16));
}
return data;
}
}
签名未通过验证。我的 Java 代码有问题吗?也许 OpenSSL 对 dss1 做了一些奇怪的事情?
我能够使用 OpenSSL 验证签名:
openssl dgst -dss1 -verify dsa_pub.pem -signature sigfile.bin foo.txt
(Unix) echo
输出它的参数,space - 如果超过一个,PLUS A NEWLINE 分隔。使用"foobar\n"
作为验证数据。或者签署 printf '%s' foobar >foo.txt
的结果,其中 可移植地 省略了换行符; 一些版本的echo
为此目的支持-n
,一些较旧的版本使用\c
,还有一些根本不支持。
FYI BouncyCastle 自版本 150 (2013) 起不再具有 org.bouncycastle.openssl.PEMReader
;相反,您需要 PEMParser
其中 returns org.bouncycastle.asn1.x509.SubjectPublicKeyInfo
可以通过 org.bouncycastle.openssl.jcajce.JcaPEMKeyConverter
或 KeyFactory.getInstance(alg).generatePublicKey(new X509EncodedKey(spki.getEncoded()))
转换为关键对象,这正是 JcaPEMKeyConverter
实际所做的。
OTOH 你可以使用 org.bouncycastle.jcajce.provider.asymmetric.dsa.DSAUtil.generatePublicKeyParameter
来替换参数块的摆弄;这就是 BC 提供程序接口(相对于轻量级接口)所做的。或者当然你可以首先使用 JCA 而你根本不需要 BC,因为 OpenSSL 公钥格式(与私钥不同)始终与基本 Java 加密兼容。
顺便说一下,openssl dgst
需要 -dss1
hack 只能通过版本 0.9.8;自 1.0.0 版于 2010 年发布(但由于实际或担心不兼容,许多发行版和产品并未立即升级),您只需要 -sha1
和一个 DSA 公钥。