github.com/TykTechnologies/tyk/certs
No package summary is available.
Package
Files: 1. Third party imports: 1. Imports from organisation: 0. Tests: 0. Benchmarks: 0.
Constants
const (
cacheDefaultTTL = 300 // 5 minutes.
cacheCleanInterval = 600 // 10 minutes.
)
const (
CertificatePrivate CertificateType = iota
CertificatePublic
CertificateAny
)
Vars
var (
CertManagerLogPrefix = "cert_storage"
)
var (
GenCertificate = tykcrypto.GenCertificate
GenServerCertificate = tykcrypto.GenServerCertificate
HexSHA256 = tykcrypto.HexSHA256
)
Types
CertificateBasics
This type doesn't have documentation.
| Field name | Field type | Comment |
|---|---|---|
| ID |
|
No comment on field. |
| IssuerCN |
|
No comment on field. |
| SubjectCN |
|
No comment on field. |
| DNSNames |
|
No comment on field. |
| HasPrivateKey |
|
No comment on field. |
| NotBefore |
|
No comment on field. |
| NotAfter |
|
No comment on field. |
| IsCA |
|
No comment on field. |
type CertificateBasics struct {
ID string `json:"id"`
IssuerCN string `json:"issuer_cn"`
SubjectCN string `json:"subject_cn"`
DNSNames []string `json:"dns_names"`
HasPrivateKey bool `json:"has_private"`
NotBefore time.Time `json:"not_before"`
NotAfter time.Time `json:"not_after"`
IsCA bool `json:"is_ca"`
}
CertificateManager
This type doesn't have documentation.
| Field name | Field type | Comment |
|---|---|---|
| type |
|
No comment on field. |
type CertificateManager interface {
List(certIDs []string, mode CertificateType) (out []*tls.Certificate)
ListPublicKeys(keyIDs []string) (out []string)
ListRawPublicKey(keyID string) (out interface{})
ListAllIds(prefix string) (out []string)
GetRaw(certID string) (string, error)
Add(certData []byte, orgID string) (string, error)
Delete(certID string, orgID string)
CertPool(certIDs []string) *x509.CertPool
FlushCache()
}
CertificateMeta
This type doesn't have documentation.
| Field name | Field type | Comment |
|---|---|---|
| ID |
|
No comment on field. |
| Fingerprint |
|
No comment on field. |
| HasPrivateKey |
|
No comment on field. |
| Issuer |
|
No comment on field. |
| Subject |
|
No comment on field. |
| NotBefore |
|
No comment on field. |
| NotAfter |
|
No comment on field. |
| DNSNames |
|
No comment on field. |
| IsCA |
|
No comment on field. |
type CertificateMeta struct {
ID string `json:"id"`
Fingerprint string `json:"fingerprint"`
HasPrivateKey bool `json:"has_private"`
Issuer pkix.Name `json:"issuer,omitempty"`
Subject pkix.Name `json:"subject,omitempty"`
NotBefore time.Time `json:"not_before,omitempty"`
NotAfter time.Time `json:"not_after,omitempty"`
DNSNames []string `json:"dns_names,omitempty"`
IsCA bool `json:"is_ca"`
}
CertificateType
This type doesn't have documentation.
| Field name | Field type | Comment |
|---|---|---|
| type |
|
No comment on field. |
type CertificateType int
certificateManager
This type doesn't have documentation.
| Field name | Field type | Comment |
|---|---|---|
| storage |
|
No comment on field. |
| logger |
|
No comment on field. |
| cache |
|
No comment on field. |
| secret |
|
No comment on field. |
| migrateCertList |
|
No comment on field. |
type certificateManager struct {
storage storage.Handler
logger *logrus.Entry
cache cache.Repository
secret string
migrateCertList bool
}
Functions
func ExtractCertificateBasics
func ExtractCertificateBasics(cert *tls.Certificate, certID string) *CertificateBasics {
return &CertificateBasics{
ID: certID,
IssuerCN: cert.Leaf.Issuer.CommonName,
SubjectCN: cert.Leaf.Subject.CommonName,
DNSNames: cert.Leaf.DNSNames,
HasPrivateKey: !isPrivateKeyEmpty(cert),
NotAfter: cert.Leaf.NotAfter,
NotBefore: cert.Leaf.NotBefore,
IsCA: cert.Leaf.IsCA,
}
}
Cognitive complexity: 1, Cyclomatic complexity: 1
func ExtractCertificateMeta
func ExtractCertificateMeta(cert *tls.Certificate, certID string) *CertificateMeta {
return &CertificateMeta{
ID: certID,
Fingerprint: string(cert.Leaf.Extensions[0].Value),
HasPrivateKey: !isPrivateKeyEmpty(cert),
Issuer: cert.Leaf.Issuer,
Subject: cert.Leaf.Subject,
NotBefore: cert.Leaf.NotBefore,
NotAfter: cert.Leaf.NotAfter,
DNSNames: cert.Leaf.DNSNames,
IsCA: cert.Leaf.IsCA,
}
}
Cognitive complexity: 1, Cyclomatic complexity: 1
func GetCertIDAndChainPEM
func GetCertIDAndChainPEM(certData []byte, secret string) (string, []byte, error) {
var keyPEM, keyRaw []byte
var publicKeyPem []byte
var certBlocks [][]byte
var certID string
var certChainPEM []byte
rest := certData
for {
var block *pem.Block
block, rest = pem.Decode(rest)
if block == nil {
break
}
if strings.HasSuffix(block.Type, "PRIVATE KEY") {
if len(keyRaw) > 0 {
err := errors.New("Found multiple private keys")
return certID, certChainPEM, err
}
keyRaw = block.Bytes
keyPEM = pem.EncodeToMemory(block)
} else if block.Type == "CERTIFICATE" {
cert, err := x509.ParseCertificate(block.Bytes)
if err != nil {
return certID, certChainPEM, err
}
if cert.NotAfter.Before(time.Now()) {
return certID, certChainPEM, errors.New("certificate is expired")
}
certBlocks = append(certBlocks, pem.EncodeToMemory(block))
} else if block.Type == "PUBLIC KEY" {
publicKeyPem = pem.EncodeToMemory(block)
}
}
certChainPEM = bytes.Join(certBlocks, []byte("\n"))
if len(certChainPEM) == 0 {
if len(publicKeyPem) == 0 {
err := errors.New("Failed to decode certificate. It should be PEM encoded.")
return certID, certChainPEM, err
} else {
certChainPEM = publicKeyPem
}
} else if len(publicKeyPem) > 0 {
err := errors.New("Public keys can't be combined with certificates")
return certID, certChainPEM, err
}
// Found private key, check if it match the certificate
if len(keyPEM) > 0 {
cert, err := tls.X509KeyPair(certChainPEM, keyPEM)
if err != nil {
return certID, certChainPEM, err
}
// Encrypt private key and append it to the chain
encryptedKeyPEMBlock, err := x509.EncryptPEMBlock(rand.Reader, "ENCRYPTED PRIVATE KEY", keyRaw, []byte(secret), x509.PEMCipherAES256)
if err != nil {
return certID, certChainPEM, err
}
certChainPEM = append(certChainPEM, []byte("\n")...)
certChainPEM = append(certChainPEM, pem.EncodeToMemory(encryptedKeyPEMBlock)...)
certID = tykcrypto.HexSHA256(cert.Certificate[0])
} else if len(publicKeyPem) > 0 {
publicKey, _ := pem.Decode(publicKeyPem)
certID = tykcrypto.HexSHA256(publicKey.Bytes)
} else {
// Get first cert
certRaw, _ := pem.Decode(certChainPEM)
cert, err := x509.ParseCertificate(certRaw.Bytes)
if err != nil {
err := errors.New("Error while parsing certificate: " + err.Error())
return certID, certChainPEM, err
}
certID = tykcrypto.HexSHA256(cert.Raw)
}
return certID, certChainPEM, nil
}
Cognitive complexity: 36, Cyclomatic complexity: 17
func NewCertificateManager
func NewCertificateManager(storage storage.Handler, secret string, logger *logrus.Logger, migrateCertList bool) *certificateManager {
if logger == nil {
logger = logrus.New()
}
return &certificateManager{
storage: storage,
logger: logger.WithFields(logrus.Fields{"prefix": CertManagerLogPrefix}),
cache: cache.New(cacheDefaultTTL, cacheCleanInterval),
secret: secret,
migrateCertList: migrateCertList,
}
}
Cognitive complexity: 4, Cyclomatic complexity: 2
func NewSlaveCertManager
func NewSlaveCertManager(localStorage, rpcStorage storage.Handler, secret string, logger *logrus.Logger, migrateCertList bool) *certificateManager {
if logger == nil {
logger = logrus.New()
}
log := logger.WithFields(logrus.Fields{"prefix": CertManagerLogPrefix})
cm := &certificateManager{
logger: log,
cache: cache.New(cacheDefaultTTL, cacheCleanInterval),
secret: secret,
migrateCertList: migrateCertList,
}
callbackOnPullCertFromRPC := func(key, val string) error {
// calculate the orgId from the keyId
certID, _, _ := GetCertIDAndChainPEM([]byte(val), "")
orgID := getOrgFromKeyID(key, certID)
// save the cert in local redis
_, err := cm.Add([]byte(val), orgID)
return err
}
mdcbStorage := storage.NewMdcbStorage(localStorage, rpcStorage, log, callbackOnPullCertFromRPC)
cm.storage = mdcbStorage
return cm
}
Cognitive complexity: 5, Cyclomatic complexity: 2
func ParsePEM
func ParsePEM(data []byte, secret string) ([]*pem.Block, error) {
var pemBlocks []*pem.Block
for {
var block *pem.Block
block, data = pem.Decode(data)
if block == nil {
break
}
if x509.IsEncryptedPEMBlock(block) {
var err error
block.Bytes, err = x509.DecryptPEMBlock(block, []byte(secret))
block.Headers = nil
block.Type = strings.Replace(block.Type, "ENCRYPTED ", "", 1)
if err != nil {
return nil, err
}
}
pemBlocks = append(pemBlocks, block)
}
return pemBlocks, nil
}
Cognitive complexity: 8, Cyclomatic complexity: 5
func ParsePEMCertificate
func ParsePEMCertificate(data []byte, secret string) (*tls.Certificate, error) {
var cert tls.Certificate
blocks, err := ParsePEM(data, secret)
if err != nil {
return nil, err
}
var certID string
for _, block := range blocks {
if block.Type == "CERTIFICATE" {
certID = tykcrypto.HexSHA256(block.Bytes)
cert.Certificate = append(cert.Certificate, block.Bytes)
continue
}
if strings.HasSuffix(block.Type, "PRIVATE KEY") {
cert.PrivateKey, err = parsePrivateKey(block.Bytes)
if err != nil {
return nil, err
}
continue
}
if block.Type == "PUBLIC KEY" {
// Create a dummny cert just for listing purpose
cert.Certificate = append(cert.Certificate, block.Bytes)
cert.Leaf = tykcrypto.PrefixPublicKeyCommonName(block.Bytes)
}
}
if len(cert.Certificate) == 0 {
return nil, errors.New("Can't find CERTIFICATE block")
}
if cert.Leaf == nil {
cert.Leaf, err = x509.ParseCertificate(cert.Certificate[0])
if err != nil {
return nil, err
}
}
// Cache certificate fingerprint
cert.Leaf.Extensions = append([]pkix.Extension{{
Value: []byte(certID),
}}, cert.Leaf.Extensions...)
return &cert, nil
}
Cognitive complexity: 21, Cyclomatic complexity: 10
func (*certificateManager) Add
func (c *certificateManager) Add(certData []byte, orgID string) (string, error) {
certID, certChainPEM, err := GetCertIDAndChainPEM(certData, c.secret)
if err != nil {
c.logger.Error(err)
return "", err
}
certID = orgID + certID
if found, err := c.storage.Exists("raw-" + certID); err == nil && found {
return "", errors.New("Certificate with " + certID + " id already exists")
}
if err := c.storage.SetKey("raw-"+certID, string(certChainPEM), 0); err != nil {
c.logger.Error(err)
return "", err
}
if orgID != "" {
c.storage.AppendToSet(orgID+"-index", "raw-"+certID)
}
return certID, nil
}
Cognitive complexity: 8, Cyclomatic complexity: 6
func (*certificateManager) CertPool
func (c *certificateManager) CertPool(certIDs []string) *x509.CertPool {
pool := x509.NewCertPool()
for _, cert := range c.List(certIDs, CertificatePublic) {
if cert != nil && !tykcrypto.IsPublicKey(cert) {
pool.AddCert(cert.Leaf)
}
}
return pool
}
Cognitive complexity: 5, Cyclomatic complexity: 4
func (*certificateManager) Delete
func (c *certificateManager) Delete(certID string, orgID string) {
if orgID != "" {
c.storage.RemoveFromList(orgID+"-index", "raw-"+certID)
}
c.storage.DeleteKey("raw-" + certID)
c.cache.Delete(certID)
}
Cognitive complexity: 2, Cyclomatic complexity: 2
func (*certificateManager) FlushCache
func (c *certificateManager) FlushCache() {
c.cache.Flush()
}
Cognitive complexity: 0, Cyclomatic complexity: 1
func (*certificateManager) GetRaw
func (c *certificateManager) GetRaw(certID string) (string, error) {
return c.storage.GetKey("raw-" + certID)
}
Cognitive complexity: 0, Cyclomatic complexity: 1
func (*certificateManager) List
func (c *certificateManager) List(certIDs []string, mode CertificateType) (out []*tls.Certificate) {
var cert *tls.Certificate
var rawCert []byte
for _, id := range certIDs {
if cert, found := c.cache.Get(id); found {
if isCertCanBeListed(cert.(*tls.Certificate), mode) {
out = append(out, cert.(*tls.Certificate))
}
continue
}
val, err := c.storage.GetKey("raw-" + id)
// fallback to file
if err != nil {
// Try read from file
rawCert, err = ioutil.ReadFile(id)
if err != nil {
c.logger.Warn("Can't retrieve certificate:", id, err)
out = append(out, nil)
continue
}
} else {
rawCert = []byte(val)
}
cert, err = ParsePEMCertificate(rawCert, c.secret)
if err != nil {
c.logger.Error("Error while parsing certificate: ", id, " ", err)
c.logger.Debug("Failed certificate: ", string(rawCert))
out = append(out, nil)
continue
}
c.cache.Set(id, cert, cache.DefaultExpiration)
if isCertCanBeListed(cert, mode) {
out = append(out, cert)
}
}
return out
}
Cognitive complexity: 17, Cyclomatic complexity: 8
func (*certificateManager) ListAllIds
func (c *certificateManager) ListAllIds(prefix string) (out []string) {
indexKey := prefix + "-index"
exists, _ := c.storage.Exists(indexKey)
if !c.migrateCertList || (exists && prefix != "") {
keys, _ := c.storage.GetListRange(indexKey, 0, -1)
for _, key := range keys {
out = append(out, strings.TrimPrefix(key, "raw-"))
}
} else {
// If list is not exists, but migrated record exists, it means it just empty
if _, err := c.storage.GetKey(indexKey + "-migrated"); err == nil {
return out
}
keys := c.storage.GetKeys("raw-" + prefix + "*")
for _, key := range keys {
if prefix != "" {
c.storage.AppendToSet(indexKey, key)
}
out = append(out, strings.TrimPrefix(key, "raw-"))
}
}
c.storage.SetKey(indexKey+"-migrated", "1", 0)
return out
}
Cognitive complexity: 14, Cyclomatic complexity: 8
func (*certificateManager) ListPublicKeys
Returns list of fingerprints
func (c *certificateManager) ListPublicKeys(keyIDs []string) (out []string) {
var rawKey []byte
var err error
for _, id := range keyIDs {
if fingerprint, found := c.cache.Get("pub-" + id); found {
out = append(out, fingerprint.(string))
continue
}
if isSHA256(id) {
var val string
val, err := c.storage.GetKey("raw-" + id)
if err != nil {
c.logger.Warn("Can't retrieve public key from Redis:", id, err)
out = append(out, "")
continue
}
rawKey = []byte(val)
} else {
rawKey, err = ioutil.ReadFile(id)
if err != nil {
c.logger.Error("Error while reading public key from file:", id, err)
out = append(out, "")
continue
}
}
block, _ := pem.Decode(rawKey)
if block == nil {
c.logger.Error("Can't parse public key:", id)
out = append(out, "")
continue
}
fingerprint := tykcrypto.HexSHA256(block.Bytes)
c.cache.Set("pub-"+id, fingerprint, cache.DefaultExpiration)
out = append(out, fingerprint)
}
return out
}
Cognitive complexity: 15, Cyclomatic complexity: 7
func (*certificateManager) ListRawPublicKey
Returns list of fingerprints
func (c *certificateManager) ListRawPublicKey(keyID string) (out interface{}) {
var rawKey []byte
var err error
if isSHA256(keyID) {
var val string
val, err := c.storage.GetKey("raw-" + keyID)
if err != nil {
c.logger.Warn("Can't retrieve public key from Redis:", keyID, err)
return nil
}
rawKey = []byte(val)
} else {
rawKey, err = ioutil.ReadFile(keyID)
if err != nil {
c.logger.Error("Error while reading public key from file:", keyID, err)
return nil
}
}
block, _ := pem.Decode(rawKey)
if block == nil {
c.logger.Error("Can't parse public key:", keyID)
return nil
}
out, err = x509.ParsePKIXPublicKey(block.Bytes)
if err != nil {
c.logger.Error("Error while parsing public key:", keyID, err)
return nil
}
return out
}
Cognitive complexity: 13, Cyclomatic complexity: 6
Private functions
func getOrgFromKeyID
getOrgFromKeyID (key,certID string) string
References: strings.ReplaceAll.
func isCertCanBeListed
isCertCanBeListed (cert *tls.Certificate, mode CertificateType) bool
func isPrivateKeyEmpty
isPrivateKeyEmpty (cert *tls.Certificate) bool
func isSHA256
isSHA256 (value string) bool
References: hex.DecodeString.
func parsePrivateKey
Extracted from: https://golang.org/src/crypto/tls/tls.go
Attempt to parse the given private key DER block. OpenSSL 0.9.8 generates PKCS#1 private keys by default, while OpenSSL 1.0.0 generates PKCS#8 keys. OpenSSL ecparam generates SEC1 EC private keys for ECDSA. We try all three.
parsePrivateKey (der []byte) (crypto.PrivateKey, error)
References: ecdsa.PrivateKey, errors.New, rsa.PrivateKey, x509.ParseECPrivateKey, x509.ParsePKCS1PrivateKey, x509.ParsePKCS8PrivateKey.
func publicKey
publicKey (priv interface{}) interface{}
References: ecdsa.PrivateKey, rsa.PrivateKey.
func flushStorage
flushStorage ()
Tests
Files: 1. Third party imports: 1. Imports from organisation: 0. Tests: 3. Benchmarks: 0.