Bouncycastle和PKCS#1 v2.1,使用RSASSA-PSS& amp;使用带有RSAES-OAEP的AES CBC进行加密

时间:2017-04-04 10:54:23

标签: java cryptography rsa bouncycastle signing

在这个图书馆度过了无数个小时后,我仍然无法正常工作。

我想发送带有充气城堡库的smime消息,用RSASSA-PSS签名,用AES加密,其中密钥传输应该是RSAES-OAEP,所有P1#v2.1

签名者首先,这就是它的创建方式:

SMIMESignedGenerator gen = new SMIMESignedGenerator();
            SignerInfoGenerator signer
            = new JcaSimpleSignerInfoGeneratorBuilder()
            .setProvider("BC")
            .build("SHA256withRSAAndMGF1", pk.getPrivateKey(), pk.getCertificate()
            );
gen.addSignerInfoGenerator(signer);
        gen.addCertificates(certStore);
        MimeMultipart mmp = gen.generate(message);

所以现在应该签名,加密和使用OAEP填充:

    OutputEncryptor enc = new BcCMSContentEncryptorBuilder(CMSAlgorithm.AES192_CBC).build();
        SMIMEEnvelopedGenerator gen = new SMIMEEnvelopedGenerator();

        for (X509Certificate nCert : certs) {
                RecipientInfoGenerator keyTransportRecipient = 
                new JceKeyTransRecipientInfoGenerator(nCert).setProvider("BC").
setAlgorithmMapping(PKCSObjectIdentifiers.rsaEncryption, "RSA/ECB/OAEPWithSHA256AndMGF1Padding");
                gen.addRecipientInfoGenerator(keyTransportRecipient);
        }
                MimeBodyPart encryptedMimeBodyPart = gen.generate(message, enc);

我找不到合适的setAlgorithmMapping()描述,所以我尝试了以下组合:

.setAlgorithmMapping(PKCSObjectIdentifiers.rsaEncryption, "RSA/NONE/PKCS1Padding");
.setAlgorithmMapping(PKCSObjectIdentifiers.rsaEncryption, "RSA/ECB/OAEPWithSHA256AndMGF1Padding");
.setAlgorithmMapping(PKCSObjectIdentifiers.id_RSAES_OAEP, "RSA/ECB/OAEPWithSHA56AndMGF1Padding");
不过,有谁能解释一下,这个模式到底意味着什么 - “RSA / ECB / OAEPWithSHA256AndMGF1Padding”? 如果第一个参数是公钥算法,我是对的,第二个“ECB”我是ECB AES模式吗? (我也尝试了CBC模式,但没有得到这样的算法异常,也尝试了NONE)而最后一个(“OAEPWithSHA56AndMGF1Padding”)显然是OAEP p1#v2.1,我实际上想要它。

所以,此时应该签署消息&加密。 当我现在检查我的邮箱时(使用 Thunderbird ),它说:“Thunderbird无法解密此邮件”,“发件人使用您的一个数字证书将此邮件加密给您,但是Thunderbird无法找到这个证书和相应的私钥。“

但是,当我与老签名者签约时

