bouncycastle mail api 实现加密和签名邮件
1, 下载 bouncycastle 的 bcmail 版本,注意:一定要和 bcprovider 的版本号一致。
2, 下载 webeasymail 作为自己在 xp 下的一个 mail 服务器,这样比较方便程序测试,因为下载除了 gmail 以为其他 mail 服务器的免费服务都不支持 smtp 服务了。
3, 熟悉 javamail 的使用方法,熟悉 bc 的 keystore api 等。
实现了简单的发送和接受加密和签名邮件的小程序,发送邮件 SendMail ,接受邮件的 ReceiveMail ,仅供参考,里面几个 load 需要相应的 keystore 的支持。
1,SendMail:
import java.io.FileInputStream; import java.security.KeyStore; import java.security.PrivateKey; import java.security.cert.X509Certificate; import java.security.cert.CertStore; import java.security.cert.Certificate; import java.security.cert.CollectionCertStoreParameters; import javax.security.auth.x500.X500PrivateCredential; import java.util.Arrays; import java.util.Iterator; import java.util.Collections; import java.util.Properties; import javax.mail.internet.MimeBodyPart; import javax.mail.internet.MimeMessage; import javax.mail.internet.MimeMultipart; import javax.mail.internet.InternetAddress; import javax.mail.Message; import javax.mail.Session; import javax.mail.Transport; import javax.mail.Address; import javax.mail.Authenticator; import org.bouncycastle.asn1.ASN1EncodableVector; import org.bouncycastle.asn1.cms.AttributeTable; import org.bouncycastle.asn1.smime.SMIMECapabilitiesAttribute; import org.bouncycastle.asn1.smime.SMIMECapability; import org.bouncycastle.asn1.smime.SMIMECapabilityVector; import org.bouncycastle.asn1.smime.SMIMEEncryptionKeyPreferenceAttribute; import org.bouncycastle.mail.smime.SMIMESigned; import org.bouncycastle.mail.smime.SMIMESignedGenerator; import org.bouncycastle.mail.smime.SMIMEUtil; import org.bouncycastle.mail.smime.SMIMEEnveloped; import org.bouncycastle.mail.smime.SMIMEEnvelopedGenerator; public class SendMail{ public static void main(String args[]){ try{ KeyStore credentials = KeyStore.getInstance("JKS"); credentials.load(new FileInputStream("kgn163.jks"),"kgn123".toCharArray()); PrivateKey key = (PrivateKey)credentials.getKey("kgn","kgn123".toCharArray()); Certificate[] chain = credentials.getCertificateChain("kgn"); CertStore certsAndCRLs = CertStore.getInstance("Collection", new CollectionCertStoreParameters(Arrays.asList(chain)), "BC"); X509Certificate cert = (X509Certificate)chain[0]; sendEnvelopedMail(cert); sendSignedMail(cert,key,certsAndCRLs); }catch (Exception e){ e.printStackTrace(System.out); } } public static Session getSendMailSession(){ try{ Properties props = System.getProperties(); props.put("mail.smtp.host", "127.0.0.1"); props.put("mail.smtp.auth", "true"); Authenticator myauth = new MyAuthenticator("[email protected]","123456"); Session session = Session.getDefaultInstance(props, myauth); return session; }catch (Exception e){ e.printStackTrace(System.out); return null; } } public static void sendEnvelopedMail(X509Certificate cert){ try{ MimeBodyPart dataPart = new MimeBodyPart(); dataPart.setText("Hello world!"); SMIMEEnvelopedGenerator gen = new SMIMEEnvelopedGenerator(); gen.addKeyTransRecipient(cert); MimeBodyPart envPart = gen.generate(dataPart, SMIMEEnvelopedGenerator.DES_EDE3_CBC, "BC"); Session session = getSendMailSession(); MimeMessage mail = createMimeMessage("example enveloped message", envPart.getContent(), envPart.getContentType(),session); Transport.send(mail); }catch (Exception e){ e.printStackTrace(System.out); } } public static void sendSignedMail(X509Certificate cert,PrivateKey key,CertStore certsAndCRLs){ try{ MimeBodyPart dataPart = new MimeBodyPart(); dataPart.setText("Hello world!"); MimeMultipart multiPart = createMultipartWithSignature(key, cert, certsAndCRLs, dataPart); Session session = getSendMailSession(); MimeMessage mail = createMimeMessage("example signed message", multiPart, multiPart.getContentType(),session); Transport.send(mail); }catch (Exception e){ e.printStackTrace(System.out); } } public static MimeMessage createMimeMessage(String subject, Object content, String contentType,Session session){ try{ Address fromUser = new InternetAddress("[email protected]"); Address toUser = new InternetAddress("[email protected]"); MimeMessage message = new MimeMessage(session); message.setFrom(fromUser); message.setRecipient(Message.RecipientType.TO, toUser); message.setSubject(subject); message.setContent(content, contentType); message.saveChanges(); return message; }catch(Exception e){ e.printStackTrace(System.out); return null; } } public static MimeMultipart createMultipartWithSignature(PrivateKey key, X509Certificate cert, CertStore certsAndCRLs, MimeBodyPart dataPart){ try{ 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))); SMIMESignedGenerator gen = new SMIMESignedGenerator(); gen.addSigner(key, cert, SMIMESignedGenerator.DIGEST_SHA1, new AttributeTable(signedAttrs), null); gen.addCertificatesAndCRLs(certsAndCRLs); return gen.generate(dataPart, "BC"); }catch (Exception e){ e.printStackTrace(System.out); return null; } } }
2,ReceiveMail:
import java.io.FileInputStream; import java.io.ByteArrayInputStream; import java.security.KeyStore; import java.security.PrivateKey; import java.security.cert.X509Certificate; import java.security.cert.PKIXBuilderParameters; import java.security.cert.PKIXCertPathBuilderResult; import java.security.cert.CertPathBuilder; import java.security.cert.CertStore; import java.security.cert.X509CertSelector; import java.security.cert.Certificate; import java.security.cert.TrustAnchor; import java.security.cert.CollectionCertStoreParameters; import java.util.Collections; import java.util.Properties; import java.util.Iterator; import java.util.Properties; import java.util.Arrays; import javax.mail.internet.MimeBodyPart; import javax.mail.internet.MimeMessage; import javax.mail.internet.MimeMultipart; import javax.mail.internet.InternetAddress; import javax.mail.Multipart; import javax.mail.Part; import javax.mail.Message; import javax.mail.Session; import javax.mail.Address; import javax.mail.Folder; import javax.mail.Store; import javax.mail.Flags; import org.bouncycastle.mail.smime.SMIMESigned; import org.bouncycastle.mail.smime.SMIMEUtil; import org.bouncycastle.mail.smime.SMIMEEnveloped; import org.bouncycastle.cms.RecipientId; import org.bouncycastle.cms.RecipientInformationStore; import org.bouncycastle.cms.RecipientInformation; import org.bouncycastle.cms.CMSSignedData; import org.bouncycastle.cms.SignerInformation; import org.bouncycastle.cms.SignerInformationStore; public class ReceiveMail{ public static void main(String args[]){ try{ KeyStore credentials = KeyStore.getInstance("JKS"); credentials.load(new FileInputStream("kgn163.jks"),"kgn123".toCharArray()); PrivateKey key = (PrivateKey)credentials.getKey("kgn","kgn123".toCharArray()); Certificate[] chain = credentials.getCertificateChain("kgn"); CertStore certsAndCRLs = CertStore.getInstance("Collection", new CollectionCertStoreParameters(Arrays.asList(chain)), "BC"); X509Certificate cert = (X509Certificate)chain[0]; X509Certificate rootCert = (X509Certificate)credentials.getCertificate("root"); Folder folder = getFolder("INBOX"); Message message[] = folder.getMessages(); for(int i=0;i<message.length;i++){ System.out.println("the "+(i+1)+" mail" +"------------------------------------------------"); MimeMessage mail = (MimeMessage)message[i]; String out_from = ((InternetAddress)message[i].getFrom()[0]).getAddress(); System.out.println("From:"+out_from); System.out.println("Subject:"+message[i].getSubject()); if (mail.isMimeType("multipart/signed")){ System.out.println("a signed mail"); receiveSignedMail(mail,rootCert); }else if(mail.isMimeType("application/pkcs7-mime")||mail.isMimeType("application/x-pkcs7-mime")){ System.out.println("a enveloped mail"); receiveEnveloped(mail,key,cert); }else { System.out.println("not a identified mail"); } message[i].setFlag(Flags.Flag.DELETED,true); } folder.close(true); }catch(Exception e){ e.printStackTrace(System.out); } } public static void receiveEnveloped(Message mail,PrivateKey key, X509Certificate cert){ try{ SMIMEEnveloped enveloped = new SMIMEEnveloped((MimeMessage)mail); RecipientId recId = new RecipientId(); recId.setSerialNumber(cert.getSerialNumber()); recId.setIssuer(cert.getIssuerX500Principal().getEncoded()); RecipientInformationStore recipients = enveloped.getRecipientInfos(); RecipientInformation recipient = recipients.get(recId); if (recipient != null){ MimeBodyPart recoveredPart = SMIMEUtil.toMimeBodyPart(recipient.getContent(key, "BC")); System.out.print("Content: "); System.out.println(recoveredPart.getContentType()); if(recoveredPart.isMimeType("multipart/alternative")){ Multipart mp = (Multipart) recoveredPart.getContent(); int index = 0; if (mp.getCount() > 1) index = 1; Part tmp = mp.getBodyPart(index); System.out.println(tmp.getContent()); }else { System.out.println(recoveredPart.getContent()); } }else{ System.out.println("decrypt error"); } }catch(Exception e){ e.printStackTrace(System.out); } } public static void receiveSignedMail(Message mail,X509Certificate rootCert){ try{ SMIMESigned signed = new SMIMESigned((MimeMultipart)mail.getContent()); if (isValid(signed, rootCert)){ System.out.println("verification succeeded"); }else{ System.out.println("verification failed"); } MimeBodyPart content = signed.getContent(); System.out.print("Content: "); System.out.println(content.getContent()); }catch(Exception e){ e.printStackTrace(System.out); } } public static Folder getFolder(String folderName){ try{ Properties props = System.getProperties(); props.put("mail.smtp.host", "127.0.0.1"); Session session = Session.getDefaultInstance(props,null); Store store = session.getStore("pop3"); store.connect("127.0.0.1","[email protected]","123456"); Folder folder = store.getFolder(folderName); folder.open(Folder.READ_WRITE); return folder; }catch(Exception e){ e.printStackTrace(System.out); return null; } } public static PKIXCertPathBuilderResult buildPath(X509Certificate rootCert, X509CertSelector endConstraints, CertStore certsAndCRLs){ try{ CertPathBuilder builder = CertPathBuilder.getInstance("PKIX", "BC"); PKIXBuilderParameters buildParams = new PKIXBuilderParameters(Collections.singleton(new TrustAnchor(rootCert, null)), endConstraints); buildParams.addCertStore(certsAndCRLs); buildParams.setRevocationEnabled(false); return (PKIXCertPathBuilderResult)builder.build(buildParams); }catch (Exception e){ e.printStackTrace(System.out); return null; } } static boolean[] getKeyUsageForSignature(){ boolean[] val = new boolean[9]; val[0] = true; //Digital Signature. val[1] = false; //Non Repudiation. val[2] = true; //Key Encipherment. val[3] = true; //Data Encipherment. val[4] = false; //Key Agreement. val[5] = false; //Certificate Sign. val[6] = false; //CRL Sign. val[7] = false; //Encipher Only. val[8] = false; //Decipher Only. return val; } public static boolean isValid(CMSSignedData signedData,X509Certificate rootCert){ try{ CertStore certsAndCRLs = signedData.getCertificatesAndCRLs("Collection", "BC"); SignerInformationStore signers = signedData.getSignerInfos(); Iterator it = signers.getSigners().iterator(); if (it.hasNext()){ SignerInformation signer = (SignerInformation)it.next(); X509CertSelector signerConstraints = signer.getSID(); signerConstraints.setKeyUsage(getKeyUsageForSignature()); System.out.println("Signed by "+signerConstraints.getIssuer()); PKIXCertPathBuilderResult result = buildPath(rootCert, signerConstraints, certsAndCRLs); return signer.verify(result.getPublicKey(), "BC"); } return false; }catch (Exception e){ e.printStackTrace(System.out); return false; } } }