416 lines
11 KiB
Go
416 lines
11 KiB
Go
|
// Copyright (C) 2017. See AUTHORS.
|
||
|
//
|
||
|
// Licensed under the Apache License, Version 2.0 (the "License");
|
||
|
// you may not use this file except in compliance with the License.
|
||
|
// You may obtain a copy of the License at
|
||
|
//
|
||
|
// http://www.apache.org/licenses/LICENSE-2.0
|
||
|
//
|
||
|
// Unless required by applicable law or agreed to in writing, software
|
||
|
// distributed under the License is distributed on an "AS IS" BASIS,
|
||
|
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||
|
// See the License for the specific language governing permissions and
|
||
|
// limitations under the License.
|
||
|
|
||
|
package openssl
|
||
|
|
||
|
// #include "shim.h"
|
||
|
import "C"
|
||
|
|
||
|
import (
|
||
|
"errors"
|
||
|
"io/ioutil"
|
||
|
"math/big"
|
||
|
"runtime"
|
||
|
"time"
|
||
|
"unsafe"
|
||
|
)
|
||
|
|
||
|
type EVP_MD int
|
||
|
|
||
|
const (
|
||
|
EVP_NULL EVP_MD = iota
|
||
|
EVP_MD5 EVP_MD = iota
|
||
|
EVP_MD4 EVP_MD = iota
|
||
|
EVP_SHA EVP_MD = iota
|
||
|
EVP_SHA1 EVP_MD = iota
|
||
|
EVP_DSS EVP_MD = iota
|
||
|
EVP_DSS1 EVP_MD = iota
|
||
|
EVP_MDC2 EVP_MD = iota
|
||
|
EVP_RIPEMD160 EVP_MD = iota
|
||
|
EVP_SHA224 EVP_MD = iota
|
||
|
EVP_SHA256 EVP_MD = iota
|
||
|
EVP_SHA384 EVP_MD = iota
|
||
|
EVP_SHA512 EVP_MD = iota
|
||
|
)
|
||
|
|
||
|
// X509_Version represents a version on an x509 certificate.
|
||
|
type X509_Version int
|
||
|
|
||
|
// Specify constants for x509 versions because the standard states that they
|
||
|
// are represented internally as one lower than the common version name.
|
||
|
const (
|
||
|
X509_V1 X509_Version = 0
|
||
|
X509_V3 X509_Version = 2
|
||
|
)
|
||
|
|
||
|
type Certificate struct {
|
||
|
x *C.X509
|
||
|
Issuer *Certificate
|
||
|
ref interface{}
|
||
|
pubKey PublicKey
|
||
|
}
|
||
|
|
||
|
type CertificateInfo struct {
|
||
|
Serial *big.Int
|
||
|
Issued time.Duration
|
||
|
Expires time.Duration
|
||
|
Country string
|
||
|
Organization string
|
||
|
CommonName string
|
||
|
}
|
||
|
|
||
|
type Name struct {
|
||
|
name *C.X509_NAME
|
||
|
}
|
||
|
|
||
|
// Allocate and return a new Name object.
|
||
|
func NewName() (*Name, error) {
|
||
|
n := C.X509_NAME_new()
|
||
|
if n == nil {
|
||
|
return nil, errors.New("could not create x509 name")
|
||
|
}
|
||
|
name := &Name{name: n}
|
||
|
runtime.SetFinalizer(name, func(n *Name) {
|
||
|
C.X509_NAME_free(n.name)
|
||
|
})
|
||
|
return name, nil
|
||
|
}
|
||
|
|
||
|
// AddTextEntry appends a text entry to an X509 NAME.
|
||
|
func (n *Name) AddTextEntry(field, value string) error {
|
||
|
cfield := C.CString(field)
|
||
|
defer C.free(unsafe.Pointer(cfield))
|
||
|
cvalue := (*C.uchar)(unsafe.Pointer(C.CString(value)))
|
||
|
defer C.free(unsafe.Pointer(cvalue))
|
||
|
ret := C.X509_NAME_add_entry_by_txt(
|
||
|
n.name, cfield, C.MBSTRING_ASC, cvalue, -1, -1, 0)
|
||
|
if ret != 1 {
|
||
|
return errors.New("failed to add x509 name text entry")
|
||
|
}
|
||
|
return nil
|
||
|
}
|
||
|
|
||
|
// AddTextEntries allows adding multiple entries to a name in one call.
|
||
|
func (n *Name) AddTextEntries(entries map[string]string) error {
|
||
|
for f, v := range entries {
|
||
|
if err := n.AddTextEntry(f, v); err != nil {
|
||
|
return err
|
||
|
}
|
||
|
}
|
||
|
return nil
|
||
|
}
|
||
|
|
||
|
// GetEntry returns a name entry based on NID. If no entry, then ("", false) is
|
||
|
// returned.
|
||
|
func (n *Name) GetEntry(nid NID) (entry string, ok bool) {
|
||
|
entrylen := C.X509_NAME_get_text_by_NID(n.name, C.int(nid), nil, 0)
|
||
|
if entrylen == -1 {
|
||
|
return "", false
|
||
|
}
|
||
|
buf := (*C.char)(C.malloc(C.size_t(entrylen + 1)))
|
||
|
defer C.free(unsafe.Pointer(buf))
|
||
|
C.X509_NAME_get_text_by_NID(n.name, C.int(nid), buf, entrylen+1)
|
||
|
return C.GoStringN(buf, entrylen), true
|
||
|
}
|
||
|
|
||
|
// NewCertificate generates a basic certificate based
|
||
|
// on the provided CertificateInfo struct
|
||
|
func NewCertificate(info *CertificateInfo, key PublicKey) (*Certificate, error) {
|
||
|
c := &Certificate{x: C.X509_new()}
|
||
|
runtime.SetFinalizer(c, func(c *Certificate) {
|
||
|
C.X509_free(c.x)
|
||
|
})
|
||
|
|
||
|
name, err := c.GetSubjectName()
|
||
|
if err != nil {
|
||
|
return nil, err
|
||
|
}
|
||
|
err = name.AddTextEntries(map[string]string{
|
||
|
"C": info.Country,
|
||
|
"O": info.Organization,
|
||
|
"CN": info.CommonName,
|
||
|
})
|
||
|
if err != nil {
|
||
|
return nil, err
|
||
|
}
|
||
|
// self-issue for now
|
||
|
if err := c.SetIssuerName(name); err != nil {
|
||
|
return nil, err
|
||
|
}
|
||
|
if err := c.SetSerial(info.Serial); err != nil {
|
||
|
return nil, err
|
||
|
}
|
||
|
if err := c.SetIssueDate(info.Issued); err != nil {
|
||
|
return nil, err
|
||
|
}
|
||
|
if err := c.SetExpireDate(info.Expires); err != nil {
|
||
|
return nil, err
|
||
|
}
|
||
|
if err := c.SetPubKey(key); err != nil {
|
||
|
return nil, err
|
||
|
}
|
||
|
return c, nil
|
||
|
}
|
||
|
|
||
|
func (c *Certificate) GetSubjectName() (*Name, error) {
|
||
|
n := C.X509_get_subject_name(c.x)
|
||
|
if n == nil {
|
||
|
return nil, errors.New("failed to get subject name")
|
||
|
}
|
||
|
return &Name{name: n}, nil
|
||
|
}
|
||
|
|
||
|
func (c *Certificate) GetIssuerName() (*Name, error) {
|
||
|
n := C.X509_get_issuer_name(c.x)
|
||
|
if n == nil {
|
||
|
return nil, errors.New("failed to get issuer name")
|
||
|
}
|
||
|
return &Name{name: n}, nil
|
||
|
}
|
||
|
|
||
|
func (c *Certificate) SetSubjectName(name *Name) error {
|
||
|
if C.X509_set_subject_name(c.x, name.name) != 1 {
|
||
|
return errors.New("failed to set subject name")
|
||
|
}
|
||
|
return nil
|
||
|
}
|
||
|
|
||
|
// SetIssuer updates the stored Issuer cert
|
||
|
// and the internal x509 Issuer Name of a certificate.
|
||
|
// The stored Issuer reference is used when adding extensions.
|
||
|
func (c *Certificate) SetIssuer(issuer *Certificate) error {
|
||
|
name, err := issuer.GetSubjectName()
|
||
|
if err != nil {
|
||
|
return err
|
||
|
}
|
||
|
if err = c.SetIssuerName(name); err != nil {
|
||
|
return err
|
||
|
}
|
||
|
c.Issuer = issuer
|
||
|
return nil
|
||
|
}
|
||
|
|
||
|
// SetIssuerName populates the issuer name of a certificate.
|
||
|
// Use SetIssuer instead, if possible.
|
||
|
func (c *Certificate) SetIssuerName(name *Name) error {
|
||
|
if C.X509_set_issuer_name(c.x, name.name) != 1 {
|
||
|
return errors.New("failed to set subject name")
|
||
|
}
|
||
|
return nil
|
||
|
}
|
||
|
|
||
|
// SetSerial sets the serial of a certificate.
|
||
|
func (c *Certificate) SetSerial(serial *big.Int) error {
|
||
|
sno := C.ASN1_INTEGER_new()
|
||
|
defer C.ASN1_INTEGER_free(sno)
|
||
|
bn := C.BN_new()
|
||
|
defer C.BN_free(bn)
|
||
|
|
||
|
serialBytes := serial.Bytes()
|
||
|
if bn = C.BN_bin2bn((*C.uchar)(unsafe.Pointer(&serialBytes[0])), C.int(len(serialBytes)), bn); bn == nil {
|
||
|
return errors.New("failed to set serial")
|
||
|
}
|
||
|
if sno = C.BN_to_ASN1_INTEGER(bn, sno); sno == nil {
|
||
|
return errors.New("failed to set serial")
|
||
|
}
|
||
|
if C.X509_set_serialNumber(c.x, sno) != 1 {
|
||
|
return errors.New("failed to set serial")
|
||
|
}
|
||
|
return nil
|
||
|
}
|
||
|
|
||
|
// SetIssueDate sets the certificate issue date relative to the current time.
|
||
|
func (c *Certificate) SetIssueDate(when time.Duration) error {
|
||
|
offset := C.long(when / time.Second)
|
||
|
result := C.X509_gmtime_adj(C.X_X509_get0_notBefore(c.x), offset)
|
||
|
if result == nil {
|
||
|
return errors.New("failed to set issue date")
|
||
|
}
|
||
|
return nil
|
||
|
}
|
||
|
|
||
|
// SetExpireDate sets the certificate issue date relative to the current time.
|
||
|
func (c *Certificate) SetExpireDate(when time.Duration) error {
|
||
|
offset := C.long(when / time.Second)
|
||
|
result := C.X509_gmtime_adj(C.X_X509_get0_notAfter(c.x), offset)
|
||
|
if result == nil {
|
||
|
return errors.New("failed to set expire date")
|
||
|
}
|
||
|
return nil
|
||
|
}
|
||
|
|
||
|
// SetPubKey assigns a new public key to a certificate.
|
||
|
func (c *Certificate) SetPubKey(pubKey PublicKey) error {
|
||
|
c.pubKey = pubKey
|
||
|
if C.X509_set_pubkey(c.x, pubKey.evpPKey()) != 1 {
|
||
|
return errors.New("failed to set public key")
|
||
|
}
|
||
|
return nil
|
||
|
}
|
||
|
|
||
|
// Sign a certificate using a private key and a digest name.
|
||
|
// Accepted digest names are 'sha256', 'sha384', and 'sha512'.
|
||
|
func (c *Certificate) Sign(privKey PrivateKey, digest EVP_MD) error {
|
||
|
switch digest {
|
||
|
case EVP_SHA256:
|
||
|
case EVP_SHA384:
|
||
|
case EVP_SHA512:
|
||
|
default:
|
||
|
return errors.New("Unsupported digest" +
|
||
|
"You're probably looking for 'EVP_SHA256' or 'EVP_SHA512'.")
|
||
|
}
|
||
|
return c.insecureSign(privKey, digest)
|
||
|
}
|
||
|
|
||
|
func (c *Certificate) insecureSign(privKey PrivateKey, digest EVP_MD) error {
|
||
|
var md *C.EVP_MD = getDigestFunction(digest)
|
||
|
if C.X509_sign(c.x, privKey.evpPKey(), md) <= 0 {
|
||
|
return errors.New("failed to sign certificate")
|
||
|
}
|
||
|
return nil
|
||
|
}
|
||
|
|
||
|
func getDigestFunction(digest EVP_MD) (md *C.EVP_MD) {
|
||
|
switch digest {
|
||
|
// please don't use these digest functions
|
||
|
case EVP_NULL:
|
||
|
md = C.X_EVP_md_null()
|
||
|
case EVP_MD5:
|
||
|
md = C.X_EVP_md5()
|
||
|
case EVP_SHA:
|
||
|
md = C.X_EVP_sha()
|
||
|
case EVP_SHA1:
|
||
|
md = C.X_EVP_sha1()
|
||
|
case EVP_DSS:
|
||
|
md = C.X_EVP_dss()
|
||
|
case EVP_DSS1:
|
||
|
md = C.X_EVP_dss1()
|
||
|
case EVP_RIPEMD160:
|
||
|
md = C.X_EVP_ripemd160()
|
||
|
case EVP_SHA224:
|
||
|
md = C.X_EVP_sha224()
|
||
|
// you actually want one of these
|
||
|
case EVP_SHA256:
|
||
|
md = C.X_EVP_sha256()
|
||
|
case EVP_SHA384:
|
||
|
md = C.X_EVP_sha384()
|
||
|
case EVP_SHA512:
|
||
|
md = C.X_EVP_sha512()
|
||
|
}
|
||
|
return md
|
||
|
}
|
||
|
|
||
|
// Add an extension to a certificate.
|
||
|
// Extension constants are NID_* as found in openssl.
|
||
|
func (c *Certificate) AddExtension(nid NID, value string) error {
|
||
|
issuer := c
|
||
|
if c.Issuer != nil {
|
||
|
issuer = c.Issuer
|
||
|
}
|
||
|
var ctx C.X509V3_CTX
|
||
|
C.X509V3_set_ctx(&ctx, c.x, issuer.x, nil, nil, 0)
|
||
|
ex := C.X509V3_EXT_conf_nid(nil, &ctx, C.int(nid), C.CString(value))
|
||
|
if ex == nil {
|
||
|
return errors.New("failed to create x509v3 extension")
|
||
|
}
|
||
|
defer C.X509_EXTENSION_free(ex)
|
||
|
if C.X509_add_ext(c.x, ex, -1) <= 0 {
|
||
|
return errors.New("failed to add x509v3 extension")
|
||
|
}
|
||
|
return nil
|
||
|
}
|
||
|
|
||
|
// Wraps AddExtension using a map of NID to text extension.
|
||
|
// Will return without finishing if it encounters an error.
|
||
|
func (c *Certificate) AddExtensions(extensions map[NID]string) error {
|
||
|
for nid, value := range extensions {
|
||
|
if err := c.AddExtension(nid, value); err != nil {
|
||
|
return err
|
||
|
}
|
||
|
}
|
||
|
return nil
|
||
|
}
|
||
|
|
||
|
// LoadCertificateFromPEM loads an X509 certificate from a PEM-encoded block.
|
||
|
func LoadCertificateFromPEM(pem_block []byte) (*Certificate, error) {
|
||
|
if len(pem_block) == 0 {
|
||
|
return nil, errors.New("empty pem block")
|
||
|
}
|
||
|
runtime.LockOSThread()
|
||
|
defer runtime.UnlockOSThread()
|
||
|
bio := C.BIO_new_mem_buf(unsafe.Pointer(&pem_block[0]),
|
||
|
C.int(len(pem_block)))
|
||
|
cert := C.PEM_read_bio_X509(bio, nil, nil, nil)
|
||
|
C.BIO_free(bio)
|
||
|
if cert == nil {
|
||
|
return nil, errorFromErrorQueue()
|
||
|
}
|
||
|
x := &Certificate{x: cert}
|
||
|
runtime.SetFinalizer(x, func(x *Certificate) {
|
||
|
C.X509_free(x.x)
|
||
|
})
|
||
|
return x, nil
|
||
|
}
|
||
|
|
||
|
// MarshalPEM converts the X509 certificate to PEM-encoded format
|
||
|
func (c *Certificate) MarshalPEM() (pem_block []byte, err error) {
|
||
|
bio := C.BIO_new(C.BIO_s_mem())
|
||
|
if bio == nil {
|
||
|
return nil, errors.New("failed to allocate memory BIO")
|
||
|
}
|
||
|
defer C.BIO_free(bio)
|
||
|
if int(C.PEM_write_bio_X509(bio, c.x)) != 1 {
|
||
|
return nil, errors.New("failed dumping certificate")
|
||
|
}
|
||
|
return ioutil.ReadAll(asAnyBio(bio))
|
||
|
}
|
||
|
|
||
|
// PublicKey returns the public key embedded in the X509 certificate.
|
||
|
func (c *Certificate) PublicKey() (PublicKey, error) {
|
||
|
pkey := C.X509_get_pubkey(c.x)
|
||
|
if pkey == nil {
|
||
|
return nil, errors.New("no public key found")
|
||
|
}
|
||
|
key := &pKey{key: pkey}
|
||
|
runtime.SetFinalizer(key, func(key *pKey) {
|
||
|
C.EVP_PKEY_free(key.key)
|
||
|
})
|
||
|
return key, nil
|
||
|
}
|
||
|
|
||
|
// GetSerialNumberHex returns the certificate's serial number in hex format
|
||
|
func (c *Certificate) GetSerialNumberHex() (serial string) {
|
||
|
asn1_i := C.X509_get_serialNumber(c.x)
|
||
|
bignum := C.ASN1_INTEGER_to_BN(asn1_i, nil)
|
||
|
hex := C.BN_bn2hex(bignum)
|
||
|
serial = C.GoString(hex)
|
||
|
C.BN_free(bignum)
|
||
|
C.X_OPENSSL_free(unsafe.Pointer(hex))
|
||
|
return
|
||
|
}
|
||
|
|
||
|
// GetVersion returns the X509 version of the certificate.
|
||
|
func (c *Certificate) GetVersion() X509_Version {
|
||
|
return X509_Version(C.X_X509_get_version(c.x))
|
||
|
}
|
||
|
|
||
|
// SetVersion sets the X509 version of the certificate.
|
||
|
func (c *Certificate) SetVersion(version X509_Version) error {
|
||
|
cvers := C.long(version)
|
||
|
if C.X_X509_set_version(c.x, cvers) != 1 {
|
||
|
return errors.New("failed to set certificate version")
|
||
|
}
|
||
|
return nil
|
||
|
}
|