Skip to content

Repository files navigation

DRIT.Mail

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.

.NET License: MIT Tests


Table of Contents


What is DRIT.Mail?

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 MapiMessage with full property-level access (every PidTag* 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, and ImapClient with STARTTLS/implicit TLS, SASL authentication, OAuth 2.0 (XOAUTH2), folder management, message flags, server-side search, IDLE push notifications, and DKIM integration.

Features

  • 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 MailMessageConverter facade — 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.

Supported File Formats

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

Platform Independence

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.


Get Started

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.

Load and Convert a Message

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);

Read a PST File

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);

Read and Write MBOX

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");

Send a Message via SMTP

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);
}

Send with DKIM Signing

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);
}

Receive via POP3

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);
}

Receive and Search via IMAP

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);
    }
}

S/MIME Sign and Verify

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);

Architecture

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 Implementation Layer

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:

  1. No System.Security.Cryptography.Pkcs dependency. 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.
  2. DKIM verification, not just signing. The DkimVerifier retrieves the public key from DNS via the built-in DnsClient, recomputes the body and header hashes, and verifies the RSA signature — returning per-signature results with full status reporting (Passed / Failed / Permerror / Temperror / Skipped).

Message Security

Capability Implementation
S/MIME sign (detached / clear-signed) SMimeSigner.SignDetachedmultipart/signed
S/MIME sign (opaque) SMimeSigner.SignOpaqueapplication/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.


Testing & Maturity

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. The Rfc/ 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.

Repository Contents

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.

Building

Open DRIT.Mail.slnx in Visual Studio 2022 (17.10+) or build from the command line:

dotnet build DRIT.Mail.slnx -c Release

The solution targets net48 and netstandard2.0. The Release build generates XML API documentation.

To run the tests:

dotnet test DRIT.Mail.slnx -c Release

License

DRIT.Mail is licensed under the MIT License.

About

Pure-managed .NET library to read, write, and convert MSG, PST, EML, MBOX, iCalendar, and vCard — with a source-level RFC stack (MIME, S/MIME, DKIM, CMS, SMTP, POP3, IMAP). Zero external dependencies.

Topics

Resources

Code of conduct

Contributing

Security policy

Stars

0 stars

Watchers

0 watching

Forks

Releases

Packages

Contributors

Languages