forked from coreos/etcd-ca
-
Notifications
You must be signed in to change notification settings - Fork 0
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
Merge pull request coreos#6 from unihorn/31
feat(pkix): add certificate signing request and certificate for host
- Loading branch information
Showing
18 changed files
with
1,224 additions
and
89 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,76 @@ | ||
package pkix | ||
|
||
import ( | ||
"crypto/rand" | ||
"crypto/x509" | ||
"crypto/x509/pkix" | ||
"math/big" | ||
"time" | ||
) | ||
|
||
var ( | ||
// Build CA based on RFC5280 | ||
hostTemplate = x509.Certificate{ | ||
// **SHOULD** be filled in a unique number | ||
SerialNumber: big.NewInt(0), | ||
// **SHOULD** be filled in host info | ||
Subject: pkix.Name{}, | ||
// NotBefore is set to be 10min earlier to fix gap on time difference in cluster | ||
NotBefore: time.Now().Add(-600).UTC(), | ||
// 10-year lease | ||
NotAfter: time.Now().AddDate(10, 0, 0).UTC(), | ||
// Used for certificate signing only | ||
KeyUsage: 0, | ||
|
||
ExtKeyUsage: []x509.ExtKeyUsage{ | ||
x509.ExtKeyUsageServerAuth, | ||
x509.ExtKeyUsageClientAuth, | ||
}, | ||
UnknownExtKeyUsage: nil, | ||
|
||
// activate CA | ||
BasicConstraintsValid: false, | ||
|
||
// 160-bit SHA-1 hash of the value of the BIT STRING subjectPublicKey | ||
// (excluding the tag, length, and number of unused bits) | ||
// **SHOULD** be filled in later | ||
SubjectKeyId: nil, | ||
|
||
// Subject Alternative Name | ||
DNSNames: nil, | ||
|
||
PermittedDNSDomainsCritical: false, | ||
PermittedDNSDomains: nil, | ||
} | ||
) | ||
|
||
// CreateCertificateHost creates certificate for host. | ||
// The arguments include CA certificate, CA certificate info, CA key, certificate request. | ||
func CreateCertificateHost(crtAuth *Certificate, info *CertificateAuthorityInfo, keyAuth *Key, csr *CertificateSigningRequest) (*Certificate, error) { | ||
hostTemplate.SerialNumber.Set(info.SerialNumber) | ||
info.IncSerialNumber() | ||
|
||
rawCsr, err := csr.GetRawCertificateSigningRequest() | ||
if err != nil { | ||
return nil, err | ||
} | ||
|
||
hostTemplate.Subject = rawCsr.Subject | ||
|
||
hostTemplate.SubjectKeyId, err = GenerateSubjectKeyId(rawCsr.PublicKey) | ||
if err != nil { | ||
return nil, err | ||
} | ||
|
||
rawCrtAuth, err := crtAuth.GetRawCertificate() | ||
if err != nil { | ||
return nil, err | ||
} | ||
|
||
crtHostBytes, err := x509.CreateCertificate(rand.Reader, &hostTemplate, rawCrtAuth, rawCsr.PublicKey, keyAuth.Private) | ||
if err != nil { | ||
return nil, err | ||
} | ||
|
||
return NewCertificateFromDER(crtHostBytes), nil | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,48 @@ | ||
package pkix | ||
|
||
import ( | ||
"testing" | ||
) | ||
|
||
func TestCreateCertificateHost(t *testing.T) { | ||
crtAuth, err := NewCertificateFromPEM([]byte(certAuthPEM)) | ||
if err != nil { | ||
t.Fatal("Failed to parse certificate from PEM:", err) | ||
} | ||
|
||
key, err := NewKeyFromRSAPrivateKeyPEM([]byte(rsaPrivKeyAuthPEM)) | ||
if err != nil { | ||
t.Fatal("Failed parsing RSA private key:", err) | ||
} | ||
|
||
csr, err := NewCertificateSigningRequestFromPEM([]byte(csrPEM)) | ||
if err != nil { | ||
t.Fatal("Failed parsing certificate request from PEM:", err) | ||
} | ||
|
||
crt, err := CreateCertificateHost(crtAuth, NewCertificateAuthorityInfo(authStartSerialNumber), key, csr) | ||
if err != nil { | ||
t.Fatal("Failed creating certificate for host:", err) | ||
} | ||
|
||
rawCrt, err := crt.GetRawCertificate() | ||
if err != nil { | ||
t.Fatal("Failed to get x509.Certificate:", err) | ||
} | ||
|
||
rawCrtAuth, err := crtAuth.GetRawCertificate() | ||
if err != nil { | ||
t.Fatal("Failed to get x509.Certificate:", err) | ||
} | ||
if err = rawCrt.CheckSignatureFrom(rawCrtAuth); err != nil { | ||
t.Fatal("Failed to check signature:", err) | ||
} | ||
|
||
if err = rawCrt.VerifyHostname(csrHostname); err != nil { | ||
t.Fatal("Failed to verify hostname:", err) | ||
} | ||
|
||
if rawCrt.SerialNumber.Uint64() != authStartSerialNumber { | ||
t.Fatal("Expect serial number %v instead of %v", authStartSerialNumber, rawCrt.SerialNumber) | ||
} | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,34 @@ | ||
package pkix | ||
|
||
import ( | ||
"math/big" | ||
) | ||
|
||
// CertificateAuthorityInfo includes extra information required for CA | ||
type CertificateAuthorityInfo struct { | ||
// SerialNumber that has been used so far | ||
// Recorded to ensure all serial numbers issued by the CA are different | ||
SerialNumber *big.Int | ||
} | ||
|
||
func NewCertificateAuthorityInfo(serialNumber int64) *CertificateAuthorityInfo { | ||
return &CertificateAuthorityInfo{big.NewInt(serialNumber)} | ||
} | ||
|
||
func NewCertificateAuthorityInfoFromJSON(data []byte) (*CertificateAuthorityInfo, error) { | ||
i := big.NewInt(0) | ||
|
||
if err := i.UnmarshalJSON(data); err != nil { | ||
return nil, err | ||
} | ||
|
||
return &CertificateAuthorityInfo{i}, nil | ||
} | ||
|
||
func (n *CertificateAuthorityInfo) IncSerialNumber() { | ||
n.SerialNumber.Add(n.SerialNumber, big.NewInt(1)) | ||
} | ||
|
||
func (n *CertificateAuthorityInfo) Export() ([]byte, error) { | ||
return n.SerialNumber.MarshalJSON() | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,44 @@ | ||
package pkix | ||
|
||
import ( | ||
"encoding/base64" | ||
"testing" | ||
) | ||
|
||
const ( | ||
serialNumber = 10 | ||
infoBASE64 = "MTA=" | ||
) | ||
|
||
func TestCertificateAuthorityInfo(t *testing.T) { | ||
i := NewCertificateAuthorityInfo(serialNumber) | ||
|
||
i.IncSerialNumber() | ||
if i.SerialNumber.Uint64() != serialNumber+1 { | ||
t.Fatal("Failed incrementing serial number") | ||
} | ||
} | ||
|
||
func TestCertificateAuthorityInfoFromJSON(t *testing.T) { | ||
data, err := base64.StdEncoding.DecodeString(infoBASE64) | ||
if err != nil { | ||
t.Fatal("Failed decoding base64 string:", err) | ||
} | ||
|
||
i, err := NewCertificateAuthorityInfoFromJSON(data) | ||
if err != nil { | ||
t.Fatal("Failed init CertificateAuthorityInfo:", err) | ||
} | ||
|
||
if i.SerialNumber.Uint64() != serialNumber { | ||
t.Fatal("Failed getting correct serial number") | ||
} | ||
|
||
b, err := i.Export() | ||
if err != nil { | ||
t.Fatal("Failed exporting info:", err) | ||
} | ||
if base64.StdEncoding.EncodeToString(b) != infoBASE64 { | ||
t.Fatal("Failed exporting correct info") | ||
} | ||
} |
Oops, something went wrong.