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 year public 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 request if (!request.verify("BC")) { System.out.println("request failed to verify!"); System.exit(1); } // create the certificate using the information in the request X509V3CertificateGenerator 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 attribute ASN1Set attributes = request.getCertificationRequestInfo().getAttributes(); for (int i = 0; i != attributes.size(); i++) { Attribute attr = Attribute.getInstance(attributes.getObjectAt(i)); // process extension request if (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 value GeneralNames subjectAltNames = new GeneralNames( new GeneralName(GeneralName.rfc822Name, "[email protected]")); // create the extensions object and add it as an attribute Vector 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 respond ASN1EncodableVector 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 generator SMIMESignedGenerator gen = new SMIMESignedGenerator(); gen.addSigner(key, cert, SMIMESignedGenerator.DIGEST_SHA256, new AttributeTable(signedAttrs), null); gen.addCertificatesAndCRLs(certsAndCRLs); // create the signed message return 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 block e.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 block e.printStackTrace(); } catch (NoSuchProviderException e) { // TODO Auto-generated catch block e.printStackTrace(); } catch (SMIMEException e) { // TODO Auto-generated catch block e.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、运行程序,可以看到数字签名文件。如下图所示: