This commit is contained in:
2018-12-13 20:33:29 +01:00
parent 7bca21522a
commit d0d888c160
63 changed files with 7546 additions and 2 deletions

24
vendor/github.com/ma314smith/signedxml/.gitignore generated vendored Normal file
View File

@@ -0,0 +1,24 @@
# Compiled Object files, Static and Dynamic libs (Shared Objects)
*.o
*.a
*.so
# Folders
_obj
_test
# Architecture specific extensions/prefixes
*.[568vq]
[568vq].out
*.cgo1.go
*.cgo2.c
_cgo_defun.c
_cgo_gotypes.go
_cgo_export.*
_testmain.go
*.exe
*.test
*.prof

16
vendor/github.com/ma314smith/signedxml/.travis.yml generated vendored Normal file
View File

@@ -0,0 +1,16 @@
language: go
sudo: false
go:
- 1.x
- 1.6
- 1.7.x
- master
before_install:
- go get -v github.com/golang/lint/golint
script:
- go vet ./...
- golint ./...
- go test -cover -v ./...

21
vendor/github.com/ma314smith/signedxml/LICENSE.md generated vendored Normal file
View File

@@ -0,0 +1,21 @@
The MIT License (MIT)
Copyright (c) 2015 Matt Smith
Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal
in the Software without restriction, including without limitation the rights
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
copies of the Software, and to permit persons to whom the Software is
furnished to do so, subject to the following conditions:
The above copyright notice and this permission notice shall be included in all
copies or substantial portions of the Software.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
SOFTWARE.

98
vendor/github.com/ma314smith/signedxml/README.md generated vendored Normal file
View File

