package parsemail import ( "bytes" "crypto" "crypto/x509" "encoding/pem" "fmt" "github.com/Jinnrry/pmail/config" "github.com/Jinnrry/pmail/utils/consts" "github.com/Jinnrry/pmail/utils/context" "github.com/emersion/go-msgauth/dkim" log "github.com/sirupsen/logrus" "golang.org/x/crypto/ed25519" "io" "os" "strings" ) type Dkim struct { privateKey crypto.Signer } var instance *Dkim func Init() { privateKey, err := loadPrivateKey(config.Instance.DkimPrivateKeyPath) if err != nil { panic(config.Instance.DkimPrivateKeyPath + " DKIM load fail! Please set dkim! dkim私钥加载失败!请先设置dkim秘钥" + err.Error()) } instance = &Dkim{ privateKey: privateKey, } } func loadPrivateKey(path string) (crypto.Signer, error) { b, err := os.ReadFile(path) if err != nil { return nil, err } block, _ := pem.Decode(b) if block == nil { return nil, fmt.Errorf("no PEM data found") } switch strings.ToUpper(block.Type) { case "PRIVATE KEY": k, err := x509.ParsePKCS8PrivateKey(block.Bytes) if err != nil { return nil, err } return k.(crypto.Signer), nil case "RSA PRIVATE KEY": return x509.ParsePKCS1PrivateKey(block.Bytes) case "EDDSA PRIVATE KEY": if len(block.Bytes) != ed25519.PrivateKeySize { return nil, fmt.Errorf("invalid Ed25519 private key size") } return ed25519.PrivateKey(block.Bytes), nil default: return nil, fmt.Errorf("unknown private key type: '%v'", block.Type) } } func (p *Dkim) Sign(msgData string) []byte { var b bytes.Buffer r := strings.NewReader(msgData) options := &dkim.SignOptions{ Domain: config.Instance.Domain, Selector: "default", Signer: p.privateKey, } if err := dkim.Sign(&b, r, options); err != nil { log.Errorf("%+v", err) return []byte(msgData) } return b.Bytes() } func Check(ctx *context.Context, mail io.Reader) bool { verifications, err := dkim.Verify(mail) if err != nil { log.WithContext(ctx).Warnf("DKIM Error:%v", err) } if len(verifications) == 0 { return false } for _, v := range verifications { if v.Domain == consts.TEST_DOMAIN { return true } if v.Err == nil { log.Println("Valid signature for:", v.Domain) } else { log.Println("Invalid signature for:", v.Domain, v.Err) return false } } return true }