我有一个MailComposer,它构建一个 mime 消息并使用MailSigningService对正文部分(内容)进行数字签名。
签名在sign()和buildSignedGenerator()方法中实现。
收到邮件后,邮件客户端检测到签名但抱怨邮件可能被篡改。邮件客户端能够显示证书,它显示所有证书(包括 CA)。
因此,要么基于 Bouncycastle 的签名实现没有正确完成,要么消息本身没有按应有的方式构建。任何提示这里可能会出现什么问题?
MailComposer.java
private MimeMessage buildMimeMessage(com.jumio.jump.domain.Message message, String[] recipients, String subject, Session session) throws MessagingException, IOException {
MimeMultipart parts = buildContentParts(message);
parts = (message.getSendOnBehalfOfDomain() != null) ? signContentParts(message, parts) : parts;
return buildMimeMessage(message, recipients, subject, session, parts);
}
private MimeMessage buildMimeMessage(com.jumio.jump.domain.Message message, String[] recipients, String subject, Session session, MimeMultipart parts) throws MessagingException {
MimeMessage mimeMessage = new MimeMessage(session);
mimeMessage.setSubject(subject, "UTF-8");
mimeMessage.setHeader("Content-ParamType", "text/html; charset=UTF-8");
mimeMessage.setSentDate(new Date());
mimeMessage.setFrom(buildFromEmailAddress(message));
for (String recipient : recipients) {
if (recipient == null) {
continue;
}
InternetAddress addressTo = new InternetAddress(recipient);
mimeMessage.addRecipient(Message.RecipientType.TO, addressTo);
}
mimeMessage.setContent(parts);
return mimeMessage;
}
private MimeMultipart signContentParts(com.jumio.jump.domain.Message message, MimeMultipart contentParts)
throws MessagingException {
MimeBodyPart body = new MimeBodyPart();
body.setContent(contentParts);
MimeMultipart result;
try {
result = mailSigningService.sign(body, signerCertificate, certificateChain,
privateKey);
} catch (Exception e) {
logger.error(String.format("Error signing message $s, sending unsigned", message), e);
return contentParts;
}
return result;
}
MailSigningService.java
@Override
public MimeMultipart sign(MimeBodyPart mimeBodyPart, X509Certificate signerCertificate,
Certificate[] caCertificateChain, PrivateKey privateKey)
throws SMIMEException, OperatorCreationException, CertificateEncodingException, NoSuchProviderException,
NoSuchAlgorithmException, InvalidAlgorithmParameterException {
Validate.notNull(signerCertificate, "Valid certificate required, check keystore and/or keystore " +
"configuration");
Validate.notNull(privateKey, "Valid private key required, check keystore and/or keystore configuration");
SMIMESignedGenerator generator = buildSignedGenerator(signerCertificate, caCertificateChain, privateKey);
return generator.generate(mimeBodyPart);
}
private SMIMESignedGenerator buildSignedGenerator(X509Certificate signerCertificate, Certificate[] caCertificateChain, PrivateKey privateKey)
throws OperatorCreationException, CertificateEncodingException, NoSuchProviderException,
NoSuchAlgorithmException, InvalidAlgorithmParameterException {
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 X509Name(
signerCertificate.getIssuerDN().getName()), signerCertificate.getSerialNumber())));
attributes.add(new SMIMECapabilitiesAttribute(capabilities));
SMIMESignedGenerator generator = new SMIMESignedGenerator();
generator.addSignerInfoGenerator(new JcaSimpleSignerInfoGeneratorBuilder().setProvider(SECURITY_PROVIDER_NAME)
.setSignedAttributeGenerator(new AttributeTable(attributes)).build(SIGNER_ALGORITHM, privateKey,
signerCertificate));
List<Certificate> certificates=new ArrayList<Certificate>();
if (caCertificateChain !=null && caCertificateChain.length>0) {
certificates.addAll(Arrays.asList(caCertificateChain));
} else {
certificates.add(signerCertificate);
}
Store certificateStore = new JcaCertStore(certificates);
generator.addCertificates(certificateStore);
return generator;
}