代码之家  ›  专栏  ›  技术社区  ›  Lothar

执行S/MIME签名,然后立即检查签名失败

  •  0
  • Lothar  · 技术社区  · 7 年前

    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)));
        }
    }
    

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

    • 多部分/混合
        • 文本/普通邮件正文
      • 带文件名的文本/纯文本附件

    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主体,验证就成功了,所以我认为我的签名方式原则上应该是正确的,但是我不知道该怎么做才能使这个特定的用例生效。

    第一个版本显示的是我的第一个版本,结果是62.54。

    0 回复  |  直到 7 年前
    推荐文章