diff options
Diffstat (limited to 'key_validator.go')
-rw-r--r-- | key_validator.go | 144 |
1 files changed, 144 insertions, 0 deletions
diff --git a/key_validator.go b/key_validator.go new file mode 100644 index 0000000..fe6eb7b --- /dev/null +++ b/key_validator.go | |||
@@ -0,0 +1,144 @@ | |||
1 | package main | ||
2 | |||
3 | import ( | ||
4 | "crypto/rsa" | ||
5 | "crypto/x509" | ||
6 | "encoding/pem" | ||
7 | "fmt" | ||
8 | "gopkg.in/square/go-jose.v2" | ||
9 | "io/ioutil" | ||
10 | ) | ||
11 | |||
12 | type KeyValidator interface { | ||
13 | Validate(jose.JSONWebKey) error | ||
14 | LoadRootPEM(string) error | ||
15 | } | ||
16 | |||
17 | type keyValidator struct { | ||
18 | pkiSubject string | ||
19 | algorithms *stringSet | ||
20 | roots *x509.CertPool | ||
21 | } | ||
22 | |||
23 | func NewKeyValidator(subject string) KeyValidator { | ||
24 | return &keyValidator{ | ||
25 | pkiSubject: subject, | ||
26 | algorithms: NewStringSet("PS256", "PS385", "PS512"), | ||
27 | roots: x509.NewCertPool(), | ||
28 | } | ||
29 | } | ||
30 | |||
31 | func (v *keyValidator) LoadRootPEM(filename string) error { | ||
32 | pem_data, err := ioutil.ReadFile(filename) | ||
33 | if err != nil { | ||
34 | return err | ||
35 | } | ||
36 | |||
37 | pem_block, _ := pem.Decode(pem_data) | ||
38 | if pem_block == nil { | ||
39 | return fmt.Errorf("PEM decode failed") | ||
40 | } | ||
41 | |||
42 | cert, err := x509.ParseCertificate(pem_block.Bytes) | ||
43 | if err != nil { | ||
44 | return err | ||
45 | } | ||
46 | |||
47 | v.roots.AddCert(cert) | ||
48 | |||
49 | return nil | ||
50 | } | ||
51 | |||
52 | func (v *keyValidator) Validate(key jose.JSONWebKey) error { | ||
53 | pk, ok := key.Key.(*rsa.PublicKey) | ||
54 | if !ok { | ||
55 | return fmt.Errorf("Key type is not RSA") | ||
56 | } | ||
57 | |||
58 | if !v.algorithms.Contains(key.Algorithm) { | ||
59 | return fmt.Errorf("Key algorithm is not supported") | ||
60 | } | ||
61 | |||
62 | cert := key.Certificates[0] | ||
63 | cpk, ok := cert.PublicKey.(*rsa.PublicKey) | ||
64 | if !ok { | ||
65 | return fmt.Errorf("Public key is not RSA") | ||
66 | } | ||
67 | |||
68 | if cpk.N.BitLen() < 2048 { | ||
69 | return fmt.Errorf("Key length less than 2048 bits") | ||
70 | } | ||
71 | |||
72 | if cert.KeyUsage&x509.KeyUsageDigitalSignature != 1 { | ||
73 | return fmt.Errorf("Certificate not valid for digital signatures") | ||
74 | } | ||
75 | |||
76 | err := v.validateCertificateChain(key.Certificates) | ||
77 | if err != nil { | ||
78 | return err | ||
79 | } | ||
80 | |||
81 | err = v.validateCertificateCRL(cert) | ||
82 | if err != nil { | ||
83 | return err | ||
84 | } | ||
85 | |||
86 | err = v.validatePublicKeyInCertificate(pk, cpk) | ||
87 | if err != nil { | ||
88 | return err | ||
89 | } | ||
90 | |||
91 | return nil | ||
92 | } | ||
93 | |||
94 | // TODO | ||
95 | // Fetch CRL from distrubtion point in cert | ||
96 | // Validate CRL signed by trusted CA | ||
97 | // Validate cert not in CRL | ||
98 | func (v *keyValidator) validateCertificateCRL(cert *x509.Certificate) error { | ||
99 | return nil | ||
100 | } | ||
101 | |||
102 | func (v *keyValidator) validateCertificateChain(chain []*x509.Certificate) error { | ||
103 | vo := x509.VerifyOptions{ | ||
104 | Roots: v.roots, | ||
105 | KeyUsages: []x509.ExtKeyUsage{x509.ExtKeyUsageAny}, | ||
106 | } | ||
107 | |||
108 | if len(chain) > 1 { | ||
109 | ip := x509.NewCertPool() | ||
110 | for _, i := range chain[1:] { | ||
111 | ip.AddCert(i) | ||
112 | } | ||
113 | |||
114 | vo.Intermediates = ip | ||
115 | } | ||
116 | |||
117 | chains, err := chain[0].Verify(vo) | ||
118 | if err != nil { | ||
119 | return err | ||
120 | } | ||
121 | |||
122 | if len(chains) <= 0 { | ||
123 | return fmt.Errorf("No valid certificate chains found") | ||
124 | } | ||
125 | |||
126 | if chain[0].Subject.CommonName != v.pkiSubject { | ||
127 | return fmt.Errorf("Invalid certificate subject name") | ||
128 | } | ||
129 | |||
130 | return nil | ||
131 | } | ||
132 | |||
133 | // validate first item of x5c matches n and e | ||
134 | func (v *keyValidator) validatePublicKeyInCertificate(pk *rsa.PublicKey, cpk *rsa.PublicKey) error { | ||
135 | if cpk.E != pk.E { | ||
136 | return fmt.Errorf("E in key and E in cert do not match") | ||
137 | } | ||
138 | |||
139 | if pk.N.Cmp(cpk.N) != 0 { | ||
140 | return fmt.Errorf("N in key and N in cert do not match") | ||
141 | } | ||
142 | |||
143 | return nil | ||
144 | } | ||