การลงนามอีเมลด้วย DKIM เพื่อการตรวจสอบและการส่งต่อที่ดี

DKIM (DomainKeys Identified Mail, RFC 6376) ทำให้เจ้าของโดเมนสามารถแนบลายเซ็นเข้ารหัสกับอีเมลที่ส่งออก เซิร์ฟเวอร์รับจะดึงกุญแจสาธารณะที่ตรงกับ DNS, คำนวณแฮชใหม่, และยืนยันสองสิ่งพร้อมกัน: เนื้อหาข้อความไม่ถูกแก้ไขระหว่างการส่ง, และข้อความจริง ๆ มาจากผู้ส่งที่ได้รับอนุญาตจากโดเมน หากไม่มี DKIM ผู้ให้บริการสมัยใหม่ (Gmail, Microsoft 365, Yahoo) จะทำเครื่องหมายเมลเป็นสแปม, ทิ้งอย่างเงียบ, หรือปฏิเสธการส่ง — DKIM ไม่ได้เป็นตัวเลือกอีกต่อไปสำหรับเมลการผลิต

คู่มือฉบับนี้เป็นการเดินผ่านขั้นตอนเต็มของ Aspose.Emailเนมสเปซ DKIM ของ ’ สำหรับ .NET ทุกบล็อกโค้ดในบทความนี้มาจากตัวอย่างที่ทำงานได้ใน DKIMExamples โครงการและได้รับการตรวจสอบครบวงจรกับไลบรารี

ข้อกำหนดเบื้องต้น

ประเภท DKIM อยู่ใน Aspose.Email.DKIM เนมสเปซและจัดส่งเฉพาะกับบิลด์ .NET Framework 4.0/4.5 ของไลบรารี พวกมันถูกตัดออกโดยเจตนาจากแพคเกจ .NET Standard 2.0 และ .NET 6/8 เพื่อทำตามตัวอย่างด้านล่าง โปรเจกต์ของคุณต้องอ้างอิง assembly Aspose.Email ที่สร้างสำหรับ net45 (เป้าหมาย net48 ในส่วนที่ใช้งาน .csproj).

<Project Sdk="Microsoft.NET.Sdk">
  <PropertyGroup>
    <OutputType>Exe</OutputType>
    <TargetFramework>net48</TargetFramework>
  </PropertyGroup>
  <ItemGroup>
    <Reference Include="Aspose.Email">
      <HintPath>..\path\to\net45\Aspose.Email.dll</HintPath>
    </Reference>
  </ItemGroup>
</Project>

คุณยังต้องการคู่คีย์ RSA คีย์ส่วนตัวจะถูกเก็บในแอปพลิเคชันของคุณและใช้ในการเซ็น; คีย์สาธารณะจะถูกเผยแพร่เป็นบันทึก DNS TXT เพื่อให้ผู้รับตรวจสอบ คีย์ 2048 บิตเป็นมาตรฐานในวันนี้:

openssl genrsa -out sample-private-key.pem 2048

ของ Aspose PemReader รับทั้ง PKCS#1 (-----BEGIN RSA PRIVATE KEY-----) และ PKCS#8 (-----BEGIN PRIVATE KEY-----) ไฟล์ที่จัดรูปแบบ

| คลาส | บทบาท | |——-|——| | DKIMSignatureInfo | อธิบายลายเซ็น: selector, domain, อัลกอริทึมแฮช, การทำ canonicalization, และหัวเรื่องที่ครอบคลุม | | PemReader | โหลด RSA private key จากไฟล์หรือสตรีมที่เป็น PEM ลงใน RSACryptoServiceProvider. | | MailMessage.DKIMSign(rsa, info) | เมธอดส่วนขยาย/อินสแตนซ์ที่คืนสำเนาข้อความที่เซ็นพร้อมกับ DKIM-Signature หัวเรื่องถูกเติมเต็ม | | DkimVerifier | ทำการตรวจสอบด้วย DNS ของข้อความที่เซ็น — ไฟล์, สตรีม, ซิงค์ หรืออะซิงค์ |

