shadow1ng/fscan · error
empty certificate chain
Error message
empty certificate chain
What it means
X509CertificateChain.GetPublicKey extracts the RSA public key from the server certificate chain carried in the CONNECT_RESPONSE. If the CertBlobArray is empty there is no certificate to parse, so this error is returned. This typically means the server sent no certificate chain where one was required for the chosen security path.
Source
Thrown at libs/grdp/protocol/t125/gcc/gcc.go:410
p.SignatureBlob, _ = core.ReadBytes(int(p.SignatureBlobLen)-8, r)
p.Padding, _ = core.ReadBytes(8, r)
return nil
}
type CertBlob struct {
CbCert uint32 `struc:"little,sizeof=AbCert"`
AbCert []byte `struc:"little"`
}
type X509CertificateChain struct {
NumCertBlobs uint32 `struc:"little,sizeof=CertBlobArray"`
CertBlobArray []CertBlob `struc:"little"`
Padding []byte `struc:"[12]byte"`
}
func (x *X509CertificateChain) GetPublicKey() (*rsa.PublicKey, error) {
if len(x.CertBlobArray) == 0 {
return nil, errors.New("empty certificate chain")
}
data := x.CertBlobArray[len(x.CertBlobArray)-1].AbCert
cert, err := x509.ParseCertificate(data)
if err != nil {
return nil, fmt.Errorf("parse certificate: %w", err)
}
if cert.PublicKey == nil {
var pubKeyInfo struct {
Algorithm pkix.AlgorithmIdentifier
SubjectPublicKey asn1.BitString
}
_, err = asn1.Unmarshal(cert.RawSubjectPublicKeyInfo, &pubKeyInfo)
if err != nil {
return nil, fmt.Errorf("unmarshal public key info: %w", err)
}
rsaPublicKey, err := x509.ParsePKCS1PublicKey(pubKeyInfo.SubjectPublicKey.Bytes)
if err != nil {
return nil, fmt.Errorf("parse PKCS1 public key: %w", err)View on GitHub (pinned to 95cc12e753)
Solutions
- Verify the server's selected security protocol actually returns an X.509 certificate chain; if the server uses NLA/CredSSP, this code path may not apply — use the matching security handler
- Hex-dump the certificate PDU and check certCount: if nonzero, fix the struc decoding (alignment/endianness) of CertBlobArray
- Handle the alternate certificate types (SSCert/ProprietaryCert) instead of assuming X509CertificateChain
- Update grdp or the server config so standard TLS/RDP security with a real certificate chain is negotiated
Example fix
// before
if len(x.CertBlobArray) == 0 {
return nil, errors.New("empty certificate chain")
}
// after
if len(x.CertBlobArray) == 0 {
return nil, fmt.Errorf("empty certificate chain: server sent certCount=0 (security proto %v may not use X.509 certs)", selectedProto)
} Defensive patterns
Strategy: type-guard
Validate before calling
// check the certificate chain before extracting the key
func hasCertChain(x *gcc.X509CertificateChain) bool {
return x != nil && len(x.CertBlobArray) > 0 && len(x.CertBlobArray[len(x.CertBlobArray)-1].AbCert) > 0
} Type guard
func validCertChain(x *gcc.X509CertificateChain) bool {
return x != nil && len(x.CertBlobArray) > 0
} Try / catch
pub, err := certChain.GetPublicKey()
if err != nil {
if strings.Contains(err.Error(), "empty certificate chain") {
// server did not send an X.509 chain; switch security handler or abort
return ErrNoServerCertificate
}
return err
} Prevention
- Check the negotiated security protocol before assuming an X.509 chain is present
- Handle the alternate certificate PDU types (SSCert/Proprietary) in your connect logic
- Verify certCount in the raw PDU to distinguish encoding bugs from genuinely absent certificates
When it happens
Trigger: Calling GetPublicKey on an X509CertificateChain parsed from a server_proposals/certificate blob whose CertBlobArray decoded to zero entries — e.g. the server chose a security path without sending an X.509 chain, or the struc decoding silently produced an empty array.
Common situations: Servers configured for non-standard security (e.g. hybird/NLA-only) that omit the server certificate in the expected structure; decoding desync causing the array length field to read as 0; connecting to gateways that use different certificate PDU types (e.g. proprietary/bogus certs) this struct does not capture.
Understand the failure class
Background: "must not be empty", "cannot be empty" — required-field validation errors across open-source libraries — this error's family across 41 libraries.
- SSL/TLS and certificate errors — how TLS handshakes and certificate validation fail.
Related errors
- failed to get server public key
- parse certificate: %w
- Unsupported version
- unmarshal public key info: %w
- parse PKCS1 public key: %w
AI-assisted analysis of shadow1ng/fscan@95cc12e753 (2026-09-06).
Data as JSON: /api/errors/4d461feb4435b4eb.
Report an issue: GitHub.