-
-
Notifications
You must be signed in to change notification settings - Fork 2
/
ciscert.go
146 lines (122 loc) · 3.92 KB
/
ciscert.go
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
package fiskalhrgo
// SPDX-License-Identifier: MIT
// Copyright (c) 2024 L. D. T. d.o.o.
// Copyright (c) contributors for their respective contributions. See https://github.com/l-d-t/fiskalhrgo/graphs/contributors
import (
"crypto/x509"
"embed"
"encoding/pem"
"errors"
"fmt"
"path/filepath"
"time"
)
// Embed demo certs
//
//go:embed certDemo/democis*.pem
var demoCISCert embed.FS
// Embed production certs
//
//go:embed certProd/fiskalcis*.pem
var prodCISCert embed.FS
// type signatureCheckCIScert holds the public key, issuer, subject, serial number, and validity dates
// of a CIS certificate to check signature on CIS responses. It also holds the SSL verify pool
type signatureCheckCIScert struct {
PublicCert *x509.Certificate
Subject string
Serial string
Issuer string
ValidFrom time.Time
ValidUntil time.Time
SSLverifyPoll *x509.CertPool
}
// parseAndVerifyEmbeddedCerts parses the embedded certificates, verifies the chain, and returns the public key of the newest valid certificate
func parseAndVerifyEmbeddedCerts(certFS embed.FS, dir string, pattern string) (*signatureCheckCIScert, error) {
var newestCert *x509.Certificate
var sslpool *x509.CertPool
// Read the embedded certificate files
certFiles, err := certFS.ReadDir(dir)
if err != nil {
return nil, fmt.Errorf("failed to read embedded cert files: %w", err)
}
for _, certFile := range certFiles {
if certFile.IsDir() {
continue // Skip directories
}
if match, _ := filepath.Match(pattern, certFile.Name()); !match {
continue
}
certData, err := certFS.ReadFile(dir + "/" + certFile.Name())
if err != nil {
return nil, fmt.Errorf("failed to read cert file %s: %w", certFile.Name(), err)
}
// Parse the certificates
var certs []*x509.Certificate
for {
block, rest := pem.Decode(certData)
if block == nil {
break
}
if block.Type != "CERTIFICATE" {
return nil, errors.New("invalid PEM block type")
}
cert, err := x509.ParseCertificate(block.Bytes)
if err != nil {
return nil, fmt.Errorf("failed to parse certificate: %w", err)
}
certs = append(certs, cert)
certData = rest
}
sslpool = x509.NewCertPool()
// Verify the certificate chain
roots := x509.NewCertPool()
intermediates := x509.NewCertPool()
// Add the root certificate to the roots pool
roots.AddCert(certs[len(certs)-1])
sslpool.AddCert(certs[len(certs)-1])
// Add intermediate certificates to the intermediates pool
for i := 1; i < len(certs)-1; i++ {
intermediates.AddCert(certs[i])
sslpool.AddCert(certs[i])
}
opts := x509.VerifyOptions{
Roots: roots,
Intermediates: intermediates,
CurrentTime: time.Now(),
KeyUsages: []x509.ExtKeyUsage{x509.ExtKeyUsageAny},
}
leafCert := certs[0]
if _, err := leafCert.Verify(opts); err != nil {
continue // Skip invalid certificate chains
}
// Check if the certificate is valid and not expired
now := time.Now()
if now.Before(leafCert.NotBefore) || now.After(leafCert.NotAfter) {
continue // Skip expired or not yet valid certificates
}
// Update the newest valid certificate
if newestCert == nil || leafCert.NotBefore.After(newestCert.NotBefore) {
newestCert = leafCert
}
}
if newestCert == nil {
return nil, errors.New("no suitable certificate found")
}
return &signatureCheckCIScert{
PublicCert: newestCert,
Subject: newestCert.Subject.String(),
Serial: newestCert.SerialNumber.String(),
Issuer: newestCert.Issuer.String(),
ValidFrom: newestCert.NotBefore,
ValidUntil: newestCert.NotAfter,
SSLverifyPoll: sslpool,
}, nil
}
// Get demo public key
func getDemoPublicKey() (*signatureCheckCIScert, error) {
return parseAndVerifyEmbeddedCerts(demoCISCert, "certDemo", "democis*.pem")
}
// Get production public key
func getProductionPublicKey() (*signatureCheckCIScert, error) {
return parseAndVerifyEmbeddedCerts(prodCISCert, "certProd", "fiskalcis*.pem")
}