กระบวนการเต็มคือ: โหลดคีย์ → อธิบายลายเซ็น → สร้างข้อความ → เซ็น → (เลือก) ตรวจสอบ.

ตัวอย่างการเซ็นแบบขั้นต่ำ

นี่คือโค้ดที่สั้นที่สุดที่สร้างข้อความที่เซ็น DKIM ได้อย่างถูกต้อง:

// Load private key from PEM file
var rsa = PemReader.GetPrivateKey(PrivateKeyPath);

// Create DKIM signature information
var signInfo = new DKIMSignatureInfo("dkim", "example.com")
{
    // Default: Relaxed/Relaxed canonicalization
    // Default: RSASha1 hash algorithm
};

// Add headers to sign
signInfo.Headers.Add("From");
signInfo.Headers.Add("To");
signInfo.Headers.Add("Subject");

// Create a test email
var mailMessage = new MailMessage(
    "sender@example.com",
    "recipient@example.com",
    "Test Subject",
    "This is a test email body signed with DKIM.")
{
    From = new MailAddress("sender@example.com", "Sender Name")
};

// Sign the message
var signedMsg = mailMessage.DKIMSign(rsa, signInfo);

// Save the signed message
string outputPath = Path.Combine(DataDir, "basic-signed.eml");
signedMsg.Save(outputPath);

Console.WriteLine($"DKIM-Signature header: {signedMsg.Headers["DKIM-Signature"]?.Substring(0, 50)}...");

สองอาร์กิวเมนต์ของ DKIMSignatureInfo"dkim" และ "example.com" — คือ selector และ domain. ทั้งสองบอกผู้รับให้ค้นหากุญแจสาธารณะของคุณที่: บันทึก DNS TXT dkim._domainkey.example.com. เลือกชื่อ selector สั้น ๆ (ส่วนมากคือ default, mail, หรือวันที่เช่น 2026jan); ตัวเลือกทำให้คุณหมุนคีย์โดยไม่ต้องทิ้งคีย์เก่า

รายละเอียดสำคัญ: DKIMSign คืนค่า ใหม่ MailMessage instance พร้อมกับ DKIM-Signature หัวเรื่องถูกเพิ่มแล้ว ข้อความต้นฉบับไม่ได้เปลี่ยนแปลง บันทึกหรือส่งอ็อบเจกต์ที่ส่งคืน ไม่ใช้อินพุต

การเลือกหัวเรื่องที่จะเซ็น

หัวเรื่องที่ระบุใน signInfo.Headers เป็นหัวข้อเดียวที่ค่าถูกผูกกับลายเซ็น. สิ่งที่ไม่ได้ระบุสามารถถูกเปลี่ยนแปลงโดยรีเลย์โดยไม่ทำลายลายเซ็น, ซึ่งบางครั้งอาจต้องการ (เช่น Received: หัวข้อที่เพิ่มในขั้นตอนต่อม) และบางครั้งอาจเป็นอันตราย (เช่น การทิ้ง From ที่ไม่มีลายเซ็นทำให้ลายเซ็นไร้ความหมาย).

กฎโดยรวม: ควรลงนามเสมอ From, To, Subject, Date. From โดยเฉพาะจำเป็นสำหรับการจัดแนว DMARC.

var signInfo = new DKIMSignatureInfo("dkim2", "example.com")
{
    Headers =
    {
        "From",
        "To",
        "Subject",
        "Date",
        "MIME-Version"
    }
};

// Create email with additional headers
var mailMessage = new MailMessage(
    "sender@example.com",
    "recipient@example.com",
    "Custom Headers Test",
    "This message includes custom headers in the DKIM signature.")
{
    From = new MailAddress("sender@example.com", "Sender"),
    Date = DateTime.UtcNow
};

