一尘不染

使用BouncyCastle使用SMIME和X.509加密邮件

java

我正在尝试使用JavaMailLibrary和BouncyCastleLibrary 发送加密的邮件:

这是我编写的代码,或者我遵循了一个教程:

public class SendMail extends javax.mail.Authenticator {

private String _user;
private String _pass;

private String[] _to;
private String _from;

private String _port;
private String _sport;

private String _host;

private String _subject;
private String _body;

private boolean _auth;
private boolean _debuggable;

private Multipart _multipart;
SharedPreferences sharedPrefs;

InputStream privateKeyStoreInputStream;
InputStream publicCertificateInputStream;
InputStream publicKeystoreInputStream;

public static final String ksPassword = "mobile";
Certificate[] chain;
PrivateKey privateKey;
Certificate rcptCert;
CertificateFactory cf;

public SendMail() {

    _user = ""; // username
    _pass = ""; // password
    _from = ""; // email sent from
    _subject = ""; // email subject
    _body = ""; // email body

    _debuggable = false; // debug mode on or off - default off
    _auth = true; // smtp authentication - default on

    _multipart = new MimeMultipart();

    // There is something wrong with MailCap, javamail can not find a
    // handler for the multipart/mixed part, so this bit needs to be added.
    MailcapCommandMap mc = (MailcapCommandMap) CommandMap
            .getDefaultCommandMap();
    mc.addMailcap("application/pkcs7-signature;; x-java-content-handler=org.bouncycastle.mail.smime.handlers.pkcs7_signature");
    mc.addMailcap("application/pkcs7-mime;; x-java-content-handler=org.bouncycastle.mail.smime.handlers.pkcs7_mime");
    mc.addMailcap("application/x-pkcs7-signature;; x-java-content-handler=org.bouncycastle.mail.smime.handlers.x_pkcs7_signature");
    mc.addMailcap("application/x-pkcs7-mime;; x-java-content-handler=org.bouncycastle.mail.smime.handlers.x_pkcs7_mime");
    mc.addMailcap("multipart/signed;; x-java-content-handler=org.bouncycastle.mail.smime.handlers.multipart_signed");

    CommandMap.setDefaultCommandMap(mc);

    Security.addProvider(new BouncyCastleProvider());

}

public SendMail(Context c, InputStream privateKeyStoreInputStream,
        InputStream publicCertificateInputStream,
        InputStream publicKeystoreInputStream) {
    this();

    this.privateKeyStoreInputStream = privateKeyStoreInputStream;
    this.publicCertificateInputStream = publicCertificateInputStream;
    this.publicKeystoreInputStream = publicKeystoreInputStream;

    _host = "removed";
    _port = "25";
    _sport = "25";
    _user = "removed";
    _pass = "removed";

    try {
        KeyStore keystore = KeyStore.getInstance("PKCS12", "BC");
        keystore.load(privateKeyStoreInputStream, ksPassword.toCharArray());

        Enumeration e = keystore.aliases();
        String keyAlias = null;

        while (e.hasMoreElements() && (keyAlias == null)) {
            String alias = (String) e.nextElement();
            keyAlias = keystore.isKeyEntry(alias) ? alias : null;
        }

        if (keyAlias == null) {
            Log.e("KEY ALIAS: ", "NULL");
            return;
        }

        chain = keystore.getCertificateChain(keyAlias);

        /* Get the private key to sign the message */

        privateKey = (PrivateKey) keystore.getKey(keyAlias,
                ksPassword.toCharArray());

        if (privateKey == null) {
            Log.e("No Private key for: ", keyAlias);
        }

        /* Get the public key of reciepient */
        BufferedInputStream bis = new BufferedInputStream(
                publicCertificateInputStream);
        cf = CertificateFactory.getInstance("X.509");
        rcptCert = cf.generateCertificate(bis);

    } catch (KeyStoreException e) {
        Log.e("KeyStore Exception: ", e.getMessage());
    } catch (NoSuchProviderException e) {
        Log.e("NoSuchProvider Exception: ", e.getMessage());
    } catch (CertificateException ce) {
        Log.e("Certification Exception: ", ce.getMessage());
    } catch (NoSuchAlgorithmException ns) {
        Log.e("NoSuchAlgorithm Exception: ", ns.getMessage());
    } catch (IOException e) {
        Log.e("IO Exception: ", e.getMessage());
    } catch (UnrecoverableKeyException uke) {
        Log.e("UnrecoverableKeyException: ", uke.getMessage());
    }
}

public boolean send() throws Exception {
    Properties props = _setProperties();

    if (!_user.equals("") && !_pass.equals("") && _to.length > 0
            && !_from.equals("") && !_subject.equals("")
            && !_body.equals("")) {

        Session session = Session.getInstance(props,
                new GMailAuthenticator(_user, _pass));

        MimeMessage msg = new MimeMessage(session);

        msg.setFrom(new InternetAddress(_from));

        InternetAddress[] addressTo = new InternetAddress[_to.length];
        for (int i = 0; i < _to.length; i++) {
            addressTo[i] = new InternetAddress(_to[i]);
        }
        msg.setRecipients(MimeMessage.RecipientType.TO, addressTo);

        msg.setSubject(_subject);
        msg.setSentDate(new Date());

        // setup message body
        BodyPart messageBodyPart = new MimeBodyPart();
        messageBodyPart.setText(_body);
        _multipart.addBodyPart(messageBodyPart);

        // Put parts in message
        msg.setContent(_multipart);

        /* Create SMIMESignedGenerator */
        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
        // org.bouncycastle.asn1.cms.IssuerAndSerialNumber(new
        // X509Name(((X509Certificate)chain[0]).getIssuerDN().getName()),
        // ((X509Certificate)chain[0]).getSerialNumber())));
        attributes.add(new SMIMECapabilitiesAttribute(capabilities));

        SMIMESignedGenerator signer = new SMIMESignedGenerator();

        signer.addSigner(
                privateKey,
                (X509Certificate) chain[0],
                "DSA".equals(privateKey.getAlgorithm()) ? SMIMESignedGenerator.DIGEST_SHA1
                        : SMIMESignedGenerator.DIGEST_MD5,
                new AttributeTable(attributes), null);

        /* Add the list of certs to the generator */
        List certList = new ArrayList();
        certList.add(chain[0]);
        CertStore certs = CertStore.getInstance("Collection",
                new CollectionCertStoreParameters(certList), "BC");
        signer.addCertificatesAndCRLs(certs);

        /* Sign the message and copy all headers from original message */
        MimeMultipart multipart = signer.generate(msg, "BC");
        MimeMessage signedMessage = new MimeMessage(session);
        Enumeration headers = msg.getAllHeaderLines();

        while (headers.hasMoreElements()) {
            signedMessage.addHeaderLine((String) headers.nextElement());
        }

        signedMessage.setContent(_multipart);
        signedMessage.saveChanges();

        /* Create the encrypter and encrypt the message */
        SMIMEEnvelopedGenerator encrypter = new SMIMEEnvelopedGenerator();
        encrypter.addKeyTransRecipient((X509Certificate) chain[0]);
        encrypter.addKeyTransRecipient((X509Certificate) rcptCert);

        MimeBodyPart encryptedPart = encrypter.generate(signedMessage,
                SMIMEEnvelopedGenerator.RC2_CBC, 128, "BC");
        ByteArrayOutputStream out = new ByteArrayOutputStream();
        encryptedPart.writeTo(out);

        Session smtpSession = Session.getInstance(props, null);
        MimeMessage smtpMessage = new MimeMessage(smtpSession,
                new ByteArrayInputStream(out.toByteArray()));
        smtpMessage.saveChanges();

        Transport.send(smtpMessage);

        return true;
    } else {
        return false;
    }
}

public void addAttachment(String filename) throws Exception {
    BodyPart messageBodyPart = new MimeBodyPart();
    DataSource source = new FileDataSource(filename);
    messageBodyPart.setDataHandler(new DataHandler(source));
    messageBodyPart.setFileName(filename);

    _multipart.addBodyPart(messageBodyPart);
}

class GMailAuthenticator extends Authenticator {
    String user;
    String pw;

