summaryrefslogtreecommitdiff
path: root/key_validator.go
diff options
context:
space:
mode:
Diffstat (limited to 'key_validator.go')
-rw-r--r--key_validator.go144
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 @@
1package main
2
3import (
4 "crypto/rsa"
5 "crypto/x509"
6 "encoding/pem"
7 "fmt"
8 "gopkg.in/square/go-jose.v2"
9 "io/ioutil"
10)
11
12type KeyValidator interface {
13 Validate(jose.JSONWebKey) error
14 LoadRootPEM(string) error
15}
16
17type keyValidator struct {
18 pkiSubject string
19 algorithms *stringSet
20 roots *x509.CertPool
21}
22
23func NewKeyValidator(subject string) KeyValidator {
24 return &keyValidator{
25 pkiSubject: subject,
26 algorithms: NewStringSet("PS256", "PS385", "PS512"),
27 roots: x509.NewCertPool(),
28 }
29}
30
31func (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
52func (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
98func (v *keyValidator) validateCertificateCRL(cert *x509.Certificate) error {
99 return nil
100}
101
102func (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
134func (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}