capi/lib/expiration/certutil/certutil.go (130 lines of code) (raw):
/* This Source Code Form is subject to the terms of the Mozilla Public
* License, v. 2.0. If a copy of the MPL was not distributed with this
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
package certutil
import (
"bytes"
"crypto"
"crypto/x509"
"fmt"
"github.com/pkg/errors"
"io/ioutil"
"log"
"os"
"os/exec"
)
type Fingerprint = string
const (
//-u certusage Specify certificate usage:
//C SSL Client
//V SSL Server
//I IPsec
//L SSL CA
//A Any CA
//Y Verify CA
//S Email signer
//R Email Recipient
//O OCSP status responder
//J Object signer
CertUsageSSLClient = "C"
CertUsageSSLServer = "V"
CertUsageIPsec = "I"
CertUsageSSLCA = "L"
CertUsageAnyCA = "A"
CertUsageVerifyCA = "y"
CertUsageEmailSigner = "S"
CertUsageEmailRecipient = "R"
CertUsageOCSPStatusResponder = "O"
CertUsageObjectSigner = "J"
)
const (
NewCertificateDatabase = "-N"
NoPassword = "--empty-password"
CertDbDirectory = "-d"
InstallCert = "-A"
CertName = "-n"
TrustArgs = "-t"
TrustedPeer = "P,p,p"
TrustedImplicit = ",,"
TrustedCA = "C"
Verify = "-V"
VerifySignature = "-e"
CertUsage = "-u"
SSLServer = "V"
ListChain = "-O"
)
const (
VALID = "certutil: certificate is valid"
EXPIRED = "certutil: certificate is invalid: Peer's Certificate has expired."
ISSUER_UNKOWN = "certutil: certificate is invalid: Peer's Certificate issuer is not recognized."
)
const executable = "certutil"
type Certutil struct {
tmpDir string
}
func NewCertutil() (Certutil, error) {
tmpDir, err := ioutil.TempDir("", "")
if err != nil {
return Certutil{}, err
}
return NewCerutilInto(tmpDir)
}
func NewCerutilInto(dir string) (certutil Certutil, err error) {
certutil.tmpDir = dir
out, err := execute([]string{NewCertificateDatabase, NoPassword, CertDbDirectory, certutil.tmpDir})
if err != nil {
log.Println(string(out))
}
return
}
func CertUtilFrom(dir string) (certutil Certutil) {
certutil.tmpDir = dir
return
}
//-t trustargs Set the certificate trust attributes:
//trustargs is of the form x,y,z where x is for SSL, y is for S/MIME,
//and z is for code signing. Use ,, for no explicit trust.
//p prohibited (explicitly distrusted)
//P trusted peer
//c valid CA
//T trusted CA to issue client certs (implies c)
//C trusted CA to issue server certs (implies c)
//u user cert
//w send warning
//g make step-up cert
func (c Certutil) Install(cert *x509.Certificate) ([]byte, error) {
var trustArgs string
switch cert.Subject.CommonName == cert.Issuer.CommonName {
case true:
trustArgs = TrustedCA
case false:
trustArgs = TrustedImplicit
}
return execute([]string{
InstallCert,
TrustArgs, trustArgs,
CertName, fingerprintOf(cert),
CertDbDirectory, c.tmpDir,
}, cert.Raw...)
}
//-u certusage Specify certificate usage:
//C SSL Client
//V SSL Server
//I IPsec
//L SSL CA
//A Any CA
//Y Verify CA
//S Email signer
//R Email Recipient
//O OCSP status responder
//J Object signer
func (c Certutil) Verify(cert *x509.Certificate) ([]byte, error) {
var certUsage string
switch cert.IsCA {
case true:
certUsage = CertUsageSSLCA
case false:
certUsage = CertUsageSSLServer
}
return execute([]string{
Verify,
VerifySignature,
CertName, fingerprintOf(cert),
CertUsage, certUsage,
CertDbDirectory, c.tmpDir,
})
}
func (c Certutil) ListChain(cert *x509.Certificate) ([]Fingerprint, error) {
out, err := execute([]string{
ListChain,
CertName, fingerprintOf(cert),
CertDbDirectory, c.tmpDir,
})
if err != nil {
return []Fingerprint{}, errors.Wrap(err, string(out))
}
var fingerprints []Fingerprint
fmt.Print(string(out))
for _, link := range bytes.Split(out, []byte{byte('\n')}) {
fingerprints = append(fingerprints, string(bytes.TrimSpace(link)))
}
return fingerprints, nil
}
func (c Certutil) Delete() error {
return os.RemoveAll(c.tmpDir)
}
func execute(args []string, input ...byte) ([]byte, error) {
cmd := exec.Command(executable, args...)
cmd.Stdin = bytes.NewBuffer(input)
out, err := cmd.CombinedOutput()
return bytes.TrimSpace(out), err
}
func fingerprintOf(cert *x509.Certificate) Fingerprint {
hasher := crypto.SHA256.New()
hasher.Write(cert.Raw)
return fmt.Sprintf("%x", hasher.Sum(nil))
}