// Ensure headers listed in DKIM signature are present on the message
mailMessage.Headers.Add("MIME-Version", "1.0");
mailMessage.Headers.Add("X-Custom-Header", "CustomValue");

// Sign with custom headers
var signedMsg = mailMessage.DKIMSign(rsa, signInfo);

string outputPath = Path.Combine(DataDir, "custom-headers-signed.eml");
signedMsg.Save(outputPath);

หากคุณระบุ Date แต่ไม่ควรตั้งค่า mailMessage.Date, DKIMSign ขว้าง The following headers to be signed do not exist: Date. เพิ่มหัวข้อทั้งหมดก่อน, จากนั้นลงนาม.

อัลกอริธึมแฮช — RSA‑SHA1 vs RSA‑SHA256

DKIM รองรับอัลกอริธึมการลงนามสองแบบ ค่าเริ่มต้นใน Aspose.Email คือ RSASha1 เพื่อความเข้ากันได้ย้อนหลัง, แต่ การปรับใช้ใหม่ทุกครั้งควรใช้ RSASha256. ผู้ให้บริการหลายรายตอนนี้ทำเครื่องหมายลายเซ็น SHA-1 ว่าไม่ถูกต้องแม้จะตรวจสอบเชิง криптограф ได้.

// Example with RSASha1 (older, less secure but widely supported)
var signInfoSha1 = new DKIMSignatureInfo("dkim-sha1", "example.com")
{
    HashAlgorithm = DKIMHashAlgorithm.RSASha1,
    Headers = { "From", "To", "Subject" }
};

var mailMessage1 = new MailMessage(
    "sender@example.com",
    "recipient@example.com",
    "SHA1 Test",
    "Signed with RSASha1 algorithm.");

mailMessage1.From = new MailAddress("sender@example.com");
var signedMsgSha1 = mailMessage1.DKIMSign(rsa, signInfoSha1);

string pathSha1 = Path.Combine(DataDir, "sha1-signed.eml");
signedMsgSha1.Save(pathSha1);

// Example with RSASha256 (newer, more secure)
var signInfoSha256 = new DKIMSignatureInfo("dkim-sha256", "example.com")
{
    HashAlgorithm = DKIMHashAlgorithm.RSASha256,
    Headers = { "From", "To", "Subject" }
};

var mailMessage2 = new MailMessage(
    "sender@example.com",
    "recipient@example.com",
    "SHA256 Test",
    "Signed with RSASha256 algorithm.");

mailMessage2.From = new MailAddress("sender@example.com");
var signedMsgSha256 = mailMessage2.DKIMSign(rsa, signInfoSha256);

string pathSha256 = Path.Combine(DataDir, "sha256-signed.eml");
signedMsgSha256.Save(pathSha256);

ผลลัพธ์ที่ได้ DKIM-Signature หัวข้อโฆษณาอัลกอริธึมผ่าน a= แท็ก (a=rsa-sha1 เทียบกับ a=rsa-sha256). ผู้รับจะใช้แท็กนั้นเพื่อเลือกแฮชที่จะคำนวณใหม่.

การทำให้มาตรฐาน — Simple กับ Relaxed

การทำให้มาตรฐานคือวิธีที่ DKIM ตกลงรูปแบบข้อความที่เสถียรและเท่ากันเป็นไบต์ก่อนการแฮช มีอัลกอริธึมสองแบบสำหรับหัวข้อและเนื้อหาแยกกัน:

  • Simple — เกือบไม่มีการทำให้เป็นมาตรฐาน. บรรทัดว่างสุดท้ายในเนื้อหาถูกลบ, แต่ช่องว่างอื่น ๆ ไม่ถูกแก้ไข. ช่องว่างเดียวที่เพิ่มโดยรีเลย์จะทำให้ลายเซ็นไม่ถูกต้อง.
  • Relaxed — พับช่องว่างภายในค่าหัวข้อ, แปลงชื่อหัวข้อเป็นตัวพิมพ์เล็ก, และบีบอัดช่องว่างต่อเนื่องในเนื้อหา. ทนต่อการจัดรูปแบบเล็กน้อยที่เอเย่นต์ส่งเมลทำ.

เลือก Relaxed สำหรับหัวข้อและเนื้อหา ทั้งสองกรณี ยกเว้นกรณีที่คุณมีเหตุผลเฉพาะ ไม่ควรทำ ส่วนใหญ่การ deploy ในการผลิตทำเช่นนั้น.

// Simple canonicalization - almost no changes allowed
var signInfoSimple = new DKIMSignatureInfo("dkim-simple", "example.com")
{
    HeaderCanonicalization = CanonicalizationType.Simple,
    BodyCanonicalization = CanonicalizationType.Simple,
    Headers = { "From", "To", "Subject" }
};

var mailMessage1 = new MailMessage(
    "sender@example.com",
    "recipient@example.com",
    "Simple Canonicalization",
    "This message uses Simple canonicalization.");

mailMessage1.From = new MailAddress("sender@example.com");
var signedSimple = mailMessage1.DKIMSign(rsa, signInfoSimple);

string pathSimple = Path.Combine(DataDir, "simple-canonical.eml");
signedSimple.Save(pathSimple);

// Relaxed canonicalization - minor changes allowed (whitespace normalization)
var signInfoRelaxed = new DKIMSignatureInfo("dkim-relaxed", "example.com")
{
    HeaderCanonicalization = CanonicalizationType.Relaxed,
    BodyCanonicalization = CanonicalizationType.Relaxed,
    Headers = { "From", "To", "Subject" }
};

var mailMessage2 = new MailMessage(
    "sender@example.com",
    "recipient@example.com",
    "Relaxed Canonicalization",
    "This message uses Relaxed canonicalization.");

mailMessage2.From = new MailAddress("sender@example.com");
var signedRelaxed = mailMessage2.DKIMSign(rsa, signInfoRelaxed);

string pathRelaxed = Path.Combine(DataDir, "relaxed-canonical.eml");
signedRelaxed.Save(pathRelaxed);

การทำให้มาตรฐานของหัวข้อและเนื้อหามีอิสระ — relaxed/simple เป็นการผสมผสานที่สมบูรณ์แบบ, เขียนแบบนั้นใน c= แท็กของหัวข้อ DKIM.

การโหลดคีย์ส่วนตัว

PemReader จัดการการเข้ารหัส PEM ทั้งสองแบบอย่างโปร่งใส ไม่มี Pkcs1Reader / Pkcs8Reader — เพียงแค่ส่งพาธหรือสตรีม:

// Check if private key file exists
if (!File.Exists(PrivateKeyPath))
{
    Console.WriteLine($"Private key file not found: {PrivateKeyPath}");
    Console.WriteLine("Creating a sample private key for demonstration...");

    // Create a sample RSA key (for testing only - in production, use real keys)
    using var rsa = new System.Security.Cryptography.RSACryptoServiceProvider(2048);
    var privateKeyParams = rsa.ExportParameters(true);

    Console.WriteLine("Sample RSA key created (2048 bits).");
    Console.WriteLine("In production, load your actual private key from a secure location.");
    Console.WriteLine("PEM file format should contain either:");
    Console.WriteLine("  - BEGIN RSA PRIVATE KEY (PKCS#1)");
    Console.WriteLine("  - BEGIN PRIVATE KEY (PKCS#8)");
}
else
{
    // Load the private key
    var rsa = PemReader.GetPrivateKey(PrivateKeyPath);
    Console.WriteLine($"Private key loaded successfully from: {PrivateKeyPath}");
    Console.WriteLine("Key size: 2048 bits (typical for DKIM)");
}

