Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
57 changes: 36 additions & 21 deletions internal/identity/identity.go
Original file line number Diff line number Diff line change
Expand Up @@ -29,33 +29,17 @@ type Identity struct {

// Generate creates a new self-signed X.509 certificate and ECDSA private key.
func Generate(shortName string) (*Identity, error) {
priv, err := ecdsa.GenerateKey(elliptic.P256(), rand.Reader)
priv, err := newPrivateKey()
if err != nil {
return nil, fmt.Errorf("failed to generate private key: %w", err)
return nil, err
}

notBefore := time.Now()
notAfter := notBefore.Add(365 * 24 * time.Hour)

serialNumberLimit := new(big.Int).Lsh(big.NewInt(1), 128)
serialNumber, err := rand.Int(rand.Reader, serialNumberLimit)
template, err := newCertificateTemplate(shortName)
if err != nil {
return nil, fmt.Errorf("failed to generate serial number: %w", err)
return nil, err
}

template := x509.Certificate{
SerialNumber: serialNumber,
Subject: pkix.Name{
CommonName: shortName,
},
NotBefore: notBefore,
NotAfter: notAfter,
KeyUsage: x509.KeyUsageKeyEncipherment | x509.KeyUsageDigitalSignature,
ExtKeyUsage: []x509.ExtKeyUsage{x509.ExtKeyUsageServerAuth, x509.ExtKeyUsageClientAuth},
BasicConstraintsValid: true,
}

derBytes, err := x509.CreateCertificate(rand.Reader, &template, &template, &priv.PublicKey, priv)
derBytes, err := x509.CreateCertificate(rand.Reader, template, template, &priv.PublicKey, priv)
if err != nil {
return nil, fmt.Errorf("failed to create certificate: %w", err)
}
Expand Down Expand Up @@ -167,6 +151,37 @@ func MarshalCertificate(cert *x509.Certificate) []byte {
return pem.EncodeToMemory(&pem.Block{Type: "CERTIFICATE", Bytes: cert.Raw})
}

func newPrivateKey() (*ecdsa.PrivateKey, error) {
priv, err := ecdsa.GenerateKey(elliptic.P256(), rand.Reader)
if err != nil {
return nil, fmt.Errorf("failed to generate private key: %w", err)
}
return priv, nil
}

func newCertificateTemplate(shortName string) (*x509.Certificate, error) {
notBefore := time.Now()
notAfter := notBefore.Add(365 * 24 * time.Hour)

serialNumberLimit := new(big.Int).Lsh(big.NewInt(1), 128)
serialNumber, err := rand.Int(rand.Reader, serialNumberLimit)
if err != nil {
return nil, fmt.Errorf("failed to generate serial number: %w", err)
}

return &x509.Certificate{
SerialNumber: serialNumber,
Subject: pkix.Name{
CommonName: shortName,
},
NotBefore: notBefore,
NotAfter: notAfter,
KeyUsage: x509.KeyUsageKeyEncipherment | x509.KeyUsageDigitalSignature,
ExtKeyUsage: []x509.ExtKeyUsage{x509.ExtKeyUsageServerAuth, x509.ExtKeyUsageClientAuth},
BasicConstraintsValid: true,
}, nil
}

// UnmarshalCertificate imports a certificate from PEM format.
func UnmarshalCertificate(data []byte) (*x509.Certificate, error) {
block, _ := pem.Decode(data)
Expand Down
29 changes: 29 additions & 0 deletions internal/identity/identity_test.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,29 @@
// SPDX-License-Identifier: Apache-2.0

package identity

import (
"testing"
)

func TestGenerate(t *testing.T) {
shortName := "test-agent"
ident, err := Generate(shortName)
if err != nil {
t.Fatalf("Generate() failed: %v", err)
}

if ident == nil {
t.Fatal("Generate() returned nil identity")
}

if ident.Certificate == nil {
t.Error("Generate() returned identity with nil certificate")
} else if ident.Certificate.Subject.CommonName != shortName {
t.Errorf("Generate() certificate common name = %q, want %q", ident.Certificate.Subject.CommonName, shortName)
}

if ident.PrivateKey == nil {
t.Error("Generate() returned identity with nil private key")
}
}
Loading