trasherdk / nodemailer

✉️ Send e-mails with Node.JS – easy as cake!
http://nodemailer.com/
Other
0 stars 0 forks source link

Snippet: Encrypted email using S/MIME #2

Open trasherdk opened 4 months ago

trasherdk commented 4 months ago

Using the library nodemailer-smime-plus to encrypt a message.

Another sugestion, for GPG signed email: mime-model

If you want to modify rfc822 emails, then you can use my other module, 
called MimeModel. See an example about signing an email with GPG here:
https://github.com/andris9/mime-model/blob/master/examples/openpgp-sign.js

In short, you can import a rfc822 email, modify the structure as needed,
and then export it as a rfc822 email.

Note:

Code below, for nodemailer-smime-plus allows to to send encrypted mail and I believe I can even decrypt in AppleMail client, however there's no content inside of mail.

class EncryptedMimeNode extends MimeNode {
  constructor(private certificate: X509Certificate, private message: MimeNode) {
    super();
  }

  async build(): Promise<Buffer> {
    const filename = 'smime.p7s';
    const rootNode = new MimeNode(
      `application/pkcs7-signature; name="${filename}"; smime-type=enveloped-data;`,
    );

    const messageHeaders = this.message['_headers'] || [];
    for (const header of messageHeaders) {
      if (header.key.toLowerCase() === 'content-type') {
        continue;
      }
      rootNode.setHeader(header.key, header.value);
    }
    rootNode.setHeader(
      'Content-Disposition',
      `attachment; filename="${filename}"`,
    );
    rootNode.setHeader('Content-Description', 'S/MIME Encrypted Message');
    rootNode.setHeader('Content-Transfer-Encoding', 'base64');

    // build actual message
    const buf = await this.message.build();

    // Create a PKCS#7 signature
    const pem = this.certificate.toString();
    const certificate = forge.pki.certificateFromPem(pem);

    const p7 = forge.pkcs7.createEnvelopedData();
    p7.addRecipient(certificate);
    p7.content = forge.util.createBuffer(buf.toString('base64'));
    p7.encrypt();

    // Add encrypted content part
    const encryptedContentNode = rootNode.createChild(
      'application/octet-stream',
    );
    encryptedContentNode.setHeader('Content-Transfer-Encoding', 'base64');
    encryptedContentNode.setContent(Buffer.from(p7.content?.bytes()));

    // Add PKCS#7 signature part
    const signatureNode = rootNode.createChild('application/pkcs7-signature');
    signatureNode.setHeader('Content-Transfer-Encoding', 'base64');
    signatureNode.setContent(forge.asn1.toDer(p7.toAsn1()).getBytes());

    return rootNode.build();
  }
}

class EncryptedMailComposer extends MailComposer {
  constructor(mail: Mail.Options, private certificate: X509Certificate) {
    super(mail);
  }

  compile(): MimeNode {
    super.compile();
    if (this.message) {
      return new EncryptedMimeNode(this.certificate, this.message);
    }
    throw new Error('Unexpected error, message not compiled');
  }
}