Console.WriteLine("\nPEM File Format Examples:");
Console.WriteLine("--------------------------");
Console.WriteLine("PKCS#1 format:");
Console.WriteLine("-----BEGIN RSA PRIVATE KEY-----");
Console.WriteLine("MIIEpAIBAAKCAQEA0Z3VS5JJcds3xfn/ygWyF8PbnGy...");
Console.WriteLine("-----END RSA PRIVATE KEY-----");
Console.WriteLine();
Console.WriteLine("PKCS#8 format:");
Console.WriteLine("-----BEGIN PRIVATE KEY-----");
Console.WriteLine("MIIEvQIBADANBgkqhkiG9w0BAQEFAASCBKcwggSjAgEAAoIBAQC...");
Console.WriteLine("-----END PRIVATE KEY-----");

PemReader.GetPrivateKey ส่งคืนอ็อบเจกต์ RSACryptoServiceProvider พร้อมมอบให้ DKIMSign. มีอีกหนึ่ง GetPrivateKey(Stream) โอเวอร์โหลดสำหรับกรณีที่คีย์มาจาก vault คีย์, blob ที่เข้ารหัส, หรือทรัพยากรฝังแทนไฟล์ระบบ.

กฎการจัดการคีย์:

  1. ห้ามคอมมิตคีย์ส่วนตัวเข้าสู่ระบบควบคุมเวอร์ชัน. *.pem อยู่ใน .gitignore.
  2. ในสภาพการผลิต, โหลดคีย์จากตัวจัดการความลับ (Azure Key Vault, AWS Secrets Manager, HashiCorp Vault) ไม่ใช่จากดิสก์.
  3. หมุนคีย์ทุก 6–12 เดือน เผยแพร่วัสดุตัวเลือกใหม่ก่อน, สลับการลงนาม, แล้วถอนบันทึก DNS เก่าหลังระยะเวลาผ่อนผัน.

การตรวจสอบข้อความที่ลงนามจากไฟล์

การตรวจสอบเป็นการดำเนินการสมมาตร: ให้ลายเซ็นที่ลงนาม .eml, DkimVerifier ดึง DKIM-Signature หัวข้อ, ทำการค้นหา DNS กับ <selector>._domainkey.<domain> สำหรับคีย์สาธารณะ, และคำนวณแฮชใหม่ ผลลัพธ์คือ DkimResult กับ IsValid และ ErrorMessage.

// For this example, we need a signed message
if (!File.Exists(signedMessagePath))
{
    Console.WriteLine($"Signed message file not found: {signedMessagePath}");
    Console.WriteLine("Please provide a DKIM-signed .eml file for verification.");
    return;
}

// Verify the signature
DkimResult result = DkimVerifier.Verify(signedMessagePath);

// Check the result
if (result.IsValid)
{
    Console.WriteLine("[OK] DKIM signature verification successful!");
    Console.WriteLine("The message was signed by the claimed domain.");
}
else
{
    Console.WriteLine("[FAIL] DKIM signature verification failed!");
    Console.WriteLine($"Error: {result.ErrorMessage}");
}

Console.WriteLine("\nVerification Process:");
Console.WriteLine("1. Extracts DKIM-Signature header from the message");
Console.WriteLine("2. Queries DNS for the public key (selector._domainkey.domain)");
Console.WriteLine("3. Verifies the signature using the public key");
Console.WriteLine("4. Compares computed body hash with the signed body hash");

การตรวจสอบข้อความที่ลงนามด้วย d=example.com จะล้มเหลวเสมอด้วย "key not in dictionary" หรือข้อผิดพลาด DNS‑lookup ที่คล้ายกัน เพราะ example.com ไม่ได้เผยแพร่บันทึก DKIM ใช้โดเมนที่คุณควบคุม (หรือข้อความทดสอบที่รู้ว่าดี) เพื่อทดสอบการตรวจสอบจริง.

การตรวจสอบจากสตรีม

เมื่อข้อความไม่ได้อยู่บนดิสก์ — มาจากการดึง IMAP, คิว, การอัปโหลด HTTP หรือที่เก็บ blob — ใช้ Stream โอเวอร์โหลด. ยอมรับสตรีมอ่านที่สามารถหาตำแหน่งได้ใดก็ได้:

