package org.bouncycastle.mail.smime.test;
import java.io.ByteArrayOutputStream;
import java.io.File;
import java.io.FileInputStream;
import java.io.FileOutputStream;
import java.security.KeyPair;
import java.security.Security;
import java.security.cert.X509Certificate;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Collection;
import java.util.Iterator;
import java.util.List;
import java.util.Properties;
import javax.mail.Address;
import javax.mail.Message;
import javax.mail.Session;
import javax.mail.internet.InternetAddress;
import javax.mail.internet.MimeBodyPart;
import javax.mail.internet.MimeMessage;
import javax.mail.internet.MimeMultipart;
import junit.framework.TestCase;
import org.bouncycastle.asn1.ASN1EncodableVector;
import org.bouncycastle.asn1.cms.AttributeTable;
import org.bouncycastle.asn1.smime.SMIMECapabilitiesAttribute;
import org.bouncycastle.asn1.smime.SMIMECapability;
import org.bouncycastle.asn1.smime.SMIMECapabilityVector;
import org.bouncycastle.cert.X509CertificateHolder;
import org.bouncycastle.cert.jcajce.JcaCertStore;
import org.bouncycastle.cms.CMSAlgorithm;
import org.bouncycastle.cms.CMSException;
import org.bouncycastle.cms.RecipientInformation;
import org.bouncycastle.cms.SignerInformation;
import org.bouncycastle.cms.SignerInformationStore;
import org.bouncycastle.cms.jcajce.JcaSimpleSignerInfoGeneratorBuilder;
import org.bouncycastle.cms.jcajce.JcaSimpleSignerInfoVerifierBuilder;
import org.bouncycastle.cms.jcajce.JcaX509CertSelectorConverter;
import org.bouncycastle.cms.jcajce.JceCMSContentEncryptorBuilder;
import org.bouncycastle.cms.jcajce.JceKeyTransEnvelopedRecipient;
import org.bouncycastle.cms.jcajce.JceKeyTransRecipientInfoGenerator;
import org.bouncycastle.cms.jcajce.ZlibCompressor;
import org.bouncycastle.jce.provider.BouncyCastleProvider;
import org.bouncycastle.mail.smime.SMIMECompressedGenerator;
import org.bouncycastle.mail.smime.SMIMEEnveloped;
import org.bouncycastle.mail.smime.SMIMEEnvelopedGenerator;
import org.bouncycastle.mail.smime.SMIMESigned;
import org.bouncycastle.mail.smime.SMIMESignedGenerator;
import org.bouncycastle.mail.smime.SMIMESignedParser;
import org.bouncycastle.mail.smime.SMIMEUtil;
import org.bouncycastle.mail.smime.util.FileBackedMimeBodyPart;
import org.bouncycastle.operator.jcajce.JcaDigestCalculatorProviderBuilder;
import org.bouncycastle.util.Store;
public class SMIMEMiscTest
extends TestCase
{
static MimeBodyPart msg;
static String signDN;
static KeyPair signKP;
static X509Certificate signCert;
static String origDN;
static KeyPair origKP;
static X509Certificate origCert;
static String reciDN;
static KeyPair reciKP;
static X509Certificate reciCert;
private static final JcaX509CertSelectorConverter selectorConverter = new JcaX509CertSelectorConverter();
KeyPair dsaSignKP;
X509Certificate dsaSignCert;
KeyPair dsaOrigKP;
X509Certificate dsaOrigCert;
static
{
try
{
if (Security.getProvider("BC") == null)
{
Security.addProvider(new BouncyCastleProvider());
}
msg = SMIMETestUtil.makeMimeBodyPart("Hello world!\n");
signDN = "O=Bouncy Castle, C=AU";
signKP = CMSTestUtil.makeKeyPair();
signCert = CMSTestUtil.makeCertificate(signKP, signDN, signKP, signDN);
origDN = "CN=Eric H. Echidna, E=eric@bouncycastle.org, O=Bouncy Castle, C=AU";
origKP = CMSTestUtil.makeKeyPair();
origCert = CMSTestUtil.makeCertificate(origKP, origDN, signKP, signDN);
}
catch (Exception e)
{
throw new RuntimeException("problem setting up signed test class: " + e);
}
}
/*
*
* INFRASTRUCTURE
*
*/
public SMIMEMiscTest(String name)
{
super(name);
}
public static void main(String args[])
{
Security.addProvider(new BouncyCastleProvider());
junit.textui.TestRunner.run(SMIMEMiscTest.class);
}
public void testSHA256WithRSAParserEncryptedWithAES()
throws Exception
{
List certList = new ArrayList();
certList.add(origCert);
certList.add(signCert);
Store certs = new JcaCertStore(certList);
SMIMEEnvelopedGenerator encGen = new SMIMEEnvelopedGenerator();
encGen.addRecipientInfoGenerator(new JceKeyTransRecipientInfoGenerator(origCert).setProvider("BC"));
MimeBodyPart mp = encGen.generate(msg, new JceCMSContentEncryptorBuilder(CMSAlgorithm.AES128_CBC).setProvider("BC").build());
ASN1EncodableVector signedAttrs = generateSignedAttributes();
SMIMESignedGenerator gen = new SMIMESignedGenerator();
gen.addSignerInfoGenerator(new JcaSimpleSignerInfoGeneratorBuilder().setProvider("BC").setSignedAttributeGenerator(new AttributeTable(signedAttrs)).build("SHA256withRSA", origKP.getPrivate(), origCert));
gen.addCertificates(certs);
MimeMultipart smm = gen.generate(mp);
File tmpFile = File.createTempFile("bcTest", ".mime");
MimeMessage msg = createMimeMessage(tmpFile, smm);
SMIMESignedParser s = new SMIMESignedParser(new JcaDigestCalculatorProviderBuilder().setProvider("BC").build(), (MimeMultipart)msg.getContent());
certs = s.getCertificates();
verifyMessageBytes(mp, s.getContent());
verifySigners(certs, s.getSignerInfos());
tmpFile.delete();
}
public void testSHA256WithRSACompressed()
throws Exception
{
List certList = new ArrayList();
certList.add(origCert);
certList.add(signCert);
Store certs = new JcaCertStore(certList);
SMIMECompressedGenerator cGen = new SMIMECompressedGenerator();
MimeBodyPart mp = cGen.generate(msg, new ZlibCompressor());
ASN1EncodableVector signedAttrs = generateSignedAttributes();
SMIMESignedGenerator gen = new SMIMESignedGenerator();
gen.addSignerInfoGenerator(new JcaSimpleSignerInfoGeneratorBuilder().setProvider("BC").setSignedAttributeGenerator(new AttributeTable(signedAttrs)).build("SHA256withRSA", origKP.getPrivate(), origCert));
gen.addCertificates(certs);
MimeMultipart smm = gen.generate(mp);
File tmpFile = File.createTempFile("bcTest", ".mime");
MimeMessage msg = createMimeMessage(tmpFile, smm);
SMIMESigned s = new SMIMESigned((MimeMultipart)msg.getContent());
certs = s.getCertificates();
verifyMessageBytes(mp, s.getContent());
verifySigners(certs, s.getSignerInfos());
tmpFile.delete();
}
public void testQuotePrintableSigPreservation()
throws Exception
{
MimeMessage msg = new MimeMessage((Session)null, getClass().getResourceAsStream("qp-soft-break.eml"));
SMIMEEnvelopedGenerator encGen = new SMIMEEnvelopedGenerator();
encGen.addRecipientInfoGenerator(new JceKeyTransRecipientInfoGenerator(origCert).setProvider("BC"));
MimeBodyPart mp = encGen.generate(msg, new JceCMSContentEncryptorBuilder(CMSAlgorithm.AES128_CBC).setProvider("BC").build());
SMIMEEnveloped env = new SMIMEEnveloped(mp);
RecipientInformation ri = (RecipientInformation)env.getRecipientInfos().getRecipients().iterator().next();
MimeBodyPart mm = SMIMEUtil.toMimeBodyPart(ri.getContentStream(new JceKeyTransEnvelopedRecipient(origKP.getPrivate()).setProvider("BC")));
SMIMESigned s = new SMIMESigned((MimeMultipart)mm.getContent());
Collection c = s.getSignerInfos().getSigners();
Iterator it = c.iterator();
Store certs = s.getCertificates();
while (it.hasNext())
{
SignerInformation signer = (SignerInformation)it.next();
Collection certCollection = certs.getMatches(signer.getSID());
Iterator certIt = certCollection.iterator();
X509CertificateHolder cert = (X509CertificateHolder)certIt.next();
assertEquals(true, signer.verify(new JcaSimpleSignerInfoVerifierBuilder().setProvider("BC").build(cert)));
}
((FileBackedMimeBodyPart)mm).dispose();
}
public void testSHA256WithRSAParserCompressed()
throws Exception
{
List certList = new ArrayList();
certList.add(origCert);
certList.add(signCert);
Store certs = new JcaCertStore(certList);
SMIMECompressedGenerator cGen = new SMIMECompressedGenerator();
MimeBodyPart mp = cGen.generate(msg, new ZlibCompressor());
ASN1EncodableVector signedAttrs = generateSignedAttributes();
SMIMESignedGenerator gen = new SMIMESignedGenerator();
gen.addSignerInfoGenerator(new JcaSimpleSignerInfoGeneratorBuilder().setProvider("BC").setSignedAttributeGenerator(new AttributeTable(signedAttrs)).build("SHA256withRSA", origKP.getPrivate(), origCert));
gen.addCertificates(certs);
MimeMultipart smm = gen.generate(mp);
File tmpFile = File.createTempFile("bcTest", ".mime");
MimeMessage msg = createMimeMessage(tmpFile, smm);
SMIMESignedParser s = new SMIMESignedParser(new JcaDigestCalculatorProviderBuilder().setProvider("BC").build(), (MimeMultipart)msg.getContent());
certs = s.getCertificates();
verifyMessageBytes(mp, s.getContent());
verifySigners(certs, s.getSignerInfos());
tmpFile.delete();
}
public void testBrokenEnvelope()
throws Exception
{
Session session = Session.getDefaultInstance(System.getProperties(), null);
MimeMessage msg = new MimeMessage(session, getClass().getResourceAsStream("brokenEnv.message"));
try
{
new SMIMEEnveloped(msg);
}
catch (CMSException e)
{
if (!e.getMessage().equals("Malformed content."))
{
fail("wrong exception on bogus envelope");
}
}
}
private void verifySigners(Store certs, SignerInformationStore signers)
throws Exception
{
Collection c = signers.getSigners();
Iterator it = c.iterator();
while (it.hasNext())
{
SignerInformation signer = (SignerInformation)it.next();
Collection certCollection = certs.getMatches(signer.getSID());
Iterator certIt = certCollection.iterator();
X509CertificateHolder cert = (X509CertificateHolder)certIt.next();
assertEquals(true, signer.verify(new JcaSimpleSignerInfoVerifierBuilder().setProvider("BC").build(cert)));
}
}
private void verifyMessageBytes(MimeBodyPart a, MimeBodyPart b)
throws Exception
{
ByteArrayOutputStream bOut1 = new ByteArrayOutputStream();
a.writeTo(bOut1);
bOut1.close();
ByteArrayOutputStream bOut2 = new ByteArrayOutputStream();
b.writeTo(bOut2);
bOut2.close();
assertEquals(true, Arrays.equals(bOut1.toByteArray(), bOut2.toByteArray()));
}
/**
* Create a mime message representing the multipart. We need to do
* this as otherwise no raw content stream for the message will exist.
*/
private MimeMessage createMimeMessage(File tmpFile, MimeMultipart smm)
throws Exception
{
FileOutputStream fOut = new FileOutputStream(tmpFile);
Properties props = System.getProperties();
Session session = Session.getDefaultInstance(props, null);
Address fromUser = new InternetAddress("\"Eric H. Echidna\"<eric@bouncycastle.org>");
Address toUser = new InternetAddress("example@bouncycastle.org");
MimeMessage body = new MimeMessage(session);
body.setFrom(fromUser);
body.setRecipient(Message.RecipientType.TO, toUser);
body.setSubject("example signed message");
body.setContent(smm, smm.getContentType());
body.saveChanges();
body.writeTo(fOut);
fOut.close();
return new MimeMessage(session, new FileInputStream(tmpFile));
}
private ASN1EncodableVector generateSignedAttributes()
{
ASN1EncodableVector signedAttrs = new ASN1EncodableVector();
SMIMECapabilityVector caps = new SMIMECapabilityVector();
caps.addCapability(SMIMECapability.dES_EDE3_CBC);
caps.addCapability(SMIMECapability.rC2_CBC, 128);
caps.addCapability(SMIMECapability.dES_CBC);
signedAttrs.add(new SMIMECapabilitiesAttribute(caps));
return signedAttrs;
}
}