vendor
This commit is contained in:
200
vendor/github.com/ma314smith/signedxml/validator.go
generated
vendored
Normal file
200
vendor/github.com/ma314smith/signedxml/validator.go
generated
vendored
Normal file
@@ -0,0 +1,200 @@
|
||||
package signedxml
|
||||
|
||||
import (
|
||||
"crypto/x509"
|
||||
"encoding/base64"
|
||||
"errors"
|
||||
"fmt"
|
||||
"log"
|
||||
|
||||
"github.com/beevik/etree"
|
||||
)
|
||||
|
||||
// Validator provides options for verifying a signed XML document
|
||||
type Validator struct {
|
||||
Certificates []x509.Certificate
|
||||
signingCert x509.Certificate
|
||||
signatureData
|
||||
}
|
||||
|
||||
// NewValidator returns a *Validator for the XML provided
|
||||
func NewValidator(xml string) (*Validator, error) {
|
||||
doc := etree.NewDocument()
|
||||
err := doc.ReadFromString(xml)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
v := &Validator{signatureData: signatureData{xml: doc}}
|
||||
return v, nil
|
||||
}
|
||||
|
||||
// SetXML is used to assign the XML document that the Validator will verify
|
||||
func (v *Validator) SetXML(xml string) error {
|
||||
doc := etree.NewDocument()
|
||||
err := doc.ReadFromString(xml)
|
||||
v.xml = doc
|
||||
return err
|
||||
}
|
||||
|
||||
// SigningCert returns the certificate, if any, that was used to successfully
|
||||
// validate the signature of the XML document. This will be a zero value
|
||||
// x509.Certificate before Validator.Validate is successfully called.
|
||||
func (v *Validator) SigningCert() x509.Certificate {
|
||||
return v.signingCert
|
||||
}
|
||||
|
||||
// Validate validates the Reference digest values, and the signature value
|
||||
// over the SignedInfo.
|
||||
//
|
||||
// Deprecated: Use ValidateReferences instead
|
||||
func (v *Validator) Validate() error {
|
||||
_, err := v.ValidateReferences()
|
||||
return err
|
||||
}
|
||||
|
||||
// ValidateReferences validates the Reference digest values, and the signature value
|
||||
// over the SignedInfo.
|
||||
//
|
||||
// If the signature is enveloped in the XML, then it will be used.
|
||||
// Otherwise, an external signature should be assigned using
|
||||
// Validator.SetSignature.
|
||||
//
|
||||
// The references returned by this method can be used to verify what was signed.
|
||||
func (v *Validator) ValidateReferences() ([]string, error) {
|
||||
if err := v.loadValuesFromXML(); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
referenced, err := v.validateReferences()
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
var ref []string
|
||||
for _, doc := range referenced {
|
||||
docStr, err := doc.WriteToString()
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
ref = append(ref, docStr)
|
||||
}
|
||||
|
||||
err = v.validateSignature()
|
||||
return ref, err
|
||||
}
|
||||
|
||||
func (v *Validator) loadValuesFromXML() error {
|
||||
if v.signature == nil {
|
||||
if err := v.parseEnvelopedSignature(); err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
if err := v.parseSignedInfo(); err != nil {
|
||||
return err
|
||||
}
|
||||
if err := v.parseSigValue(); err != nil {
|
||||
return err
|
||||
}
|
||||
if err := v.parseSigAlgorithm(); err != nil {
|
||||
return err
|
||||
}
|
||||
if err := v.parseCanonAlgorithm(); err != nil {
|
||||
return err
|
||||
}
|
||||
if err := v.loadCertificates(); err != nil {
|
||||
return err
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
func (v *Validator) validateReferences() (referenced []*etree.Document, err error) {
|
||||
references := v.signedInfo.FindElements("./Reference")
|
||||
for _, ref := range references {
|
||||
doc := v.xml.Copy()
|
||||
transforms := ref.SelectElement("Transforms")
|
||||
for _, transform := range transforms.SelectElements("Transform") {
|
||||
doc, err = processTransform(transform, doc)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
}
|
||||
|
||||
doc, err = getReferencedXML(ref, doc)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
referenced = append(referenced, doc)
|
||||
|
||||
digestValueElement := ref.SelectElement("DigestValue")
|
||||
if digestValueElement == nil {
|
||||
return nil, errors.New("signedxml: unable to find DigestValue")
|
||||
}
|
||||
digestValue := digestValueElement.Text()
|
||||
|
||||
calculatedValue, err := calculateHash(ref, doc)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
if calculatedValue != digestValue {
|
||||
return nil, fmt.Errorf("signedxml: Calculated digest does not match the"+
|
||||
" expected digestvalue of %s", digestValue)
|
||||
}
|
||||
}
|
||||
return referenced, nil
|
||||
}
|
||||
|
||||
func (v *Validator) validateSignature() error {
|
||||
doc := etree.NewDocument()
|
||||
doc.SetRoot(v.signedInfo.Copy())
|
||||
signedInfo, err := doc.WriteToString()
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
canonSignedInfo, err := v.canonAlgorithm.Process(signedInfo, "")
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
b64, err := base64.StdEncoding.DecodeString(v.sigValue)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
sig := []byte(b64)
|
||||
|
||||
v.signingCert = x509.Certificate{}
|
||||
for _, cert := range v.Certificates {
|
||||
err := cert.CheckSignature(v.sigAlgorithm, []byte(canonSignedInfo), sig)
|
||||
if err == nil {
|
||||
v.signingCert = cert
|
||||
return nil
|
||||
}
|
||||
}
|
||||
|
||||
return errors.New("signedxml: Calculated signature does not match the " +
|
||||
"SignatureValue provided")
|
||||
}
|
||||
|
||||
func (v *Validator) loadCertificates() error {
|
||||
// If v.Certificates is already populated, then the client has already set it
|
||||
// to the desired cert. Otherwise, let's pull the public keys from the XML
|
||||
if len(v.Certificates) < 1 {
|
||||
keydata := v.xml.FindElements(".//X509Certificate")
|
||||
for _, key := range keydata {
|
||||
cert, err := getCertFromPEMString(key.Text())
|
||||
if err != nil {
|
||||
log.Printf("signedxml: Unable to load certificate: (%s). "+
|
||||
"Looking for another cert.", err)
|
||||
} else {
|
||||
v.Certificates = append(v.Certificates, *cert)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if len(v.Certificates) < 1 {
|
||||
return errors.New("signedxml: a certificate is required, but was not found")
|
||||
}
|
||||
return nil
|
||||
}
|
||||
Reference in New Issue
Block a user