我们正在try 与DKIM签署一封邮箱.邮箱通过Gmail的Api成功发送给收件人;然而,当我们使用具有有效DKIM设置的域对其进行签名时,它将无法通过DKIM验证. 该域具有有效的DKIM设置(https://mxtoolbox.com/SuperTool.aspx?action=dkim%3aunsub.eeunsub.com%3as1&run=toolpage),当我们使用其他邮箱Provider (如AWS SES)发送邮箱时,DKIM通过(添加了屏幕截图,见下文).

private static void SendEmail()
{
  ClientSecrets clientSecrets = new ClientSecrets
  {
      ClientId = ClientId,
      ClientSecret = ClientSecret
  };
  
  TokenResponse token = new TokenResponse
  {
      AccessToken = "",
      RefreshToken = _espApiEndpoint.RefreshToken
  };
  
  IAuthorizationCodeFlow flow =
  new GoogleAuthorizationCodeFlow(new GoogleAuthorizationCodeFlow.Initializer
  {
      ClientSecrets = clientSecrets,
      Scopes = new string[] { GmailService.Scope.GmailSend }
  });
  
  UserCredential credential = new UserCredential(flow, "me", token);
  
  BaseClientService.Initializer intializer = new BaseClientService.Initializer
  {
      ApplicationName = "GmailEspApiClient",
      HttpClientInitializer = credential
  };
  
  var gmail = new GmailService(intializer);
  
  string message = @"From: Xxx <xxx@gmail.com>
  Date: Thu, 14 Mar 2024 09:59:20 -0700
  Subject: Gmail EE est
  Message-Id: <33@xxx.com>
  Reply-To: xxx <xxx@xxx.com>
  To: Dan <xxx@xxx.com>
  X-EE-RunId: xxx
  List-Unsubscribe: <xxx.com/>, <mailto:>
  List-Unsubscribe-Post: List-Unsubscribe=One-Click
  X-Test-Header: TestHeader
  X-Testing: Testee
  MIME-Version: 1.0
  Content-Type: text/html; charset=utf-8
  
  Hello from <b>Gmail</b>
  ";
  
  using MemoryStream memoryStream = new MemoryStream(System.Text.Encoding.UTF8.GetBytes(message));
  MimeMessage mimeMessage = await MimeMessage.LoadAsync(memoryStream);
  
  string privateKey = ""; // Your private key
  byte[] byteArray = Encoding.UTF8.GetBytes(privateKey);
  using MemoryStream memory = new MemoryStream(byteArray);
  
  string domain = "xxx"; // Your Domain
  string selector = "xxx"; // Selector
  DkimSigner signer = new DkimSigner(memory, domain, selector)
  {
      HeaderCanonicalization算法rithm = DkimCanonicalization算法rithm.Simple,
      BodyCanonicalization算法rithm = DkimCanonicalization算法rithm.Simple,
      QueryMethod = "dns/txt",
  };
  
  mimeMessage.Prepare(EncodingConstraint.SevenBit);
  
  signer.Sign(mimeMessage, new HeaderId[] { HeaderId.From });
  
  Message finalmessage = new Message();
  finalmessage.Raw = Base64UrlEncode(mimeMessage.ToString());
  var result = await gmail.Users.Messages.Send(finalmessage, "me").ExecuteAsync();
}

private static string Base64UrlEncode(string input)
{
    var inputBytes = System.Text.Encoding.UTF8.GetBytes(input);;
    // Special "url-safe" base64 encode.
    return Convert.ToBase64String(inputBytes)
      .Replace('+', '-')
      .Replace('/', '_')
      .Replace("=", "");
}

GoogleAws SES的屏幕截图都有.请注意谷歌如何返回dkim=neutral (body hash did not verify).

请让我们知道你的 idea .

以前使用Google https://github.com/googleapis/google-api-dotnet-client/issues/2701打开的问题

推荐答案

这是我使用Google.net客户端库使用GoogleXoauth2的Gmail SMTP示例

// Copyright 2023 DAIMTO ([Linda Lawton](https://twitter.com/LindaLawtonDK)) :  [www.daimto.com](http://www.daimto.com/)
//
// Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with
// the License. You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software distributed under the License is distributed on
// an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the License for the
// specific language governing permissions and limitations under the License.
using Google.Apis.Auth.OAuth2;
using Google.Apis.Util.Store;
using MailKit.Net.Smtp;
using MailKit.Security;
using MimeKit;

var to = "XXXX@gmail.com";

// TODO: figure out how to get the users email back from the smtp server without having to request the profile scope. 
var from = "XXXX@gmail.com";

var path = @"C:\Development\FreeLance\GoogleSamples\Credentials\Credentials.json";
var scopes = new[] { "https://mail.google.com/" };   // You can only use this scope to connect to the smtp server.



var credential = GoogleWebAuthorizationBroker.AuthorizeAsync(GoogleClientSecrets.FromFile(path).Secrets,
    scopes,
    "GmailSmtpUser",
    CancellationToken.None,
    new FileDataStore(Directory.GetCurrentDirectory(), true)).Result;

var message = new EmailMessage()
{
    From = from,
    To = to,
    MessageText = "This is a test message using https://developers.google.com/gmail/imap/xoauth2-protocol",
    Subject = "Testing GmailSMTP with XOauth2"
};

try
{
    using (var client = new SmtpClient())
    {
        client.Connect("smtp.gmail.com", 465, true);
        
        var oauth2 = new SaslMechanismOAuth2 (message.From, credential.Token.AccessToken);
        await client.AuthenticateAsync (oauth2, CancellationToken.None);
        
        client.Send(message.GetMessage());
        client.Disconnect(true);
    }

   
}
catch (Exception ex)
{
   Console.WriteLine(ex.Message);
}


public class EmailMessage
{
    public string To { get; set; }
    public string From { get; set; }
    public string Subject { get; set; }
    public string MessageText { get; set; }

    public MimeMessage GetMessage()
    {
        var body = MessageText;
        var message = new MimeMessage();
        message.From.Add(new MailboxAddress("From a user", From));
        message.To.Add(new MailboxAddress("To a user", To));
        message.Subject = Subject;
        message.Body = new TextPart("plain") { Text = body };
        return message;
    }
}

你需要自己弄清楚DKIM的部分我还没有配置在我的工作区帐户上有一些关于堆栈的问题,可能会帮助C# DKIM gmail site:stackoverflow.com

Csharp相关问答推荐

将委托传递到serviceccollection c#web API

System.Text.Json数据化的C#类映射

ASP.NET配置kestrel以使用Windows证书存储中的HTTPS

集合表达式没有目标类型

什么时候接受(等待)信号灯?尽可能的本地化?

DbContext-传递自定义配置选项

为什么方法的值在SELECT方法中不会更改?

如何允许数组接受多个类型?

C#动态设置ServerReport报表参数

错误CS1061';AuthenticationBuilder';不包含AddOpenIdConnect的定义

Azure Functions v4中的Serilog控制台主题

使用动态键从请求体反序列化JSON

Visual Studio,Docker容器-容器调用:连接被拒绝

如何在C#控制台应用程序中获取用户输入并将其作为订单项目进行处理?

Linq SELECT的多条指令

SharpZipLib在文件名前加上目录名,生成tar.gz

避免在特定区域中设置Visual Studio代码的自动格式

.NET6最小API:操作.MapGet之后的响应

如何获取我在SQL中输入的值

我应该使用IMhemyCache来存储承载令牌,还是应该为Azure函数中的401个错误实施Polly重试策略?