blob: 8128a8664dd8cc3cfef029b2848895ce0a316b33 [file] [log] [blame]
/*
* Licensed to the Apache Software Foundation (ASF) under one or more
* contributor license agreements. See the NOTICE file distributed with
* this work for additional information regarding copyright ownership.
* The ASF licenses this file to You 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 tls
import (
"crypto"
"crypto/ecdsa"
"crypto/elliptic"
"crypto/rand"
"crypto/x509"
"crypto/x509/pkix"
"math/big"
"net"
"time"
)
import (
"github.com/pkg/errors"
)
import (
"github.com/apache/dubbo-kubernetes/pkg/core"
util_rsa "github.com/apache/dubbo-kubernetes/pkg/util/rsa"
)
var DefaultValidityPeriod = 10 * 365 * 24 * time.Hour
type CertType string
const (
ServerCertType CertType = "server"
ClientCertType CertType = "client"
DefaultAllowedClockSkew = 5 * time.Minute
DefaultCACertValidityPeriod = 10 * 365 * 24 * time.Hour
)
type KeyType func() (crypto.Signer, error)
var ECDSAKeyType KeyType = func() (crypto.Signer, error) {
return ecdsa.GenerateKey(elliptic.P256(), rand.Reader)
}
var RSAKeyType KeyType = func() (crypto.Signer, error) {
return util_rsa.GenerateKey(util_rsa.DefaultKeySize)
}
var DefaultKeyType = RSAKeyType
func NewSelfSignedCert(certType CertType, keyType KeyType, hosts ...string) (KeyPair, error) {
key, err := keyType()
if err != nil {
return KeyPair{}, errors.Wrap(err, "failed to generate TLS key")
}
csr, err := newCert(nil, certType, hosts...)
if err != nil {
return KeyPair{}, err
}
certDerBytes, err := x509.CreateCertificate(rand.Reader, &csr, &csr, key.Public(), key)
if err != nil {
return KeyPair{}, errors.Wrap(err, "failed to generate TLS certificate")
}
certBytes, err := pemEncodeCert(certDerBytes)
if err != nil {
return KeyPair{}, err
}
keyBytes, err := pemEncodeKey(key)
if err != nil {
return KeyPair{}, err
}
return KeyPair{
CertPEM: certBytes,
KeyPEM: keyBytes,
}, nil
}
// NewCert generates certificate that is signed by the CA (parent)
func NewCert(
parent x509.Certificate,
parentKey crypto.Signer,
certType CertType,
keyType KeyType,
hosts ...string,
) (KeyPair, error) {
key, err := keyType()
if err != nil {
return KeyPair{}, errors.Wrap(err, "failed to generate TLS key")
}
csr, err := newCert(&parent.Subject, certType, hosts...)
if err != nil {
return KeyPair{}, err
}
certDerBytes, err := x509.CreateCertificate(rand.Reader, &csr, &parent, key.Public(), parentKey)
if err != nil {
return KeyPair{}, errors.Wrap(err, "failed to generate TLS certificate")
}
certBytes, err := pemEncodeCert(certDerBytes)
if err != nil {
return KeyPair{}, err
}
keyBytes, err := pemEncodeKey(key)
if err != nil {
return KeyPair{}, err
}
return KeyPair{
CertPEM: certBytes,
KeyPEM: keyBytes,
}, nil
}
func newCert(issuer *pkix.Name, certType CertType, hosts ...string) (x509.Certificate, error) {
notBefore := time.Now()
notAfter := notBefore.Add(DefaultValidityPeriod)
serialNumberLimit := new(big.Int).Lsh(big.NewInt(1), 128)
serialNumber, err := rand.Int(rand.Reader, serialNumberLimit)
if err != nil {
return x509.Certificate{}, errors.Wrap(err, "failed to generate serial number")
}
csr := x509.Certificate{
SerialNumber: serialNumber,
Subject: pkix.Name{},
NotBefore: notBefore,
NotAfter: notAfter,
IsCA: issuer == nil,
KeyUsage: x509.KeyUsageKeyEncipherment | x509.KeyUsageDigitalSignature,
ExtKeyUsage: []x509.ExtKeyUsage{},
BasicConstraintsValid: true,
}
if issuer != nil {
csr.Issuer = *issuer
} else {
// root ca
csr.KeyUsage |= x509.KeyUsageCertSign
}
switch certType {
case ServerCertType:
csr.ExtKeyUsage = append(csr.ExtKeyUsage, x509.ExtKeyUsageServerAuth)
case ClientCertType:
csr.ExtKeyUsage = append(csr.ExtKeyUsage, x509.ExtKeyUsageClientAuth)
default:
return x509.Certificate{}, errors.Errorf("invalid certificate type %q, expected either %q or %q",
certType, ServerCertType, ClientCertType)
}
for _, host := range hosts {
if ip := net.ParseIP(host); ip != nil {
csr.IPAddresses = append(csr.IPAddresses, ip)
} else {
csr.DNSNames = append(csr.DNSNames, host)
}
}
return csr, nil
}
func GenerateCA(keyType KeyType, subject pkix.Name) (*KeyPair, error) {
key, err := keyType()
if err != nil {
return nil, errors.Wrap(err, "failed to generate a private key")
}
now := core.Now()
notBefore := now.Add(-DefaultAllowedClockSkew)
notAfter := now.Add(DefaultCACertValidityPeriod)
caTemplate := &x509.Certificate{
SerialNumber: big.NewInt(0),
Subject: subject,
NotBefore: notBefore,
NotAfter: notAfter,
KeyUsage: x509.KeyUsageCertSign | x509.KeyUsageCRLSign,
BasicConstraintsValid: true,
IsCA: true,
PublicKey: key.Public(),
}
ca, err := x509.CreateCertificate(rand.Reader, caTemplate, caTemplate, key.Public(), key)
if err != nil {
return nil, err
}
return ToKeyPair(key, ca)
}