    public GMailAuthenticator(String username, String password) {
        super();
        this.user = username;
        this.pw = password;
    }

    public PasswordAuthentication getPasswordAuthentication() {
        return new PasswordAuthentication(user, pw);
    }
}

private Properties _setProperties() {
    Properties props = new Properties();

    props.put("mail.smtp.host", _host);
    props.put("mail.smtp.starttls.enable", "true");
    props.put("mail.smtp.connectiontimeout", "20000"); // timeout with mail
                                                        // 20 sec.

    if (_debuggable) {
        props.put("mail.debug", "true");
    }

    if (_auth) {
        props.put("mail.smtp.auth", "true");
    }

    props.put("mail.smtp.port", _port);
    props.put("mail.smtp.socketFactory.port", _sport);
    props.put("mail.smtp.socketFactory.fallback", "true");

    return props;
}

public String getBody() {
    return _body;
}

public void setBody(String _body) {
    this._body = _body;
}

public void setTo(String[] toArr) {
    this._to = toArr;
}

public void setFrom(String string) {
    this._from = string;
}

public void setSubject(String string) {
    this._subject = string;
}
}

当我调试应用程序时,唯一的应用程序在以下行崩溃:

SMIMESignedGenerator signer = new SMIMESignedGenerator();

这是我从达尔维克获得的唯一错误消息:

DexOpt: unable to optimize static field ref 0x0991 at 0x18 in Lorg/bouncycastle/mail/smime/SMIMESignedGenerator;.<clinit>

我所有的外部.jar文件都位于该文件夹下libs

有人知道为什么会这样吗?有没有人成功使用SMIME / X.509加密邮件?还是有更简单的方法来做到这一点?


阅读 249

收藏
2020-12-03

共1个答案

一尘不染

如果我了解您要实现的目标,不仅是对MIME消息进行加密,还需要对其进行签名和加密(并且必须按此顺序进行)

BouncyCastle提供的用于签名电子邮件的示例在此处
BouncyCastle提供的用于加密电子邮件的示例在此处

最后,签名BodyPart会得到一个Multipart,您必须将其包装在要加密的MimeBodyPart中,这会为您提供一个加密的MimeBodyPart,您将该MimeBodyPart“插入”在MimeMessage中(请参见加密示例)。

2020-12-03