// Load the signed message into a stream
using (var stream = File.OpenRead(signedMessagePath))
{
    DkimResult result = DkimVerifier.Verify(stream);

    if (result.IsValid)
    {
        Console.WriteLine("[OK] DKIM signature verified successfully from stream!");
    }
    else
    {
        Console.WriteLine("[FAIL] Verification failed!");
        Console.WriteLine($"Error: {result.ErrorMessage}");
    }
}

Console.WriteLine("\nStream-based verification is useful when:");
Console.WriteLine("  - Working with email from IMAP/POP3 clients");
Console.WriteLine("  - Processing emails from databases or cloud storage");
Console.WriteLine("  - Building web applications that receive emails via HTTP");

Verify(Stream) และ Verify(string) ทำงานแบบเดียวกัน — ทั้งสองบล็อกจนกว่าการค้นหา DNS จะเสร็จ สำหรับอะไรที่ผู้ใช้เห็น, ใช้เวอร์ชันอะซิงค์ด้านล่าง.

การตรวจสอบแบบอะซิงค์

การตรวจสอบ DKIM ต้องใช้การเดินทางรอบ DNS ซึ่งอาจใช้หลายสิบมิลลิวินาทีแม้บนเครือข่ายที่เร็ว ภายในตัวจัดการคำขอ ASP.NET, ลูปการประมวลผลข้อความ หรือเธรด UI ใด ๆ การใช้เวลานี้สะสมได้เร็ว. VerifyAsync คืนค่า Task<DkimResult> ว่าคุณสามารถ await:

if (!File.Exists(signedMessagePath))
{
    Console.WriteLine("Signed message file not found.");
    return;
}

// Async verification
DkimResult result = await DkimVerifier.VerifyAsync(signedMessagePath);

if (result.IsValid)
{
    Console.WriteLine("[OK] Async verification successful!");
}
else
{
    Console.WriteLine("[FAIL] Async verification failed!");
    Console.WriteLine($"Error: {result.ErrorMessage}");
}

Console.WriteLine("\nAsync verification is recommended for:");
Console.WriteLine("  - Web applications (ASP.NET, etc.)");
Console.WriteLine("  - Services that process multiple emails");
Console.WriteLine("  - Any application where you want to avoid blocking the main thread");

ยังมี VerifyAsync(Stream) โอเวอร์โหลด. เช่นเดียวกับ API แบบซิงค์ เมธอดประกาศ async Task (ไม่ async void) — จุดเรียกควร await มัน, และจุดเริ่มต้นแอปคอนโซลสามารถทำ VerifySignatureAsync().GetAwaiter().GetResult() เมื่อมี async Main ไม่พร้อมให้บริการ.

จากต้นถึงปลาย: โหลด, ลงนาม, บันทึก, ตรวจสอบ

ชิ้นส่วนด้านบนรวมกันเป็นกระบวนการทำงานเดียว นี่คือสายการทำงานการลงนามและตรวจสอบเต็มที่คุณจะฝังเข้าในบริการอีเมลเชิงธุรกรรม:

// Step 1: Load private key
var rsa = PemReader.GetPrivateKey(PrivateKeyPath);

// Step 2: Create signature information
var signInfo = new DKIMSignatureInfo("dkim", "example.com")
{
    HeaderCanonicalization = CanonicalizationType.Relaxed,
    BodyCanonicalization = CanonicalizationType.Relaxed,
    HashAlgorithm = DKIMHashAlgorithm.RSASha256,
    Headers =
    {
        "From",
        "To",
        "Subject",
        "Date",
        "MIME-Version"
    }
};

// Step 3: Create email message
var mailMessage = new MailMessage(
    "john.doe@example.com",
    "jane.smith@example.org",
    "Important Document - DKIM Signed",
    "Please find the attached document.\n\nBest regards,\nJohn Doe");

