Skip to content

Commit 07f5f5c

Browse files
authored
improve cert parsing and formatting (#19)
1 parent 44242a1 commit 07f5f5c

5 files changed

Lines changed: 68 additions & 59 deletions

File tree

app/cert/parse.go

Lines changed: 28 additions & 40 deletions
Original file line numberDiff line numberDiff line change
@@ -1,15 +1,13 @@
11
package cert
22

33
import (
4-
"bytes"
54
"crypto/sha256"
65
"crypto/x509"
76
"encoding/asn1"
87
"encoding/hex"
98
"encoding/pem"
109
"errors"
1110
"log"
12-
"strings"
1311
"time"
1412

1513
"go.mongodb.org/mongo-driver/bson"
@@ -52,8 +50,12 @@ func (c *ParsedCert) ToBson() (bson.M, error) {
5250
if c.Cert == nil {
5351
return nil, errors.New("certificate is nil")
5452
}
53+
rawCertPem := pem.EncodeToMemory(&pem.Block{Type: "CERTIFICATE", Bytes: c.Cert.Raw})
54+
if rawCertPem == nil {
55+
return nil, errors.New("error encoding certificate to PEM format")
56+
}
5557
res := bson.M{
56-
"pem": c.Cert.Raw,
58+
"pem": rawCertPem,
5759
"serial_number": c.Cert.SerialNumber.String(),
5860
"sha256": c.Sha256(),
5961
"registers": c.Registers,
@@ -98,7 +100,8 @@ func (c *ParsedCert) UnmarshalBSON(data []byte) error {
98100
c.Position = raw["position"].(models.Position)
99101

100102
pemData := raw["pem"].([]byte)
101-
p, _ := pem.Decode(pemData) // ignore rest for now, maybe use it later
103+
// ignore rest as we expect only PEM data in the DB
104+
p, _ := pem.Decode(pemData)
102105
if p == nil {
103106
return errors.New("error parsing certificate")
104107
}
@@ -113,44 +116,29 @@ func (c *ParsedCert) CompanyId() string {
113116
return c.Cert.Subject.SerialNumber
114117
}
115118

116-
func ParseCert(data []byte) (*ParsedCert, error) {
117-
data, err := FormatCertContent(data)
118-
if err != nil {
119-
return nil, err
120-
}
121-
p, _ := pem.Decode(data) // ignore rest for now, maybe use it later
122-
if p == nil {
123-
return nil, errors.New("error parsing certificate")
124-
}
125-
x509Cert, err := x509.ParseCertificate(p.Bytes)
126-
if err != nil {
127-
return nil, err
119+
func ParseCerts(data []byte) ([]*ParsedCert, error) {
120+
if len(data) == 0 {
121+
return nil, errors.New("no data provided")
128122
}
129-
var cert ParsedCert
130-
cert.Cert = x509Cert
131-
return &cert, nil
132-
}
133-
134-
func FormatCertContent(content []byte) ([]byte, error) {
135-
certPrefix := "-----BEGIN CERTIFICATE-----"
136-
certSuffix := "-----END CERTIFICATE-----"
137-
pemLineLength := 64
138-
contentString := string(content)
139-
contentString = strings.Replace(contentString, certPrefix, "", 1)
140-
contentString = strings.Replace(contentString, certSuffix, "", 1)
141-
contentString = strings.ReplaceAll(contentString, "\n", "")
142-
contentString = strings.ReplaceAll(contentString, " ", "")
143-
contentString = strings.ReplaceAll(contentString, "\r", "")
144-
var buffer bytes.Buffer
145-
buffer.WriteString(certPrefix)
146-
buffer.WriteString("\n")
147-
for i := 0; i < len(contentString); i += pemLineLength {
148-
end := min(i+pemLineLength, len(contentString))
149-
buffer.WriteString(contentString[i:end])
150-
buffer.WriteString("\n")
123+
certs := []*ParsedCert{}
124+
for {
125+
p, rest := pem.Decode(data)
126+
if p == nil {
127+
return nil, errors.New("error parsing certificate")
128+
}
129+
x509Cert, err := x509.ParseCertificate(p.Bytes)
130+
if err != nil {
131+
return nil, err
132+
}
133+
var cert ParsedCert
134+
cert.Cert = x509Cert
135+
certs = append(certs, &cert)
136+
if len(rest) == 0 {
137+
break
138+
}
139+
data = rest
151140
}
152-
buffer.WriteString(certSuffix)
153-
return buffer.Bytes(), nil
141+
return certs, nil
154142
}
155143

156144
func (c *ParsedCert) OBScopes() ([]models.Scope, error) {

app/verify/verify.go

Lines changed: 10 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -119,13 +119,20 @@ func (s *VerifySvc) Verify(c *gin.Context) {
119119
return
120120
}
121121
result := VerifyResult{}
122-
cert, err := cert.ParseCert(req.Cert)
122+
certs, err := cert.ParseCerts(req.Cert)
123123
if err != nil {
124124
c.JSON(http.StatusBadRequest, gin.H{
125125
"error": err.Error(),
126126
})
127127
return
128128
}
129+
if len(certs) == 0 {
130+
c.JSON(http.StatusBadRequest, gin.H{
131+
"error": "No valid certificate found",
132+
})
133+
return
134+
}
135+
cert := certs[0]
129136
result.Certificate = cert
130137
tpp, err := s.getTpp(c, cert.CompanyId())
131138
if err != nil {
@@ -369,12 +376,12 @@ func (s *VerifySvc) loadCerts(c *gin.Context, body io.ReadCloser) ([]*cert.Parse
369376
log.Printf("Error reading response body: %s", err)
370377
return nil, err
371378
}
372-
crt, err := cert.ParseCert(bodyBytes)
379+
certs, err := cert.ParseCerts(bodyBytes)
373380
if err != nil {
374381
log.Printf("Error parsing certificate: %s", err)
375382
return nil, err
376383
}
377-
return []*cert.ParsedCert{crt}, nil
384+
return certs, nil
378385
}
379386

380387
func (s *VerifySvc) getScopes(c *gin.Context, crt *cert.ParsedCert, tpp *models.TPP) map[string][]string {

app/verify/verify_test.go

Lines changed: 12 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -257,10 +257,11 @@ func TestParseCert(t *testing.T) {
257257
if svc == nil {
258258
t.Fatal("Expected non-nil VerifySvc")
259259
}
260-
cert, err := cert.ParseCert([]byte(certContent))
260+
certs, err := cert.ParseCerts([]byte(certContent))
261261
if err != nil {
262262
t.Fatalf("Expected no error, got %v", err)
263263
}
264+
cert := certs[0]
264265
t.Logf("Parsed certificate: %+v", cert)
265266
if cert.CompanyId() != "12345678" {
266267
t.Errorf("Expected CompanyId '12345678', got '%s'", cert.CompanyId())
@@ -394,10 +395,14 @@ func TestVerifyCert(t *testing.T) {
394395
return
395396
}
396397
svc.AddRoot(caCert)
397-
cert, err := cert.ParseCert([]byte(certContent))
398+
certs, err := cert.ParseCerts([]byte(certContent))
398399
if err != nil {
399400
t.Fatalf("Expected no error, got %v", err)
400401
}
402+
if len(certs) == 0 {
403+
t.Fatal("Expected non-empty certificate slice")
404+
}
405+
cert := certs[0]
401406
t.Logf("Parsed certificate: %+v", cert)
402407
// Simulate a successful verification
403408

@@ -430,10 +435,14 @@ func TestGetScopes(t *testing.T) {
430435
if tpp == nil {
431436
t.Fatal("Expected non-nil TPP")
432437
}
433-
cert, err := cert.ParseCert([]byte(certContent))
438+
certs, err := cert.ParseCerts([]byte(certContent))
434439
if err != nil {
435440
t.Fatalf("Expected no error, got %v", err)
436441
}
442+
if len(certs) == 0 {
443+
t.Fatal("Expected non-empty certificate slice")
444+
}
445+
cert := certs[0]
437446
scopes := svc.getScopes(&ctx, cert, tpp)
438447
if len(scopes) == 0 {
439448
t.Fatal("Expected non-empty scopes")

server/db/mongo.go

Lines changed: 5 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -64,8 +64,11 @@ func (r *TppMongoRepository) GetTpp(ctx context.Context, id string) (*models.TPP
6464
}
6565

6666
func (r *TppMongoRepository) GetRootCertificates(ctx context.Context) ([]string, error) {
67-
// Get all certificates from the "certs" collection for now
68-
cursor, err := r.db.Collection("certs").Find(ctx, bson.M{})
67+
filter := bson.M{
68+
"is_active": true,
69+
"position": models.Root,
70+
}
71+
cursor, err := r.db.Collection("certs").Find(ctx, filter)
6972
if err != nil {
7073
return nil, err
7174
}

tools/eba_certs/main.go

Lines changed: 13 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -15,8 +15,8 @@ import (
1515
"go.mongodb.org/mongo-driver/mongo"
1616
"go.mongodb.org/mongo-driver/mongo/options"
1717

18-
"github.com/botsman/tppVerifier/app/models"
1918
"github.com/botsman/tppVerifier/app/cert"
19+
"github.com/botsman/tppVerifier/app/models"
2020
)
2121

2222
// Load XML files from EBA
@@ -153,18 +153,20 @@ func parseCerts(certChan <-chan RawCert, now time.Time) <-chan *cert.ParsedCert
153153
go func() {
154154
defer close(parsedCertChan)
155155
for crt := range certChan {
156-
parsedCert, err := cert.ParseCert([]byte(crt.Pem))
156+
parsedCerts, err := cert.ParseCerts([]byte(crt.Pem))
157157
if err != nil {
158158
fmt.Println(err)
159159
continue
160160
}
161-
parsedCert.UpdatedAt = now
162-
parsedCert.IsActive = true
163-
parsedCert.Position = models.Root
164-
parsedCert.Registers = []models.Register{
165-
models.EBA,
161+
for _, parsedCert := range parsedCerts {
162+
parsedCert.UpdatedAt = now
163+
parsedCert.IsActive = true
164+
parsedCert.Position = models.Root
165+
parsedCert.Registers = []models.Register{
166+
models.EBA,
167+
}
168+
parsedCertChan <- parsedCert
166169
}
167-
parsedCertChan <- parsedCert
168170
}
169171
}()
170172
return parsedCertChan
@@ -230,16 +232,16 @@ func main() {
230232
fmt.Println("Error updating certificate:", err)
231233
continue
232234
}
233-
235+
234236
}
235237

236238
// Clean up all certificates that are not active
237239
// We check their `updated_at` field to see if they were not updated in the
238240
// current run
239241
cleanupRes, err := certsCollection.UpdateMany(ctx,
240242
bson.M{
241-
"is_active": true,
242-
"position": bson.M{"$eq": models.Root},
243+
"is_active": true,
244+
"position": bson.M{"$eq": models.Root},
243245
"updated_at": bson.M{"$ne": now},
244246
},
245247
bson.M{

0 commit comments

Comments
 (0)