@@ -7,89 +7,162 @@ package pki
77
88import (
99 "context"
10+ "crypto"
1011 "crypto/x509/pkix"
11- "io"
12+ "fmt"
13+ "math/big"
1214 "net/http"
1315 "time"
1416
17+ "go.osspkg.com/ioutils"
18+
1519 "go.osspkg.com/encrypt/pki/internal/xocsp"
16- "golang.org/x/crypto/ocsp"
1720)
1821
1922type OCSPStatusResolver interface {
20- OCSPStatusResolve (ctx context.Context , r * ocsp. Request ) (OCSPStatus , error )
23+ OCSPStatusResolve (ctx context.Context , r * OCSPRequest ) (* OCSPResponse , error )
2124}
2225
2326type OCSPStatus int
2427
2528const (
26- OCSPStatusUnknown OCSPStatus = ocsp .Unknown
27- OCSPStatusGood OCSPStatus = ocsp .Good
28- OCSPStatusRevoked OCSPStatus = ocsp .Revoked
29- OCSPStatusServerFailed OCSPStatus = ocsp .ServerFailed
29+ OCSPStatusGood OCSPStatus = xocsp .Good
30+ OCSPStatusUnknown OCSPStatus = xocsp .Unknown
31+ OCSPStatusRevoked OCSPStatus = xocsp .Revoked
3032)
3133
32- type OCSPServer struct {
33- CA Certificate
34- Resolver OCSPStatusResolver
35- UpdateInterval time.Duration
36- }
34+ type OCSPRevocationReason int
3735
38- func (v * OCSPServer ) HTTPHandler (w http.ResponseWriter , r * http.Request ) {
39- defer r .Body .Close () //nolint:errcheck
40- raw , err := io .ReadAll (r .Body )
41- if err != nil {
42- http .Error (w , "invalid request" , http .StatusBadRequest )
43- return
44- }
36+ const (
37+ // OCSPRevocationReasonUnspecified
38+ // Unspecified (code 0): A general, default reason when a more specific one isn't applicable.
39+ OCSPRevocationReasonUnspecified OCSPRevocationReason = 0
40+ // OCSPRevocationReasonKeyCompromise
41+ // Key Compromise (code 1): The most critical reason, indicating that the
42+ // private key associated with the certificate has been compromised or is suspected of being compromised.
43+ OCSPRevocationReasonKeyCompromise OCSPRevocationReason = 1
44+ // OCSPRevocationReasonCACompromise
45+ // CA Compromise (code 2): The certificate authority that issued the certificate has been compromised.
46+ OCSPRevocationReasonCACompromise OCSPRevocationReason = 2
47+ // OCSPRevocationReasonAffiliationChanged
48+ // Affiliation Changed (code 3): The certificate holder's relationship with the organization has changed,
49+ // such as termination of employment.
50+ OCSPRevocationReasonAffiliationChanged OCSPRevocationReason = 3
51+ // OCSPRevocationReasonSuperseded
52+ // Superseded (code 4): The certificate has been replaced by a new one,
53+ // often because of a normal lifecycle event like a password change or a legal name change.
54+ OCSPRevocationReasonSuperseded OCSPRevocationReason = 4
55+ // OCSPRevocationReasonCessationOfOperation
56+ // Cessation of Operation (code 5): The system or service for which the certificate was issued is no longer in
57+ // operation.
58+ OCSPRevocationReasonCessationOfOperation OCSPRevocationReason = 5
59+ // OCSPRevocationReasonCertificateHold
60+ // Certificate Hold (code 6): Used for temporary invalidation, such as when a certificate's status is under review.
61+ OCSPRevocationReasonCertificateHold OCSPRevocationReason = 6
62+ )
4563
46- req , err := xocsp .ParseRequest (raw )
47- if err != nil {
48- http .Error (w , "invalid ocsp data" , http .StatusBadRequest )
49- return
64+ type (
65+ OCSPServer struct {
66+ CA Certificate
67+ Resolver OCSPStatusResolver
68+ UpdateInterval time.Duration
69+ OnError func (err error )
5070 }
51-
52- var nonce [] byte
53- for _ , extension := range req . Extensions {
54- if extension . Id . Equal ( xocsp . OIDNonce ) {
55- nonce = extension . Value
56- }
71+ OCSPRequest struct {
72+ HashAlgorithm crypto. Hash
73+ IssuerNameHash [] byte
74+ IssuerKeyHash [] byte
75+ SerialNumber * big. Int
76+ Extensions []pkix. Extension
5777 }
5878
59- status , err := v .Resolver .OCSPStatusResolve (r .Context (), & ocsp.Request {
60- HashAlgorithm : req .HashAlgorithm ,
61- IssuerNameHash : req .IssuerNameHash ,
62- IssuerKeyHash : req .IssuerKeyHash ,
63- SerialNumber : req .SerialNumber ,
64- })
65- if err != nil {
66- http .Error (w , err .Error (), http .StatusInternalServerError )
67- return
79+ OCSPResponse struct {
80+ Status OCSPStatus
81+ RevokedAt time.Time
82+ RevocationReason OCSPRevocationReason
6883 }
84+ )
85+
86+ func (v * OCSPServer ) HTTPHandler (w http.ResponseWriter , r * http.Request ) {
87+ reqStatus := xocsp .Success
6988
7089 template := xocsp.Response {
71- Status : int (status ),
72- SerialNumber : req .SerialNumber ,
73- ThisUpdate : time .Now (),
74- NextUpdate : time .Now ().Add (v .UpdateInterval ),
75- ProducedAt : time .Now (),
76- Certificate : v .CA .Crt ,
90+ Status : int (OCSPStatusUnknown ),
91+ ThisUpdate : time .Now ().Truncate (time .Minute ).UTC (),
92+ NextUpdate : time .Now ().Add (v .UpdateInterval ).Truncate (time .Minute ).UTC (),
93+ Certificate : v .CA .Crt ,
7794 }
7895
79- if len (nonce ) > 0 {
80- template .Extensions = append (template .Extensions , pkix.Extension {
81- Id : xocsp .OIDNonce ,
82- Critical : false ,
83- Value : nonce ,
84- })
96+ var (
97+ err error
98+ raw []byte
99+ )
100+
101+ if raw , err = ioutils .ReadAll (r .Body ); err == nil {
102+
103+ var req * xocsp.Request
104+ if req , err = xocsp .ParseRequest (raw ); err == nil {
105+
106+ template .SerialNumber = req .SerialNumber
107+
108+ for _ , extension := range req .Extensions {
109+ if extension .Id .Equal (xocsp .OIDNonce ) {
110+ template .Extensions = append (template .Extensions , pkix.Extension {
111+ Id : xocsp .OIDNonce ,
112+ Critical : false ,
113+ Value : extension .Value ,
114+ })
115+ break
116+ }
117+ }
118+
119+ var resp * OCSPResponse
120+ if resp , err = v .Resolver .OCSPStatusResolve (r .Context (), & OCSPRequest {
121+ HashAlgorithm : req .HashAlgorithm ,
122+ IssuerNameHash : req .IssuerNameHash ,
123+ IssuerKeyHash : req .IssuerKeyHash ,
124+ SerialNumber : req .SerialNumber ,
125+ Extensions : req .Extensions ,
126+ }); err == nil {
127+
128+ template .Status = int (resp .Status )
129+
130+ if resp .Status == OCSPStatusRevoked {
131+ template .RevokedAt = resp .RevokedAt
132+
133+ switch resp .RevocationReason {
134+ case OCSPRevocationReasonKeyCompromise , OCSPRevocationReasonCACompromise ,
135+ OCSPRevocationReasonAffiliationChanged , OCSPRevocationReasonSuperseded ,
136+ OCSPRevocationReasonCessationOfOperation , OCSPRevocationReasonCertificateHold :
137+ template .RevocationReason = int (resp .RevocationReason )
138+ default :
139+ template .RevocationReason = int (OCSPRevocationReasonUnspecified )
140+ }
141+ }
142+ }
143+ }
144+ }
145+
146+ if err != nil {
147+ if v .OnError != nil {
148+ v .OnError (fmt .Errorf ("ocsp: request processing: %w" , err ))
149+ }
150+ reqStatus = xocsp .InternalError
85151 }
86152
87- resp , err := xocsp .CreateResponse (v .CA .Crt , v .CA .Crt , template , v .CA .Key )
153+ resp , err := xocsp .CreateResponse (reqStatus , v .CA .Crt , v .CA .Crt , template , v .CA .Key )
88154 if err != nil {
89- http .Error (w , err .Error (), http .StatusInternalServerError )
155+ if v .OnError != nil {
156+ v .OnError (fmt .Errorf ("ocsp: create response: %w" , err ))
157+ }
158+ http .Error (w , "internal error" , http .StatusInternalServerError )
90159 return
91160 }
92161
93162 w .Header ().Set ("Content-Type" , "application/ocsp-response" )
94- w .Write (resp ) //nolint:errcheck
163+ if _ , err = w .Write (resp ); err != nil {
164+ if v .OnError != nil {
165+ v .OnError (fmt .Errorf ("ocsp: write response: %w" , err ))
166+ }
167+ }
95168}
0 commit comments