@@ -0,0 +1,98 @@
## signedxml
[![Build Status](https://travis-ci.org/ma314smith/signedxml.svg?branch=master)](https://travis-ci.org/ma314smith/signedxml)
[![GoDoc](https://godoc.org/github.com/ma314smith/signedxml?status.svg)](https://godoc.org/github.com/ma314smith/signedxml)
The signedxml package transforms and validates signed xml documents. The main use case is to support Single Sign On protocols like SAML and WS-Federation.
Other packages that provide similar functionality rely on C libraries, which makes them difficult to run across platforms without significant configuration. `signedxml` is written in pure go, and can be easily used on any platform.
### Install
`go get github.com/ma314smith/signedxml`
### Included Algorithms
- Hashes
- http://www.w3.org/2001/04/xmldsig-more#md5
- http://www.w3.org/2000/09/xmldsig#sha1
- http://www.w3.org/2001/04/xmldsig-more#sha224
- http://www.w3.org/2001/04/xmlenc#sha256
- http://www.w3.org/2001/04/xmldsig-more#sha384
- http://www.w3.org/2001/04/xmlenc#sha512
- http://www.w3.org/2001/04/xmlenc#ripemd160
- Signatures
- http://www.w3.org/2001/04/xmldsig-more#rsa-md2
- http://www.w3.org/2001/04/xmldsig-more#rsa-md5
- http://www.w3.org/2000/09/xmldsig#rsa-sha1
- http://www.w3.org/2001/04/xmldsig-more#rsa-sha256
- http://www.w3.org/2001/04/xmldsig-more#rsa-sha384
- http://www.w3.org/2001/04/xmldsig-more#rsa-sha512
- http://www.w3.org/2000/09/xmldsig#dsa-sha1
- http://www.w3.org/2000/09/xmldsig#dsa-sha256
- http://www.w3.org/2001/04/xmldsig-more#ecdsa-sha1
- http://www.w3.org/2001/04/xmldsig-more#ecdsa-sha256
- http://www.w3.org/2001/04/xmldsig-more#ecdsa-sha384
- http://www.w3.org/2001/04/xmldsig-more#ecdsa-sha512
- Canonicalization Methods/Transforms
- http://www.w3.org/2000/09/xmldsig#enveloped-signature
- http://www.w3.org/2001/10/xml-exc-c14n#
- http://www.w3.org/2001/10/xml-exc-c14n#WithComments
### Examples
#### Validating signed XML
If your signed xml contains the signature and certificate, then you can just pass in the xml and call `Validate()`.
```go
validator, err := signedxml.NewValidator(`<YourXMLString></YourXMLString>`)
xml, err = validator.ValidateReferences()
```
`ValidateReferences()` verifies the DigestValue and SignatureValue in the xml document, and returns the signed payload(s). If the error value is `nil`, then the signed xml is valid.
The x509.Certificate that was successfully used to validate the xml will be available by calling:
```go
validator.SigningCert()
```
You can then verify that you trust the certificate. You can optionally supply your trusted certificates ahead of time by assigning them to the `Certificates` property of the `Validator` object, which is an x509.Certificate array.
#### Using an external Signature
If you need to specify an external Signature, you can use the `SetSignature()` function to assign it:
```go
validator.SetSignature(<`Signature></Signature>`)
```
#### Generating signed XML
It is expected that your XML contains the Signature element with all the parameters set (except DigestValue and SignatureValue).
```go
signer, err := signedxml.NewSigner(`<YourXMLString></YourXMLString`)
signedXML, err := signer.Sign(`*rsa.PrivateKey object`)
```
`Sign()` will generate the DigestValue and SignatureValue, populate it in the XML, and return the signed XML string.
#### Implementing custom transforms
Additional Transform algorithms can be included by adding to the CanonicalizationAlgorithms map. This interface will need to be implemented:
```go
type CanonicalizationAlgorithm interface {
Process(inputXML string, transformXML string) (outputXML string, err error)
}
```
Simple Example:
```go
type NoChangeCanonicalization struct{}
func (n NoChangeCanonicalization) Process(inputXML string,
transformXML string) (outputXML string, err error) {
return inputXML, nil
}
signedxml.CanonicalizationAlgorithms["http://myTranform"] = NoChangeCanonicalization{}
```
See `envelopedsignature.go` and `exclusivecanonicalization.go` for examples of actual implementations.
### Contributions
Contributions are welcome. Just fork the repo and send a pull request.

View File

@@ -0,0 +1,39 @@
package signedxml
import (
"errors"
"github.com/beevik/etree"
)
// EnvelopedSignature implements the CanonicalizationAlgorithm
// interface and is used for processing the
// http://www.w3.org/2000/09/xmldsig#enveloped-signature transform
// algorithm
type EnvelopedSignature struct{}
// Process is called to transfrom the XML using the EnvelopedSignature
// algorithm
func (e EnvelopedSignature) Process(inputXML string,
transformXML string) (outputXML string, err error) {
doc := etree.NewDocument()
doc.ReadFromString(inputXML)
sig := doc.FindElement(".//Signature")
if sig == nil {
return "", errors.New("signedxml: unable to find Signature node")
}
sigParent := sig.Parent()
elem := sigParent.RemoveChild(sig)
if elem == nil {
return "", errors.New("signedxml: unable to remove Signature element")
}
docString, err := doc.WriteToString()
if err != nil {
return "", err
}
//logger.Println(docString)
return docString, nil
}

View File

@@ -0,0 +1,302 @@
package signedxml
import (
"sort"
"strings"
"github.com/beevik/etree"
)
// the attribute and attributes structs are used to implement the sort.Interface
type attribute struct {
prefix, uri, key, value string
}
type attributes []attribute
func (a attributes) Len() int {
return len(a)
}
// Less is part of the sort.Interface, and is used to order attributes by their
// namespace URIs and then by their keys.
func (a attributes) Less(i, j int) bool {
if a[i].uri == "" && a[j].uri != "" {
return true
}
if a[j].uri == "" && a[i].uri != "" {
return false
}
iQual := a[i].uri + a[i].key
jQual := a[j].uri + a[j].key
return iQual < jQual
}
func (a attributes) Swap(i, j int) {
a[i], a[j] = a[j], a[i]
}
// ExclusiveCanonicalization implements the CanonicalizationAlgorithm
// interface and is used for processing the
// http://www.w3.org/2001/10/xml-exc-c14n# and
// http://www.w3.org/2001/10/xml-exc-c14n#WithComments transform
// algorithms
type ExclusiveCanonicalization struct {
WithComments bool
inclusiveNamespacePrefixList []string
namespaces map[string]string
}
// Process is called to transfrom the XML using the ExclusiveCanonicalization
// algorithm
func (e ExclusiveCanonicalization) Process(inputXML string,
transformXML string) (outputXML string, err error) {
e.namespaces = make(map[string]string)
doc := etree.NewDocument()
doc.WriteSettings.CanonicalEndTags = true
doc.WriteSettings.CanonicalText = true
doc.WriteSettings.CanonicalAttrVal = true
err = doc.ReadFromString(inputXML)
if err != nil {
return "", err
}
e.loadPrefixList(transformXML)
e.processDocLevelNodes(doc)
e.processRecursive(doc.Root(), nil, "")
outputXML, err = doc.WriteToString()
return outputXML, err
}
func (e *ExclusiveCanonicalization) loadPrefixList(transformXML string) {
if transformXML != "" {
tDoc := etree.NewDocument()
tDoc.ReadFromString(transformXML)
inclNSNode := tDoc.Root().SelectElement("InclusiveNamespaces")
if inclNSNode != nil {
prefixList := inclNSNode.SelectAttrValue("PrefixList", "")
if prefixList != "" {
e.inclusiveNamespacePrefixList = strings.Split(prefixList, " ")
}
}
}
}
// process nodes outside of the root element
func (e ExclusiveCanonicalization) processDocLevelNodes(doc *etree.Document) {
// keep track of the previous node action to manage line returns in CharData
previousNodeRemoved := false
for i := 0; i < len(doc.Child); i++ {
c := doc.Child[i]
switch c := c.(type) {
case *etree.Comment:
if e.WithComments {
previousNodeRemoved = false
} else {
removeTokenFromDocument(c, doc)
i--
previousNodeRemoved = true
}
case *etree.CharData:
if isWhitespace(c.Data) {
if previousNodeRemoved {
removeTokenFromDocument(c, doc)
i--
previousNodeRemoved = true
} else {
c.Data = "\n"
}
}
case *etree.Directive:
removeTokenFromDocument(c, doc)
i--
previousNodeRemoved = true
case *etree.ProcInst:
// remove declaration, but leave other PI's
if c.Target == "xml" {
removeTokenFromDocument(c, doc)
i--
previousNodeRemoved = true
} else {
previousNodeRemoved = false
}
default:
previousNodeRemoved = false
}
}
// if the last line is CharData whitespace, then remove it
if c, ok := doc.Child[len(doc.Child)-1].(*etree.CharData); ok {
if isWhitespace(c.Data) {
removeTokenFromDocument(c, doc)
}
}
}
func (e ExclusiveCanonicalization) processRecursive(node *etree.Element,
prefixesInScope []string, defaultNS string) {
newDefaultNS, newPrefixesInScope :=
e.renderAttributes(node, prefixesInScope, defaultNS)
for _, child := range node.Child {
switch child := child.(type) {
case *etree.Comment:
if !e.WithComments {
removeTokenFromElement(etree.Token(child), node)
}
case *etree.Element:
e.processRecursive(child, newPrefixesInScope, newDefaultNS)
}
}
}
func (e ExclusiveCanonicalization) renderAttributes(node *etree.Element,
prefixesInScope []string, defaultNS string) (newDefaultNS string,
newPrefixesInScope []string) {
currentNS := node.SelectAttrValue("xmlns", defaultNS)
elementAttributes := []etree.Attr{}
nsListToRender := make(map[string]string)
attrListToRender := attributes{}
// load map with for prefix -> uri lookup
for _, attr := range node.Attr {
if attr.Space == "xmlns" {
e.namespaces[attr.Key] = attr.Value
}
}
// handle the namespace of the node itself
if node.Space != "" {
if !contains(prefixesInScope, node.Space) {
nsListToRender["xmlns:"+node.Space] = e.namespaces[node.Space]
prefixesInScope = append(prefixesInScope, node.Space)
}
} else if defaultNS != currentNS {
newDefaultNS = currentNS
elementAttributes = append(elementAttributes,
etree.Attr{Key: "xmlns", Value: currentNS})
}
for _, attr := range node.Attr {
// include the namespaces if they are in the inclusiveNamespacePrefixList
if attr.Space == "xmlns" {
if !contains(prefixesInScope, attr.Key) &&
contains(e.inclusiveNamespacePrefixList, attr.Key) {
nsListToRender["xmlns:"+attr.Key] = attr.Value
prefixesInScope = append(prefixesInScope, attr.Key)
}
}
// include namespaces for qualfied attributes
if attr.Space != "" &&
attr.Space != "xmlns" &&
!contains(prefixesInScope, attr.Space) {
nsListToRender["xmlns:"+attr.Space] = e.namespaces[attr.Space]
prefixesInScope = append(prefixesInScope, attr.Space)
}
// inclued all non-namespace attributes
if attr.Space != "xmlns" && attr.Key != "xmlns" {
attrListToRender = append(attrListToRender,
attribute{
prefix: attr.Space,
uri: e.namespaces[attr.Space],
key: attr.Key,
value: attr.Value,
})
}
}
// sort and add the namespace attributes first
sortedNSList := getSortedNamespaces(nsListToRender)
elementAttributes = append(elementAttributes, sortedNSList...)
// then sort and add the non-namespace attributes
sortedAttributes := getSortedAttributes(attrListToRender)
elementAttributes = append(elementAttributes, sortedAttributes...)
// replace the nodes attributes with the sorted copy
node.Attr = elementAttributes
return currentNS, prefixesInScope
}
func contains(slice []string, value string) bool {
for _, s := range slice {
if s == value {
return true
}
}
return false
}
// getSortedNamespaces sorts the namespace attributes by their prefix
func getSortedNamespaces(list map[string]string) []etree.Attr {
var keys []string
for k := range list {
keys = append(keys, k)
}
sort.Strings(keys)
elem := etree.Element{}
for _, k := range keys {
elem.CreateAttr(k, list[k])
}
return elem.Attr
}
// getSortedAttributes sorts attributes by their namespace URIs
func getSortedAttributes(list attributes) []etree.Attr {
sort.Sort(list)
attrs := make([]etree.Attr, len(list))
for i, a := range list {
attrs[i] = etree.Attr{
Space: a.prefix,
Key: a.key,
Value: a.value,
}
}
return attrs
}
func removeTokenFromElement(token etree.Token, e *etree.Element) *etree.Token {
for i, t := range e.Child {
if t == token {
e.Child = append(e.Child[0:i], e.Child[i+1:]...)
return &t
}
}
return nil
}
func removeTokenFromDocument(token etree.Token, d *etree.Document) *etree.Token {
for i, t := range d.Child {
if t == token {
d.Child = append(d.Child[0:i], d.Child[i+1:]...)
return &t
}
}
return nil
}
// isWhitespace returns true if the byte slice contains only
// whitespace characters.
func isWhitespace(s string) bool {
for i := 0; i < len(s); i++ {
if c := s[i]; c != ' ' && c != '\t' && c != '\n' && c != '\r' {
return false
}
}
return true
}

319
vendor/github.com/ma314smith/signedxml/signedxml.go generated vendored Normal file
View 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
}

171
vendor/github.com/ma314smith/signedxml/signer.go generated vendored Normal file
View File

@@ -0,0 +1,171 @@
package signedxml
import (
"crypto"
"crypto/rand"
"crypto/rsa"
"crypto/x509"
"encoding/base64"
"errors"
"github.com/beevik/etree"
)
var signingAlgorithms map[x509.SignatureAlgorithm]cryptoHash
func init() {
signingAlgorithms = map[x509.SignatureAlgorithm]cryptoHash{
// MD2 not supported
// x509.MD2WithRSA: cryptoHash{algorithm: "rsa", hash: crypto.MD2},
x509.MD5WithRSA: cryptoHash{algorithm: "rsa", hash: crypto.MD5},
x509.SHA1WithRSA: cryptoHash{algorithm: "rsa", hash: crypto.SHA1},
x509.SHA256WithRSA: cryptoHash{algorithm: "rsa", hash: crypto.SHA256},
x509.SHA384WithRSA: cryptoHash{algorithm: "rsa", hash: crypto.SHA384},
x509.SHA512WithRSA: cryptoHash{algorithm: "rsa", hash: crypto.SHA512},
// DSA not supported
// x509.DSAWithSHA1: cryptoHash{algorithm: "dsa", hash: crypto.SHA1},
// x509.DSAWithSHA256:cryptoHash{algorithm: "dsa", hash: crypto.SHA256},
// Golang ECDSA support is lacking, can't seem to load private keys
// x509.ECDSAWithSHA1: cryptoHash{algorithm: "ecdsa", hash: crypto.SHA1},
// x509.ECDSAWithSHA256: cryptoHash{algorithm: "ecdsa", hash: crypto.SHA256},
// x509.ECDSAWithSHA384: cryptoHash{algorithm: "ecdsa", hash: crypto.SHA384},
// x509.ECDSAWithSHA512: cryptoHash{algorithm: "ecdsa", hash: crypto.SHA512},
}
}
type cryptoHash struct {
algorithm string
hash crypto.Hash
}
// Signer provides options for signing an XML document
type Signer struct {
signatureData
privateKey interface{}
}
// NewSigner returns a *Signer for the XML provided
func NewSigner(xml string) (*Signer, error) {
doc := etree.NewDocument()
err := doc.ReadFromString(xml)
if err != nil {
return nil, err
}
s := &Signer{signatureData: signatureData{xml: doc}}
return s, nil
}
// Sign populates the XML digest and signature based on the parameters present and privateKey given
func (s *Signer) Sign(privateKey interface{}) (string, error) {
s.privateKey = privateKey
if s.signature == nil {
if err := s.parseEnvelopedSignature(); err != nil {
return "", err
}
}
if err := s.parseSignedInfo(); err != nil {
return "", err
}
if err := s.parseSigAlgorithm(); err != nil {
return "", err
}
if err := s.parseCanonAlgorithm(); err != nil {
return "", err
}
if err := s.setDigest(); err != nil {
return "", err
}
if err := s.setSignature(); err != nil {
return "", err
}
xml, err := s.xml.WriteToString()
if err != nil {
return "", err
}
return xml, nil
}
func (s *Signer) setDigest() (err error) {
references := s.signedInfo.FindElements("./Reference")
for _, ref := range references {
doc := s.xml.Copy()
transforms := ref.SelectElement("Transforms")
for _, transform := range transforms.SelectElements("Transform") {
doc, err = processTransform(transform, doc)
if err != nil {
return err
}
}
doc, err := getReferencedXML(ref, doc)
if err != nil {
return err
}
calculatedValue, err := calculateHash(ref, doc)
if err != nil {
return err
}
digestValueElement := ref.SelectElement("DigestValue")
if digestValueElement == nil {
return errors.New("signedxml: unable to find DigestValue")
}
digestValueElement.SetText(calculatedValue)
}
return nil
}
func (s *Signer) setSignature() error {
doc := etree.NewDocument()
doc.SetRoot(s.signedInfo.Copy())
signedInfo, err := doc.WriteToString()
if err != nil {
return err
}
canonSignedInfo, err := s.canonAlgorithm.Process(signedInfo, "")
if err != nil {
return err
}
var hashed, signature []byte
//var h1, h2 *big.Int
signingAlgorithm, ok := signingAlgorithms[s.sigAlgorithm]
if !ok {
return errors.New("signedxml: unsupported algorithm")
}
hasher := signingAlgorithm.hash.New()
hasher.Write([]byte(canonSignedInfo))
hashed = hasher.Sum(nil)
switch signingAlgorithm.algorithm {
case "rsa":
signature, err = rsa.SignPKCS1v15(rand.Reader, s.privateKey.(*rsa.PrivateKey), signingAlgorithm.hash, hashed)
/*
case "dsa":
h1, h2, err = dsa.Sign(rand.Reader, s.privateKey.(*dsa.PrivateKey), hashed)
case "ecdsa":
h1, h2, err = ecdsa.Sign(rand.Reader, s.privateKey.(*ecdsa.PrivateKey), hashed)
*/
}
if err != nil {
return err
}
// DSA and ECDSA has not been validated
/*
if signature == nil && h1 != nil && h2 != nil {
signature = append(h1.Bytes(), h2.Bytes()...)
}
*/
b64 := base64.StdEncoding.EncodeToString(signature)
sigValueElement := s.signature.SelectElement("SignatureValue")
sigValueElement.SetText(b64)
return nil
}

200
vendor/github.com/ma314smith/signedxml/validator.go generated vendored Normal file
View 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
}