Java/BouncyCastle 签名的 OpenSSL 等价物
OpenSSL equivalent for Java/BouncyCastle signature
我有一个软件做数字签名如下。
MessageDigest md = MessageDigest.getInstance("SHA1", "BC");
Cipher cipher = Cipher.getInstance("RSA/None/NoPadding", "BC");
cipher.init(Cipher.ENCRYPT_MODE, privateKey);
byte[] hash = md.digest("message".getBytes());
byte[] signature = cipher.doFinal(hash);
我想用batch/openssl的几行替换它,但无法重现此签名。基本上它是 SHA1 和 RSA 的组合,这还不错。我想知道为什么散列和加密是分开的。使用 openssl 执行此操作的两种方式都会产生不同的签名(忽略格式):
openssl dgst -sha1 -binary msg.txt > hash
openssl rsautl -sign -inkey priv.pem -in hash -hexdump
和
openssl dgst -sha1 -sign priv.pem -hex < msg.txt
所以我缺少一些东西...
更新:
感谢您的评论!当然这个 Java 代码不是正确签名的方法,但是验证代码不在我手中,我的目标是创建一个可以验证的签名。不过,我会提出改变整个事情的建议。
但我终于设法重现了签名。我自己不回答这个问题,因为我想要一个合适的 openssl 解决方案。这就是我得到的:
上面的 Java 代码 进行填充 !哈希码用零字节预填充到 256 的长度!
您的 msg.txt 的内容很可能与您在 Java 源中散列的 "message"
不同。我在 linux 上尝试了你的样本并得到了这个:
# echo message > msg.txt
# xxd msg.txt
0000000: 6d65 7373 6167 650a message.
您会注意到字符串末尾有额外的换行符 (0x0a)。
一旦你trim:
# dd if=msg.txt of=msg2.txt bs=1 count=7
# xxd msg2.txt
0000000: 6d65 7373 6167 65 message
您得到的校验和与 Java 代码生成的校验和相同。
就 RSA 而言,一旦获得相同的源数据,就没有理由不工作。
cipher.init(Cipher.ENCRYPT_MODE, privateKey);
我敢肯定这不太正确。
没有"encrypt with the private key"这样的操作。在高层次上,当您创建签名时,您将要签名的散列和填充视为密文的一个实例。然后你 "decrypt with the private key" 到达签名。
如果您真的想要 "encrypt with the private key",那么您应该调查一个带恢复的签名方案。这可能是您最接近 "encrypt with the private key"。
您可能应该使用 Java 的内置签名方案之一,尤其是当您尝试与 OpenSSL 命令行实用程序互操作时。参见,例如,Java Tutorial » Security » RSA algorithm.
否则,请查看 RSASignature
的 Sun 资源。他们直接使用 BigIntegers
并执行求幂, 不要 尝试执行 "encrypt with private key"。例如,参见 Sun 的 RSASignature class and the crtCrypt method.
Java 代码通过 SHA-1 哈希执行原始或教科书 RSA。这不安全,您应该在 Java 代码中使用 PKCS#1 v1.5 填充(用于签名生成)或 PSS。例如,PKCS#1 v1.5 使用 "SHA1withRSA"
算法字符串执行 Signature
class.
尽管它不安全,但我尝试使用 -raw
参数复制 Java 代码的结果,但由于检查 OpenSSL 代码而失败:
$ openssl rsautl -encrypt -raw -inkey priv.pem -in hash -hexdump
RSA operation error
3077884104:error:0406B07A:rsa routines:RSA_padding_add_none:data too small for key size:rsa_none.c:76:
所以 OpenSSL 不会让你犯这个错误。顺便也试了签名,结果一样。
默认情况下,OpenSSL 使用 PKCS#1 v1.5 填充(用于签名)进行签名生成,使用 PKCS#1 v1.5 填充(用于加密)进行加密。这将生成与 Java 代码中的原始签名不同的输出。
到目前为止我找到的最佳解决方案是使用 236 个零字节(填充)和另一个临时文件(散列)进行手动填充。
COPY padding hash
openssl dgst -sha1 -binary msg.txt >>hash
openssl pkeyutl -sign -in hash -inkey priv.pem -out signature -pkeyopt rsa_padding_mode:none
我尝试改用 openssl dgst
并指定各种填充方案,但没有成功。我假设 openssl 不支持这种填充方案,并且怀疑它是否是标准方案。
我有一个软件做数字签名如下。
MessageDigest md = MessageDigest.getInstance("SHA1", "BC");
Cipher cipher = Cipher.getInstance("RSA/None/NoPadding", "BC");
cipher.init(Cipher.ENCRYPT_MODE, privateKey);
byte[] hash = md.digest("message".getBytes());
byte[] signature = cipher.doFinal(hash);
我想用batch/openssl的几行替换它,但无法重现此签名。基本上它是 SHA1 和 RSA 的组合,这还不错。我想知道为什么散列和加密是分开的。使用 openssl 执行此操作的两种方式都会产生不同的签名(忽略格式):
openssl dgst -sha1 -binary msg.txt > hash
openssl rsautl -sign -inkey priv.pem -in hash -hexdump
和
openssl dgst -sha1 -sign priv.pem -hex < msg.txt
所以我缺少一些东西...
更新:
感谢您的评论!当然这个 Java 代码不是正确签名的方法,但是验证代码不在我手中,我的目标是创建一个可以验证的签名。不过,我会提出改变整个事情的建议。
但我终于设法重现了签名。我自己不回答这个问题,因为我想要一个合适的 openssl 解决方案。这就是我得到的:
上面的 Java 代码 进行填充 !哈希码用零字节预填充到 256 的长度!
您的 msg.txt 的内容很可能与您在 Java 源中散列的 "message"
不同。我在 linux 上尝试了你的样本并得到了这个:
# echo message > msg.txt
# xxd msg.txt
0000000: 6d65 7373 6167 650a message.
您会注意到字符串末尾有额外的换行符 (0x0a)。
一旦你trim:
# dd if=msg.txt of=msg2.txt bs=1 count=7
# xxd msg2.txt
0000000: 6d65 7373 6167 65 message
您得到的校验和与 Java 代码生成的校验和相同。
就 RSA 而言,一旦获得相同的源数据,就没有理由不工作。
cipher.init(Cipher.ENCRYPT_MODE, privateKey);
我敢肯定这不太正确。
没有"encrypt with the private key"这样的操作。在高层次上,当您创建签名时,您将要签名的散列和填充视为密文的一个实例。然后你 "decrypt with the private key" 到达签名。
如果您真的想要 "encrypt with the private key",那么您应该调查一个带恢复的签名方案。这可能是您最接近 "encrypt with the private key"。
您可能应该使用 Java 的内置签名方案之一,尤其是当您尝试与 OpenSSL 命令行实用程序互操作时。参见,例如,Java Tutorial » Security » RSA algorithm.
否则,请查看 RSASignature
的 Sun 资源。他们直接使用 BigIntegers
并执行求幂, 不要 尝试执行 "encrypt with private key"。例如,参见 Sun 的 RSASignature class and the crtCrypt method.
Java 代码通过 SHA-1 哈希执行原始或教科书 RSA。这不安全,您应该在 Java 代码中使用 PKCS#1 v1.5 填充(用于签名生成)或 PSS。例如,PKCS#1 v1.5 使用 "SHA1withRSA"
算法字符串执行 Signature
class.
尽管它不安全,但我尝试使用 -raw
参数复制 Java 代码的结果,但由于检查 OpenSSL 代码而失败:
$ openssl rsautl -encrypt -raw -inkey priv.pem -in hash -hexdump
RSA operation error
3077884104:error:0406B07A:rsa routines:RSA_padding_add_none:data too small for key size:rsa_none.c:76:
所以 OpenSSL 不会让你犯这个错误。顺便也试了签名,结果一样。
默认情况下,OpenSSL 使用 PKCS#1 v1.5 填充(用于签名)进行签名生成,使用 PKCS#1 v1.5 填充(用于加密)进行加密。这将生成与 Java 代码中的原始签名不同的输出。
到目前为止我找到的最佳解决方案是使用 236 个零字节(填充)和另一个临时文件(散列)进行手动填充。
COPY padding hash
openssl dgst -sha1 -binary msg.txt >>hash
openssl pkeyutl -sign -in hash -inkey priv.pem -out signature -pkeyopt rsa_padding_mode:none
我尝试改用 openssl dgst
并指定各种填充方案,但没有成功。我假设 openssl 不支持这种填充方案,并且怀疑它是否是标准方案。