[英]Using Java Mail to verify an email with signature from Outlook 2013
我發送一封帶有簽名的電子郵件到我的收件箱。 然后我使用Java Mail接收郵件並驗證簽名。 但總是有一條錯誤消息' message-digest屬性值與計算值不匹配 '。
org.bouncycastle.cms.CMSSignerDigestMismatchException: message-digest attribute value does not match calculated value
at org.bouncycastle.cms.SignerInformation.doVerify(SignerInformation.java:517)
at org.bouncycastle.cms.SignerInformation.verify(SignerInformation.java:601)
我用maven
<dependency> <groupId>javax.mail</groupId> <artifactId>mail</artifactId> <version>1.4.5</version> </dependency> <!-- Bouncy Castle Provider --> <dependency> <groupId>org.bouncycastle</groupId> <artifactId>bcprov-jdk15on</artifactId> <version>1.54</version> </dependency> <!-- BC Mail --> <dependency> <groupId>org.bouncycastle</groupId> <artifactId>bcmail-jdk15on</artifactId> <version>1.54</version> </dependency>
這條線我得到了一個例外:
verify = signer.verify(new JcaSimpleSignerInfoVerifierBuilder().setProvider("BC").build(certificate));
這是我的代碼:
public static void receiveSignedMail(Message msg) throws MessagingException {
try {
SMIMESigned signed = new SMIMESigned((MimeMultipart) msg.getContent());
if (isValid(signed)) {
System.out.println("verification succeeded");
} else {
System.out.println("verification failed");
}
signed = new SMIMESigned((MimeMultipart) msg.getContent());
MimeBodyPart content = signed.getContent();
System.out.println("Content: " + content.getContent());
} catch (Exception e) {
e.printStackTrace(System.out);
}
}
public static boolean isValid(CMSSignedData signedData) {
try {
SignerInformationStore signers = signedData.getSignerInfos();
Iterator<SignerInformation> it = signers.getSigners().iterator();
boolean verify = false;
while (it.hasNext()) {
SignerInformation signer = it.next();
org.bouncycastle.util.Store store = (org.bouncycastle.util.Store) signedData.getCertificates();
Collection certCollection = ((org.bouncycastle.util.Store) store).getMatches(signer.getSID());
Iterator certIt = certCollection.iterator();
X509CertificateHolder certHolder = (X509CertificateHolder) certIt.next();
X509Certificate certificate = new JcaX509CertificateConverter().setProvider("BC").getCertificate(certHolder);
verify = signer.verify(new JcaSimpleSignerInfoVerifierBuilder().setProvider("BC").build(certificate));
}
return verify;
} catch (Exception e) {
e.printStackTrace(System.out);
return false;
}
}
public static Folder getFolder(String folderName) {
try {
String host = "your mail server";
boolean auth = true;
/* You must use SSL for incoming mail, SSL ports are 993 for IMAP and 995 for POP3 */
/* To support SSL, using protocl 'pop3s' or 'imaps' */
String port = "993";
String receivingProtocol = "imaps";
String username = "your email address";
String password = "your email password";
Properties props = System.getProperties();
props.put("mail.smtp.host", host);
props.put("mail.smtp.port", port);
props.put("mail.smtp.auth", auth);
props.put("mail.store.protocol", receivingProtocol);
Session session = Session.getDefaultInstance(props, null);
Store store = session.getStore(receivingProtocol);
store.connect(host, username, password);
Folder folder = store.getFolder(folderName);
folder.open(Folder.READ_WRITE);
return folder;
} catch (Exception e) {
e.printStackTrace(System.out);
return null;
}
}
以及運行它的主要方法
public static void main(String args[]) {
Security.addProvider(new org.bouncycastle.jce.provider.BouncyCastleProvider());
try {
String inbox = "Your Inbox";
Folder folder = getFolder(inbox);
SearchTerm st = new OrTerm(new FromStringTerm("search condition"), new SubjectTerm("search condition"));
Message[] messages = folder.search(st);
int mailCounts = messages.length;
System.out.println("Found: " + mailCounts + " mails matched with the condition.");
if (messages.length == 0) {
System.out.println("No Message!");
}
for (int i = 0; i < messages.length; i++) {
System.out.println("the " + (i + 1) + " mail" + "------------------------------------------------");
MimeMessage mail = (MimeMessage) messages[i];
String out_from = ((InternetAddress) messages[i].getFrom()[0]).getAddress();
System.out.println("From:" + out_from);
System.out.println("Subject:" + messages[i].getSubject());
System.out.println("Type: " + mail.getContentType());
if (mail.isMimeType("multipart/signed")) {
System.out.println("a signed mail");
receiveSignedMail(mail);
}
}
folder.close(true);
} catch (Exception e) {
e.printStackTrace(System.out);
}
}
我和所有其他電子郵件客戶端(Thunderbird,Outlook等等)有同樣的問題,但我能夠解決它。 我認為這個問題是由javax.mail庫中的錯誤引起的。
我使用以下Maven依賴項:
<dependency>
<groupId>com.sun.mail</groupId>
<artifactId>javax.mail</artifactId>
<version>1.6.2</version>
</dependency>
<dependency>
<groupId>org.bouncycastle</groupId>
<artifactId>bcprov-jdk15on</artifactId>
<version>1.62</version>
</dependency>
<dependency>
<groupId>org.bouncycastle</groupId>
<artifactId>bcmail-jdk15on</artifactId>
<version>1.62</version>
</dependency>
解:
通過IMAP輪詢來自電子郵件服務器的電子郵件時,您將其作為Message對象數組接收:
Message[] messages = pollFolder.getMessages();
現在,您將迭代此數組並分別處理每個消息。 這意味着,您正在直接使用Message對象。
但是如果你想用Bouncy Castle驗證郵件的簽名,你需要使用MimeMessage對象(參見Bouncy Castle的官方示例代碼;代碼行93)
要將Message轉換為MimeMessage ,通常需要以這種方式轉換對象:
MimeMessage mimeMsg = (MimeMessage) message;
但由於某種原因,這無法正常工作,所以稍后我在使用Bouncy Castle驗證電子郵件簽名時遇到以下異常: org.bouncycastle.cms.CMSSignerDigestMismatchException: message-digest attribute value does not match calculated value
我作為一種解決方法所做的是,使用MimeMessage構造函數復制已經轉換的MimeMessage對象!
所以這解決了我的問題 :
MimeMessage mimeMsg = new MimeMessage((MimeMessage) message);
這是完整的工作代碼 :
Message message = // get Message from folder...
MimeMessage mimeMsg = new MimeMessage((MimeMessage) message);
boolean emailSignatureValid = verifySignature(mimeMsg);
// code from Bouncy Castle
public boolean verifySignature(MimeMessage signedMimeMessage) {
try {
if (signedMimeMessage.isMimeType("multipart/signed")) {
SMIMESignedParser s = new SMIMESignedParser(new JcaDigestCalculatorProviderBuilder().build(), (MimeMultipart) signedMimeMessage.getContent());
return verify(s);
} else if (signedMimeMessage.isMimeType("application/pkcs7-mime")) {
SMIMESignedParser s = new SMIMESignedParser(new JcaDigestCalculatorProviderBuilder().build(), signedMimeMessage);
return verify(s);
} else {
throw new IllegalArgumentException("This mimeMessage is not signed: " + EmailPrinter.printMessage(signedMimeMessage));
}
} catch (Exception e) {
log.error("Could not verify signature of mimeMessage", e);
return false;
}
}
// code from Bouncy Castle
private static boolean verify(SMIMESignedParser s) throws Exception {
Store certs = s.getCertificates();
SignerInformationStore signers = s.getSignerInfos();
Collection c = signers.getSigners();
Iterator it = c.iterator();
while (it.hasNext()) {
SignerInformation signer = (SignerInformation) it.next();
Collection certCollection = certs.getMatches(signer.getSID());
Iterator certIt = certCollection.iterator();
X509Certificate cert = new JcaX509CertificateConverter().setProvider(BC).getCertificate((X509CertificateHolder) certIt.next());
try {
return signer.verify(new JcaSimpleSignerInfoVerifierBuilder().setProvider(BC).build(cert));
} catch (Exception e) {
log.error("Error occured during verification",e);
}
}
return false;
}
我知道這是一個奇怪的解決方法,但這是唯一的方法,我可以解決問題。 我希望你也覺得它很有用。
干杯!
BEEveloper
聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.