1、下载最新的bc包,bcmail-jdk15on-149.jar、bcprov-jdk15on149.jar、bcpkix-jdk15on-149.jar,并导入工程。
2、编写SignedMail类。新建SignedMail类。
package com.suresec.simplemail;import java.math.BigInteger;import java.security.KeyPair;import java.security.KeyPairGenerator;import java.security.KeyStore;import java.security.NoSuchAlgorithmException;import java.security.NoSuchProviderException;import java.security.PrivateKey;import java.security.PublicKey;import java.security.SecureRandom;import java.security.Security;import java.security.cert.CertStore;import java.security.cert.Certificate;import java.security.cert.CollectionCertStoreParameters;import java.security.cert.X509Certificate;import java.util.Arrays;import java.util.Date;import java.util.Enumeration;import java.util.Vector;import javax.mail.internet.MimeBodyPart;import javax.mail.internet.MimeMultipart;import javax.security.auth.x500.X500Principal;import javax.security.auth.x500.X500PrivateCredential;import org.bouncycastle.asn1.ASN1EncodableVector;import org.bouncycastle.asn1.ASN1Set;import org.bouncycastle.asn1.DERObjectIdentifier;import org.bouncycastle.asn1.DEROctetString;import org.bouncycastle.asn1.DERSet;import org.bouncycastle.asn1.cms.AttributeTable;import org.bouncycastle.asn1.pkcs.Attribute;import org.bouncycastle.asn1.pkcs.PKCSObjectIdentifiers;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.x509.BasicConstraints;import org.bouncycastle.asn1.x509.GeneralName;import org.bouncycastle.asn1.x509.GeneralNames;import org.bouncycastle.asn1.x509.X509Extension;import org.bouncycastle.asn1.x509.X509Extensions;import org.bouncycastle.jce.PKCS10CertificationRequest;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.mail.smime.SMIMEUtil;import org.bouncycastle.x509.X509V1CertificateGenerator;import org.bouncycastle.x509.X509V3CertificateGenerator;import org.bouncycastle.x509.extension.AuthorityKeyIdentifierStructure;import org.bouncycastle.x509.extension.SubjectKeyIdentifierStructure;public class SignedMail {public static String ROOT_ALIAS = "root";public static String INTERMEDIATE_ALIAS = "intermediate";public static String END_ENTITY_ALIAS = "end";private static final int VALIDITY_PERIOD = 365 * 24 * 60 * 60 * 1000; // one yearpublic static char[] KEY_PASSWD = "suresec".toCharArray();public static KeyStore credentials;public static PrivateKey key;public static Certificate[] chain;public static X509Certificate cert;public static CertStore certsAndCRLs;public boolean init() throws Exception{Security.addProvider(new BouncyCastleProvider());credentials = createCredentials();key = (PrivateKey)credentials.getKey(END_ENTITY_ALIAS, KEY_PASSWD);chain = credentials.getCertificateChain(END_ENTITY_ALIAS);certsAndCRLs = CertStore.getInstance("Collection",new CollectionCertStoreParameters(Arrays.asList(chain)), "BC");cert = (X509Certificate)chain[0];return true;}/*** Create a KeyStore containing the a private credential with* certificate chain and a trust anchor.*/public static KeyStore createCredentials()throws Exception{KeyStore store = KeyStore.getInstance("JKS");store.load(null, null);X500PrivateCredential rootCredential = createRootCredential();X500PrivateCredential interCredential = createIntermediateCredential(rootCredential.getPrivateKey(), rootCredential.getCertificate());X500PrivateCredential endCredential = createEndEntityCredential(interCredential.getPrivateKey(), interCredential.getCertificate());store.setCertificateEntry(rootCredential.getAlias(), rootCredential.getCertificate());store.setKeyEntry(endCredential.getAlias(), endCredential.getPrivateKey(), KEY_PASSWD,new Certificate[] { endCredential.getCertificate(), interCredential.getCertificate(), rootCredential.getCertificate() });return store;}/*** Generate a X500PrivateCredential for the end entity.*/public static X500PrivateCredential createEndEntityCredential(PrivateKey caKey,X509Certificate caCert)throws Exception{KeyPair endPair = generateRSAKeyPair();X509Certificate endCert = generateEndEntityCert(endPair.getPublic(), caKey, caCert);return new X500PrivateCredential(endCert, endPair.getPrivate(), END_ENTITY_ALIAS);}/*** Generate a sample V3 certificate to use as an end entity certificate*/public static X509Certificate generateEndEntityCert(PublicKey entityKey, PrivateKey caKey, X509Certificate caCert)throws Exception{X509V3CertificateGenerator certGen = new X509V3CertificateGenerator();certGen.setSerialNumber(BigInteger.valueOf(1));certGen.setIssuerDN(caCert.getSubjectX500Principal());certGen.setNotBefore(new Date(System.currentTimeMillis()));certGen.setNotAfter(new Date(System.currentTimeMillis() + VALIDITY_PERIOD));certGen.setSubjectDN(new X500Principal("CN=Test End Certificate"));certGen.setPublicKey(entityKey);certGen.setSignatureAlgorithm("SHA1WithRSAEncryption");certGen.addExtension(X509Extensions.AuthorityKeyIdentifier, false, new AuthorityKeyIdentifierStructure(caCert));certGen.addExtension(X509Extensions.SubjectKeyIdentifier, false, new SubjectKeyIdentifierStructure(entityKey));certGen.addExtension(X509Extensions.BasicConstraints, true, new BasicConstraints(false));//certGen.addExtension(X509Extensions.KeyUsage, true, new KeyUsage(KeyUsage.digitalSignature | KeyUsage.keyEncipherment));return certGen.generateX509Certificate(caKey, "BC");}/*** Generate a X500PrivateCredential for the intermediate entity.*/public static X500PrivateCredential createIntermediateCredential(PrivateKey caKey,X509Certificate caCert)throws Exception{KeyPair interPair = generateRSAKeyPair();X509Certificate interCert = generateIntermediateCert(interPair, caKey, caCert);return new X500PrivateCredential(interCert, interPair.getPrivate(), INTERMEDIATE_ALIAS);}/*** Generate a sample V3 certificate to use as an intermediate CA certificate*/public static X509Certificate generateIntermediateCert(KeyPair pair, PrivateKey caKey, X509Certificate caCert)throws Exception{PKCS10CertificationRequest request = generateRequest(pair);// validate the certification requestif (!request.verify("BC")){System.out.println("request failed to verify!");System.exit(1);}// create the certificate using the information in the requestX509V3CertificateGenerator certGen = new X509V3CertificateGenerator();certGen.setSerialNumber(BigInteger.valueOf(System.currentTimeMillis()));certGen.setIssuerDN(caCert.getSubjectX500Principal());certGen.setNotBefore(new Date(System.currentTimeMillis()));certGen.setNotAfter(new Date(System.currentTimeMillis() + VALIDITY_PERIOD));certGen.setSubjectDN(new X500Principal(request.getCertificationRequestInfo().getSubject().getEncoded()));certGen.setPublicKey(request.getPublicKey("BC"));certGen.setSignatureAlgorithm("SHA1WithRSAEncryption");certGen.addExtension(X509Extensions.AuthorityKeyIdentifier, false, new AuthorityKeyIdentifierStructure(caCert));certGen.addExtension(X509Extensions.SubjectKeyIdentifier, false, new SubjectKeyIdentifierStructure(request.getPublicKey("BC")));certGen.addExtension(X509Extensions.BasicConstraints, true, new BasicConstraints(true));//certGen.addExtension(X509Extensions.KeyUsage, true, new KeyUsage(KeyUsage.keyCertSign | KeyUsage.cRLSign));//certGen.addExtension(X509Extensions.ExtendedKeyUsage, true, new ExtendedKeyUsage(KeyPurposeId.id_kp_serverAuth));// extract the extension request attributeASN1Set attributes = request.getCertificationRequestInfo().getAttributes();for (int i = 0; i != attributes.size(); i++){Attribute attr = Attribute.getInstance(attributes.getObjectAt(i));// process extension requestif (attr.getAttrType().equals(PKCSObjectIdentifiers.pkcs_9_at_extensionRequest)){X509Extensions extensions = X509Extensions.getInstance(attr.getAttrValues().getObjectAt(0));Enumeration e = extensions.oids();while (e.hasMoreElements()){DERObjectIdentifier oid = (DERObjectIdentifier)e.nextElement();X509Extension ext = extensions.getExtension(oid);certGen.addExtension(oid, ext.isCritical(), ext.getValue().getOctets());}}}X509Certificate issuedCert = certGen.generateX509Certificate(caKey, "BC");return issuedCert;}public static PKCS10CertificationRequest generateRequest(KeyPair pair)throws Exception{// create a SubjectAlternativeName extension valueGeneralNames subjectAltNames = new GeneralNames(new GeneralName(GeneralName.rfc822Name, "test@test.test"));// create the extensions object and add it as an attributeVector oids = new Vector();Vector values = new Vector();oids.add(X509Extensions.SubjectAlternativeName);values.add(new X509Extension(false, new DEROctetString(subjectAltNames)));X509Extensions extensions = new X509Extensions(oids, values);Attribute attribute = new Attribute(PKCSObjectIdentifiers.pkcs_9_at_extensionRequest,new DERSet(extensions));return new PKCS10CertificationRequest("SHA1withRSA",new X500Principal("CN=Test Certificate"),pair.getPublic(),new DERSet(attribute),pair.getPrivate());}/*** Generate a X500PrivateCredential for the root entity.*/public static X500PrivateCredential createRootCredential()throws Exception{KeyPair rootPair = generateRSAKeyPair();X509Certificate rootCert = generateRootCert(rootPair);return new X500PrivateCredential(rootCert, rootPair.getPrivate(), ROOT_ALIAS);}/*** Create a random 1024 bit RSA key pair*/public static KeyPair generateRSAKeyPair()throws Exception{KeyPairGenerator kpGen = KeyPairGenerator.getInstance("RSA", "BC");kpGen.initialize(1024, new SecureRandom());return kpGen.generateKeyPair();}/*** Generate a sample V1 certificate to use as a CA root certificate*/public static X509Certificate generateRootCert(KeyPair pair)throws Exception{X509V1CertificateGenerator certGen = new X509V1CertificateGenerator();certGen.setSerialNumber(BigInteger.valueOf(1));certGen.setIssuerDN(new X500Principal("CN=Test CA Certificate"));certGen.setNotBefore(new Date(System.currentTimeMillis()));certGen.setNotAfter(new Date(System.currentTimeMillis() + VALIDITY_PERIOD));certGen.setSubjectDN(new X500Principal("CN=Test CA Certificate"));certGen.setPublicKey(pair.getPublic());certGen.setSignatureAlgorithm("SHA1WithRSAEncryption");return certGen.generateX509Certificate(pair.getPrivate(), "BC");}public MimeMultipart createMultipartWithSignature(PrivateKey key,X509Certificate cert,CertStore certsAndCRLs,MimeBodyPart dataPart)throws Exception{// create some smime capabilities in case someone wants to respondASN1EncodableVector signedAttrs = new ASN1EncodableVector();SMIMECapabilityVector caps = new SMIMECapabilityVector();caps.addCapability(SMIMECapability.aES256_CBC);caps.addCapability(SMIMECapability.dES_EDE3_CBC);caps.addCapability(SMIMECapability.rC2_CBC, 128);signedAttrs.add(new SMIMECapabilitiesAttribute(caps));signedAttrs.add(new SMIMEEncryptionKeyPreferenceAttribute(SMIMEUtil.createIssuerAndSerialNumberFor(cert)));// set up the generatorSMIMESignedGenerator gen = new SMIMESignedGenerator();gen.addSigner(key, cert, SMIMESignedGenerator.DIGEST_SHA256, new AttributeTable(signedAttrs), null);gen.addCertificatesAndCRLs(certsAndCRLs);// create the signed messagereturn gen.generate(dataPart, "BC");}public MimeMultipart signMail(MimeBodyPart dataPart){try {return createMultipartWithSignature(this.key, this.cert, this.certsAndCRLs, dataPart);} catch (Exception e) {// TODO Auto-generated catch blocke.printStackTrace();}return null;}/*** 邮件加密* @param dataPart* @return*/public MimeBodyPart encryptMail(MimeBodyPart dataPart){SMIMEEnvelopedGenerator gen = new SMIMEEnvelopedGenerator();MimeBodyPart envPart;try {envPart = gen.generate(dataPart, SMIMEEnvelopedGenerator.DES_EDE3_CBC, "BC");return envPart;} catch (NoSuchAlgorithmException e) {// TODO Auto-generated catch blocke.printStackTrace();} catch (NoSuchProviderException e) {// TODO Auto-generated catch blocke.printStackTrace();} catch (SMIMEException e) {// TODO Auto-generated catch blocke.printStackTrace();}return null;}// public static MimeMessage createMimeMessage(// Object content,// String contentType,// MimeMessage message)// throws MessagingException// {// message.setContent(content, contentType);// message.saveChanges();//// return message;// }}
3、对SimpleMailSender类稍作修改,接上篇博文。
MimeBodyPart dataPart = new MimeBodyPart();dataPart.setText("Hello world!");//MimeMultipart multiPart = signMail.signMail(dataPart);//mailMessage = signMail.createMimeMessage(multiPart, multiPart.getContentType(), mailMessage);//mailMessage.setText(mailContent);// 发送邮件MimeBodyPart envPart = signMail.encryptMail(dataPart);mailMessage.setContent(envPart.getContent(), envPart.getContentType());Transport.send(mailMessage);
4、运行程序,可以看到数字签名文件。如下图所示: