vendor
This commit is contained in:
24
vendor/github.com/ma314smith/signedxml/.gitignore
generated
vendored
Normal file
24
vendor/github.com/ma314smith/signedxml/.gitignore
generated
vendored
Normal 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
16
vendor/github.com/ma314smith/signedxml/.travis.yml
generated
vendored
Normal 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
21
vendor/github.com/ma314smith/signedxml/LICENSE.md
generated
vendored
Normal 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
98
vendor/github.com/ma314smith/signedxml/README.md
generated
vendored
Normal file
@@ -0,0 +1,98 @@
|
||||
## signedxml
|
||||
|
||||
[](https://travis-ci.org/ma314smith/signedxml)
|
||||
[](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.
|
||||
39
vendor/github.com/ma314smith/signedxml/envelopedsignature.go
generated
vendored
Normal file
39
vendor/github.com/ma314smith/signedxml/envelopedsignature.go
generated
vendored
Normal 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
|
||||
}
|
||||
302
vendor/github.com/ma314smith/signedxml/exclusivecanonicalization.go
generated
vendored
Normal file
302
vendor/github.com/ma314smith/signedxml/exclusivecanonicalization.go
generated
vendored
Normal 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
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
|
||||
}
|
||||
171
vendor/github.com/ma314smith/signedxml/signer.go
generated
vendored
Normal file
171
vendor/github.com/ma314smith/signedxml/signer.go
generated
vendored
Normal 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
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