如何在 Java 中签署和验证 RSA PKCS#1 v2.0 签名

How to sign and validate RSA PKCS#1 v2.0 signatures in Java

我正在尝试使用 PKCS1 v2.0 填充在 RSA+SHA1 中签名和验证签名。 我没有在文档(JDK 或 JCE)中找到我必须使用的 algorithm/padding。 看来我必须使用OAEP填充但我没有成功:

为了验证,我尝试通过密码在 DECRYPT_MODE 中使用签名者发送的 public 密钥:

Cipher cipher = Cipher.getInstance("RSA/ECB/OAEPWITHSHA1ANDMGF1PADDING", "SunJCE"); 
cipher.init(Cipher.DECRYPT_MODE, pub); //exception !

但我遇到了一个例外:

  java.security.InvalidKeyException: OAEP cannot be used to sign or verify signatures
        at com.sun.crypto.provider.RSACipher.init(RSACipher.java:303)
        at com.sun.crypto.provider.RSACipher.engineInit(RSACipher.java:207)
    ...

(由于未知原因,OAEPWITHSHA1ANDMGF1PADDING 仅接受 ENCRYPT_MODE+PUBLIC KEY 或 DECRYPT_MODE+PRIVATE KEY,与我想做的相反....)

对于签名,我正在尝试使用 Signature 但我不知道该使用哪种算法:

Signature mySig2 = Signature.getInstance("SHA1withRSAandMGF1");

无效:

java.security.NoSuchAlgorithmException: SHA1withRSAandMGF1 Signature not available
    at java.security.Signature.getInstance(Signature.java:229)
...

有人可以帮助我吗?

I'm trying to sign and validate signature in RSA+SHA1 with PKCS1 v2.0 padding.

这是一个奇怪的要求。

首先,即使使用 PKCS#1 v1.5 兼容填充,RSA 也是安全的。但是,当使用 SHA-1 对 input/message 进行哈希处理时,它并不安全,原因很简单,出于这种目的,SHA-1 不再被认为是安全的。

其次,尽管 PKCS#1 v2.0 包含一种新的用于签名生成的填充模式,称为 PSS 或 - 更准确地说 - RSASSA-PSS,但它 包含单个签名标准 1.5 的生成模式称为 RSASSA-PKCS1-v1_5。所以至少理论上你的要求还不清楚


现在 PKCS#1 v1.5 padding 用于加密 已知可以防止 padding oracle 攻击。仅出于这个原因,PKCS#1 v2.0 中引入的 OAEP 加密已普遍可用。

用于签名生成的PSS 具有更好的安全证明,但PKCS#1 v1.5 仍被认为是安全的。这可能是 Oracle / SUN 没有在 Java SE JCA 中包含 PSS 的原因。如果你想要它,你可以投票给它 here.


I haven't found in the documentation (JDK or JCE) which algorithm/padding I have to use. It seems that I've to use OAEP padding but I didn't succeed.

OAEP 代表最佳非对称加密 填充。用于加密的填充 cannot/should 不用于签名生成。


添加 Bouncy Castle 提供程序后,RSA PSS 变得可用。您可以将您的(不安全)方案与 "SHA1withRSA/PSS" 或完全相同的 "SHA1withRSAandMGF1" 一起使用。一旦 TLS 1.3 规范最终确定,Oracle 的 Java SE 可能会支持 RSA/PSS。

希望我对此做出回应还不算太晚。最近我不得不面对与 RSA 加密相同的问题。我的签名者想要对通过 POST 发送的数据进行验证。这就是我在 Java 中设法解决这个问题的方式。

第 1 步 - 将以下内容添加到您的项目中

bcprov-ext-jdk14-1.45.jar 
commons-codec-1.4.jar

第 2 步 - 声明您的密码

private Cipher cipher;

第 3 步 - 在您的 class 构造函数中调用下方

Security.addProvider(new org.bouncycastle.jce.provider.BouncyCastleProvider());
this.cipher = Cipher.getInstance("RSA/NONE/OAEPWithSHA1AndMGF1Padding");

第 4 步 - 从 public 密钥文本字符串

获取您的公钥
public PublicKey getPublic(String pubKey) throws Exception {
    byte[] publicBytes = Base64.decodeBase64(pubKey);
    X509EncodedKeySpec keySpec = new X509EncodedKeySpec(publicBytes);
    KeyFactory keyFactory = KeyFactory.getInstance("RSA");
    return keyFactory.generatePublic(keySpec);
}

第 5 步 - 验证方法

public boolean verfyRSA(String signature, String data, PublicKey key) throws Exception {

    byte[] sign = Base64.decodeBase64(signature);
    byte[] dat = Base64.decodeBase64(data);

    Signature rsaVerify = Signature.getInstance("SHA1withRSAandMGF1");
    rsaVerify.initVerify(key);
    rsaVerify.update(dat);
    return rsaVerify.verify(sign);
}

第 6 步 - 用法

Yourclass yc = new YourClass();
PublicKey publicKey = yc.getPublic("Your PublicKey without leading and trailing -------text------");
boolean b = yc.verfyRSA("POST_Signature", "POST_data", publicKey);

希望这对您有所帮助。注意 getInstance() 参数。这些是解决问题的方法。干杯