mailMessage.From = new MailAddress("john.doe@example.com", "John Doe");
mailMessage.To.Add(new MailAddress("jane.smith@example.org", "Jane Smith"));
mailMessage.Date = DateTime.UtcNow;
mailMessage.Headers.Add("MIME-Version", "1.0");

// Step 4: Sign the message
var signedMessage = mailMessage.DKIMSign(rsa, signInfo);

// Step 5: Save the signed message
string signedPath = Path.Combine(DataDir, "end-to-end-signed.eml");
signedMessage.Save(signedPath);

// Step 6: Verify the signature
var result = DkimVerifier.Verify(signedPath);

if (result.IsValid)
{
    Console.WriteLine("  [OK] Signature verification successful!");
    Console.WriteLine("  [OK] The message integrity is confirmed.");
    Console.WriteLine("  [OK] The sender domain is authenticated.");
}
else
{
    Console.WriteLine($"  [FAIL] Verification failed: {result.ErrorMessage}");
}

// Step 7: Display DKIM-Signature header
Console.WriteLine("\nStep 7: DKIM-Signature header:");
Console.WriteLine("  " + signedMessage.Headers["DKIM-Signature"]);

ชุดเต็ม DKIM-Signature หัวข้อที่โค้ดนี้สร้างออกมามีลักษณะดังนี้:

DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=example.com;
 h=from:to:subject:date:mime-version; q=dns/txt; s=dkim; t=1780252880;
 bh=e2RkwEm0k0yLcVmhi3KeXpt14Ppx3Xx49S33/+J8OGc=;
 b=Zd3sfCGGD9YdOLw1o3YHLFs7DO6V9cc7anye5E3kQ/MEdkU14daTru0sXTZCtCryvJubCs...

แท็กที่น่าสนใจ:

  • v=1 — เวอร์ชัน DKIM.
  • a=rsa-sha256 — อัลกอริธึม; สะท้อน HashAlgorithm.
  • c=relaxed/relaxed — การทำให้เป็นมาตรฐาน (หัวข้อ/เนื้อหา).
  • d=example.com / s=dkim — ตัวเลือกและโดเมน; รวมกันจะเป็นชื่อการค้นหา DNS.
  • h=from:to:subject:date:mime-version — หัวข้อที่ลงนามตามลำดับ.
  • bh= — แฮช base64 ของเนื้อหาที่ทำให้เป็นมาตรฐาน.
  • b= — ลายเซ็น RSA จริงที่คำนวนจากหัวข้อที่ทำให้เป็นมาตรฐาน.

หากคุณเปลี่ยนค่าหัวข้อที่ลงนามใด ๆ หรือแก้ไขเนื้อหา, bh และ b ไม่ตรงกันแล้วและการตรวจสอบล้มเหลว.

การเผยแพร่คีย์สาธารณะใน DNS

การลงนามในเครื่องเป็นเพียงครึ่งหนึ่งของงาน เพื่อให้ผู้รับตรวจสอบได้ คีย์สาธารณะของ RSA ของคุณต้องถูกเผยแพร่เป็นบันทึก DNS TXT ที่ <selector>._domainkey.<domain>:

Record: dkim._domainkey.example.com
Type:   TXT
Value:  v=DKIM1; k=rsa; p=MIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBC...

ดึงคีย์สาธารณะจาก PEM ของคุณด้วย OpenSSL:

openssl rsa -in sample-private-key.pem -pubout -out sample-public-key.pem

ลบบรรทัดหัว/ท้ายและเชื่อมต่อเนื้อหา base64 สำหรับ p= ค่า. จากนั้นยืนยันว่าบันทึกใช้งานได้ก่อนพึ่งพา:

dig dkim._domainkey.example.com TXT

เครื่องมือออนไลน์เช่น DKIM Validator และ MXToolbox DKIM Checker จะบอกคุณว่าข้อความจริงที่คุณส่งสามารถตรวจสอบได้จากต้นถึงปลายหรือไม่ ใช้ก่อนที่จะเปลี่ยนการจราจรผลิตจริง.