@@ -13,6 +13,7 @@ import (
13
13
"crypto/tls"
14
14
"crypto/x509"
15
15
"crypto/x509/pkix"
16
+ "encoding/base64"
16
17
"encoding/json"
17
18
"encoding/pem"
18
19
"fmt"
@@ -34,6 +35,7 @@ import (
34
35
"github.com/smallstep/certificates/logging"
35
36
"github.com/smallstep/certificates/templates"
36
37
"go.step.sm/crypto/jose"
38
+ "go.step.sm/crypto/x509util"
37
39
"golang.org/x/crypto/ssh"
38
40
)
39
41
@@ -920,32 +922,104 @@ func Test_caHandler_Renew(t *testing.T) {
920
922
cs := & tls.ConnectionState {
921
923
PeerCertificates : []* x509.Certificate {parseCertificate (certPEM )},
922
924
}
925
+
926
+ // Prepare root and leaf for renew after expiry test.
927
+ now := time .Now ()
928
+ rootPub , rootPriv , err := ed25519 .GenerateKey (rand .Reader )
929
+ if err != nil {
930
+ t .Fatal (err )
931
+ }
932
+ leafPub , leafPriv , err := ed25519 .GenerateKey (rand .Reader )
933
+ if err != nil {
934
+ t .Fatal (err )
935
+ }
936
+ root := & x509.Certificate {
937
+ Subject : pkix.Name {CommonName : "Test Root CA" },
938
+ PublicKey : rootPub ,
939
+ KeyUsage : x509 .KeyUsageCertSign ,
940
+ BasicConstraintsValid : true ,
941
+ IsCA : true ,
942
+ NotBefore : now .Add (- 2 * time .Hour ),
943
+ NotAfter : now .Add (time .Hour ),
944
+ }
945
+ root , err = x509util .CreateCertificate (root , root , rootPub , rootPriv )
946
+ if err != nil {
947
+ t .Fatal (err )
948
+ }
949
+ expiredLeaf := & x509.Certificate {
950
+ Subject : pkix.Name {CommonName : "Leaf certificate" },
951
+ PublicKey : leafPub ,
952
+ KeyUsage : x509 .KeyUsageDigitalSignature ,
953
+ ExtKeyUsage : []x509.ExtKeyUsage {x509 .ExtKeyUsageServerAuth , x509 .ExtKeyUsageClientAuth },
954
+ NotBefore : now .Add (- time .Hour ),
955
+ NotAfter : now .Add (- time .Minute ),
956
+ EmailAddresses : []string {"test@example.org" },
957
+ }
958
+ expiredLeaf , err = x509util .CreateCertificate (expiredLeaf , root , leafPub , rootPriv )
959
+ if err != nil {
960
+ t .Fatal (err )
961
+ }
962
+
963
+ // Generate renew after expiry token
964
+ so := new (jose.SignerOptions )
965
+ so .WithType ("JWT" )
966
+ so .WithHeader ("x5cInsecure" , []string {base64 .StdEncoding .EncodeToString (expiredLeaf .Raw )})
967
+ sig , err := jose .NewSigner (jose.SigningKey {Algorithm : jose .EdDSA , Key : leafPriv }, so )
968
+ if err != nil {
969
+ t .Fatal (err )
970
+ }
971
+ generateX5cToken := func (claims jose.Claims ) string {
972
+ s , err := jose .Signed (sig ).Claims (claims ).CompactSerialize ()
973
+ if err != nil {
974
+ t .Fatal (err )
975
+ }
976
+ return s
977
+ }
978
+
923
979
tests := []struct {
924
980
name string
925
981
tls * tls.ConnectionState
982
+ header http.Header
926
983
cert * x509.Certificate
927
984
root * x509.Certificate
928
985
err error
929
986
statusCode int
930
987
}{
931
- {"ok" , cs , parseCertificate (certPEM ), parseCertificate (rootPEM ), nil , http .StatusCreated },
932
- {"no tls" , nil , nil , nil , nil , http .StatusBadRequest },
933
- {"no peer certificates" , & tls.ConnectionState {}, nil , nil , nil , http .StatusBadRequest },
934
- {"renew error" , cs , nil , nil , errs .Forbidden ("an error" ), http .StatusForbidden },
988
+ {"ok" , cs , nil , parseCertificate (certPEM ), parseCertificate (rootPEM ), nil , http .StatusCreated },
989
+ {"ok renew after expiry" , & tls.ConnectionState {}, http.Header {
990
+ "Authorization" : []string {"Bearer " + generateX5cToken (jose.Claims {
991
+ NotBefore : jose .NewNumericDate (now ), Expiry : jose .NewNumericDate (now .Add (5 * time .Minute )),
992
+ })},
993
+ }, expiredLeaf , root , nil , http .StatusCreated },
994
+ {"no tls" , nil , nil , nil , nil , nil , http .StatusBadRequest },
995
+ {"no peer certificates" , & tls.ConnectionState {}, nil , nil , nil , nil , http .StatusBadRequest },
996
+ {"renew error" , cs , nil , nil , nil , errs .Forbidden ("an error" ), http .StatusForbidden },
997
+ {"fail expired token" , & tls.ConnectionState {}, http.Header {
998
+ "Authorization" : []string {"Bearer " + generateX5cToken (jose.Claims {
999
+ NotBefore : jose .NewNumericDate (now .Add (- time .Hour )), Expiry : jose .NewNumericDate (now .Add (- time .Minute )),
1000
+ })},
1001
+ }, expiredLeaf , root , errs .Forbidden ("an error" ), http .StatusUnauthorized },
1002
+ {"fail invalid root" , & tls.ConnectionState {}, http.Header {
1003
+ "Authorization" : []string {"Bearer " + generateX5cToken (jose.Claims {
1004
+ NotBefore : jose .NewNumericDate (now .Add (- time .Hour )), Expiry : jose .NewNumericDate (now .Add (- time .Minute )),
1005
+ })},
1006
+ }, expiredLeaf , parseCertificate (rootPEM ), errs .Forbidden ("an error" ), http .StatusUnauthorized },
935
1007
}
936
1008
937
- expected := []byte (`{"crt":"` + strings .ReplaceAll (certPEM , "\n " , `\n` ) + `\n","ca":"` + strings .ReplaceAll (rootPEM , "\n " , `\n` ) + `\n","certChain":["` + strings .ReplaceAll (certPEM , "\n " , `\n` ) + `\n","` + strings .ReplaceAll (rootPEM , "\n " , `\n` ) + `\n"]}` )
938
-
939
1009
for _ , tt := range tests {
940
1010
t .Run (tt .name , func (t * testing.T ) {
941
1011
h := New (& mockAuthority {
942
1012
ret1 : tt .cert , ret2 : tt .root , err : tt .err ,
1013
+ getRoots : func () ([]* x509.Certificate , error ) {
1014
+ return []* x509.Certificate {tt .root }, nil
1015
+ },
943
1016
getTLSOptions : func () * authority.TLSOptions {
944
1017
return nil
945
1018
},
946
1019
}).(* caHandler )
947
1020
req := httptest .NewRequest ("POST" , "http://example.com/renew" , nil )
948
1021
req .TLS = tt .tls
1022
+ req .Header = tt .header
949
1023
w := httptest .NewRecorder ()
950
1024
h .Renew (logging .NewResponseLogger (w ), req )
951
1025
res := w .Result ()
@@ -960,8 +1034,14 @@ func Test_caHandler_Renew(t *testing.T) {
960
1034
t .Errorf ("caHandler.Renew unexpected error = %v" , err )
961
1035
}
962
1036
if tt .statusCode < http .StatusBadRequest {
1037
+ expected := []byte (`{"crt":"` + strings .ReplaceAll (string (pem .EncodeToMemory (& pem.Block {Type : "CERTIFICATE" , Bytes : tt .cert .Raw })), "\n " , `\n` ) + `",` +
1038
+ `"ca":"` + strings .ReplaceAll (string (pem .EncodeToMemory (& pem.Block {Type : "CERTIFICATE" , Bytes : tt .root .Raw })), "\n " , `\n` ) + `",` +
1039
+ `"certChain":["` +
1040
+ strings .ReplaceAll (string (pem .EncodeToMemory (& pem.Block {Type : "CERTIFICATE" , Bytes : tt .cert .Raw })), "\n " , `\n` ) + `","` +
1041
+ strings .ReplaceAll (string (pem .EncodeToMemory (& pem.Block {Type : "CERTIFICATE" , Bytes : tt .root .Raw })), "\n " , `\n` ) + `"]}` )
1042
+
963
1043
if ! bytes .Equal (bytes .TrimSpace (body ), expected ) {
964
- t .Errorf ("caHandler.Root Body = %s, wants %s" , body , expected )
1044
+ t .Errorf ("caHandler.Root Body = \n %s, wants \n %s" , body , expected )
965
1045
}
966
1046
}
967
1047
})
0 commit comments