A pure-managed .NET library for reading, writing, converting, signing, encrypting, and verifying Outlook/Exchange mail file formats — MSG, PST, EML, iCalendar, vCard, and MBOX — with a first-class, source-level RFC implementation layer (MIME, S/MIME, DKIM, CMS, ASN.1, X.509, DNS, SMTP, POP3, IMAP).
DRIT.Mail is built around the MAPI object model: a "message" is a MapiMessage regardless of whether it lives in a .msg file or a .pst file. MSG and PST are storage backends for the same object model, and cross-format conversion is a one-liner.
- What is DRIT.Mail?
- Features
- Supported File Formats
- Platform Independence
- Get Started
- Architecture
- The RFC Implementation Layer
- Message Security
- Testing & Maturity
- Repository Contents
- Building
- Contributing
- License
DRIT.Mail is a pure-managed .NET mail file-format and message-security library. It does not depend on Microsoft Outlook, on System.Security.Cryptography.Pkcs, or on any third-party NuGet package. The entire stack — OLE Compound File engine, PST NDB/LTP layer, MIME parser, S/MIME crypto, DKIM, DNS client, and the SMTP/POP3/IMAP clients — is implemented in source.
Key positioning:
- MAPI object model is the core abstraction. A message is a
MapiMessagewith full property-level access (everyPidTag*property tag, named properties, multi-value properties, RTF body via MS-OXRTFCP compression). - Deep format fidelity. Full PST NDB (Node Database) and LTP (List/Table/Property) layer; full MS-CFB (OLE Compound File) engine in-house; named property mapping; embedded messages; RTF body compression.
- First-class RFC implementations. Hand-written MIME (RFC 5322/2045–2049), iCalendar (RFC 5545), vCard (RFC 6350/2426), S/MIME (RFC 5751), DKIM (RFC 6376), CMS (RFC 5652), ASN.1 DER, X.509, DNS client (RFC 1035), SMTP (RFC 5321/2920/3207/4954/1652/6531), POP3 (RFC 1939), IMAP (RFC 9051/3501), and MBOX (RFC 4155).
- Cross-format conversion. The headline feature:
message.Save("output.msg", MailMessageFormat.Msg)regardless of whether the message was loaded from PST, MSG, or EML. - Protocol clients. Full
SmtpClient,Pop3Client, andImapClientwith STARTTLS/implicit TLS, SASL authentication, OAuth 2.0 (XOAUTH2), folder management, message flags, server-side search, IDLE push notifications, and DKIM integration.
- Read and write Outlook MSG (OLE Compound File, [MS-OXMSG]) and PST ([MS-PST], ANSI/Unicode/Unicode-4k) files with full MAPI property access.
- Read and write internet email EML (RFC 5322/MIME), MBOX (RFC 4155,
mboxrd, streaming), iCalendar (RFC 5545), and vCard (RFC 6350/2426, versions 2.1/3.0/4.0). - Cross-format conversion between MSG, PST, EML, iCal, and vCard via the
MailMessageConverterfacade — 14 conversion paths. - Full MAPI object model:
MapiMessage,MapiContact,MapiAppointment,MapiTask,MapiJournal,MapiNote,MapiMeeting(with Request/Response/Cancellation/Forward subtypes),MapiRss,MapiReminder. - MAPI property system: every property tag, named properties (GUID-identified and string-named), multi-value properties, RTF body (MS-OXRTFCP compressed, decompressed on read).
- Attachments: file, inline (Content-ID), and embedded messages (
AttachMethod == 5), with nested MSG support. - TNEF (
winmail.dat) parsing. - SMTP client (RFC 5321) with STARTTLS, implicit TLS, SASL, XOAUTH2, pipelining, mass sending, and DKIM integration.
- POP3 client (RFC 1939) with USER/PASS, APOP, SASL, STLS, implicit TLS, and UIDL.
- IMAP client (RFC 9051/3501) with LOGIN + SASL, XOAUTH2, SELECT/EXAMINE/CLOSE, LIST/LSUB/CREATE/DELETE/RENAME, FETCH, STORE, COPY/MOVE, EXPUNGE, SEARCH + UID SEARCH, IDLE (RFC 2177), STATUS, and MOVE (RFC 6851).
- S/MIME (RFC 5751): sign (detached/clear-signed and opaque), verify, encrypt (AES-CBC + RSA key transport), and decrypt — with a self-contained CMS, ASN.1 DER, and X.509 implementation.
- DKIM (RFC 6376): sign and verify (with a built-in DNS client for public-key lookup), simple/relaxed canonicalization, RSA-SHA1/SHA256.
- DNS client (RFC 1035): resolves TXT records for DKIM key lookup, with UDP transport and TCP fallback.
- PST search folders (SUD — Search Update Descriptors) and client-side content search across PST and MSG.
- File structure validation for PST, MSG, and OLE Compound files.
- Message comparison (property-level diffing) for round-trip verification.
- Zero external dependencies. No NuGet packages, no native libraries, no Outlook installation required.
| Format | Read | Write | Specification |
|---|---|---|---|
| MSG (Outlook Item) | ✅ | ✅ | [MS-OXMSG] — OLE Compound File |
| PST (Personal Folders) | ✅ | ✅ | [MS-PST] — ANSI / Unicode / Unicode-4k |
| EML (Internet Message) | ✅ | ✅ | RFC 5322 / RFC 2045–2049 (MIME) |
| MBOX (Mailbox Archive) | ✅ | ✅ | RFC 4155 (mboxrd, streaming) |
| ICS (iCalendar) | ✅ | ✅ | RFC 5545 |
| VCF (vCard) | ✅ | ✅ | RFC 6350 / RFC 2426 (2.1 / 3.0 / 4.0) |
| HTML | — | ✅ | Rendered message body |
| OFT (Outlook Template) | ✅ | ✅ | Via MSG format |
TNEF (winmail.dat) |
✅ | — | Transport-Neutral Encapsulation Format |
DRIT.Mail is implemented in pure managed C# and targets net48 and netstandard2.0, so it runs on:
- .NET Framework 4.8+
- .NET Core / .NET 5+ (via netstandard2.0)
- Windows, Linux, and macOS
It has no external dependencies — no NuGet packages, no native libraries, and no Microsoft Outlook installation required.
The MailFile static class is the high-level entry point for loading, saving, sending, and receiving messages. Format is auto-detected from the file extension.
using DRIT.Mail;
// Auto-detect format from the extension and load.
IMailMessage message = MailFile.Load("input.eml");
// Convert to MSG — one line, regardless of source format.
message.Save("output.msg", MailMessageFormat.Msg);
// Convert to HTML.
message.Save("output.html", MailMessageFormat.Html);
// Access the underlying MAPI object model.
MapiMessage mapi = message.MapiMessage;
Console.WriteLine("Subject: {0}", mapi.Subject);
Console.WriteLine("From: {0}", mapi.SenderName);
foreach (MapiRecipient recipient in mapi.Recipients)
Console.WriteLine(" {0}: {1}", recipient.RecipientType, recipient.EmailAddress);
foreach (MapiAttachment attachment in mapi.Attachments)
Console.WriteLine(" Attachment: {0}", attachment.DisplayName);using DRIT.Mail;
using DRIT.Mail.Pst.PhysicalLayout;
// Open a PST file from disk.
PstFile pst = MailFile.OpenPst("archive.pst");
// Walk the folder hierarchy and export every message to MSG.
CompositeFolder root = pst.Folders.RootCompositeFolder;
foreach (CompositeFolder folder in root.GetDepthFirstFolderEnumerator())
{
Console.WriteLine("Folder: {0}", folder.Folder.DisplayName);
foreach (PstMapiMessage item in folder.GetMapiMessages())
{
string safeName = string.IsNullOrWhiteSpace(item.Subject)
? "untitled"
: string.Join("_", item.Subject.Split(Path.GetInvalidFileNameChars()));
MailFile.Save(item, $"{safeName}.msg", MailMessageFormat.Msg);
}
}
// Validate the file structure (header CRC, allocation map, block/page CRCs,
// node database integrity, table templates).
ValidationResults results = pst.Validate();
Console.WriteLine("PST valid: {0}", !results.HasErrors);using DRIT.Mail.Rfc.Mbox;
using DRIT.Mail.Rfc.Mime;
// Load all messages from an MBOX file.
MboxMessageCollection messages = MboxMessageCollection.Load("Collection.mbox");
Console.WriteLine("Loaded {0} message(s).", messages.Count);
foreach (MimeMessage message in messages)
Console.WriteLine(" {0}: {1}", message.From, message.Subject);
// Create a new message and append it.
MimeMessage newMessage = new MimeMessageBuilder()
.From(new MailboxAddress("sender@example.com"))
.To(new MailboxAddress("receiver@example.com"))
.Subject("Test email message with a text body")
.TextBody("This is a test message with a text body.")
.Build();
messages.Add(newMessage);
messages.Save("Modified Collection.mbox");using DRIT.Mail;
using DRIT.Mail.Rfc.Mime;
using DRIT.Mail.Rfc.Smtp;
using DRIT.Mail.Rfc.Smtp.Auth;
// Build a MIME message.
MimeMessage message = new MimeMessageBuilder()
.From(new MailboxAddress("sender@example.com"))
.To(new MailboxAddress("recipient@example.com"))
.Subject("Hello from DRIT.Mail")
.TextBody("This message was created and sent with DRIT.Mail.")
.Build();
// Configure and send.
var options = new SmtpClientOptions("smtp.example.com", 587)
{
Credentials = new SmtpCredentials("user", "password"),
SslMode = SmtpSslMode.Auto, // STARTTLS on 587, implicit TLS on 465
};
using (var smtp = new SmtpClient(options))
{
SmtpDeliveryResult result = smtp.Send(message);
Console.WriteLine("Delivered: {0}", result.IsSuccessful);
}using System.Security.Cryptography;
using DRIT.Mail.Rfc.Dkim;
using DRIT.Mail.Rfc.Smtp;
// Load the RSA private key (PEM) and configure the signer.
RSA privateKey = RSA.Create();
privateKey.ImportFromPem(File.ReadAllText("private.pem"));
var dkimSigner = new DkimSigner(new DkimSignerOptions
{
Domain = "example.com",
Selector = "default",
PrivateKey = privateKey,
HeaderCanonicalization = DkimCanonicalizationMode.Relaxed,
BodyCanonicalization = DkimCanonicalizationMode.Relaxed,
});
// Send a signed message — the SMTP client signs on the wire.
using (var smtp = new SmtpClient(options))
{
SmtpDeliveryResult result = smtp.Send(message, dkimSigner);
}using DRIT.Mail;
using DRIT.Mail.Rfc.Pop3;
using DRIT.Mail.Rfc.Sasl;
var options = new Pop3ClientOptions("pop.example.com", 995)
{
Credentials = new SaslCredentials("user", "password"),
SslMode = Pop3SslMode.Auto,
};
using (var pop3 = new Pop3Client(options))
{
pop3.Connect();
pop3.Authenticate();
foreach (Pop3MessageInfo info in pop3.ListMessages())
{
MimeMessage message = pop3.GetMessage(info.MessageNumber);
Console.WriteLine("{0}: {1}", info.MessageNumber, message.Subject);
}
}
// Or use the high-level facade, which returns MapiMessage objects.
using (var pop3 = new Pop3Client(options))
{
pop3.Connect();
pop3.Authenticate();
IReadOnlyList<MapiMessage> messages = MailFile.Receive(pop3);
}using DRIT.Mail.Rfc.Imap;
using DRIT.Mail.Rfc.Sasl;
var options = new ImapClientOptions("imap.example.com", 993)
{
Credentials = new SaslCredentials("user", "password"),
SslMode = ImapSslMode.Auto,
};
using (var imap = new ImapClient(options))
{
imap.Connect();
imap.Authenticate();
// Select the Inbox.
ImapMailbox inbox = imap.SelectFolder("INBOX");
Console.WriteLine("Total: {0}, Recent: {1}", inbox.Exists, inbox.Recent);
// Server-side search: unseen messages since 2026-01-01.
var criteria = ImapSearchCriteria.Create()
.Unseen()
.Since(new DateTimeOffset(2026, 1, 1, 0, 0, 0, TimeSpan.Zero));
IReadOnlyList<long> uids = imap.Search(criteria);
foreach (long uid in uids)
{
MimeMessage message = imap.FetchMessage(
ImapMessageSet.FromUids(new[] { uid }),
ImapFetchOptions.Body);
Console.WriteLine("[{0}] {1}", uid, message.Subject);
}
}using DRIT.Mail.Rfc.SMime;
using DRIT.Mail.Rfc.Crypto.X509;
// Load the signer certificate and private key.
var certInfo = X509CertificateReader.ReadFromPem(File.ReadAllText("signer.pem"));
var signerOptions = new SMimeSignerOptions
{
SignerCertificate = certInfo.Certificate,
PrivateKey = certInfo.RsaPrivateKey,
DigestAlgorithm = DigestAlgorithm.Sha256,
IncludeSigningTime = true,
};
// Sign (detached / clear-signed produces multipart/signed).
var signer = new SMimeSigner(signerOptions);
MimeMessage signed = signer.SignDetached(message);
// Verify.
var verifier = new SMimeVerifier();
SMimeVerificationResult result = verifier.Verify(signed);
Console.WriteLine("Valid: {0}", result.IsValid);
foreach (SMimeSignatureInfo sig in result.Signatures)
Console.WriteLine(" {0} ({1})", sig.Subject, sig.Status);DRIT.Mail/
├── MailFile.cs High-level facade: Load / Save / Send / Receive / OpenPst / OpenMbox
├── IMailMessage.cs Message interface (MapiMessage + SourceFormat + Save)
├── MailMessageFormat.cs Format enum (Msg, Pst, Eml, ICal, VCard, Mbox, Html)
├── Mapi/ MAPI object model (MapiMessage, MapiContact, MapiAppointment, ...)
│ ├── Property/ MAPI property system (tags, named properties, multi-value)
│ ├── Structures/ MAPI structures (OXCDATA, OXOCAL, OXOCNTC, OXOMSG)
│ └── Algorithms/ RTF compression (CLZF, MS-OXRTFCP)
├── Msg/ MSG storage backend (OLE Compound File, [MS-OXMSG])
│ └── Office/Compound/ In-house MS-CFB engine ([MS-CFB])
├── Pst/ PST storage backend ([MS-PST])
│ ├── PhysicalLayout/ NDB (Node Database) + header + allocation
│ ├── LTPLayer/ LTP (List/Table/Property) — PC, TC, heap-on-node
│ ├── Folders/ Folder hierarchy, contents tables, search folders
│ ├── Tnef/ TNEF (winmail.dat) parser
│ └── Validation/ Structural validation (CRC, allocation map, NDB integrity)
├── Conversion/ MailMessageConverter — cross-format conversion facade
├── Comparison/ Message comparison (property-level diffing)
├── Search/ Client-side content search (PST + MSG)
├── Validation/ File structure validation
└── Rfc/ First-class RFC implementation layer
├── Mime/ RFC 5322 / 2045–2049 (MimeMessage, MimeReader, MimeWriter)
├── ICal/ RFC 5545 (iCalendar — VEVENT, VTODO, VJOURNAL, VFREEBUSY, VALARM, RRULE)
├── VCard/ RFC 6350 / 2426 (vCard 2.1 / 3.0 / 4.0)
├── SMime/ RFC 5751 (S/MIME — sign, verify, encrypt, decrypt)
├── Dkim/ RFC 6376 (DKIM — sign and verify)
├── Crypto/ CMS (RFC 5652), ASN.1 DER, X.509, PEM, RSA key reading
├── Net/ DNS client (RFC 1035) + TCP transport
├── Smtp/ RFC 5321 / 2920 / 3207 / 4954 / 1652 / 6531
├── Pop3/ RFC 1939 / 1734 / 5034 / 2595
├── Imap/ RFC 9051 / 3501 / 2177 / 6851
├── Mbox/ RFC 4155 (mboxrd, streaming reader/writer)
├── Sasl/ SASL mechanism framework (PLAIN, LOGIN, CRAM-MD5, ...)
└── Shared/ Shared protocol primitives
The Rfc/ directory is a first-class, source-level implementation of the relevant internet standards — not a wrapper around platform or third-party libraries. This gives DRIT.Mail two properties that are unusual in the .NET email ecosystem:
- No
System.Security.Cryptography.Pkcsdependency. The S/MIME stack (sign, verify, encrypt, decrypt) is built on a self-contained CMS (RFC 5652), ASN.1 DER codec, and X.509 certificate parser. - DKIM verification, not just signing. The
DkimVerifierretrieves the public key from DNS via the built-inDnsClient, recomputes the body and header hashes, and verifies the RSA signature — returning per-signature results with full status reporting (Passed / Failed / Permerror / Temperror / Skipped).
| Capability | Implementation |
|---|---|
| S/MIME sign (detached / clear-signed) | SMimeSigner.SignDetached → multipart/signed |
| S/MIME sign (opaque) | SMimeSigner.SignOpaque → application/pkcs7-mime; smime-type=signed-data |
| S/MIME verify | SMimeVerifier.Verify → per-signature SMimeSignatureInfo |
| S/MIME encrypt | SMimeEncryptor.Encrypt → AES-128/192/256-CBC + RSA key transport |
| S/MIME decrypt | SMimeDecryptor.Decrypt |
| S/MIME detection | SMimeMessageDetector (DetachedSigned / OpaqueSigned / Encrypted / none) |
| DKIM sign | DkimSigner — RSA-SHA1/SHA256, simple/relaxed canonicalization, l=/t=/x=/i= tags |
| DKIM verify | DkimVerifier + DkimDnsKeyResolver — full status reporting |
| CMS (RFC 5652) | CmsReader / CmsWriter / CmsSignedDataBuilder / CmsEnvelopedDataBuilder |
| ASN.1 DER | AsnReader / AsnWriter / DerLength / OidEncoder (self-contained) |
| X.509 | X509CertificateReader / X509CertificateInfo / X509Name (self-contained) |
| DNS client | DnsClient — TXT records, UDP + TCP fallback, system nameserver auto-detection |
| PEM | PemEncoding / PemBlock / RsaKeyReader (PKCS#1) |
Digest algorithms: SHA-256, SHA-384, SHA-512. Content encryption: AES-128/192/256-CBC.
DRIT.Mail ships with a substantial test suite:
DRIT.Mail.UnitTest/— ~1314 passing unit tests covering MSG parsing/creation, PST NDB/LTP, MAPI property read/write, attachments, embedded messages, named properties, conversion, search, validation, and comparison. TheRfc/layer has dedicated suites for S/MIME (sign-then-verify round-trips, sign-then-encrypt-then-decrypt-then-verify, expired/revoked certificate handling), DKIM (simple/relaxed canonicalization, revoked/expired keys), iCalendar (RRULE evaluation with RDATE/EXDATE), vCard (2.1/3.0/4.0 round-trips), MIME, and DNS.
| Directory | Description |
|---|---|
DRIT.Mail/ |
The core library (MAPI model, MSG, PST, RFC layer, conversion, security). |
DRIT.Mail.UnitTest/ |
Unit tests (~1314 passing). |
DRIT.Mail.Examples.Mbox/ |
A runnable example: load, modify, and save an MBOX file. |
Open DRIT.Mail.slnx in Visual Studio 2022 (17.10+) or build from the command line:
dotnet build DRIT.Mail.slnx -c ReleaseThe solution targets net48 and netstandard2.0. The Release build generates XML API documentation.
To run the tests:
dotnet test DRIT.Mail.slnx -c ReleaseDRIT.Mail is licensed under the MIT License.