S/MIME sign and immediate verification fails
Lothar Kimmeringer <[email protected]> Fri, 14 Jun 2019 15:44:32 +0200
| Newsgroups | gmane.comp.encryption.bouncy-castle.devel |
|---|---|
| Message-ID | <[email protected]> |
Hi,
I'm struggling with this since yesterday and I don't see an error. Attached
you can find my JUnit test class that reproduces the problem I see with my
"real" test case (that's the reason why there is non-ascii data, changing that
to pure ascii has no effect on the problem). Here's what I'm doing:
- Creating a MimeBodyPart, containing a multipart/alternative block with text/plain
and text/html data, that has been added to a multipart/mixed block together
with an attachment containing a text-file
- Signing the MimeBodyPart the "usual" way, i.e. the way you e.g. find it
in the example files that are provided with BC itself.
- Put the resulting MimeMultipart into a MimeBodyPart and use that to
verify the signature, again the "normal" way.
Whatever I try, I always get a verification check error:
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)
The strange thing is: When I compress before signing, the signature verifcation
check succeeds, the same is the case when I encrypt the signed data afterwards.
I can understand the former in some way but the latter is what I don't get.
I'd really appreciate if somebody can point me where I do something wrong,
because after staring on it for nearly a day, I've gone blind I suppose.
Thanks and best regards,
Lothar Kimmeringer
__Run_SMIMESigning.java
(text/plain, 5.6 KB)
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)));
}
}