rsa/pkg/inspect/cert.go

284 lines
6.2 KiB
Go
Raw Permalink Normal View History

2023-03-01 21:28:48 +00:00
package inspect
import (
"crypto/rsa"
"crypto/x509"
"errors"
"strings"
)
// DERCertificate attempts to parse a DER-form X.509 certificate, and returns
// information about it.
func DERCertificateInfo(loc string, der []byte) Info {
c, err := x509.ParseCertificate(der)
if err != nil {
return &BadInfo{
Typ: TypeX509Certificate,
Loc: loc,
Underlying: err,
}
}
return CertificateInfo(loc, c)
}
// CertificateInfo returns structured information about the given certificate.
// It can return BadInfo if the certificate is not for an RSA key. Note that
// the returned Info structure holds a reference to the passed certificate.
func CertificateInfo(loc string, cert *x509.Certificate) Info {
pubkey, ok := cert.PublicKey.(*rsa.PublicKey)
if !ok {
return &BadInfo{
Typ: TypeX509Certificate,
Loc: loc,
Underlying: errors.New("certificate is not for an RSA key"),
}
}
c := &Certificate{
Loc: loc,
Key: PublicKeyInfo(loc, pubkey),
Cert: cert,
}
if (cert.KeyUsage & x509.KeyUsageDigitalSignature) != 0 {
c.ValidUses = append(c.ValidUses, "DigitalSignature")
}
if (cert.KeyUsage & x509.KeyUsageCertSign) != 0 {
c.ValidUses = append(c.ValidUses, "CertSign")
}
if (cert.KeyUsage & x509.KeyUsageCRLSign) != 0 {
c.ValidUses = append(c.ValidUses, "CRLSign")
}
for _, use := range cert.ExtKeyUsage {
switch use {
case x509.ExtKeyUsageAny:
c.ValidUses = append(c.ValidUses, "any")
case x509.ExtKeyUsageServerAuth:
c.ValidUses = append(c.ValidUses, "ServerAuth")
case x509.ExtKeyUsageClientAuth:
c.ValidUses = append(c.ValidUses, "ClientAuth")
}
}
if err := cert.CheckSignatureFrom(cert); err == nil {
c.IsSelfSigned = true
}
return c
}
type Certificate struct {
// Loc is the location the certificate was encountered.
Loc string
// Key holds information about the public key of the certificate.
Key *PublicKey
// Cert is the original certificate.
Cert *x509.Certificate
// The following section contains any computed values that are not
// immediately obtainable from the certificate.
// ValidUses is a set of named certificate uses.
ValidUses []string
// IsSelfSigned reports whether the certificate is self-signed.
IsSelfSigned bool
}
// Type indicates this is an X.509 certificate.
func (c *Certificate) Type() Type {
return TypeX509Certificate
}
// Location returns the location at which the certificate was encountered.
func (c *Certificate) Location() string {
return c.Loc
}
// Info returns structured information about the X.509 Certificate.
func (c *Certificate) Info() []Section {
var s []Section
s = append(s, c.UsageSection())
s = append(s, c.ValiditySection())
s = append(s, c.SubjectSection())
s = append(s, c.IssuerSection())
if c.Cert.IsCA {
s = append(s, c.CASection())
}
s = append(s, c.Key.PublicKeyInfoSection())
return s
}
// UsageSection returns information regarding the key usage bits.
func (c *Certificate) UsageSection() Section {
return Section{
Title: "Certificate usage",
Fields: []Field{
Field{
Key: "Is CA",
Value: c.Cert.IsCA,
},
Field{
Key: "Self-signed",
Value: c.IsSelfSigned,
},
Field{
Key: "Valid uses",
Value: strings.Join(c.ValidUses, " | "),
},
},
}
}
// ValiditySection returns information regarding the validity of the certificate.
func (c *Certificate) ValiditySection() Section {
return Section{
Title: "Validity",
Fields: []Field{
Field{
Key: "Valid from",
Value: c.Cert.NotBefore,
},
Field{
Key: "Valid until",
Value: c.Cert.NotAfter,
},
},
}
}
// SubjectSection returns information about the Subject of the certificate.
func (c *Certificate) SubjectSection() Section {
f := []Field{
Field{
Key: "Description",
Value: c.Cert.Subject.CommonName,
},
}
f = appendX509DNField(f, c.Cert.Subject)
if len(c.Cert.DNSNames) > 0 {
f = append(f, Field{
Key: "Hostnames",
Value: c.Cert.DNSNames,
})
}
if len(c.Cert.IPAddresses) > 0 {
var ips []string
for _, ip := range c.Cert.IPAddresses {
ips = append(ips, ip.String())
}
f = append(f, Field{
Key: "IP addresses",
Value: ips,
})
}
if len(c.Cert.SubjectKeyId) > 0 {
f = append(f, Field{
Key: "Key ID",
Value: FormatHexBytes(c.Cert.SubjectKeyId),
})
}
return Section{
Title: "Subject",
Fields: f,
}
}
// IssuerSection returns information about the Issuer of the certificate.
func (c *Certificate) IssuerSection() Section {
f := []Field{
Field{
Key: "Serial",
Value: FormatHexBytes(c.Cert.SerialNumber.Bytes()),
},
}
if c.Cert.Issuer.CommonName != "" {
f = append(f, Field{
Key: "Description",
Value: c.Cert.Issuer.CommonName,
})
}
f = appendX509DNField(f, c.Cert.Issuer)
if len(c.Cert.AuthorityKeyId) > 0 {
f = append(f, Field{
Key: "Key ID",
Value: FormatHexBytes(c.Cert.AuthorityKeyId),
})
}
return Section{
Title: "Issuer",
Fields: f,
}
}
// CASection returns information about a CA certificate.
func (c *Certificate) CASection() Section {
var f []Field
switch {
case c.Cert.MaxPathLen < 0,
c.Cert.MaxPathLen == 0 && !c.Cert.MaxPathLenZero:
f = append(f, Field{
Key: "Max path len",
Value: "unlimited",
})
default:
f = append(f, Field{
Key: "Max path len",
Value: c.Cert.MaxPathLen,
})
}
if len(c.Cert.PermittedDNSDomains) > 0 {
f = append(f, Field{
Key: "Permitted DNS names",
Value: c.Cert.PermittedDNSDomains,
})
}
if len(c.Cert.ExcludedDNSDomains) > 0 {
f = append(f, Field{
Key: "Excluded DNS names",
Value: c.Cert.ExcludedDNSDomains,
})
}
if len(c.Cert.PermittedIPRanges) > 0 {
var ips []string
for _, ip := range c.Cert.PermittedIPRanges {
ips = append(ips, ip.String())
}
f = append(f, Field{
Key: "Permitted IP addresses",
Value: ips,
})
}
if len(c.Cert.ExcludedIPRanges) > 0 {
var ips []string
for _, ip := range c.Cert.ExcludedIPRanges {
ips = append(ips, ip.String())
}
f = append(f, Field{
Key: "Excluded IP addresses",
Value: ips,
})
}
if len(c.Cert.CRLDistributionPoints) > 0 {
f = append(f, Field{
Key: "CRL distribution points",
Value: c.Cert.CRLDistributionPoints,
})
}
return Section{
Title: "Certificate authority",
Fields: f,
}
}