{"record":{"id":"49df159452d93733","repo":"golang/go","slug":"tls-client-certificate-uses-ml-dsa-which-require","errorCode":null,"errorMessage":"tls: client certificate uses ML-DSA, which requires TLS 1.3","messagePattern":"tls: client certificate uses ML-DSA, which requires TLS 1\\.3","errorType":"validation","errorClass":null,"httpStatus":null,"severity":"error","filePath":"src/crypto/tls/handshake_server.go","lineNumber":1017,"sourceCode":"\t}\n\n\tc.peerCertificates = certs\n\tc.ocspResponse = certificate.OCSPStaple\n\tc.scts = certificate.SignedCertificateTimestamps\n\n\tif len(certs) > 0 {\n\t\tif fips140tls.Required() && !isCertificateAllowedFIPS(certs[0]) {\n\t\t\tc.sendAlert(alertBadCertificate)\n\t\t\terr := errors.New(\"client's certificate is not allowed in FIPS 140-3 mode\")\n\t\t\treturn &CertificateVerificationError{UnverifiedCertificates: certs, Err: err}\n\t\t}\n\n\t\tswitch certs[0].PublicKey.(type) {\n\t\tcase *ecdsa.PublicKey, *rsa.PublicKey, ed25519.PublicKey:\n\t\tcase *mldsa.PublicKey:\n\t\t\tif c.vers < VersionTLS13 {\n\t\t\t\tc.sendAlert(alertIllegalParameter)\n\t\t\t\treturn errors.New(\"tls: client certificate uses ML-DSA, which requires TLS 1.3\")\n\t\t\t}\n\t\tdefault:\n\t\t\tc.sendAlert(alertUnsupportedCertificate)\n\t\t\treturn fmt.Errorf(\"tls: client certificate contains an unsupported public key of type %T\", certs[0].PublicKey)\n\t\t}\n\t}\n\n\tif c.config.VerifyPeerCertificate != nil {\n\t\tif err := c.config.VerifyPeerCertificate(certificates, c.verifiedChains); err != nil {\n\t\t\tc.sendAlert(alertBadCertificate)\n\t\t\treturn err\n\t\t}\n\t}\n\n\treturn nil\n}\n\nfunc clientHelloInfo(ctx context.Context, c *Conn, clientHello *clientHelloMsg) *ClientHelloInfo {","sourceCodeStart":999,"sourceCodeEnd":1035,"githubUrl":"https://github.com/golang/go/blob/b6b368adc57c96c3151d224d172029f233ead2c3/src/crypto/tls/handshake_server.go#L999-L1035","documentation":"The client presented an ML-DSA (post-quantum, FIPS 204) certificate on a connection negotiated below TLS 1.3. ML-DSA is only defined for TLS 1.3 client authentication (hybrid post-quantum signatures require the 1.3 CertificateVerify semantics); older versions cannot use it. The server rejects with illegal_parameter.","triggerScenarios":"In processCertsFromClient, certs[0].PublicKey is *mldsa.PublicKey and c.vers < VersionTLS13. The client offered an ML-DSA cert but the connection downgraded to TLS 1.2 or below.","commonSituations":"A client configured with a post-quantum ML-DSA certificate connecting to a server that does not support TLS 1.3, or a middlebox forcing a downgrade. Also seen in mixed-version migrations where some servers lack TLS 1.3 support.","solutions":["Require TLS 1.3 on both ends — set MinVersion: tls.VersionTLS13 on client and server.","If the server must support older TLS, provide the client with a fallback classical (ECDSA/RSA) certificate for those connections.","Remove middleboxes or proxies that cap the negotiated version below 1.3.","Verify both peers run TLS stacks new enough to recognize ML-DSA."],"exampleFix":"// before: version cap allows downgrade\ncfg := &tls.Config{\n    MaxVersion: tls.VersionTLS12,\n    Certificates: []tls.Certificate{mlDSAOnly},\n}\n\n// after: require TLS 1.3 for ML-DSA client certs\ncfg := &tls.Config{\n    MinVersion: tls.VersionTLS13,\n    MaxVersion: tls.VersionTLS13,\n    Certificates: []tls.Certificate{mlDSAOnly},\n}","handlingStrategy":"validation","validationCode":"// Client: gate ML-DSA cert usage on TLS 1.3 negotiation.\nif _, isMLDSA := cert.PrivateKey.(*mldsa.PrivateKey); isMLDSA {\n    cfg.MinVersion = tls.VersionTLS13\n    cfg.MaxVersion = tls.VersionTLS13\n}","typeGuard":null,"tryCatchPattern":"// Client: catch and fall back to a classical cert for legacy servers.\nif err != nil && strings.Contains(err.Error(), \"ML-DSA, which requires TLS 1.3\") {\n    cfg.Certificates = []tls.Certificate{classicalCert}\n    // retry\n}","preventionTips":["Require TLS 1.3 when configuring ML-DSA client certificates.","Provide a classical fallback cert for legacy-server interoperability.","Remove version-capping middleboxes when deploying post-quantum certs."],"tags":["tls","server-handshake","ml-dsa","post-quantum","client-certificate","tls13","version-negotiation"],"backgroundTag":null,"analyzedSha":"b6b368adc57c96c3151d224d172029f233ead2c3","analyzedAt":"2026-08-12T00:22:02.250Z","schemaVersion":2},"datasetVersion":"2026-08-12T12:31:55.035Z"}