diff options
Diffstat (limited to 'mail/src/main/java/org/spongycastle/mail/smime/examples')
16 files changed, 2151 insertions, 0 deletions
diff --git a/mail/src/main/java/org/spongycastle/mail/smime/examples/CreateCompressedMail.java b/mail/src/main/java/org/spongycastle/mail/smime/examples/CreateCompressedMail.java new file mode 100644 index 00000000..5d1df695 --- /dev/null +++ b/mail/src/main/java/org/spongycastle/mail/smime/examples/CreateCompressedMail.java @@ -0,0 +1,57 @@ +package org.bouncycastle.mail.smime.examples; + +import java.io.FileOutputStream; +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 org.bouncycastle.cms.jcajce.ZlibCompressor; +import org.bouncycastle.mail.smime.SMIMECompressedGenerator; + +/** + * a simple example that creates a single compressed mail message. + */ +public class CreateCompressedMail +{ + public static void main( + String args[]) + throws Exception + { + // + // create the generator for creating an smime/compressed message + // + SMIMECompressedGenerator gen = new SMIMECompressedGenerator(); + + // + // create the base for our message + // + MimeBodyPart msg = new MimeBodyPart(); + + msg.setText("Hello world!"); + + MimeBodyPart mp = gen.generate(msg, new ZlibCompressor()); + + // + // Get a Session object and create the mail message + // + 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 compressed message"); + body.setContent(mp.getContent(), mp.getContentType()); + body.saveChanges(); + + body.writeTo(new FileOutputStream("compressed.message")); + } +} diff --git a/mail/src/main/java/org/spongycastle/mail/smime/examples/CreateEncryptedMail.java b/mail/src/main/java/org/spongycastle/mail/smime/examples/CreateEncryptedMail.java new file mode 100644 index 00000000..6ef42d9d --- /dev/null +++ b/mail/src/main/java/org/spongycastle/mail/smime/examples/CreateEncryptedMail.java @@ -0,0 +1,128 @@ +package org.bouncycastle.mail.smime.examples; + +import java.io.FileInputStream; +import java.io.FileOutputStream; +import java.security.KeyStore; +import java.security.Security; +import java.security.cert.Certificate; +import java.security.cert.X509Certificate; +import java.util.Enumeration; +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 org.bouncycastle.cms.CMSAlgorithm; +import org.bouncycastle.cms.jcajce.JceCMSContentEncryptorBuilder; +import org.bouncycastle.cms.jcajce.JceKeyTransRecipientInfoGenerator; +import org.bouncycastle.jce.provider.BouncyCastleProvider; +import org.bouncycastle.mail.smime.SMIMEEnvelopedGenerator; + +/** + * a simple example that creates a single encrypted mail message. + * <p> + * The key store can be created using the class in + * org.bouncycastle.jce.examples.PKCS12Example - the program expects only one + * key to be present in the key file. + * <p> + * Note: while this means that both the private key is available to + * the program, the private key is retrieved from the keystore only for + * the purposes of locating the corresponding public key, in normal circumstances + * you would only be doing this with a certificate available. + */ +public class CreateEncryptedMail +{ + public static void main( + String args[]) + throws Exception + { + if (args.length != 2) + { + System.err.println("usage: CreateEncryptedMail pkcs12Keystore password"); + System.exit(0); + } + + if (Security.getProvider("BC") == null) + { + Security.addProvider(new BouncyCastleProvider()); + } + + // + // Open the key store + // + KeyStore ks = KeyStore.getInstance("PKCS12", "BC"); + + ks.load(new FileInputStream(args[0]), args[1].toCharArray()); + + Enumeration e = ks.aliases(); + String keyAlias = null; + + while (e.hasMoreElements()) + { + String alias = (String)e.nextElement(); + + if (ks.isKeyEntry(alias)) + { + keyAlias = alias; + } + } + + if (keyAlias == null) + { + System.err.println("can't find a private key!"); + System.exit(0); + } + + Certificate[] chain = ks.getCertificateChain(keyAlias); + + // + // create the generator for creating an smime/encrypted message + // + SMIMEEnvelopedGenerator gen = new SMIMEEnvelopedGenerator(); + + gen.addRecipientInfoGenerator(new JceKeyTransRecipientInfoGenerator((X509Certificate)chain[0]).setProvider("BC")); + + // + // create a subject key id - this has to be done the same way as + // it is done in the certificate associated with the private key + // version 3 only. + // + /* + MessageDigest dig = MessageDigest.getInstance("SHA1", "BC"); + + dig.update(cert.getPublicKey().getEncoded()); + + gen.addKeyTransRecipient(cert.getPublicKey(), dig.digest()); + */ + + // + // create the base for our message + // + MimeBodyPart msg = new MimeBodyPart(); + + msg.setText("Hello world!"); + + MimeBodyPart mp = gen.generate(msg, new JceCMSContentEncryptorBuilder(CMSAlgorithm.RC2_CBC).setProvider("BC").build()); + // + // Get a Session object and create the mail message + // + 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 encrypted message"); + body.setContent(mp.getContent(), mp.getContentType()); + body.saveChanges(); + + body.writeTo(new FileOutputStream("encrypted.message")); + } +} diff --git a/mail/src/main/java/org/spongycastle/mail/smime/examples/CreateLargeCompressedMail.java b/mail/src/main/java/org/spongycastle/mail/smime/examples/CreateLargeCompressedMail.java new file mode 100644 index 00000000..63c5125d --- /dev/null +++ b/mail/src/main/java/org/spongycastle/mail/smime/examples/CreateLargeCompressedMail.java @@ -0,0 +1,63 @@ +package org.bouncycastle.mail.smime.examples; + +import java.io.File; +import java.io.FileOutputStream; +import java.util.Properties; + +import javax.activation.DataHandler; +import javax.activation.FileDataSource; +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 org.bouncycastle.cms.jcajce.ZlibCompressor; +import org.bouncycastle.mail.smime.SMIMECompressedGenerator; + +/** + * a simple example that creates a single compressed mail message using the large + * file model. + */ +public class CreateLargeCompressedMail +{ + public static void main( + String args[]) + throws Exception + { + // + // create the generator for creating an smime/compressed message + // + SMIMECompressedGenerator gen = new SMIMECompressedGenerator(); + + // + // create the base for our message + // + MimeBodyPart msg = new MimeBodyPart(); + + msg.setDataHandler(new DataHandler(new FileDataSource(new File(args[0])))); + msg.setHeader("Content-Type", "application/octet-stream"); + msg.setHeader("Content-Transfer-Encoding", "binary"); + + MimeBodyPart mp = gen.generate(msg, new ZlibCompressor()); + + // + // Get a Session object and create the mail message + // + 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 compressed message"); + body.setContent(mp.getContent(), mp.getContentType()); + body.saveChanges(); + + body.writeTo(new FileOutputStream("compressed.message")); + } +} diff --git a/mail/src/main/java/org/spongycastle/mail/smime/examples/CreateLargeEncryptedMail.java b/mail/src/main/java/org/spongycastle/mail/smime/examples/CreateLargeEncryptedMail.java new file mode 100644 index 00000000..5fc7663a --- /dev/null +++ b/mail/src/main/java/org/spongycastle/mail/smime/examples/CreateLargeEncryptedMail.java @@ -0,0 +1,105 @@ +package org.bouncycastle.mail.smime.examples; + +import java.io.File; +import java.io.FileOutputStream; +import java.security.KeyStore; +import java.security.cert.Certificate; +import java.security.cert.X509Certificate; +import java.util.Properties; + +import javax.activation.DataHandler; +import javax.activation.FileDataSource; +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 org.bouncycastle.cms.CMSAlgorithm; +import org.bouncycastle.cms.jcajce.JceCMSContentEncryptorBuilder; +import org.bouncycastle.cms.jcajce.JceKeyTransRecipientInfoGenerator; +import org.bouncycastle.mail.smime.SMIMEEnvelopedGenerator; + +/** + * a simple example that creates a single encrypted mail message. + * <p> + * The key store can be created using the class in + * org.bouncycastle.jce.examples.PKCS12Example - the program expects only one + * key to be present in the key file. + * <p> + * Note: while this means that both the private key is available to + * the program, the private key is retrieved from the keystore only for + * the purposes of locating the corresponding public key, in normal circumstances + * you would only be doing this with a certificate available. + */ +public class CreateLargeEncryptedMail +{ + public static void main( + String args[]) + throws Exception + { + if (args.length != 3) + { + System.err.println("usage: CreateLargeEncryptedMail pkcs12Keystore password inputFile"); + System.exit(0); + } + + // + // Open the key store + // + KeyStore ks = KeyStore.getInstance("PKCS12", "BC"); + String keyAlias = ExampleUtils.findKeyAlias(ks, args[0], args[1].toCharArray()); + + Certificate[] chain = ks.getCertificateChain(keyAlias); + + // + // create the generator for creating an smime/encrypted message + // + SMIMEEnvelopedGenerator gen = new SMIMEEnvelopedGenerator(); + + gen.addRecipientInfoGenerator(new JceKeyTransRecipientInfoGenerator((X509Certificate)chain[0]).setProvider("BC")); + + // + // create a subject key id - this has to be done the same way as + // it is done in the certificate associated with the private key + // version 3 only. + // + /* + MessageDigest dig = MessageDigest.getInstance("SHA1", "BC"); + + dig.update(cert.getPublicKey().getEncoded()); + + gen.addKeyTransRecipient(cert.getPublicKey(), dig.digest()); + */ + + // + // create the base for our message + // + MimeBodyPart msg = new MimeBodyPart(); + + msg.setDataHandler(new DataHandler(new FileDataSource(new File(args[2])))); + msg.setHeader("Content-Type", "application/octet-stream"); + msg.setHeader("Content-Transfer-Encoding", "binary"); + + MimeBodyPart mp = gen.generate(msg, new JceCMSContentEncryptorBuilder(CMSAlgorithm.RC2_CBC).setProvider("BC").build()); + + // + // Get a Session object and create the mail message + // + 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 encrypted message"); + body.setContent(mp.getContent(), mp.getContentType()); + body.saveChanges(); + + body.writeTo(new FileOutputStream("encrypted.message")); + } +} diff --git a/mail/src/main/java/org/spongycastle/mail/smime/examples/CreateLargeSignedMail.java b/mail/src/main/java/org/spongycastle/mail/smime/examples/CreateLargeSignedMail.java new file mode 100644 index 00000000..ffb092ab --- /dev/null +++ b/mail/src/main/java/org/spongycastle/mail/smime/examples/CreateLargeSignedMail.java @@ -0,0 +1,198 @@ +package org.bouncycastle.mail.smime.examples; + +import java.io.File; +import java.io.FileOutputStream; +import java.io.IOException; +import java.math.BigInteger; +import java.security.GeneralSecurityException; +import java.security.KeyPair; +import java.security.KeyPairGenerator; +import java.security.PrivateKey; +import java.security.PublicKey; +import java.security.SecureRandom; +import java.security.cert.X509Certificate; +import java.util.ArrayList; +import java.util.Date; +import java.util.List; +import java.util.Properties; + +import javax.activation.DataHandler; +import javax.activation.FileDataSource; +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 org.bouncycastle.asn1.ASN1EncodableVector; +import org.bouncycastle.asn1.cms.AttributeTable; +import org.bouncycastle.asn1.cms.IssuerAndSerialNumber; +import org.bouncycastle.asn1.smime.SMIMECapabilitiesAttribute; +import org.bouncycastle.asn1.smime.SMIMECapability; +import org.bouncycastle.asn1.smime.SMIMECapabilityVector; +import org.bouncycastle.asn1.smime.SMIMEEncryptionKeyPreferenceAttribute; +import org.bouncycastle.asn1.x500.X500Name; +import org.bouncycastle.asn1.x509.X509Extension; +import org.bouncycastle.cert.X509v3CertificateBuilder; +import org.bouncycastle.cert.jcajce.JcaCertStore; +import org.bouncycastle.cert.jcajce.JcaX509CertificateConverter; +import org.bouncycastle.cert.jcajce.JcaX509ExtensionUtils; +import org.bouncycastle.cert.jcajce.JcaX509v3CertificateBuilder; +import org.bouncycastle.cms.jcajce.JcaSimpleSignerInfoGeneratorBuilder; +import org.bouncycastle.mail.smime.SMIMESignedGenerator; +import org.bouncycastle.operator.OperatorCreationException; +import org.bouncycastle.operator.jcajce.JcaContentSignerBuilder; +import org.bouncycastle.util.Store; + +/** + * a simple example that creates a single signed mail message. + */ +public class CreateLargeSignedMail +{ + // + // certificate serial number seed. + // + static int serialNo = 1; + + /** + * create a basic X509 certificate from the given keys + */ + static X509Certificate makeCertificate( + KeyPair subKP, + String subDN, + KeyPair issKP, + String issDN) + throws GeneralSecurityException, IOException, OperatorCreationException + { + 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(serialNo++), new Date(System.currentTimeMillis()), new Date(System.currentTimeMillis() + (1000L * 60 * 60 * 24 * 100)), new X500Name(subDN), subPub); + + v3CertGen.addExtension( + X509Extension.subjectKeyIdentifier, + false, + extUtils.createSubjectKeyIdentifier(subPub)); + + v3CertGen.addExtension( + X509Extension.authorityKeyIdentifier, + false, + extUtils.createAuthorityKeyIdentifier(issPub)); + + return new JcaX509CertificateConverter().setProvider("BC").getCertificate(v3CertGen.build(new JcaContentSignerBuilder("MD5withRSA").setProvider("BC").build(issPriv))); + } + + public static void main( + String args[]) + throws Exception + { + // + // set up our certs + // + KeyPairGenerator kpg = KeyPairGenerator.getInstance("RSA", "BC"); + + kpg.initialize(1024, new SecureRandom()); + + // + // cert that issued the signing certificate + // + String signDN = "O=Bouncy Castle, C=AU"; + KeyPair signKP = kpg.generateKeyPair(); + X509Certificate signCert = makeCertificate( + signKP, signDN, signKP, signDN); + + // + // cert we sign against + // + String origDN = "CN=Eric H. Echidna, E=eric@bouncycastle.org, O=Bouncy Castle, C=AU"; + KeyPair origKP = kpg.generateKeyPair(); + X509Certificate origCert = makeCertificate( + origKP, origDN, signKP, signDN); + + List certList = new ArrayList(); + + certList.add(origCert); + certList.add(signCert); + + // + // create a CertStore containing the certificates we want carried + // in the signature + // + Store certs = new JcaCertStore(certList); + + // + // create some smime capabilities in case someone wants to respond + // + 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)); + + // + // add an encryption key preference for encrypted responses - + // normally this would be different from the signing certificate... + // + IssuerAndSerialNumber issAndSer = new IssuerAndSerialNumber( + new X500Name(signDN), origCert.getSerialNumber()); + + signedAttrs.add(new SMIMEEncryptionKeyPreferenceAttribute(issAndSer)); + + // + // create the generator for creating an smime/signed message + // + SMIMESignedGenerator gen = new SMIMESignedGenerator(); + + // + // add a signer to the generator - this specifies we are using SHA1 and + // adding the smime attributes above to the signed attributes that + // will be generated as part of the signature. The encryption algorithm + // used is taken from the key - in this RSA with PKCS1Padding + // + gen.addSignerInfoGenerator(new JcaSimpleSignerInfoGeneratorBuilder().setProvider("BC").setSignedAttributeGenerator(new AttributeTable(signedAttrs)).build("SHA1withRSA", origKP.getPrivate(), origCert)); + + // + // add our pool of certs and cerls (if any) to go with the signature + // + gen.addCertificates(certs); + + // + // create the base for our message + // + MimeBodyPart msg = new MimeBodyPart(); + + msg.setDataHandler(new DataHandler(new FileDataSource(new File(args[0])))); + msg.setHeader("Content-Type", "application/octet-stream"); + msg.setHeader("Content-Transfer-Encoding", "base64"); + + // + // extract the multipart object from the SMIMESigned object. + // + MimeMultipart mm = gen.generate(msg); + + // + // Get a Session object and create the mail message + // + 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(mm, mm.getContentType()); + body.saveChanges(); + + body.writeTo(new FileOutputStream("signed.message")); + } +} diff --git a/mail/src/main/java/org/spongycastle/mail/smime/examples/CreateSignedMail.java b/mail/src/main/java/org/spongycastle/mail/smime/examples/CreateSignedMail.java new file mode 100644 index 00000000..8f3fb787 --- /dev/null +++ b/mail/src/main/java/org/spongycastle/mail/smime/examples/CreateSignedMail.java @@ -0,0 +1,221 @@ +package org.bouncycastle.mail.smime.examples; + +import java.io.ByteArrayInputStream; +import java.io.FileOutputStream; +import java.io.IOException; +import java.math.BigInteger; +import java.security.GeneralSecurityException; +import java.security.KeyPair; +import java.security.KeyPairGenerator; +import java.security.PrivateKey; +import java.security.PublicKey; +import java.security.SecureRandom; +import java.security.cert.X509Certificate; +import java.util.ArrayList; +import java.util.Date; +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 org.bouncycastle.asn1.ASN1EncodableVector; +import org.bouncycastle.asn1.ASN1InputStream; +import org.bouncycastle.asn1.ASN1Sequence; +import org.bouncycastle.asn1.cms.AttributeTable; +import org.bouncycastle.asn1.cms.IssuerAndSerialNumber; +import org.bouncycastle.asn1.smime.SMIMECapabilitiesAttribute; +import org.bouncycastle.asn1.smime.SMIMECapability; +import org.bouncycastle.asn1.smime.SMIMECapabilityVector; +import org.bouncycastle.asn1.smime.SMIMEEncryptionKeyPreferenceAttribute; +import org.bouncycastle.asn1.x500.X500Name; +import org.bouncycastle.asn1.x509.AuthorityKeyIdentifier; +import org.bouncycastle.asn1.x509.SubjectKeyIdentifier; +import org.bouncycastle.asn1.x509.SubjectPublicKeyInfo; +import org.bouncycastle.asn1.x509.X509Extension; +import org.bouncycastle.cert.X509v3CertificateBuilder; +import org.bouncycastle.cert.bc.BcX509ExtensionUtils; +import org.bouncycastle.cert.jcajce.JcaCertStore; +import org.bouncycastle.cert.jcajce.JcaX509CertificateConverter; +import org.bouncycastle.cert.jcajce.JcaX509v3CertificateBuilder; +import org.bouncycastle.cms.jcajce.JcaSimpleSignerInfoGeneratorBuilder; +import org.bouncycastle.mail.smime.SMIMESignedGenerator; +import org.bouncycastle.operator.OperatorCreationException; +import org.bouncycastle.operator.jcajce.JcaContentSignerBuilder; +import org.bouncycastle.util.Store; + +/** + * a simple example that creates a single signed mail message. + */ +public class CreateSignedMail +{ + // + // certificate serial number seed. + // + static int serialNo = 1; + + static AuthorityKeyIdentifier createAuthorityKeyId( + PublicKey pub) + throws IOException + { + ByteArrayInputStream bIn = new ByteArrayInputStream(pub.getEncoded()); + SubjectPublicKeyInfo info = new SubjectPublicKeyInfo( + (ASN1Sequence)new ASN1InputStream(bIn).readObject()); + + return new AuthorityKeyIdentifier(info); + } + + static SubjectKeyIdentifier createSubjectKeyId( + PublicKey pub) + throws IOException + { + ByteArrayInputStream bIn = new ByteArrayInputStream(pub.getEncoded()); + + SubjectPublicKeyInfo info = new SubjectPublicKeyInfo( + (ASN1Sequence)new ASN1InputStream(bIn).readObject()); + + return new BcX509ExtensionUtils().createSubjectKeyIdentifier(info); + } + + /** + * create a basic X509 certificate from the given keys + */ + static X509Certificate makeCertificate( + KeyPair subKP, + String subDN, + KeyPair issKP, + String issDN) + throws GeneralSecurityException, IOException, OperatorCreationException + { + PublicKey subPub = subKP.getPublic(); + PrivateKey issPriv = issKP.getPrivate(); + PublicKey issPub = issKP.getPublic(); + + X509v3CertificateBuilder v3CertGen = new JcaX509v3CertificateBuilder(new X500Name(issDN), BigInteger.valueOf(serialNo++), new Date(System.currentTimeMillis()), new Date(System.currentTimeMillis() + (1000L * 60 * 60 * 24 * 100)), new X500Name(subDN), subPub); + + v3CertGen.addExtension( + X509Extension.subjectKeyIdentifier, + false, + createSubjectKeyId(subPub)); + + v3CertGen.addExtension( + X509Extension.authorityKeyIdentifier, + false, + createAuthorityKeyId(issPub)); + + return new JcaX509CertificateConverter().setProvider("BC").getCertificate(v3CertGen.build(new JcaContentSignerBuilder("MD5withRSA").setProvider("BC").build(issPriv))); + } + + public static void main( + String args[]) + throws Exception + { + // + // set up our certs + // + KeyPairGenerator kpg = KeyPairGenerator.getInstance("RSA", "BC"); + + kpg.initialize(1024, new SecureRandom()); + + // + // cert that issued the signing certificate + // + String signDN = "O=Bouncy Castle, C=AU"; + KeyPair signKP = kpg.generateKeyPair(); + X509Certificate signCert = makeCertificate( + signKP, signDN, signKP, signDN); + + // + // cert we sign against + // + String origDN = "CN=Eric H. Echidna, E=eric@bouncycastle.org, O=Bouncy Castle, C=AU"; + KeyPair origKP = kpg.generateKeyPair(); + X509Certificate origCert = makeCertificate( + origKP, origDN, signKP, signDN); + + List certList = new ArrayList(); + + certList.add(origCert); + certList.add(signCert); + + // + // create a CertStore containing the certificates we want carried + // in the signature + // + Store certs = new JcaCertStore(certList); + + // + // create some smime capabilities in case someone wants to respond + // + 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)); + + // + // add an encryption key preference for encrypted responses - + // normally this would be different from the signing certificate... + // + IssuerAndSerialNumber issAndSer = new IssuerAndSerialNumber( + new X500Name(signDN), origCert.getSerialNumber()); + + signedAttrs.add(new SMIMEEncryptionKeyPreferenceAttribute(issAndSer)); + + // + // create the generator for creating an smime/signed message + // + SMIMESignedGenerator gen = new SMIMESignedGenerator(); + + // + // add a signer to the generator - this specifies we are using SHA1 and + // adding the smime attributes above to the signed attributes that + // will be generated as part of the signature. The encryption algorithm + // used is taken from the key - in this RSA with PKCS1Padding + // + gen.addSignerInfoGenerator(new JcaSimpleSignerInfoGeneratorBuilder().setProvider("BC").setSignedAttributeGenerator(new AttributeTable(signedAttrs)).build("SHA1withRSA", origKP.getPrivate(), origCert)); + + // + // add our pool of certs and cerls (if any) to go with the signature + // + gen.addCertificates(certs); + + // + // create the base for our message + // + MimeBodyPart msg = new MimeBodyPart(); + + msg.setText("Hello world!"); + + // + // extract the multipart object from the SMIMESigned object. + // + MimeMultipart mm = gen.generate(msg); + + // + // Get a Session object and create the mail message + // + 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(mm, mm.getContentType()); + body.saveChanges(); + + body.writeTo(new FileOutputStream("signed.message")); + } +} diff --git a/mail/src/main/java/org/spongycastle/mail/smime/examples/CreateSignedMultipartMail.java b/mail/src/main/java/org/spongycastle/mail/smime/examples/CreateSignedMultipartMail.java new file mode 100644 index 00000000..20d1b3ea --- /dev/null +++ b/mail/src/main/java/org/spongycastle/mail/smime/examples/CreateSignedMultipartMail.java @@ -0,0 +1,213 @@ +package org.bouncycastle.mail.smime.examples; + +import java.io.FileOutputStream; +import java.io.IOException; +import java.math.BigInteger; +import java.security.GeneralSecurityException; +import java.security.KeyPair; +import java.security.KeyPairGenerator; +import java.security.PrivateKey; +import java.security.PublicKey; +import java.security.SecureRandom; +import java.security.cert.X509Certificate; +import java.util.ArrayList; +import java.util.Date; +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 org.bouncycastle.asn1.ASN1EncodableVector; +import org.bouncycastle.asn1.cms.AttributeTable; +import org.bouncycastle.asn1.cms.IssuerAndSerialNumber; +import org.bouncycastle.asn1.smime.SMIMECapabilitiesAttribute; +import org.bouncycastle.asn1.smime.SMIMECapability; +import org.bouncycastle.asn1.smime.SMIMECapabilityVector; +import org.bouncycastle.asn1.smime.SMIMEEncryptionKeyPreferenceAttribute; +import org.bouncycastle.asn1.x500.X500Name; +import org.bouncycastle.asn1.x509.X509Extension; +import org.bouncycastle.cert.X509v3CertificateBuilder; +import org.bouncycastle.cert.jcajce.JcaCertStore; +import org.bouncycastle.cert.jcajce.JcaX509CertificateConverter; +import org.bouncycastle.cert.jcajce.JcaX509ExtensionUtils; +import org.bouncycastle.cert.jcajce.JcaX509v3CertificateBuilder; +import org.bouncycastle.cms.jcajce.JcaSimpleSignerInfoGeneratorBuilder; +import org.bouncycastle.mail.smime.SMIMESignedGenerator; +import org.bouncycastle.operator.OperatorCreationException; +import org.bouncycastle.operator.jcajce.JcaContentSignerBuilder; +import org.bouncycastle.util.Store; + +/** + * a simple example that creates a single signed multipart mail message. + */ +public class CreateSignedMultipartMail +{ + // + // certificate serial number seed. + // + static int serialNo = 1; + + /** + * create a basic X509 certificate from the given keys + */ + static X509Certificate makeCertificate( + KeyPair subKP, + String subDN, + KeyPair issKP, + String issDN) + throws GeneralSecurityException, IOException, OperatorCreationException + { + 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(serialNo++), new Date(System.currentTimeMillis()), new Date(System.currentTimeMillis() + (1000L * 60 * 60 * 24 * 100)), new X500Name(subDN), subPub); + + v3CertGen.addExtension( + X509Extension.subjectKeyIdentifier, + false, + extUtils.createSubjectKeyIdentifier(subPub)); + + v3CertGen.addExtension( + X509Extension.authorityKeyIdentifier, + false, + extUtils.createAuthorityKeyIdentifier(issPub)); + + return new JcaX509CertificateConverter().setProvider("BC").getCertificate(v3CertGen.build(new JcaContentSignerBuilder("MD5withRSA").setProvider("BC").build(issPriv))); + } + + public static void main( + String args[]) + throws Exception + { + // + // set up our certs + // + KeyPairGenerator kpg = KeyPairGenerator.getInstance("RSA", "BC"); + + kpg.initialize(1024, new SecureRandom()); + + // + // cert that issued the signing certificate + // + String signDN = "O=Bouncy Castle, C=AU"; + KeyPair signKP = kpg.generateKeyPair(); + X509Certificate signCert = makeCertificate( + signKP, signDN, signKP, signDN); + + // + // cert we sign against + // + String origDN = "CN=Eric H. Echidna, E=eric@bouncycastle.org, O=Bouncy Castle, C=AU"; + KeyPair origKP = kpg.generateKeyPair(); + X509Certificate origCert = makeCertificate( + origKP, origDN, signKP, signDN); + + List certList = new ArrayList(); + + certList.add(origCert); + certList.add(signCert); + + // + // create a CertStore containing the certificates we want carried + // in the signature + // + Store certs = new JcaCertStore(certList); + + // + // create some smime capabilities in case someone wants to respond + // + 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)); + + // + // add an encryption key preference for encrypted responses - + // normally this would be different from the signing certificate... + // + IssuerAndSerialNumber issAndSer = new IssuerAndSerialNumber( + new X500Name(signDN), origCert.getSerialNumber()); + + signedAttrs.add(new SMIMEEncryptionKeyPreferenceAttribute(issAndSer)); + + // + // create the generator for creating an smime/signed message + // + SMIMESignedGenerator gen = new SMIMESignedGenerator(); + + // + // add a signer to the generator - this specifies we are using SHA1 and + // adding the smime attributes above to the signed attributes that + // will be generated as part of the signature. The encryption algorithm + // used is taken from the key - in this RSA with PKCS1Padding + // + gen.addSignerInfoGenerator(new JcaSimpleSignerInfoGeneratorBuilder().setProvider("BC").setSignedAttributeGenerator(new AttributeTable(signedAttrs)).build("SHA1withRSA", origKP.getPrivate(), origCert)); + + // + // add our pool of certs and cerls (if any) to go with the signature + // + gen.addCertificates(certs); + + // + // create the base for our message + // + MimeBodyPart msg1 = new MimeBodyPart(); + + msg1.setText("Hello part 1!"); + + MimeBodyPart msg2 = new MimeBodyPart(); + + msg2.setText("Hello part 2!"); + + MimeMultipart mp = new MimeMultipart(); + + mp.addBodyPart(msg1); + mp.addBodyPart(msg2); + + MimeBodyPart m = new MimeBodyPart(); + + // + // be careful about setting extra headers here. Some mail clients + // ignore the To and From fields (for example) in the body part + // that contains the multipart. The result of this will be that the + // signature fails to verify... Outlook Express is an example of + // a client that exhibits this behaviour. + // + m.setContent(mp); + + // + // extract the multipart object from the SMIMESigned object. + // + MimeMultipart mm = gen.generate(m); + + // + // Get a Session object and create the mail message + // + 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(mm, mm.getContentType()); + body.saveChanges(); + + body.writeTo(new FileOutputStream("signed.message")); + } +} diff --git a/mail/src/main/java/org/spongycastle/mail/smime/examples/ExampleUtils.java b/mail/src/main/java/org/spongycastle/mail/smime/examples/ExampleUtils.java new file mode 100644 index 00000000..10c0f06c --- /dev/null +++ b/mail/src/main/java/org/spongycastle/mail/smime/examples/ExampleUtils.java @@ -0,0 +1,77 @@ +package org.bouncycastle.mail.smime.examples; + +import java.io.FileInputStream; +import java.io.FileOutputStream; +import java.io.IOException; +import java.io.InputStream; +import java.io.OutputStream; +import java.security.KeyStore; +import java.util.Enumeration; + +import javax.mail.MessagingException; +import javax.mail.internet.MimeBodyPart; + +public class ExampleUtils +{ + /** + * Dump the content of the passed in BodyPart to the file fileName. + * + * @throws MessagingException + * @throws IOException + */ + public static void dumpContent( + MimeBodyPart bodyPart, + String fileName) + throws MessagingException, IOException + { + // + // print mime type of compressed content + // + System.out.println("content type: " + bodyPart.getContentType()); + + // + // recover the compressed content + // + OutputStream out = new FileOutputStream(fileName); + InputStream in = bodyPart.getInputStream(); + + byte[] buf = new byte[10000]; + int len; + + while ((len = in.read(buf, 0, buf.length)) > 0) + { + out.write(buf, 0, len); + } + + out.close(); + } + + public static String findKeyAlias( + KeyStore store, + String storeName, + char[] password) + throws Exception + { + store.load(new FileInputStream(storeName), password); + + Enumeration e = store.aliases(); + String keyAlias = null; + + while (e.hasMoreElements()) + { + String alias = (String)e.nextElement(); + + if (store.isKeyEntry(alias)) + { + keyAlias = alias; + } + } + + if (keyAlias == null) + { + throw new IllegalArgumentException("can't find a private key in keyStore: " + storeName); + } + + return keyAlias; + } +} diff --git a/mail/src/main/java/org/spongycastle/mail/smime/examples/ReadCompressedMail.java b/mail/src/main/java/org/spongycastle/mail/smime/examples/ReadCompressedMail.java new file mode 100644 index 00000000..b462b336 --- /dev/null +++ b/mail/src/main/java/org/spongycastle/mail/smime/examples/ReadCompressedMail.java @@ -0,0 +1,41 @@ +package org.bouncycastle.mail.smime.examples; + +import java.io.FileInputStream; +import java.util.Properties; + +import javax.mail.Session; +import javax.mail.internet.MimeBodyPart; +import javax.mail.internet.MimeMessage; + +import org.bouncycastle.cms.jcajce.ZlibExpanderProvider; +import org.bouncycastle.mail.smime.SMIMECompressed; +import org.bouncycastle.mail.smime.SMIMEUtil; + +/** + * a simple example that reads a compressed email. + * <p> + */ +public class ReadCompressedMail +{ + public static void main( + String args[]) + throws Exception + { + // + // Get a Session object with the default properties. + // + Properties props = System.getProperties(); + + Session session = Session.getDefaultInstance(props, null); + + MimeMessage msg = new MimeMessage(session, new FileInputStream("compressed.message")); + + SMIMECompressed m = new SMIMECompressed(msg); + + MimeBodyPart res = SMIMEUtil.toMimeBodyPart(m.getContent(new ZlibExpanderProvider())); + + System.out.println("Message Contents"); + System.out.println("----------------"); + System.out.println(res.getContent()); + } +} diff --git a/mail/src/main/java/org/spongycastle/mail/smime/examples/ReadEncryptedMail.java b/mail/src/main/java/org/spongycastle/mail/smime/examples/ReadEncryptedMail.java new file mode 100644 index 00000000..a180994f --- /dev/null +++ b/mail/src/main/java/org/spongycastle/mail/smime/examples/ReadEncryptedMail.java @@ -0,0 +1,94 @@ +package org.bouncycastle.mail.smime.examples; + +import java.io.FileInputStream; +import java.security.KeyStore; +import java.security.PrivateKey; +import java.security.cert.X509Certificate; +import java.util.Enumeration; +import java.util.Properties; + +import javax.mail.Session; +import javax.mail.internet.MimeBodyPart; +import javax.mail.internet.MimeMessage; + +import org.bouncycastle.cms.RecipientId; +import org.bouncycastle.cms.RecipientInformation; +import org.bouncycastle.cms.RecipientInformationStore; +import org.bouncycastle.cms.jcajce.JceKeyTransEnvelopedRecipient; +import org.bouncycastle.cms.jcajce.JceKeyTransRecipientId; +import org.bouncycastle.mail.smime.SMIMEEnveloped; +import org.bouncycastle.mail.smime.SMIMEUtil; + +/** + * a simple example that reads an encrypted email. + * <p> + * The key store can be created using the class in + * org.bouncycastle.jce.examples.PKCS12Example - the program expects only one + * key to be present. + */ +public class ReadEncryptedMail +{ + public static void main( + String args[]) + throws Exception + { + if (args.length != 2) + { + System.err.println("usage: ReadEncryptedMail pkcs12Keystore password"); + System.exit(0); + } + + // + // Open the key store + // + KeyStore ks = KeyStore.getInstance("PKCS12", "BC"); + + ks.load(new FileInputStream(args[0]), args[1].toCharArray()); + + Enumeration e = ks.aliases(); + String keyAlias = null; + + while (e.hasMoreElements()) + { + String alias = (String)e.nextElement(); + + if (ks.isKeyEntry(alias)) + { + keyAlias = alias; + } + } + + if (keyAlias == null) + { + System.err.println("can't find a private key!"); + System.exit(0); + } + + // + // find the certificate for the private key and generate a + // suitable recipient identifier. + // + X509Certificate cert = (X509Certificate)ks.getCertificate(keyAlias); + RecipientId recId = new JceKeyTransRecipientId(cert); + + // + // Get a Session object with the default properties. + // + Properties props = System.getProperties(); + + Session session = Session.getDefaultInstance(props, null); + + MimeMessage msg = new MimeMessage(session, new FileInputStream("encrypted.message")); + + SMIMEEnveloped m = new SMIMEEnveloped(msg); + + RecipientInformationStore recipients = m.getRecipientInfos(); + RecipientInformation recipient = recipients.get(recId); + + MimeBodyPart res = SMIMEUtil.toMimeBodyPart(recipient.getContent(new JceKeyTransEnvelopedRecipient((PrivateKey)ks.getKey(keyAlias, null)).setProvider("BC"))); + + System.out.println("Message Contents"); + System.out.println("----------------"); + System.out.println(res.getContent()); + } +} diff --git a/mail/src/main/java/org/spongycastle/mail/smime/examples/ReadLargeCompressedMail.java b/mail/src/main/java/org/spongycastle/mail/smime/examples/ReadLargeCompressedMail.java new file mode 100644 index 00000000..795d0497 --- /dev/null +++ b/mail/src/main/java/org/spongycastle/mail/smime/examples/ReadLargeCompressedMail.java @@ -0,0 +1,38 @@ +package org.bouncycastle.mail.smime.examples; + +import java.util.Properties; + +import javax.mail.Session; +import javax.mail.internet.MimeBodyPart; +import javax.mail.internet.MimeMessage; + +import org.bouncycastle.cms.jcajce.ZlibExpanderProvider; +import org.bouncycastle.mail.smime.SMIMECompressedParser; +import org.bouncycastle.mail.smime.SMIMEUtil; +import org.bouncycastle.mail.smime.util.SharedFileInputStream; + +/** + * a simple example that reads an oversize compressed email and writes data contained + * in the compressed part into a file. + */ +public class ReadLargeCompressedMail +{ + public static void main( + String args[]) + throws Exception + { + // + // Get a Session object with the default properties. + // + Properties props = System.getProperties(); + + Session session = Session.getDefaultInstance(props, null); + + MimeMessage msg = new MimeMessage(session, new SharedFileInputStream("compressed.message")); + + SMIMECompressedParser m = new SMIMECompressedParser(msg); + MimeBodyPart res = SMIMEUtil.toMimeBodyPart(m.getContent(new ZlibExpanderProvider())); + + ExampleUtils.dumpContent(res, args[0]); + } +} diff --git a/mail/src/main/java/org/spongycastle/mail/smime/examples/ReadLargeEncryptedMail.java b/mail/src/main/java/org/spongycastle/mail/smime/examples/ReadLargeEncryptedMail.java new file mode 100644 index 00000000..8389b443 --- /dev/null +++ b/mail/src/main/java/org/spongycastle/mail/smime/examples/ReadLargeEncryptedMail.java @@ -0,0 +1,71 @@ +package org.bouncycastle.mail.smime.examples; + +import java.security.KeyStore; +import java.security.PrivateKey; +import java.security.cert.X509Certificate; +import java.util.Properties; + +import javax.mail.Session; +import javax.mail.internet.MimeBodyPart; +import javax.mail.internet.MimeMessage; + +import org.bouncycastle.cms.RecipientId; +import org.bouncycastle.cms.RecipientInformation; +import org.bouncycastle.cms.RecipientInformationStore; +import org.bouncycastle.cms.jcajce.JceKeyTransEnvelopedRecipient; +import org.bouncycastle.cms.jcajce.JceKeyTransRecipientId; +import org.bouncycastle.mail.smime.SMIMEEnvelopedParser; +import org.bouncycastle.mail.smime.SMIMEUtil; +import org.bouncycastle.mail.smime.util.SharedFileInputStream; + +/** + * a simple example that reads an encrypted email using the large file model. + * <p> + * The key store can be created using the class in + * org.bouncycastle.jce.examples.PKCS12Example - the program expects only one + * key to be present. + */ +public class ReadLargeEncryptedMail +{ + public static void main( + String args[]) + throws Exception + { + if (args.length != 3) + { + System.err.println("usage: ReadLargeEncryptedMail pkcs12Keystore password outputFile"); + System.exit(0); + } + + // + // Open the key store + // + KeyStore ks = KeyStore.getInstance("PKCS12", "BC"); + String keyAlias = ExampleUtils.findKeyAlias(ks, args[0], args[1].toCharArray()); + + // + // find the certificate for the private key and generate a + // suitable recipient identifier. + // + X509Certificate cert = (X509Certificate)ks.getCertificate(keyAlias); + RecipientId recId = new JceKeyTransRecipientId(cert); + + // + // Get a Session object with the default properties. + // + Properties props = System.getProperties(); + + Session session = Session.getDefaultInstance(props, null); + + MimeMessage msg = new MimeMessage(session, new SharedFileInputStream("encrypted.message")); + + SMIMEEnvelopedParser m = new SMIMEEnvelopedParser(msg); + + RecipientInformationStore recipients = m.getRecipientInfos(); + RecipientInformation recipient = recipients.get(recId); + + MimeBodyPart res = SMIMEUtil.toMimeBodyPart(recipient.getContentStream(new JceKeyTransEnvelopedRecipient((PrivateKey)ks.getKey(keyAlias, null)).setProvider("BC"))); + + ExampleUtils.dumpContent(res, args[2]); + } +} diff --git a/mail/src/main/java/org/spongycastle/mail/smime/examples/ReadLargeSignedMail.java b/mail/src/main/java/org/spongycastle/mail/smime/examples/ReadLargeSignedMail.java new file mode 100644 index 00000000..91074337 --- /dev/null +++ b/mail/src/main/java/org/spongycastle/mail/smime/examples/ReadLargeSignedMail.java @@ -0,0 +1,125 @@ +package org.bouncycastle.mail.smime.examples; + +import java.security.cert.X509Certificate; +import java.util.Collection; +import java.util.Iterator; +import java.util.Properties; + +import javax.mail.Session; +import javax.mail.internet.MimeMessage; +import javax.mail.internet.MimeMultipart; + +import org.bouncycastle.cert.X509CertificateHolder; +import org.bouncycastle.cert.jcajce.JcaX509CertificateConverter; +import org.bouncycastle.cms.SignerInformation; +import org.bouncycastle.cms.SignerInformationStore; +import org.bouncycastle.cms.jcajce.JcaSimpleSignerInfoVerifierBuilder; +import org.bouncycastle.jce.provider.BouncyCastleProvider; +import org.bouncycastle.mail.smime.SMIMESignedParser; +import org.bouncycastle.mail.smime.util.SharedFileInputStream; +import org.bouncycastle.operator.jcajce.JcaDigestCalculatorProviderBuilder; +import org.bouncycastle.util.Store; + +/** + * a simple example that reads a basic SMIME signed mail file. + */ +public class ReadLargeSignedMail +{ + private static final String BC = BouncyCastleProvider.PROVIDER_NAME; + + /** + * verify the signature (assuming the cert is contained in the message) + */ + private static void verify( + SMIMESignedParser s) + throws Exception + { + // + // extract the information to verify the signatures. + // + + // + // certificates and crls passed in the signature - this must happen before + // s.getSignerInfos() + // + Store certs = s.getCertificates(); + + // + // SignerInfo blocks which contain the signatures + // + SignerInformationStore signers = s.getSignerInfos(); + + Collection c = signers.getSigners(); + Iterator it = c.iterator(); + + // + // check each signer + // + while (it.hasNext()) + { + SignerInformation signer = (SignerInformation)it.next(); + Collection certCollection = certs.getMatches(signer.getSID()); + + Iterator certIt = certCollection.iterator(); + X509Certificate cert = new JcaX509CertificateConverter().setProvider(BC).getCertificate((X509CertificateHolder)certIt.next()); + + + // + // verify that the sig is correct and that it was generated + // when the certificate was current + // + if (signer.verify(new JcaSimpleSignerInfoVerifierBuilder().setProvider(BC).build(cert))) + { + System.out.println("signature verified"); + } + else + { + System.out.println("signature failed!"); + } + } + } + + public static void main( + String[] args) + throws Exception + { + // + // Get a Session object with the default properties. + // + Properties props = System.getProperties(); + + Session session = Session.getDefaultInstance(props, null); + + MimeMessage msg = new MimeMessage(session, new SharedFileInputStream("signed.message")); + + // + // make sure this was a multipart/signed message - there should be + // two parts as we have one part for the content that was signed and + // one part for the actual signature. + // + if (msg.isMimeType("multipart/signed")) + { + SMIMESignedParser s = new SMIMESignedParser(new JcaDigestCalculatorProviderBuilder().build(), + (MimeMultipart)msg.getContent()); + + System.out.println("Status:"); + + verify(s); + } + else if (msg.isMimeType("application/pkcs7-mime")) + { + // + // in this case the content is wrapped in the signature block. + // + SMIMESignedParser s = new SMIMESignedParser(new JcaDigestCalculatorProviderBuilder().build(), msg); + + System.out.println("Status:"); + + verify(s); + } + else + { + System.err.println("Not a signed message!"); + } + } +} diff --git a/mail/src/main/java/org/spongycastle/mail/smime/examples/ReadSignedMail.java b/mail/src/main/java/org/spongycastle/mail/smime/examples/ReadSignedMail.java new file mode 100644 index 00000000..370106d9 --- /dev/null +++ b/mail/src/main/java/org/spongycastle/mail/smime/examples/ReadSignedMail.java @@ -0,0 +1,176 @@ +package org.bouncycastle.mail.smime.examples; + +import java.io.FileInputStream; +import java.security.cert.X509Certificate; +import java.util.Collection; +import java.util.Iterator; +import java.util.Properties; + +import javax.mail.BodyPart; +import javax.mail.Multipart; +import javax.mail.Session; +import javax.mail.internet.MimeBodyPart; +import javax.mail.internet.MimeMessage; +import javax.mail.internet.MimeMultipart; + +import org.bouncycastle.cert.X509CertificateHolder; +import org.bouncycastle.cert.jcajce.JcaX509CertificateConverter; +import org.bouncycastle.cms.SignerInformation; +import org.bouncycastle.cms.SignerInformationStore; +import org.bouncycastle.cms.jcajce.JcaSimpleSignerInfoVerifierBuilder; +import org.bouncycastle.jce.provider.BouncyCastleProvider; +import org.bouncycastle.mail.smime.SMIMESigned; +import org.bouncycastle.util.Store; + +/** + * a simple example that reads a basic SMIME signed mail file. + */ +public class ReadSignedMail +{ + private static final String BC = BouncyCastleProvider.PROVIDER_NAME; + + /** + * verify the signature (assuming the cert is contained in the message) + */ + private static void verify( + SMIMESigned s) + throws Exception + { + // + // extract the information to verify the signatures. + // + + // + // certificates and crls passed in the signature + // + Store certs = s.getCertificates(); + + // + // SignerInfo blocks which contain the signatures + // + SignerInformationStore signers = s.getSignerInfos(); + + Collection c = signers.getSigners(); + Iterator it = c.iterator(); + + // + // check each signer + // + while (it.hasNext()) + { + SignerInformation signer = (SignerInformation)it.next(); + Collection certCollection = certs.getMatches(signer.getSID()); + + Iterator certIt = certCollection.iterator(); + X509Certificate cert = new JcaX509CertificateConverter().setProvider(BC).getCertificate((X509CertificateHolder)certIt.next()); + + // + // verify that the sig is correct and that it was generated + // when the certificate was current + // + if (signer.verify(new JcaSimpleSignerInfoVerifierBuilder().setProvider(BC).build(cert))) + { + System.out.println("signature verified"); + } + else + { + System.out.println("signature failed!"); + } + } + } + + public static void main( + String[] args) + throws Exception + { + // + // Get a Session object with the default properties. + // + Properties props = System.getProperties(); + + Session session = Session.getDefaultInstance(props, null); + + MimeMessage msg = new MimeMessage(session, new FileInputStream("signed.message")); + + // + // make sure this was a multipart/signed message - there should be + // two parts as we have one part for the content that was signed and + // one part for the actual signature. + // + if (msg.isMimeType("multipart/signed")) + { + SMIMESigned s = new SMIMESigned( + (MimeMultipart)msg.getContent()); + + // + // extract the content + // + MimeBodyPart content = s.getContent(); + + System.out.println("Content:"); + + Object cont = content.getContent(); + + if (cont instanceof String) + { + System.out.println((String)cont); + } + else if (cont instanceof Multipart) + { + Multipart mp = (Multipart)cont; + int count = mp.getCount(); + for (int i = 0; i < count; i++) + { + BodyPart m = mp.getBodyPart(i); + Object part = m.getContent(); + + System.out.println("Part " + i); + System.out.println("---------------------------"); + + if (part instanceof String) + { + System.out.println((String)part); + } + else + { + System.out.println("can't print..."); + } + } + } + + System.out.println("Status:"); + + verify(s); + } + else if (msg.isMimeType("application/pkcs7-mime") + || msg.isMimeType("application/x-pkcs7-mime")) + { + // + // in this case the content is wrapped in the signature block. + // + SMIMESigned s = new SMIMESigned(msg); + + // + // extract the content + // + MimeBodyPart content = s.getContent(); + + System.out.println("Content:"); + + Object cont = content.getContent(); + + if (cont instanceof String) + { + System.out.println((String)cont); + } + + System.out.println("Status:"); + + verify(s); + } + else + { + System.err.println("Not a signed message!"); + } + } +} diff --git a/mail/src/main/java/org/spongycastle/mail/smime/examples/SendSignedAndEncryptedMail.java b/mail/src/main/java/org/spongycastle/mail/smime/examples/SendSignedAndEncryptedMail.java new file mode 100644 index 00000000..8861152e --- /dev/null +++ b/mail/src/main/java/org/spongycastle/mail/smime/examples/SendSignedAndEncryptedMail.java @@ -0,0 +1,192 @@ +package org.bouncycastle.mail.smime.examples; + +import java.io.ByteArrayInputStream; +import java.io.ByteArrayOutputStream; +import java.io.FileInputStream; +import java.security.KeyStore; +import java.security.PrivateKey; +import java.security.Security; +import java.security.cert.Certificate; +import java.security.cert.X509Certificate; +import java.util.ArrayList; +import java.util.Enumeration; +import java.util.List; +import java.util.Properties; + +import javax.activation.CommandMap; +import javax.activation.MailcapCommandMap; +import javax.mail.Message; +import javax.mail.Session; +import javax.mail.Transport; +import javax.mail.internet.InternetAddress; +import javax.mail.internet.MimeBodyPart; +import javax.mail.internet.MimeMessage; +import javax.mail.internet.MimeMultipart; + +import org.bouncycastle.asn1.ASN1EncodableVector; +import org.bouncycastle.asn1.cms.AttributeTable; +import org.bouncycastle.asn1.cms.IssuerAndSerialNumber; +import org.bouncycastle.asn1.smime.SMIMECapabilitiesAttribute; +import org.bouncycastle.asn1.smime.SMIMECapability; +import org.bouncycastle.asn1.smime.SMIMECapabilityVector; +import org.bouncycastle.asn1.smime.SMIMEEncryptionKeyPreferenceAttribute; +import org.bouncycastle.asn1.x500.X500Name; +import org.bouncycastle.cert.jcajce.JcaCertStore; +import org.bouncycastle.cms.CMSAlgorithm; +import org.bouncycastle.cms.jcajce.JcaSimpleSignerInfoGeneratorBuilder; +import org.bouncycastle.cms.jcajce.JceCMSContentEncryptorBuilder; +import org.bouncycastle.cms.jcajce.JceKeyTransRecipientInfoGenerator; +import org.bouncycastle.jce.provider.BouncyCastleProvider; +import org.bouncycastle.mail.smime.SMIMEEnvelopedGenerator; +import org.bouncycastle.mail.smime.SMIMEException; +import org.bouncycastle.mail.smime.SMIMESignedGenerator; +import org.bouncycastle.util.Store; +import org.bouncycastle.util.Strings; + +/** + * Example that sends a signed and encrypted mail message. + */ +public class SendSignedAndEncryptedMail +{ + public static void main(String args[]) + { + if (args.length != 5) + { + System.err + .println("usage: SendSignedAndEncryptedMail <pkcs12Keystore> <password> <keyalias> <smtp server> <email address>"); + System.exit(0); + } + + try + { + MailcapCommandMap mailcap = (MailcapCommandMap)CommandMap + .getDefaultCommandMap(); + + mailcap + .addMailcap("application/pkcs7-signature;; x-java-content-handler=org.bouncycastle.mail.smime.handlers.pkcs7_signature"); + mailcap + .addMailcap("application/pkcs7-mime;; x-java-content-handler=org.bouncycastle.mail.smime.handlers.pkcs7_mime"); + mailcap + .addMailcap("application/x-pkcs7-signature;; x-java-content-handler=org.bouncycastle.mail.smime.handlers.x_pkcs7_signature"); + mailcap + .addMailcap("application/x-pkcs7-mime;; x-java-content-handler=org.bouncycastle.mail.smime.handlers.x_pkcs7_mime"); + mailcap + .addMailcap("multipart/signed;; x-java-content-handler=org.bouncycastle.mail.smime.handlers.multipart_signed"); + + CommandMap.setDefaultCommandMap(mailcap); + + /* Add BC */ + Security.addProvider(new BouncyCastleProvider()); + + /* Open the keystore */ + KeyStore keystore = KeyStore.getInstance("PKCS12", "BC"); + keystore.load(new FileInputStream(args[0]), args[1].toCharArray()); + Certificate[] chain = keystore.getCertificateChain(args[2]); + + /* Get the private key to sign the message with */ + PrivateKey privateKey = (PrivateKey)keystore.getKey(args[2], + args[1].toCharArray()); + if (privateKey == null) + { + throw new Exception("cannot find private key for alias: " + + args[2]); + } + + /* Create the message to sign and encrypt */ + Properties props = System.getProperties(); + props.put("mail.smtp.host", args[3]); + Session session = Session.getDefaultInstance(props, null); + + MimeMessage body = new MimeMessage(session); + body.setFrom(new InternetAddress(args[4])); + body.setRecipient(Message.RecipientType.TO, new InternetAddress( + args[4])); + body.setSubject("example encrypted message"); + body.setContent("example encrypted message", "text/plain"); + body.saveChanges(); + + /* Create the SMIMESignedGenerator */ + SMIMECapabilityVector capabilities = new SMIMECapabilityVector(); + capabilities.addCapability(SMIMECapability.dES_EDE3_CBC); + capabilities.addCapability(SMIMECapability.rC2_CBC, 128); + capabilities.addCapability(SMIMECapability.dES_CBC); + + ASN1EncodableVector attributes = new ASN1EncodableVector(); + attributes.add(new SMIMEEncryptionKeyPreferenceAttribute( + new IssuerAndSerialNumber( + new X500Name(((X509Certificate)chain[0]) + .getIssuerDN().getName()), + ((X509Certificate)chain[0]).getSerialNumber()))); + attributes.add(new SMIMECapabilitiesAttribute(capabilities)); + + SMIMESignedGenerator signer = new SMIMESignedGenerator(); + signer.addSignerInfoGenerator(new JcaSimpleSignerInfoGeneratorBuilder().setProvider("BC").setSignedAttributeGenerator(new AttributeTable(attributes)).build("DSA".equals(privateKey.getAlgorithm()) ? "SHA1withDSA" : "MD5withRSA", privateKey, (X509Certificate)chain[0])); + + + /* Add the list of certs to the generator */ + List certList = new ArrayList(); + certList.add(chain[0]); + Store certs = new JcaCertStore(certList); + signer.addCertificates(certs); + + /* Sign the message */ + MimeMultipart mm = signer.generate(body); + MimeMessage signedMessage = new MimeMessage(session); + + /* Set all original MIME headers in the signed message */ + Enumeration headers = body.getAllHeaderLines(); + while (headers.hasMoreElements()) + { + signedMessage.addHeaderLine((String)headers.nextElement()); + } + + /* Set the content of the signed message */ + signedMessage.setContent(mm); + signedMessage.saveChanges(); + + /* Create the encrypter */ + SMIMEEnvelopedGenerator encrypter = new SMIMEEnvelopedGenerator(); + encrypter.addRecipientInfoGenerator(new JceKeyTransRecipientInfoGenerator((X509Certificate)chain[0]).setProvider("BC")); + + /* Encrypt the message */ + MimeBodyPart encryptedPart = encrypter.generate(signedMessage, + new JceCMSContentEncryptorBuilder(CMSAlgorithm.RC2_CBC).setProvider("BC").build()); + + /* + * Create a new MimeMessage that contains the encrypted and signed + * content + */ + ByteArrayOutputStream out = new ByteArrayOutputStream(); + encryptedPart.writeTo(out); + + MimeMessage encryptedMessage = new MimeMessage(session, + new ByteArrayInputStream(out.toByteArray())); + + /* Set all original MIME headers in the encrypted message */ + headers = body.getAllHeaderLines(); + while (headers.hasMoreElements()) + { + String headerLine = (String)headers.nextElement(); + /* + * Make sure not to override any content-* headers from the + * original message + */ + if (!Strings.toLowerCase(headerLine).startsWith("content-")) + { + encryptedMessage.addHeaderLine(headerLine); + } + } + + Transport.send(encryptedMessage); + } + catch (SMIMEException ex) + { + ex.getUnderlyingException().printStackTrace(System.err); + ex.printStackTrace(System.err); + } + catch (Exception ex) + { + ex.printStackTrace(System.err); + } + } +} diff --git a/mail/src/main/java/org/spongycastle/mail/smime/examples/ValidateSignedMail.java b/mail/src/main/java/org/spongycastle/mail/smime/examples/ValidateSignedMail.java new file mode 100644 index 00000000..31961f1e --- /dev/null +++ b/mail/src/main/java/org/spongycastle/mail/smime/examples/ValidateSignedMail.java @@ -0,0 +1,352 @@ +package org.bouncycastle.mail.smime.examples; + +import java.io.FileInputStream; +import java.io.InputStream; +import java.security.KeyPairGenerator; +import java.security.KeyStore; +import java.security.PublicKey; +import java.security.SecureRandom; +import java.security.Security; +import java.security.cert.CertStore; +import java.security.cert.CertificateFactory; +import java.security.cert.CollectionCertStoreParameters; +import java.security.cert.PKIXParameters; +import java.security.cert.TrustAnchor; +import java.security.cert.X509CRL; +import java.security.cert.X509Certificate; +import java.util.ArrayList; +import java.util.HashSet; +import java.util.Iterator; +import java.util.List; +import java.util.Locale; +import java.util.Properties; +import java.util.Set; + +import javax.mail.Session; +import javax.mail.internet.MimeMessage; +import javax.security.auth.x500.X500Principal; + +import org.bouncycastle.asn1.ASN1Encodable; +import org.bouncycastle.asn1.ASN1Encoding; +import org.bouncycastle.asn1.x509.X509Extension; +import org.bouncycastle.cms.SignerInformation; +import org.bouncycastle.i18n.ErrorBundle; +import org.bouncycastle.jce.provider.BouncyCastleProvider; +import org.bouncycastle.mail.smime.validator.SignedMailValidator; +import org.bouncycastle.x509.PKIXCertPathReviewer; +import org.bouncycastle.x509.extension.X509ExtensionUtil; + +/** + * An Example that reads a signed mail and validates its signature. Also + * validating the certificate path from the signers key to a trusted entity + */ +public class ValidateSignedMail +{ + + /* + * Use trusted certificates from $JAVA_HOME/lib/security/cacerts as + * trustanchors + */ + public static final boolean useCaCerts = false; + + public static void main(String[] args) throws Exception + { + + Security.addProvider(new BouncyCastleProvider()); + + // + // Get a Session object with the default properties. + // + Properties props = System.getProperties(); + + Session session = Session.getDefaultInstance(props, null); + + // read message + MimeMessage msg = new MimeMessage(session, new FileInputStream( + "signed.message")); + + // create PKIXparameters + PKIXParameters param; + + if (useCaCerts) + { + KeyStore caCerts = KeyStore.getInstance("JKS"); + String javaHome = System.getProperty("java.home"); + caCerts.load( + new FileInputStream(javaHome + "/lib/security/cacerts"), + "changeit".toCharArray()); + + param = new PKIXParameters(caCerts); + } + else + { + // load trustanchors from files (here we only load one) + Set trustanchors = new HashSet(); + TrustAnchor trust = getTrustAnchor("trustanchor"); + + // create a dummy trustanchor if we can not find any trustanchor. so + // we can still try to validate the message + if (trust == null) + { + System.out + .println("no trustanchor file found, using a dummy trustanchor"); + trust = getDummyTrustAnchor(); + } + trustanchors.add(trust); + + param = new PKIXParameters(trustanchors); + } + + // load one ore more crls from files (here we only load one crl) + List crls = new ArrayList(); + X509CRL crl = loadCRL("crl.file"); + if (crl != null) + { + crls.add(crl); + } + CertStore certStore = CertStore.getInstance("Collection", + new CollectionCertStoreParameters(crls), "BC"); + + // add crls and enable revocation checking + param.addCertStore(certStore); + param.setRevocationEnabled(true); + + // or disable revocation checking + // param.setRevocationEnabled(false); + + verifySignedMail(msg, param); + } + + public static final int TITLE = 0; + public static final int TEXT = 1; + public static final int SUMMARY = 2; + public static final int DETAIL = 3; + + static int dbgLvl = DETAIL; + + private static final String RESOURCE_NAME = "org.bouncycastle.mail.smime.validator.SignedMailValidatorMessages"; + + public static void verifySignedMail(MimeMessage msg, PKIXParameters param) + throws Exception + { + // set locale for the output + Locale loc = Locale.ENGLISH; + // Locale loc = Locale.GERMAN; + + // validate signatures + SignedMailValidator validator = new SignedMailValidator(msg, param); + + // iterate over all signatures and print results + Iterator it = validator.getSignerInformationStore().getSigners() + .iterator(); + while (it.hasNext()) + { + SignerInformation signer = (SignerInformation) it.next(); + SignedMailValidator.ValidationResult result = validator + .getValidationResult(signer); + if (result.isValidSignature()) + { + ErrorBundle errMsg = new ErrorBundle(RESOURCE_NAME, + "SignedMailValidator.sigValid"); + System.out.println(errMsg.getText(loc)); + } + else + { + ErrorBundle errMsg = new ErrorBundle(RESOURCE_NAME, + "SignedMailValidator.sigInvalid"); + System.out.println(errMsg.getText(loc)); + // print errors + System.out.println("Errors:"); + Iterator errorsIt = result.getErrors().iterator(); + while (errorsIt.hasNext()) + { + ErrorBundle errorMsg = (ErrorBundle) errorsIt.next(); + if (dbgLvl == DETAIL) + { + System.out.println("\t\t" + errorMsg.getDetail(loc)); + } + else + { + System.out.println("\t\t" + errorMsg.getText(loc)); + } + } + } + if (!result.getNotifications().isEmpty()) + { + System.out.println("Notifications:"); + Iterator notIt = result.getNotifications().iterator(); + while (notIt.hasNext()) + { + ErrorBundle notMsg = (ErrorBundle) notIt.next(); + if (dbgLvl == DETAIL) + { + System.out.println("\t\t" + notMsg.getDetail(loc)); + } + else + { + System.out.println("\t\t" + notMsg.getText(loc)); + } + } + } + PKIXCertPathReviewer review = result.getCertPathReview(); + if (review != null) + { + if (review.isValidCertPath()) + { + System.out.println("Certificate path valid"); + } + else + { + System.out.println("Certificate path invalid"); + } + + System.out.println("\nCertificate path validation results:"); + // global errors + System.out.println("Errors:"); + Iterator errorsIt = review.getErrors(-1).iterator(); + while (errorsIt.hasNext()) + { + ErrorBundle errorMsg = (ErrorBundle) errorsIt.next(); + if (dbgLvl == DETAIL) + { + System.out.println("\t\t" + errorMsg.getDetail(loc)); + } + else + { + System.out.println("\t\t" + errorMsg.getText(loc)); + } + } + + System.out.println("Notifications:"); + Iterator notificationsIt = review.getNotifications(-1) + .iterator(); + while (notificationsIt.hasNext()) + { + ErrorBundle noteMsg = (ErrorBundle) notificationsIt.next(); + System.out.println("\t" + noteMsg.getText(loc)); + } + + // per certificate errors and notifications + Iterator certIt = review.getCertPath().getCertificates() + .iterator(); + int i = 0; + while (certIt.hasNext()) + { + X509Certificate cert = (X509Certificate) certIt.next(); + System.out.println("\nCertificate " + i + "\n========"); + System.out.println("Issuer: " + + cert.getIssuerDN().getName()); + System.out.println("Subject: " + + cert.getSubjectDN().getName()); + + // errors + System.out.println("\tErrors:"); + errorsIt = review.getErrors(i).iterator(); + while (errorsIt.hasNext()) + { + ErrorBundle errorMsg = (ErrorBundle) errorsIt.next(); + if (dbgLvl == DETAIL) + { + System.out + .println("\t\t" + errorMsg.getDetail(loc)); + } + else + { + System.out.println("\t\t" + errorMsg.getText(loc)); + } + } + + // notifications + System.out.println("\tNotifications:"); + notificationsIt = review.getNotifications(i).iterator(); + while (notificationsIt.hasNext()) + { + ErrorBundle noteMsg = (ErrorBundle) notificationsIt + .next(); + if (dbgLvl == DETAIL) + { + System.out.println("\t\t" + noteMsg.getDetail(loc)); + } + else + { + System.out.println("\t\t" + noteMsg.getText(loc)); + } + } + + i++; + } + } + } + + } + + protected static TrustAnchor getTrustAnchor(String trustcert) + throws Exception + { + X509Certificate cert = loadCert(trustcert); + if (cert != null) + { + byte[] ncBytes = cert + .getExtensionValue(X509Extension.nameConstraints.getId()); + + if (ncBytes != null) + { + ASN1Encodable extValue = X509ExtensionUtil + .fromExtensionValue(ncBytes); + return new TrustAnchor(cert, extValue.toASN1Primitive().getEncoded(ASN1Encoding.DER)); + } + return new TrustAnchor(cert, null); + } + return null; + } + + protected static X509Certificate loadCert(String certfile) + { + X509Certificate cert = null; + try + { + InputStream in = new FileInputStream(certfile); + + CertificateFactory cf = CertificateFactory.getInstance("X.509", + "BC"); + cert = (X509Certificate) cf.generateCertificate(in); + } + catch (Exception e) + { + System.out.println("certfile \"" + certfile + + "\" not found - classpath is " + + System.getProperty("java.class.path")); + } + return cert; + } + + protected static X509CRL loadCRL(String crlfile) + { + X509CRL crl = null; + try + { + InputStream in = new FileInputStream(crlfile); + + CertificateFactory cf = CertificateFactory.getInstance("X.509", + "BC"); + crl = (X509CRL) cf.generateCRL(in); + } + catch (Exception e) + { + System.out.println("crlfile \"" + crlfile + + "\" not found - classpath is " + + System.getProperty("java.class.path")); + } + return crl; + } + + private static TrustAnchor getDummyTrustAnchor() throws Exception + { + X500Principal principal = new X500Principal("CN=Dummy Trust Anchor"); + KeyPairGenerator kpg = KeyPairGenerator.getInstance("RSA", "BC"); + kpg.initialize(1024, new SecureRandom()); + PublicKey trustPubKey = kpg.generateKeyPair().getPublic(); + return new TrustAnchor(principal, trustPubKey, null); + } + +} |