github.com/blend/go-sdk@v1.20220411.3/webutil/cert_info.go (about)

     1  /*
     2  
     3  Copyright (c) 2022 - Present. Blend Labs, Inc. All rights reserved
     4  Use of this source code is governed by a MIT license that can be found in the LICENSE file.
     5  
     6  */
     7  
     8  package webutil
     9  
    10  import (
    11  	"crypto/tls"
    12  	"crypto/x509"
    13  	"fmt"
    14  	"net/http"
    15  	"time"
    16  )
    17  
    18  // ParseCertInfo returns a new cert info from a response from a check.
    19  func ParseCertInfo(res *http.Response) *CertInfo {
    20  	if res == nil || res.TLS == nil || len(res.TLS.PeerCertificates) == 0 {
    21  		return nil
    22  	}
    23  
    24  	var earliestNotAfter time.Time
    25  	var latestNotBefore time.Time
    26  	for _, cert := range res.TLS.PeerCertificates {
    27  		if earliestNotAfter.IsZero() || earliestNotAfter.After(cert.NotAfter) {
    28  			earliestNotAfter = cert.NotAfter
    29  		}
    30  		if latestNotBefore.IsZero() || latestNotBefore.Before(cert.NotBefore) {
    31  			latestNotBefore = cert.NotBefore
    32  		}
    33  	}
    34  
    35  	firstCert := res.TLS.PeerCertificates[0]
    36  	var issuerNames []string
    37  	for _, name := range firstCert.Issuer.Names {
    38  		issuerNames = append(issuerNames, fmt.Sprint(name.Value))
    39  	}
    40  
    41  	return &CertInfo{
    42  		SubjectCommonName: firstCert.Subject.CommonName,
    43  		IssuerNames:       issuerNames,
    44  		IssuerCommonName:  firstCert.Issuer.CommonName,
    45  		DNSNames:          firstCert.DNSNames,
    46  		NotAfter:          earliestNotAfter,
    47  		NotBefore:         latestNotBefore,
    48  	}
    49  }
    50  
    51  // NewCertInfo returns a new cert info.
    52  func NewCertInfo(cert *tls.Certificate) (*CertInfo, error) {
    53  	leaf, err := x509.ParseCertificate(cert.Certificate[0])
    54  	if err != nil {
    55  		return nil, err
    56  	}
    57  	var issuerNames []string
    58  	for _, name := range leaf.Issuer.Names {
    59  		issuerNames = append(issuerNames, fmt.Sprint(name.Value))
    60  	}
    61  
    62  	return &CertInfo{
    63  		SubjectCommonName: leaf.Subject.CommonName,
    64  		IssuerNames:       issuerNames,
    65  		IssuerCommonName:  leaf.Issuer.CommonName,
    66  		DNSNames:          leaf.DNSNames,
    67  		NotAfter:          leaf.NotAfter,
    68  		NotBefore:         leaf.NotBefore,
    69  	}, nil
    70  }
    71  
    72  // CertInfo is the information for a certificate.
    73  type CertInfo struct {
    74  	SubjectCommonName string    `json:"subjectCommonName" yaml:"subjectCommonName"`
    75  	IssuerCommonName  string    `json:"issuerCommonName" yaml:"issuerCommonName"`
    76  	IssuerNames       []string  `json:"issuerNames" yaml:"issuerNames"`
    77  	DNSNames          []string  `json:"dnsNames" yaml:"dnsNames"`
    78  	NotAfter          time.Time `json:"notAfter" yaml:"notAfter"`
    79  	NotBefore         time.Time `json:"notBefore" yaml:"notBefore"`
    80  }
    81  
    82  // IsExpired returns if the certificate is strictly expired
    83  // and would not be accepted by browsers.
    84  func (ci CertInfo) IsExpired() bool {
    85  	return ci.WillBeExpired(time.Now().UTC())
    86  }
    87  
    88  // WillBeExpired returns if the certificate is strictly expired
    89  // and would not be accepted by browsers at a given time.
    90  func (ci CertInfo) WillBeExpired(at time.Time) bool {
    91  	if !ci.NotAfter.IsZero() {
    92  		if at.UTC().After(ci.NotAfter) {
    93  			return true
    94  		}
    95  	}
    96  	if !ci.NotBefore.IsZero() {
    97  		if at.UTC().Before(ci.NotBefore) {
    98  			return true
    99  		}
   100  	}
   101  	return false
   102  }