/
service.go
136 lines (114 loc) · 3.87 KB
/
service.go
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
package scepserver
import (
"context"
"crypto/rsa"
"crypto/x509"
"errors"
"github.com/inverse-inc/scep/scep"
"github.com/go-kit/kit/log"
)
// Service is the interface for all supported SCEP server operations.
type Service interface {
// GetCACaps returns a list of options
// which are supported by the server.
GetCACaps(ctx context.Context) ([]byte, error)
// GetCACert returns CA certificate or
// a CA certificate chain with intermediates
// in a PKCS#7 Degenerate Certificates format
// message is an optional string for the CA
GetCACert(ctx context.Context, message string) ([]byte, int, error)
// PKIOperation handles incoming SCEP messages such as PKCSReq and
// sends back a CertRep PKIMessag.
PKIOperation(ctx context.Context, msg []byte) ([]byte, error)
// GetNextCACert returns a replacement certificate or certificate chain
// when the old one expires. The response format is a PKCS#7 Degenerate
// Certificates type.
GetNextCACert(ctx context.Context) ([]byte, error)
}
type service struct {
// The service certificate and key for SCEP exchanges. These are
// quite likely the same as the CA keypair but may be its own SCEP
// specific keypair in the case of e.g. RA (proxy) operation.
crt *x509.Certificate
key *rsa.PrivateKey
// Optional additional CA certificates for e.g. RA (proxy) use.
// Only used in this service when responding to GetCACert.
addlCa []*x509.Certificate
// The (chainable) CSR signing function. Intended to handle all
// SCEP request functionality such as CSR & challenge checking, CA
// issuance, RA proxying, etc.
signer CSRSigner
/// info logging is implemented in the service middleware layer.
debugLogger log.Logger
}
func (svc *service) GetCACaps(ctx context.Context) ([]byte, error) {
defaultCaps := []byte("Renewal\nSHA-1\nSHA-256\nAES\nDES3\nSCEPStandard\nPOSTPKIOperation")
return defaultCaps, nil
}
func (svc *service) GetCACert(ctx context.Context, _ string) ([]byte, int, error) {
if svc.crt == nil {
return nil, 0, errors.New("missing CA certificate")
}
if len(svc.addlCa) < 1 {
return svc.crt.Raw, 1, nil
}
certs := []*x509.Certificate{svc.crt}
certs = append(certs, svc.addlCa...)
data, err := scep.DegenerateCertificates(certs)
return data, len(svc.addlCa) + 1, err
}
func (svc *service) PKIOperation(ctx context.Context, data []byte) ([]byte, error) {
msg, err := scep.ParsePKIMessage(data, scep.WithLogger(svc.debugLogger))
if err != nil {
return nil, err
}
if err := msg.DecryptPKIEnvelope(svc.crt, svc.key); err != nil {
return nil, err
}
crt, err := svc.signer.SignCSR(msg.CSRReqMessage)
if err == nil && crt == nil {
err = errors.New("no signed certificate")
}
if err != nil {
svc.debugLogger.Log("msg", "failed to sign CSR", "err", err)
certRep, err := msg.Fail(svc.crt, svc.key, scep.BadRequest)
return certRep.Raw, err
}
certRep, err := msg.Success(svc.crt, svc.key, crt)
return certRep.Raw, err
}
func (svc *service) GetNextCACert(ctx context.Context) ([]byte, error) {
panic("not implemented")
}
// ServiceOption is a server configuration option
type ServiceOption func(*service) error
// WithLogger configures a logger for the SCEP Service.
// By default, a no-op logger is used.
func WithLogger(logger log.Logger) ServiceOption {
return func(s *service) error {
s.debugLogger = logger
return nil
}
}
// WithAddlCA appends an additional certificate to the slice of CA certs
func WithAddlCA(ca *x509.Certificate) ServiceOption {
return func(s *service) error {
s.addlCa = append(s.addlCa, ca)
return nil
}
}
// NewService creates a new scep service
func NewService(crt *x509.Certificate, key *rsa.PrivateKey, signer CSRSigner, opts ...ServiceOption) (Service, error) {
s := &service{
crt: crt,
key: key,
signer: signer,
debugLogger: log.NewNopLogger(),
}
for _, opt := range opts {
if err := opt(s); err != nil {
return nil, err
}
}
return s, nil
}