Skip to content
File

Blob: spec/pki/pki.go

go109 lines
1package pki
2 
3import (
4 "bytes"
5 "crypto/ed25519"
6 "crypto/rand"
7 "crypto/tls"
8 "crypto/x509"
9 "crypto/x509/pkix"
10 "encoding/pem"
11 "fmt"
12 "math/big"
13 "time"
14 
15 "go.uber.org/zap"
16)
17 
18const (
19 HashcashDifficulty int = 18
20 HashcashExpires time.Duration = time.Second * 10
21 
22 // DefaultCertValidity is the default validity duration for client certificates.
23 DefaultCertValidity time.Duration = time.Hour * 24 * 365 * 5 // 5 years
24)
25 
26type IdentityRequest struct {
27 PublicKey []byte
28 Subject pkix.Name
29 
30 // ValidFor specifies the certificate validity duration.
31 // If zero, defaults to DefaultCertValidity.
32 ValidFor time.Duration
33}
34 
35func GenerateCertificate(logger *zap.Logger, ca tls.Certificate, req IdentityRequest) (derBytes []byte, err error) {
36 if len(req.PublicKey) != ed25519.PublicKeySize {
37 err = fmt.Errorf("pki: public key is not ed25519")
38 return
39 }
40 
41 caCert, err := x509.ParseCertificate(ca.Certificate[0])
42 if err != nil {
43 err = fmt.Errorf("pki: failed to parse client ca: %w", err)
44 return
45 }
46 
47 sn, err := rand.Int(rand.Reader, max)
48 if err != nil {
49 err = fmt.Errorf("pki: failed to generate certificate serial: %w", err)
50 return
51 }
52 
53 now := time.Now()
54 validFor := DefaultCertValidity
55 if req.ValidFor > 0 {
56 validFor = req.ValidFor
57 }
58 notAfter := now.Add(validFor)
59 
60 cert := &x509.Certificate{
61 SerialNumber: sn,
62 Subject: req.Subject,
63 NotBefore: now,
64 NotAfter: notAfter,
65 ExtKeyUsage: []x509.ExtKeyUsage{x509.ExtKeyUsageClientAuth},
66 KeyUsage: x509.KeyUsageDigitalSignature,
67 BasicConstraintsValid: true,
68 IsCA: false,
69 }
70 
71 certBytes, err := x509.CreateCertificate(rand.Reader, cert, caCert, ed25519.PublicKey(req.PublicKey), ca.PrivateKey)
72 if err != nil {
73 err = fmt.Errorf("pki: failed to generate client certificate: %w", err)
74 return
75 }
76 
77 logger.Info("New client certificate issued", zap.String("commonName", req.Subject.CommonName))
78 
79 return certBytes, nil
80}
81 
82func GeneratePrivKey() (privKey ed25519.PublicKey, keyPem string) {
83 certPubKey, certPrivKey, err := ed25519.GenerateKey(rand.Reader)
84 if err != nil {
85 panic(err)
86 }
87 
88 x509PrivKey, err := x509.MarshalPKCS8PrivateKey(certPrivKey)
89 if err != nil {
90 panic(err)
91 }
92 
93 certPrivKeyPEM := new(bytes.Buffer)
94 pem.Encode(certPrivKeyPEM, &pem.Block{
95 Type: "PRIVATE KEY",
96 Bytes: x509PrivKey,
97 })
98 
99 return certPubKey, certPrivKeyPEM.String()
100}
101 
102var (
103 max = new(big.Int)
104)
105 
106func init() {
107 max.Exp(big.NewInt(2), big.NewInt(130), nil).Sub(max, big.NewInt(1))
108}