以下来源是一个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)));
}
}
我正在创建今天很典型的邮件:
然后我使用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(当前版本)显示结果没有差异。