vendor
This commit is contained in:
319
vendor/github.com/ma314smith/signedxml/signedxml.go
generated
vendored
Normal file
319
vendor/github.com/ma314smith/signedxml/signedxml.go
generated
vendored
Normal file
@@ -0,0 +1,319 @@
|
||||
// Package signedxml transforms and validates signedxml documents
|
||||
package signedxml
|
||||
|
||||
import (
|
||||
"crypto"
|
||||
"crypto/x509"
|
||||
"encoding/base64"
|
||||
"encoding/pem"
|
||||
"errors"
|
||||
"fmt"
|
||||
"log"
|
||||
"os"
|
||||
"strings"
|
||||
|
||||
"github.com/beevik/etree"
|
||||
)
|
||||
|
||||
var logger = log.New(os.Stdout, "DEBUG-SIGNEDXML: ", log.Ldate|log.Ltime|log.Lshortfile)
|
||||
|
||||
func init() {
|
||||
hashAlgorithms = map[string]crypto.Hash{
|
||||
"http://www.w3.org/2001/04/xmldsig-more#md5": crypto.MD5,
|
||||
"http://www.w3.org/2000/09/xmldsig#sha1": crypto.SHA1,
|
||||
"http://www.w3.org/2001/04/xmldsig-more#sha224": crypto.SHA224,
|
||||
"http://www.w3.org/2001/04/xmlenc#sha256": crypto.SHA256,
|
||||
"http://www.w3.org/2001/04/xmldsig-more#sha384": crypto.SHA384,
|
||||
"http://www.w3.org/2001/04/xmlenc#sha512": crypto.SHA512,
|
||||
"http://www.w3.org/2001/04/xmlenc#ripemd160": crypto.RIPEMD160,
|
||||
}
|
||||
|
||||
signatureAlgorithms = map[string]x509.SignatureAlgorithm{
|
||||
"http://www.w3.org/2001/04/xmldsig-more#rsa-md2": x509.MD2WithRSA,
|
||||
"http://www.w3.org/2001/04/xmldsig-more#rsa-md5": x509.MD5WithRSA,
|
||||
"http://www.w3.org/2000/09/xmldsig#rsa-sha1": x509.SHA1WithRSA,
|
||||
"http://www.w3.org/2001/04/xmldsig-more#rsa-sha256": x509.SHA256WithRSA,
|
||||
"http://www.w3.org/2001/04/xmldsig-more#rsa-sha384": x509.SHA384WithRSA,
|
||||
"http://www.w3.org/2001/04/xmldsig-more#rsa-sha512": x509.SHA512WithRSA,
|
||||
"http://www.w3.org/2000/09/xmldsig#dsa-sha1": x509.DSAWithSHA1,
|
||||
"http://www.w3.org/2000/09/xmldsig#dsa-sha256": x509.DSAWithSHA256,
|
||||
"http://www.w3.org/2001/04/xmldsig-more#ecdsa-sha1": x509.ECDSAWithSHA1,
|
||||
"http://www.w3.org/2001/04/xmldsig-more#ecdsa-sha256": x509.ECDSAWithSHA256,
|
||||
"http://www.w3.org/2001/04/xmldsig-more#ecdsa-sha384": x509.ECDSAWithSHA384,
|
||||
"http://www.w3.org/2001/04/xmldsig-more#ecdsa-sha512": x509.ECDSAWithSHA512,
|
||||
}
|
||||
|
||||
CanonicalizationAlgorithms = map[string]CanonicalizationAlgorithm{
|
||||
"http://www.w3.org/2000/09/xmldsig#enveloped-signature": EnvelopedSignature{},
|
||||
"http://www.w3.org/2001/10/xml-exc-c14n#": ExclusiveCanonicalization{},
|
||||
"http://www.w3.org/2001/10/xml-exc-c14n#WithComments": ExclusiveCanonicalization{WithComments: true},
|
||||
}
|
||||
}
|
||||
|
||||
// CanonicalizationAlgorithm defines an interface for processing an XML
|
||||
// document into a standard format.
|
||||
//
|
||||
// If any child elements are in the Transform node, the entire transform node
|
||||
// will be passed to the Process method through the transformXML parameter as an
|
||||
// XML string. This is necessary for transforms that need additional processing
|
||||
// data, like XPath (http://www.w3.org/TR/xmldsig-core/#sec-XPath). If there are
|
||||
// no child elements in Transform (or CanonicalizationMethod), then an empty
|
||||
// string will be passed through.
|
||||
type CanonicalizationAlgorithm interface {
|
||||
Process(inputXML string, transformXML string) (outputXML string, err error)
|
||||
}
|
||||
|
||||
// CanonicalizationAlgorithms maps the CanonicalizationMethod or
|
||||
// Transform Algorithm URIs to a type that implements the
|
||||
// CanonicalizationAlgorithm interface.
|
||||
//
|
||||
// Implementations are provided for the following transforms:
|
||||
// http://www.w3.org/2001/10/xml-exc-c14n# (ExclusiveCanonicalization)
|
||||
// http://www.w3.org/2001/10/xml-exc-c14n#WithComments (ExclusiveCanonicalizationWithComments)
|
||||
// http://www.w3.org/2000/09/xmldsig#enveloped-signature (EnvelopedSignature)
|
||||
//
|
||||
// Custom implementations can be added to the map
|
||||
var CanonicalizationAlgorithms map[string]CanonicalizationAlgorithm
|
||||
var hashAlgorithms map[string]crypto.Hash
|
||||
var signatureAlgorithms map[string]x509.SignatureAlgorithm
|
||||
|
||||
// signatureData provides options for verifying a signed XML document
|
||||
type signatureData struct {
|
||||
xml *etree.Document
|
||||
signature *etree.Element
|
||||
signedInfo *etree.Element
|
||||
sigValue string
|
||||
sigAlgorithm x509.SignatureAlgorithm
|
||||
canonAlgorithm CanonicalizationAlgorithm
|
||||
}
|
||||
|
||||
// SetSignature can be used to assign an external signature for the XML doc
|
||||
// that Validator will verify
|
||||
func (s *signatureData) SetSignature(sig string) error {
|
||||
doc := etree.NewDocument()
|
||||
err := doc.ReadFromString(sig)
|
||||
s.signature = doc.Root()
|
||||
return err
|
||||
}
|
||||
|
||||
func (s *signatureData) parseEnvelopedSignature() error {
|
||||
sig := s.xml.FindElement(".//Signature")
|
||||
if sig != nil {
|
||||
s.signature = sig
|
||||
} else {
|
||||
return errors.New("signedxml: Unable to find a unique signature element " +
|
||||
"in the xml document. The signature must either be enveloped in the " +
|
||||
"xml doc or externally assigned to Validator.SetSignature")
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
func (s *signatureData) parseSignedInfo() error {
|
||||
s.signedInfo = nil
|
||||
s.signedInfo = s.signature.SelectElement("SignedInfo")
|
||||
if s.signedInfo == nil {
|
||||
return errors.New("signedxml: unable to find SignedInfo element")
|
||||
}
|
||||
|
||||
// move the Signature level namespace down to SignedInfo so that the signature
|
||||
// value will match up
|
||||
if s.signedInfo.Space != "" {
|
||||
attr := s.signature.SelectAttr(s.signedInfo.Space)
|
||||
if attr != nil {
|
||||
s.signedInfo.Attr = []etree.Attr{*attr}
|
||||
}
|
||||
} else {
|
||||
attr := s.signature.SelectAttr("xmlns")
|
||||
if attr != nil {
|
||||
s.signedInfo.Attr = []etree.Attr{*attr}
|
||||
}
|
||||
}
|
||||
|
||||
// Copy SignedInfo xmlns: into itself if it does not exist and is defined as a root attribute
|
||||
root := s.xml.Root()
|
||||
|
||||
if root != nil {
|
||||
sigNS := root.SelectAttr("xmlns:" + s.signedInfo.Space)
|
||||
if sigNS != nil {
|
||||
if s.signedInfo.SelectAttr("xmlns:"+s.signedInfo.Space) == nil {
|
||||
s.signedInfo.CreateAttr("xmlns:"+s.signedInfo.Space, sigNS.Value)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
func (s *signatureData) parseSigValue() error {
|
||||
s.sigValue = ""
|
||||
sigValueElement := s.signature.SelectElement("SignatureValue")
|
||||
if sigValueElement != nil {
|
||||
s.sigValue = sigValueElement.Text()
|
||||
return nil
|
||||
}
|
||||
return errors.New("signedxml: unable to find SignatureValue")
|
||||
}
|
||||
|
||||
func (s *signatureData) parseSigAlgorithm() error {
|
||||
s.sigAlgorithm = x509.UnknownSignatureAlgorithm
|
||||
sigMethod := s.signedInfo.SelectElement("SignatureMethod")
|
||||
|
||||
var sigAlgoURI string
|
||||
if sigMethod == nil {
|
||||
return errors.New("signedxml: Unable to find SignatureMethod element")
|
||||
}
|
||||
|
||||
sigAlgoURI = sigMethod.SelectAttrValue("Algorithm", "")
|
||||
sigAlgo, ok := signatureAlgorithms[sigAlgoURI]
|
||||
if ok {
|
||||
s.sigAlgorithm = sigAlgo
|
||||
return nil
|
||||
}
|
||||
|
||||
return errors.New("signedxml: Unable to find Algorithm in SignatureMethod element")
|
||||
}
|
||||
|
||||
func (s *signatureData) parseCanonAlgorithm() error {
|
||||
s.canonAlgorithm = nil
|
||||
canonMethod := s.signedInfo.SelectElement("CanonicalizationMethod")
|
||||
|
||||
var canonAlgoURI string
|
||||
if canonMethod == nil {
|
||||
return errors.New("signedxml: Unable to find CanonicalizationMethod element")
|
||||
}
|
||||
|
||||
canonAlgoURI = canonMethod.SelectAttrValue("Algorithm", "")
|
||||
canonAlgo, ok := CanonicalizationAlgorithms[canonAlgoURI]
|
||||
if ok {
|
||||
s.canonAlgorithm = canonAlgo
|
||||
return nil
|
||||
}
|
||||
|
||||
return errors.New("signedxml: Unable to find Algorithm in " +
|
||||
"CanonicalizationMethod element")
|
||||
}
|
||||
|
||||
func getReferencedXML(reference *etree.Element, inputDoc *etree.Document) (outputDoc *etree.Document, err error) {
|
||||
uri := reference.SelectAttrValue("URI", "")
|
||||
uri = strings.Replace(uri, "#", "", 1)
|
||||
// populate doc with the referenced xml from the Reference URI
|
||||
if uri == "" {
|
||||
outputDoc = inputDoc
|
||||
} else {
|
||||
path := fmt.Sprintf(".//[@ID='%s']", uri)
|
||||
e := inputDoc.FindElement(path)
|
||||
if e != nil {
|
||||
outputDoc = etree.NewDocument()
|
||||
outputDoc.SetRoot(e.Copy())
|
||||
} else {
|
||||
// SAML v1.1 Assertions use AssertionID
|
||||
path := fmt.Sprintf(".//[@AssertionID='%s']", uri)
|
||||
e := inputDoc.FindElement(path)
|
||||
if e != nil {
|
||||
outputDoc = etree.NewDocument()
|
||||
outputDoc.SetRoot(e.Copy())
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if outputDoc == nil {
|
||||
return nil, errors.New("signedxml: unable to find refereced xml")
|
||||
}
|
||||
|
||||
return outputDoc, nil
|
||||
}
|
||||
|
||||
func getCertFromPEMString(pemString string) (*x509.Certificate, error) {
|
||||
pubkey := fmt.Sprintf("-----BEGIN PUBLIC KEY-----\n%s\n-----END PUBLIC KEY-----",
|
||||
pemString)
|
||||
|
||||
pemBlock, _ := pem.Decode([]byte(pubkey))
|
||||
if pemBlock == nil {
|
||||
return &x509.Certificate{}, errors.New("Could not parse Public Key PEM")
|
||||
}
|
||||
if pemBlock.Type != "PUBLIC KEY" {
|
||||
return &x509.Certificate{}, errors.New("Found wrong key type")
|
||||
}
|
||||
|
||||
cert, err := x509.ParseCertificate(pemBlock.Bytes)
|
||||
return cert, err
|
||||
}
|
||||
|
||||
func processTransform(transform *etree.Element,
|
||||
docIn *etree.Document) (docOut *etree.Document, err error) {
|
||||
|
||||
transformAlgoURI := transform.SelectAttrValue("Algorithm", "")
|
||||
if transformAlgoURI == "" {
|
||||
return nil, errors.New("signedxml: unable to find Algorithm in Transform")
|
||||
}
|
||||
|
||||
transformAlgo, ok := CanonicalizationAlgorithms[transformAlgoURI]
|
||||
if !ok {
|
||||
return nil, fmt.Errorf("signedxml: unable to find matching transform"+
|
||||
"algorithm for %s in CanonicalizationAlgorithms", transformAlgoURI)
|
||||
}
|
||||
|
||||
var transformContent string
|
||||
|
||||
if transform.ChildElements() != nil {
|
||||
tDoc := etree.NewDocument()
|
||||
tDoc.SetRoot(transform.Copy())
|
||||
transformContent, err = tDoc.WriteToString()
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
}
|
||||
|
||||
docString, err := docIn.WriteToString()
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
docString, err = transformAlgo.Process(docString, transformContent)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
docOut = etree.NewDocument()
|
||||
docOut.ReadFromString(docString)
|
||||
|
||||
return docOut, nil
|
||||
}
|
||||
|
||||
func calculateHash(reference *etree.Element, doc *etree.Document) (string, error) {
|
||||
digestMethodElement := reference.SelectElement("DigestMethod")
|
||||
if digestMethodElement == nil {
|
||||
return "", errors.New("signedxml: unable to find DigestMethod")
|
||||
}
|
||||
|
||||
digestMethodURI := digestMethodElement.SelectAttrValue("Algorithm", "")
|
||||
if digestMethodURI == "" {
|
||||
return "", errors.New("signedxml: unable to find Algorithm in DigestMethod")
|
||||
}
|
||||
|
||||
digestAlgo, ok := hashAlgorithms[digestMethodURI]
|
||||
if !ok {
|
||||
return "", fmt.Errorf("signedxml: unable to find matching hash"+
|
||||
"algorithm for %s in hashAlgorithms", digestMethodURI)
|
||||
}
|
||||
|
||||
doc.WriteSettings.CanonicalEndTags = true
|
||||
doc.WriteSettings.CanonicalText = true
|
||||
doc.WriteSettings.CanonicalAttrVal = true
|
||||
|
||||
h := digestAlgo.New()
|
||||
docBytes, err := doc.WriteToBytes()
|
||||
if err != nil {
|
||||
return "", err
|
||||
}
|
||||
|
||||
//ioutil.WriteFile("C:/Temp/SignedXML/Suspect.xml", docBytes, 0644)
|
||||
//s, _ := doc.WriteToString()
|
||||
//logger.Println(s)
|
||||
|
||||
h.Write(docBytes)
|
||||
d := h.Sum(nil)
|
||||
calculatedValue := base64.StdEncoding.EncodeToString(d)
|
||||
|
||||
return calculatedValue, nil
|
||||
}
|
||||
Reference in New Issue
Block a user