pkg/authority/cert/util.go (246 lines of code) (raw):
// 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 cert
import (
"bytes"
"crypto/ecdsa"
"crypto/elliptic"
"crypto/rand"
"crypto/x509"
"crypto/x509/pkix"
"encoding/asn1"
"encoding/pem"
"log"
"math/big"
"net/url"
"time"
"github.com/apache/dubbo-admin/pkg/authority/rule"
"github.com/apache/dubbo-admin/pkg/logger"
)
const (
UNITag int = 6
)
// The OID for the SAN extension (See
// http://www.alvestrand.no/objectid/2.5.29.17.html).
var oidSubjectAlternativeName = asn1.ObjectIdentifier{2, 5, 29, 17}
func DecodeCert(cert string) *x509.Certificate {
block, _ := pem.Decode([]byte(cert))
if block == nil {
logger.Sugar().Warnf("Failed to parse public key.")
return nil
}
p, err := x509.ParseCertificate(block.Bytes)
if err != nil {
logger.Sugar().Warnf("Failed to parse public key. " + err.Error())
return nil
}
return p
}
func DecodePrivateKey(cert string) *ecdsa.PrivateKey {
block, _ := pem.Decode([]byte(cert))
if block == nil {
logger.Sugar().Warnf("Failed to parse private key.")
return nil
}
p, err := x509.ParseECPrivateKey(block.Bytes)
if err != nil {
logger.Sugar().Warnf("Failed to parse private key. " + err.Error())
return nil
}
return p
}
func GenerateAuthorityCert(rootCert *Cert, caValidity int64) *Cert {
cert := &x509.Certificate{
SerialNumber: big.NewInt(2019),
Subject: pkix.Name{
CommonName: "Dubbo RA",
Organization: []string{"Apache Dubbo"},
},
Issuer: pkix.Name{
CommonName: "Dubbo CA",
Organization: []string{"Apache Dubbo"},
},
NotBefore: time.Now(),
NotAfter: time.Now().Add(time.Duration(caValidity) * time.Millisecond),
IsCA: true,
ExtKeyUsage: []x509.ExtKeyUsage{x509.ExtKeyUsageAny},
KeyUsage: x509.KeyUsageDigitalSignature | x509.KeyUsageCertSign,
BasicConstraintsValid: true,
}
privateKey, err := ecdsa.GenerateKey(elliptic.P256(), rand.Reader)
if err != nil {
log.Fatal(err)
}
caBytes, err := x509.CreateCertificate(rand.Reader, cert, cert, &privateKey.PublicKey, privateKey)
if err != nil {
log.Fatal(err)
}
caPEM := new(bytes.Buffer)
err = pem.Encode(caPEM, &pem.Block{
Type: "CERTIFICATE",
Bytes: caBytes,
})
if err != nil {
logger.Sugar().Warnf("Failed to encode certificate. " + err.Error())
panic(err)
}
return &Cert{
Cert: DecodeCert(caPEM.String()),
CertPem: caPEM.String(),
PrivateKey: privateKey,
}
}
func SignServerCert(authorityCert *Cert, serverName []string, certValidity int64) *Cert {
privateKey, err := ecdsa.GenerateKey(elliptic.P256(), rand.Reader)
if err != nil {
log.Fatal(err)
}
cert := &x509.Certificate{
SerialNumber: big.NewInt(2019),
Issuer: authorityCert.Cert.Subject,
Subject: pkix.Name{
CommonName: "Dubbo",
Organization: []string{"Apache Dubbo"},
},
NotBefore: time.Now(),
NotAfter: time.Now().Add(time.Duration(certValidity) * time.Millisecond),
KeyUsage: x509.KeyUsageDigitalSignature,
ExtKeyUsage: []x509.ExtKeyUsage{x509.ExtKeyUsageServerAuth},
}
cert.DNSNames = serverName
c, err := x509.CreateCertificate(rand.Reader, cert, authorityCert.Cert, &privateKey.PublicKey, authorityCert.PrivateKey)
if err != nil {
log.Fatal(err)
}
certPem := new(bytes.Buffer)
err = pem.Encode(certPem, &pem.Block{
Type: "CERTIFICATE",
Bytes: c,
})
if err != nil {
logger.Sugar().Warnf("Failed to encode certificate. " + err.Error())
panic(err)
}
return &Cert{
Cert: cert,
CertPem: certPem.String(),
PrivateKey: privateKey,
}
}
func GenerateCSR() (string, *ecdsa.PrivateKey, error) {
csrTemplate := x509.CertificateRequest{
Subject: pkix.Name{
CommonName: "Dubbo",
Organization: []string{"Apache Dubbo"},
},
}
privateKey, err := ecdsa.GenerateKey(elliptic.P256(), rand.Reader)
if err != nil {
log.Fatal(err)
return "", nil, err
}
csrBytes, err := x509.CreateCertificateRequest(rand.Reader, &csrTemplate, privateKey)
if err != nil {
return "", nil, err
}
csr := new(bytes.Buffer)
err = pem.Encode(csr, &pem.Block{
Type: "CERTIFICATE REQUEST",
Bytes: csrBytes,
})
if err != nil {
logger.Sugar().Warnf("Failed to encode certificate. " + err.Error())
return "", nil, err
}
return csr.String(), privateKey, nil
}
func LoadCSR(csrString string) (*x509.CertificateRequest, error) {
block, _ := pem.Decode([]byte(csrString))
if block == nil {
return nil, nil
}
csr, err := x509.ParseCertificateRequest(block.Bytes)
if err != nil {
return nil, err
}
return csr, nil
}
func SignFromCSR(csr *x509.CertificateRequest, endpoint *rule.Endpoint, authorityCert *Cert, certValidity int64) (string, error) {
csrTemplate := &x509.Certificate{
PublicKeyAlgorithm: csr.PublicKeyAlgorithm,
PublicKey: csr.PublicKey,
SerialNumber: big.NewInt(3),
Issuer: authorityCert.Cert.Subject,
Subject: csr.Subject,
NotBefore: time.Now(),
NotAfter: time.Now().Add(time.Duration(certValidity) * time.Millisecond),
KeyUsage: x509.KeyUsageDigitalSignature,
ExtKeyUsage: []x509.ExtKeyUsage{x509.ExtKeyUsageAny},
}
if endpoint != nil {
AppendEndpoint(endpoint, csrTemplate)
}
// TODO support ecdsa
result, err := x509.CreateCertificate(rand.Reader, csrTemplate, authorityCert.Cert, csrTemplate.PublicKey, authorityCert.PrivateKey)
if err != nil {
return "", err
}
certPem := new(bytes.Buffer)
err = pem.Encode(certPem, &pem.Block{
Type: "CERTIFICATE",
Bytes: result,
})
if err != nil {
return "", err
}
cert := certPem.String()
return cert, nil
}
func AppendEndpoint(endpoint *rule.Endpoint, cert *x509.Certificate) {
if endpoint.ID != "" {
cert.Subject.CommonName = endpoint.ID
}
if endpoint.SpiffeID != "" {
spiffeId, err := url.Parse(endpoint.SpiffeID)
if err != nil {
logger.Sugar().Warnf("failed to parse the spiffe id (err: %s)", err)
return
}
cert.URIs = append(cert.URIs, spiffeId)
}
}
func EncodePrivateKey(caPrivKey *ecdsa.PrivateKey) string {
caPrivKeyPEM := new(bytes.Buffer)
pri, err := x509.MarshalECPrivateKey(caPrivKey)
if err != nil {
logger.Sugar().Warnf("Failed to marshal EC private key. " + err.Error())
return ""
}
err = pem.Encode(caPrivKeyPEM, &pem.Block{
Type: "EC PRIVATE KEY",
Bytes: pri,
})
if err != nil {
logger.Sugar().Warnf("Failed to encode private key. " + err.Error())
return ""
}
return caPrivKeyPEM.String()
}
func EncodePublicKey(pub *ecdsa.PublicKey) (res string) {
caPrivKeyPEM := new(bytes.Buffer)
defer func() {
if err := recover(); err != nil {
logger.Sugar().Warnf("Failed to marshal EC public key. %v", err)
res = ""
}
}()
pri, err := x509.MarshalPKIXPublicKey(pub)
if err != nil {
logger.Sugar().Warnf("Failed to marshal EC public key. " + err.Error())
return ""
}
err = pem.Encode(caPrivKeyPEM, &pem.Block{
Type: "EC PUBLIC KEY",
Bytes: pri,
})
if err != nil {
logger.Sugar().Warnf("Failed to encode public key. " + err.Error())
return ""
}
return caPrivKeyPEM.String()
}