#ifndef __DKIMSigner_h__ #define __DKIMSigner_h__ #include #include #include namespace mail { /// /// DKIMSigner produces a DKIM-Signature header (RFC 6376) for an /// outbound message, using the relaxed/relaxed canonicalization and /// rsa-sha256, the combination essentially every real deployment /// uses (relaxed tolerates the whitespace/line-ending massaging a /// message can pick up in transit; simple does not, and breaks /// easily as a result). /// /// One DKIMSigner is built for one (domain, selector, private key) /// triple and can sign any number of messages with it - construction /// loads and parses the private key once; sign() itself does no I/O. /// class DKIMSigner { public: DKIMSigner(std::string domain, std::string selector, std::string privateKeyPath); ~DKIMSigner(); /// /// message is the complete outbound message exactly as it will be /// sent in the DATA phase - CRLF-terminated header lines, a blank /// line, then the body. Returns a complete "DKIM-Signature: /// ...\r\n" line, ready to prepend to `message` as its new first /// header. /// std::string sign(const std::string &message); private: struct HeaderField { std::string name; // original case, as it appeared std::string value; // unfolded (continuation lines joined), not yet whitespace-collapsed }; std::vector parseHeaders(const std::string &message); std::string bodyOf(const std::string &message); std::string collapseWSP(const std::string &s); std::string canonicalizeHeaderField(const std::string &name, const std::string &value); std::string canonicalizeBody(const std::string &body); std::string base64(const std::string &raw); std::string sha256Base64(const std::string &data); std::string rsaSha256SignBase64(const std::string &data); std::string domain; std::string selector; EVP_PKEY *privateKey; }; } #endif