@@ -57,14 +57,18 @@ const (
5757 // Draft or likely-to-change paths
5858 renewalInfoPath = "/draft-ietf-acme-ari-03/renewalInfo/"
5959
60- // Theses entrypoints are not a part of the standard ACME endpoints,
60+ // These entrypoints are not a part of the standard ACME endpoints,
6161 // and are exposed by Pebble as an integration test tool. We export
6262 // RootCertPath so that the pebble binary can reference it.
6363 RootCertPath = "/roots/"
6464 rootKeyPath = "/root-keys/"
6565 intermediateCertPath = "/intermediates/"
6666 intermediateKeyPath = "/intermediate-keys/"
6767 certStatusBySerial = "/cert-status-by-serial/"
68+ // Post certificate PEM and desired literal response for renewal info
69+ // (the renewal info response is not validated so may be intentionally
70+ // malformed).
71+ setRenewalInfoPath = "/set-renewal-info/"
6872
6973 // How long do pending authorizations last before expiring?
7074 pendingAuthzExpire = time .Hour
@@ -542,6 +546,9 @@ func (wfe *WebFrontEndImpl) ManagementHandler() http.Handler {
542546 wfe .HandleManagementFunc (m , intermediateCertPath , wfe .handleCert (wfe .ca .GetIntermediateCert , intermediateCertPath ))
543547 wfe .HandleManagementFunc (m , intermediateKeyPath , wfe .handleKey (wfe .ca .GetIntermediateKey , intermediateKeyPath ))
544548 wfe .HandleManagementFunc (m , certStatusBySerial , wfe .handleCertStatusBySerial )
549+
550+ // POST only handlers
551+ wfe .HandleFunc (m , setRenewalInfoPath , wfe .SetRenewalInfo , http .MethodPost )
545552 return m
546553}
547554
@@ -1903,7 +1910,18 @@ func (wfe *WebFrontEndImpl) RenewalInfo(_ context.Context, response http.Respons
19031910 return
19041911 }
19051912
1906- renewalInfo , err := wfe .determineARIWindow (certID )
1913+ cert := wfe .db .GetCertificateBySerial (certID .SerialNumber )
1914+ if cert == nil {
1915+ wfe .sendError (acme .NotFoundProblem ("failed to retrieve existing certificate serial" ), response )
1916+ return
1917+ }
1918+
1919+ if cert .ARIResponse != "" {
1920+ _ , _ = response .Write ([]byte (cert .ARIResponse ))
1921+ return
1922+ }
1923+
1924+ renewalInfo , err := wfe .determineARIWindow (certID , cert )
19071925 if err != nil {
19081926 wfe .sendError (acme .InternalErrorProblem (fmt .Sprintf ("Error determining renewal window: %s" , err )), response )
19091927 return
@@ -1917,7 +1935,47 @@ func (wfe *WebFrontEndImpl) RenewalInfo(_ context.Context, response http.Respons
19171935 }
19181936}
19191937
1920- func (wfe * WebFrontEndImpl ) determineARIWindow (id * core.CertID ) (* core.RenewalInfo , error ) {
1938+ // SetRenewalInfo overrides the default ARI response for a certificate.
1939+ func (wfe * WebFrontEndImpl ) SetRenewalInfo (_ context.Context , response http.ResponseWriter , request * http.Request ) {
1940+ body , err := io .ReadAll (request .Body )
1941+ if err != nil {
1942+ wfe .sendError (acme .InternalErrorProblem ("Error reading body" ), response )
1943+ }
1944+
1945+ var reqJSON struct {
1946+ Certificate string // in PEM form
1947+ ARIResponse string // can be anything, even malformed JSON, so that users can test client response to malformed data
1948+ }
1949+ err = json .Unmarshal (body , & reqJSON )
1950+ if err != nil {
1951+ wfe .sendError (acme .MalformedProblem ("Error unmarshaling request body" ), response )
1952+ return
1953+ }
1954+
1955+ // Decode and parse the PEM certificate
1956+ block , _ := pem .Decode ([]byte (reqJSON .Certificate ))
1957+ if block == nil || block .Type != "CERTIFICATE" {
1958+ wfe .sendError (acme .MalformedProblem ("Error decoding certificate PEM" ), response )
1959+ return
1960+ }
1961+
1962+ cert , err := x509 .ParseCertificate (block .Bytes )
1963+ if err != nil {
1964+ wfe .sendError (acme .MalformedProblem ("Error parsing certificate" ), response )
1965+ return
1966+ }
1967+
1968+ // Look up certificate by serial number and update response
1969+ err = wfe .db .SetARIResponse (cert .SerialNumber , reqJSON .ARIResponse )
1970+ if err != nil {
1971+ wfe .sendError (acme .NotFoundProblem (err .Error ()), response )
1972+ return
1973+ }
1974+
1975+ response .WriteHeader (http .StatusOK )
1976+ }
1977+
1978+ func (wfe * WebFrontEndImpl ) determineARIWindow (id * core.CertID , cert * core.Certificate ) (* core.RenewalInfo , error ) {
19211979 if id == nil {
19221980 return nil , errors .New ("CertID was nil" )
19231981 }
@@ -1928,11 +1986,6 @@ func (wfe *WebFrontEndImpl) determineARIWindow(id *core.CertID) (*core.RenewalIn
19281986 return core .RenewalInfoImmediate (time .Now ().In (time .UTC )), nil
19291987 }
19301988
1931- cert := wfe .db .GetCertificateBySerial (id .SerialNumber )
1932- if cert == nil {
1933- return nil , errors .New ("failed to retrieve existing certificate serial" )
1934- }
1935-
19361989 return core .RenewalInfoSimple (cert .Cert .NotBefore , cert .Cert .NotAfter ), nil
19371990}
19381991
0 commit comments