执行S / MIME签名并立即检查签名失败

时间:2019-06-15 20:24:03

标签: java cryptography javamail bouncycastle

以下来源是一个JUnit-Test,它显示了有问题的问题。

import java.math.BigInteger;
import java.security.KeyPair;
import java.security.KeyPairGenerator;
import java.security.PrivateKey;
import java.security.PublicKey;
import java.security.SecureRandom;
import java.security.Security;
import java.security.cert.X509Certificate;
import java.util.Date;

import javax.mail.MessagingException;
import javax.mail.internet.MimeBodyPart;
import javax.mail.internet.MimeMultipart;

import org.bouncycastle.asn1.x500.X500Name;
import org.bouncycastle.asn1.x509.Extension;
import org.bouncycastle.cert.X509v3CertificateBuilder;
import org.bouncycastle.cert.jcajce.JcaX509CertificateConverter;
import org.bouncycastle.cert.jcajce.JcaX509ExtensionUtils;
import org.bouncycastle.cert.jcajce.JcaX509v3CertificateBuilder;
import org.bouncycastle.cms.SignerInfoGenerator;
import org.bouncycastle.cms.SignerInformation;
import org.bouncycastle.cms.SignerInformationVerifier;
import org.bouncycastle.cms.jcajce.JcaSimpleSignerInfoGeneratorBuilder;
import org.bouncycastle.cms.jcajce.JcaSimpleSignerInfoVerifierBuilder;
import org.bouncycastle.jce.provider.BouncyCastleProvider;
import org.bouncycastle.mail.smime.SMIMESigned;
import org.bouncycastle.mail.smime.SMIMESignedGenerator;
import org.bouncycastle.operator.jcajce.JcaContentSignerBuilder;

import junit.framework.TestCase;

/**
 * Test class to check out S/MIME signature and verification
 */
public class __Run_SMIMESigning extends TestCase {
    final static SecureRandom random = new SecureRandom();

    static {
        Security.addProvider(new BouncyCastleProvider());
    }

    /**
     * Tests the issue
     * @throws Exception
     */
    public void testMailSigning() throws Exception {
        KeyPairGenerator kpg = KeyPairGenerator.getInstance("RSA", "BC");
        kpg.initialize(1024, random);

        KeyPair aliceKey = kpg.generateKeyPair();
        X509Certificate aliceCert = makeCertificate(aliceKey, "CN=Alice's Certiicate", aliceKey, "CN=Alice's Certiicate");

        MimeBodyPart mbp = createMailBodyPart();
        mbp.writeTo(System.out);
        System.out.println();

        SMIMESignedGenerator gen = new SMIMESignedGenerator();
        JcaSimpleSignerInfoGeneratorBuilder builder = new JcaSimpleSignerInfoGeneratorBuilder().setProvider("BC");
        SignerInfoGenerator sigGen = builder.build("SHA256WithRSA", aliceKey.getPrivate(), aliceCert);
        gen.addSignerInfoGenerator(sigGen);

        MimeMultipart signedMM = gen.generate(mbp);
        MimeBodyPart signedBody = new MimeBodyPart();
        signedBody.setContent(signedMM, signedMM.getContentType());

        System.out.println("signed:");
        signedBody.writeTo(System.out);
        System.out.println();

        MimeMultipart content = (MimeMultipart) signedBody.getContent();
        SMIMESigned sig = new SMIMESigned(content);
        SignerInformation si = sig.getSignerInfos().getSigners().iterator().next();
        SignerInformationVerifier verifier = new JcaSimpleSignerInfoVerifierBuilder().setProvider("BC").build(aliceCert);
        System.out.println("verified: " + si.verify(verifier));
    }

    private MimeBodyPart createMailBodyPart() throws MessagingException {
        MimeBodyPart plainText = new MimeBodyPart();
        plainText.setText("Plain text with non-ascii: €", "utf8", "plain");
        MimeBodyPart htmlText = new MimeBodyPart();
        htmlText.setText("<h1>Some HTML</h1><p>Some text with non-ascii: €</p>", "utf8", "html");

        MimeBodyPart alternatives = new MimeBodyPart();
        alternatives.setContent(new MimeMultipart("alternative", plainText, htmlText));

        MimeBodyPart textAttachment = new MimeBodyPart();
        textAttachment.setText("some attachment data containing non-ascii-data like €", "utf8", "plain");
        textAttachment.setHeader("Content-Transfer-Encoding", "base64");
        textAttachment.setDisposition("attachment");
        textAttachment.setFileName("filename.txt");

        MimeBodyPart ret = new MimeBodyPart();
        ret.setContent(new MimeMultipart(alternatives, textAttachment));
        return ret;
    }

    // taken from the BC test case class
    // at https://github.com/bcgit/bc-java/blob/master/mail/src/main/java/org/bouncycastle/mail/smime/examples/CreateSignedMultipartMail.java
    static X509Certificate makeCertificate(
            KeyPair subKP,
            String  subDN,
            KeyPair issKP,
            String  issDN)
            throws Exception
        {
            PublicKey  subPub  = subKP.getPublic();
            PrivateKey issPriv = issKP.getPrivate();
            PublicKey  issPub  = issKP.getPublic();

            JcaX509ExtensionUtils extUtils = new JcaX509ExtensionUtils();
            X509v3CertificateBuilder v3CertGen = new JcaX509v3CertificateBuilder(new X500Name(issDN), BigInteger.valueOf(random.nextLong()), new Date(System.currentTimeMillis()), new Date(System.currentTimeMillis() + (1000L * 60 * 60 * 24 * 100)), new X500Name(subDN), subPub);

            v3CertGen.addExtension(
                    Extension.subjectKeyIdentifier,
                false,
                extUtils.createSubjectKeyIdentifier(subPub));

            v3CertGen.addExtension(
                    Extension.authorityKeyIdentifier,
                false,
                extUtils.createAuthorityKeyIdentifier(issPub));

            return new JcaX509CertificateConverter().setProvider("BC").getCertificate(v3CertGen.build(new JcaContentSignerBuilder("SHA256withRSA").setProvider("BC").build(issPriv)));
    }
}

我正在创建今天很典型的邮件:

  • 多份/混合
    • 多部分/替代
      • 文本/普通邮件正文
      • text / html邮件正文
    • 带有文件名的文本/纯文本附件

然后我使用BouncyCastle对整个内容进行签名。此后,当我尝试立即验证签名时,它会失败,但会出现异常

org.bouncycastle.cms.CMSSignerDigestMismatchException: message-digest attribute value does not match calculated value
    at org.bouncycastle.cms.SignerInformation.doVerify(Unknown Source)
    at org.bouncycastle.cms.SignerInformation.verify(Unknown Source)
    at __Run_SMIMESigning.testMailSigning(__Run_SMIMESigning.java:77) 

如果我在签名之前压缩了多部分的mime主体,则验证成功,因此我认为我的签名方式原则上应该是正确的,但是我不知道如何使此特定用例正常工作

我的第一个测试是使用BouncyCastle 1.54完成的,但1.62(当前版本)显示结果没有差异。

0 个答案:

没有答案