-
-
Notifications
You must be signed in to change notification settings - Fork 0
/
Copy pathsigned_xml.go
140 lines (116 loc) · 3.54 KB
/
signed_xml.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
package xmldsig
import (
"context"
"crypto/x509"
"encoding/base64"
"errors"
"github.com/beevik/etree"
)
type SignedXml struct {
document *etree.Document
signature *Signature
nsUris map[string]string
nsPrefixes map[string]string
}
func LoadSignedXml(doc *etree.Document) (*SignedXml, error) {
xml := &SignedXml{
document: doc,
}
err := xml.loadXml(doc)
if err != nil {
return nil, err
}
return xml, nil
}
func (xml *SignedXml) ValidateSignature(ctx context.Context, cert *x509.Certificate) ([]*etree.Element, error) {
if xml.signature == nil || xml.signature.SignedInfo == nil {
return nil, errors.New("signature or signed info is nil")
}
validated, err := xml.signature.SignedInfo.validateDigests(ctx)
if err != nil {
return nil, err
}
err = xml.signature.SignedInfo.validateSignature(ctx, cert)
if err != nil {
return nil, err
}
return validated, nil
}
func (xml *SignedXml) GetCertificate() (*x509.Certificate, error) {
if xml.signature == nil {
return nil, errors.New("signature or signed info is nil")
}
signatureXml := xml.signature.cachedXml
if signatureXml == nil {
return nil, errors.New("signature xml is nil")
}
keyInfoElements := signatureXml.SelectElements("KeyInfo")
if len(keyInfoElements) != 1 {
return nil, errors.New("signature does not contain a single KeyInfo element")
}
x509DataElements := keyInfoElements[0].SelectElements("X509Data")
if len(x509DataElements) > 0 {
x509CertificateElements := x509DataElements[0].SelectElements("X509Certificate")
if len(x509CertificateElements) != 1 {
return nil, errors.New("signature does not contain a single X509Certificate element")
}
x509Data, err := base64.StdEncoding.DecodeString(x509CertificateElements[0].Text())
if err != nil {
return nil, err
}
x509Cert, err := x509.ParseCertificate(x509Data)
if err != nil {
return nil, err
}
return x509Cert, nil
}
tokenReferenceElements := keyInfoElements[0].SelectElements("SecurityTokenReference")
if len(tokenReferenceElements) > 0 {
referenceElements := tokenReferenceElements[0].SelectElements("Reference")
if len(referenceElements) != 1 {
return nil, errors.New("signature does not contain a single Reference element")
}
uri := referenceElements[0].SelectAttrValue("URI", "")
if uri == "" {
return nil, errors.New("signature does not contain a URI")
}
securityTokenElements := xml.document.FindElements("//BinarySecurityToken[@Id='" + uri[1:] + "']")
if len(securityTokenElements) != 1 {
return nil, errors.New("document does not contain a single BinarySecurityToken element")
}
x509Data, err := base64.StdEncoding.DecodeString(securityTokenElements[0].Text())
if err != nil {
return nil, err
}
x509Cert, err := x509.ParseCertificate(x509Data)
if err != nil {
return nil, err
}
return x509Cert, nil
}
return nil, errors.New("certificate not found")
}
func (xml *SignedXml) SetNamespacePrefix(prefix string, uri string) {
xml.nsPrefixes[uri] = prefix
xml.nsUris[prefix] = uri
}
func (xml *SignedXml) getElementSpace(uri string) string {
prefix, found := xml.nsPrefixes[uri]
if !found {
return uri
}
return prefix
}
func (xml *SignedXml) loadXml(doc *etree.Document) error {
// Get the signature
signatureElements := doc.FindElements("//Signature[namespace-uri()='" + XmlDSigNamespaceUri + "']")
if len(signatureElements) != 1 {
return errors.New("element does not contain a single Signature element")
}
xml.signature = newSignature(xml)
err := xml.signature.loadXml(signatureElements[0])
if err != nil {
return err
}
return nil
}