build("SHA256withRSAEncryption", pk.getPrivateKey(), pk.getCertificate()

并使用旧密钥转移支持方案,即

setProvider("BC").setAlgorithmMapping(PKCSObjectIdentifiers.rsaEncryption, "RSA/NONE/PKCS1Padding");
一切顺利。显然我的自签x509证书不是问题,如果我在这里,请纠正我。

我还使用 Outlook(2013)

对其进行了测试

旧方案(SHA256withRSAE加密签名+ PKCS1Padding密钥传输) - >一切都很好。

新方案(SHA256withRSAAndMGF1签署+ RSA / ECB / OAEPWithSHA256AndMGF1Padding) - > “基础安全系统无法找到您的数字身份证名称”错误。

此时我不知道究竟出了什么问题。 这就是我用openssl创建证书的方式:

openssl req -new -x509 -nodes -sha256 -days 365 -newkey rsa:2048 -out certificate.cer -keyout private.key -sigopt rsa_padding_mode:pss -sigopt rsa_pss_saltlen:32 -passin pass:mypass -utf8 -config _openssl.cfg -extensions v3_req
openssl pkcs12 -export -out certificate.pfx -name "testname" -inkey private.key -in certificate.cer

3 个答案:

答案 0 :(得分:1)

我想我解决了你的问题。至少在我的情况下它是有效的。 对于PKCSObjectIdentifiers.id_RSAES_OAEP的AlgorithmIdentifier,我添加了以下代码行:

pubilc AlgorithmIdentifier getAlgo() {

        JcaAlgorithmParametersConverter paramsConverter = new JcaAlgorithmParametersConverter();
        OAEPParameterSpec oaepSpec = new OAEPParameterSpec("SHA-256", "MGF1", new MGF1ParameterSpec("SHA-256"), new PSource.PSpecified(new byte[]{1, 2, 3, 4, 5}));
    return paramsConverter.getAlgorithmIdentifier(PKCSObjectIdentifiers.id_RSAES_OAEP, oaepSpec);
}

使用此参数化AlgorithmIdentifier,您现在可以加密消息:

AlgorithmIdentifier algo = getAlgo();
    SMIMEEnvelopedGenerator encrypter = new SMIMEEnvelopedGenerator();
    RecipientInfoGenerator keyTransportRecipient = 
                            new JceKeyTransRecipientInfoGenerator(cert).setProvider("BC").setAlgorithmMapping(PKCSObjectIdentifiers.id_RSAES_OAEP, "RSA/ECB/OAEPWithSHA256AndMGF1Padding");

encrypter.generate(bodyPart, new JceCMSContentEncryptorBuilder(algo).setProvider("BC").build());                            encrypter.addRecipientInfoGenerator(keyTransportRecipient);

您在签署问题上取得了哪些成功?就我而言,Outlook表示签名无效。

答案 1 :(得分:1)

我认为问题是大多数S / MIME客户端不支持 RSASSA-PSS。唯一的工具(除了BC)我发现支持验证 S / MIME RSASSA-PSS签名是openssl的最新版本。

根据openssl文档,这需要openssl> = 1.1.0。

openssl cms -verify -in email.eml -CAfile root.pem

这仅适用于openssl cms命令,而不适用于openssl smime命令。

答案 2 :(得分:0)

将RSA-OAEP的AlgorithmIdentifier授予OutputEncryptor是错误的方法。至少对我来说,它将导致如下错误:

 ... no such algorithm: 1.2.840.113549.1.1.7 for provider BC

如果要使用AES和RSA-OAEP,则必须将AES分配给OutputEncryptor(谁负责对称加密) 和RSA-OAEP到RecipientInfoGenerator(分别负责密钥加密和非对称加密)。

代码如下:

OAEPParameterSpec的定义已经在此处的上一篇文章中得到了正确解释)。

// Note that at JcaAlgorithmParametersConverter is available since BC 1.50.
JcaAlgorithmParametersConverter paramsConverter = new JcaAlgorithmParametersConverter();
OAEPParameterSpec oaepParameters = new OAEPParameterSpec("SHA-256", "MGF1", new MGF1ParameterSpec(
        "SHA-256"), PSource.PSpecified.DEFAULT);
AlgorithmIdentifier idRsaOaep = paramsConverter.getAlgorithmIdentifier(PKCSObjectIdentifiers.id_RSAES_OAEP, oaepParameters);

SMIMEEnvelopedGenerator encryptor = new SMIMEEnvelopedGenerator();

// Assign OAEPParameterSpec and an instance of X509Certificate to the RecipientInfoGenerator.
// prov is an instance of BouncyCastleProvider.
encryptor.addRecipientInfoGenerator(new JceKeyTransRecipientInfoGenerator(certificate, idRsaOaep)
        .setProvider(prov));

// Use an OutputEncryptor with AES.
OutputEncryptor outputEncryptor = new JceCMSContentEncryptorBuilder(CMSAlgorithm.AES128_CBC)
        .setProvider(prov)
        .build();

// Do encryption (were plaintextMsg is a MimeMessage instance).
MimeBodyPart payload = encryptor.generate(plaintextMsg, outputEncryptor);