diff --git a/.gitignore b/.gitignore old mode 100644 new mode 100755 index daf913b..a5e5c2a --- a/.gitignore +++ b/.gitignore @@ -22,3 +22,5 @@ _testmain.go *.exe *.test *.prof + +.vscode/ \ No newline at end of file diff --git a/.travis.yml b/.travis.yml deleted file mode 100644 index 17364db..0000000 --- a/.travis.yml +++ /dev/null @@ -1,14 +0,0 @@ -language: go -sudo: false - -go: - - 1.x - - master - -before_install: - - go get -v golang.org/x/lint/golint - -script: - - go vet ./... - - golint ./... - - go test -cover -v ./... diff --git a/LICENSE.md b/LICENSE.md old mode 100644 new mode 100755 diff --git a/README.md b/README.md old mode 100644 new mode 100755 diff --git a/c14n.go b/c14n.go new file mode 100755 index 0000000..457e6ec --- /dev/null +++ b/c14n.go @@ -0,0 +1,85 @@ +package signedxml + +import ( + // dsig "github.com/russellhaering/goxmldsig" + "github.com/lestrrat-go/libxml2/clib" + "github.com/lestrrat-go/libxml2/parser" +) + +/* +libxml2 based canonicalization: +- C14N10 - v1.0 inclusive canonicalization, with or without comments +- C14N11 - v1.1 inclusive canonicalization, with or without comments +- C14N10Exclusive - v1.0 exclusive canonicalation, with or without comments: problem: can't pass +namespace-prefix array with this + +*/ + +type C14N10Canonicalizer struct { + WithComments bool +} + +func (c C14N10Canonicalizer) Process(inputXML string, + transformXML string) (outputXML string, err error) { + + // parse string with libxml2 + p := parser.New() + doc, err := p.ParseString(inputXML) + if err != nil { + return "", err + } + + // canonicalize + canonicalString, err := clib.XMLC14NDocDumpMemory(doc, 0, c.WithComments) // http://xmlsoft.org/html/libxml-c14n.html#xmlC14NMode + if err != nil { + return "", err + } + + return canonicalString, nil +} + +type C14N11Canonicalizer struct { + WithComments bool +} + +func (c C14N11Canonicalizer) Process(inputXML string, + transformXML string) (outputXML string, err error) { + + // parse string with libxml2 + p := parser.New() + doc, err := p.ParseString(inputXML) + if err != nil { + return "", err + } + + // canonicalize + canonicalString, err := clib.XMLC14NDocDumpMemory(doc, 2, c.WithComments) // http://xmlsoft.org/html/libxml-c14n.html#xmlC14NMode + if err != nil { + return "", err + } + + return canonicalString, nil +} + +type C14N10ExclusiveCanonicalizer struct { + WithComments bool +} + +func (c C14N10ExclusiveCanonicalizer) Process(inputXML string, + transformXML string) (outputXML string, err error) { + + // parse string with libxml2 + p := parser.New() + doc, err := p.ParseString(inputXML) + if err != nil { + return "", err + } + + // canonicalize + canonicalString, err := clib.XMLC14NDocDumpMemory(doc, 1, c.WithComments) // http://xmlsoft.org/html/libxml-c14n.html#xmlC14NMode + if err != nil { + return "", err + } + + return canonicalString, nil +} diff --git a/envelopedsignature.go b/envelopedsignature.go old mode 100644 new mode 100755 diff --git a/examples/canonicalization_test.go b/examples/canonicalization_test.go new file mode 100644 index 0000000..072f5e1 --- /dev/null +++ b/examples/canonicalization_test.go @@ -0,0 +1,215 @@ +package examples + +import ( + "fmt" + "testing" + + "github.com/daugminas/signedxml" + + . "github.com/smartystreets/goconvey/convey" +) + +var example31Input = ` + + + + + +Hello, world! + + + + + +` + +var example31Output = ` +Hello, world! +` + +var example31OutputWithComments = ` +Hello, world! + + +` + +var example32Input = ` + + A B + + A + + B + A B + C + +` + +var example32Output = ` + + A B + + A + + B + A B + C + +` + +var example33Input = `]> + + + + + + + + + + + + + +` + +var example33Output = ` + + + + + + + + + + + + +` + +var example34Input = ` + +]> + + First line Second line + 2 + "0" && value<"10" ?"valid":"error"]]> + valid + + +` + +var example34Output = ` + First line +Second line + 2 + value>"0" && value<"10" ?"valid":"error" + valid + + +` + +// modified to not include DTD processing. still tests for whitespace treated as +// CDATA +var example34ModifiedOutput = ` + First line +Second line + 2 + value>"0" && value<"10" ?"valid":"error" + valid + + +` + +var example35Input = ` + + + + +]> + + &ent1;, &ent2;! + + +` + +var example35Output = ` + Hello, world! +` + +var example36Input = ` +©` + +var example36Output = "\u00A9" + +var example37Input = ` + +]> + + + + + + +` + +var example37SubsetExpression = ` + +(//. | //@* | //namespace::*) +[ + self::ietf:e1 or (parent::ietf:e1 and not(self::text() or self::e2)) + or + count(id("E3")|ancestor-or-self::node()) = count(ancestor-or-self::node()) +]` + +var example37Output = `` + +type exampleXML struct { + input string + output string + withComments bool + expression string +} + +// test examples from the spec (www.w3.org/TR/2001/REC-xml-c14n-20010315#Examples) +func TestCanonicalizationExamples(t *testing.T) { + Convey("Given XML Input", t, func() { + cases := map[string]exampleXML{ + "(Example 3.1 w/o Comments)": {input: example31Input, output: example31Output}, + "(Example 3.1 w/Comments)": {input: example31Input, output: example31OutputWithComments, withComments: true}, + "(Example 3.2)": {input: example32Input, output: example32Output}, + // 3.3 is for Canonical NOT ExclusiveCanonical (one of the exceptions here: http://www.w3.org/TR/xml-exc-c14n/#sec-Specification) + // "(Example 3.3)": {input: example33Input, output: example33Output}, + "(Example 3.4)": {input: example34Input, output: example34ModifiedOutput}, + // "(Example 3.5)": {input: example35Input, output: example35Output}, + // 3.6 will work, but requires a change to the etree package first: + // http://stackoverflow.com/questions/6002619/unmarshal-an-iso-8859-1-xml-input-in-go + // "(Example 3.6)": {input: example36Input, output: example36Output}, + "(Example 3.7)": {input: example37Input, output: example37Output, expression: example37SubsetExpression}, + } + for description, test := range cases { + Convey(fmt.Sprintf("When transformed %s", description), func() { + transform := signedxml.ExclusiveCanonicalization{ + WithComments: test.withComments, + } + resultXML, err := transform.Process(test.input, "") + Convey("Then the resulting XML match the example output", func() { + So(err, ShouldBeNil) + So(resultXML, ShouldEqual, test.output) + }) + }) + } + }) +} + +func TestTODO(t *testing.T) { + // The XML specifications cover the following examples, but our library does not successfully transform. + t.Logf("Input:\n%s\nOutput:\n%s\n", example33Input, example33Output) // Example 3.3 + t.Logf("Input:\n%s\nOutput:\n%s\n", example35Input, example35Output) // Example 3.5 + t.Logf("Input:\n%s\nOutput:\n%s\n", example36Input, example36Output) // Example 3.6 + t.Logf("Input:\n%s\nOutput:\n%s\n", example37Input, example37Output) // Example 3.7 +} diff --git a/examples/custom.go b/examples/custom.go new file mode 100755 index 0000000..27cb539 --- /dev/null +++ b/examples/custom.go @@ -0,0 +1,89 @@ +package examples + +import ( + "fmt" + "io/ioutil" + "os" + + "github.com/beevik/etree" + "github.com/daugminas/signedxml" +) + +func testLBmsgValidator() { + + // read xml file + // xmlFile, err := os.Open("../testdata/LB/ilpnot.xml") + xmlFile, err := os.Open("../testdata/LB/iltsoinf.xml") + // xmlFile, err := os.Open("../testdata/LB/roinvstg.xml") + // xmlFile, err := os.Open("../testdata/LB/rsltnofinvstgtn.xml") + if err != nil { + panic(err) + } + defer xmlFile.Close() + xmlBytes, _ := ioutil.ReadAll(xmlFile) + + // loax XML to validator + validator, err := signedxml.NewValidator(string(xmlBytes)) + if err != nil { + panic(err) + } + + // read cert + var certPEM string = "MIIFiDCCBHCgAwIBAgIKSZtmdgAAAAAA9TANBgkqhkiG9w0BAQUFADBdMQswCQYDVQQGEwJMVDEQMA4GA1UEBxMHVmlsbml1czEYMBYGA1UEChMPTGlldHV2b3MgYmFua2FzMQwwCgYDVQQLEwNNU0QxFDASBgNVBAMTC0xCLUxJVEFTLUNBMB4XDTExMDUyNjExMjMyOFoXDTQwMDgyNzA3NDMzM1owga8xMDAuBgoJkiaJk/IsZAEBDCBBNDI3QjFGM0M0MDFBMEQ0RTA0MzBBQzIwMzI5QTBENDEUMBIGA1UEBRMLMDAxMC8wMzUvMDExCzAJBgNVBAYTAkxUMRgwFgYDVQQKDA9MaWV0dXZvcyBiYW5rYXMxKTAnBgNVBAsMIE1va8SXamltbyBzaXN0ZW3FsyBkZXBhcnRhbWVudGFzMRMwEQYDVQQDDApURVNUIExJVEFTMIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEAl/bRLV1F9k1CCHPEedLbKVH3CDmrSlmNUfczG94tBabRdumiHTH14lopRkr6tS/2bN4xHU94Gd3Y9CdZ/lIBScJJVLlHJkZID8B7o4NyJXSSPOIg43fCbDO9q3eC/6WCTGklfmzhHxIOu5qqXetaWCtxDZKsLGJz625fRC/80Kw1JADMgQVl+mavj48LBQmkqfcHq5KDJQfHc1sthW+BGBdbNPE9wdGpmHQaCbt4fM3QczEhrHcDLSEvuFxUE9Z8WWLDs8GzrT4KPa5Y2MWZ5bdi2Rf3NiO46OLVuF97OcjsPQU5dx9jykd/ONTB8OOX1nEzK43d19zC8HkrlpFiYQIDAQABo4IB9TCCAfEwDgYDVR0PAQH/BAQDAgbAMIIBGwYDVR0gBIIBEjCCAQ4wggEKBgYEAI96AQIwgf8wgfwGCCsGAQUFBwICMIHvHoHsAFMAZQByAHQAaQBmAGkAawBhAHQAYQBzACAAbgBhAHUAZABvAGoAYQBtAGEAcwAgAHQAaQBrACAATABpAGUAdAB1AHYAbwBzACAAYgBhAG4AawBvACAAaQBuAGYAbwByAG0AYQBjAGkAbgEXAHMAZQAgAHMAaQBzAHQAZQBtAG8AcwBlAC4AIABGAG8AcgAgAHUAcwBhAGcAZQAgAGkAbgAgAHQAaABlACAASQBTACAAbwBmACAAdABoAGUAIABCAGEAbgBrACAAbwBmACAATABpAHQAaAB1AGEAbgBpAGEAIABvAG4AbAB5AC4wHQYDVR0OBBYEFCCU5ADFpsIgTc0MdhbwMr6oIAowMB8GA1UdIwQYMBaAFGCYgBf9iIkVmk22OnVwjPYOoFhzMDkGA1UdHwQyMDAwLqAsoCqGKGh0dHA6Ly93d3cubGIubHQvcGtpL2NybC9MQi1MSVRBUy1DQS5jcmwwRQYIKwYBBQUHAQEEOTA3MDUGCCsGAQUFBzAChilodHRwOi8vd3d3LmxiLmx0L3BraS9jZXJ0L0xCLUxJVEFTLUNBLmNydDANBgkqhkiG9w0BAQUFAAOCAQEArS4jZ0TeVLGJmwYMsNJNSzlMZ5GlnDzOHfNm/6/Dx1v0RfYJhL57V6H5REDCJzzdzemXsWLtYgjnP2UN1wTMcFcnYdRdbf7qrgSWdRCCQQlb8UOHct02bLyfOG4YzIsTqpYvcsmMu4dePquOabgLplGnSVWSHYsSgtkFXv7CR9e9bJ7QNvxj9hHQEtdyDOySEzkca784EqWS7x9R7m8Cyjj5EcZIgVN7s31kadZN3hoyMoqEeVc07z5SbKYDKxX7JHigzQeXlKYxQScJYov0JdzwKuH5LSy5+8kNruqz7y/KHRXiscrq6wEDmgfSx8NWW8KSj3ng75Nr+ZFx4AVSQg==" + cert, err := signedxml.LoadCertFromPEMString(certPEM, "CERTIFICATE") + if err != nil { + panic(err) + } + doc := etree.NewDocument() + err = doc.ReadFromBytes(xmlBytes) + if err != nil { + panic(err) + } + var certDigest, digestMethodURI string + if el := doc.FindElement(".//CertDigest/DigestMethod"); el != nil { + digestMethodURI = el.SelectAttrValue("Algorithm", "") + } + if el := doc.FindElement(".//CertDigest/DigestValue"); el != nil { + certDigest = el.Text() + } + err = signedxml.ValidateCertificate(cert, certDigest, digestMethodURI, "", "") + if err != nil { + panic(err) + } + + // set LB cert to validator - avoid fails in ref + validator.SetValidationCert(cert) + + // validate XML references (digests & signature) + err = validator.Validate() + if err != nil { + panic(err) + } + + fmt.Println("Example Validation Succeeded") +} + +func testOwnSignedDocValidator() { + // xmlFile, err := os.Open("../testdata/own/minimal_signed.xml") + // xmlFile, err := os.Open("../testdata/own/minimal_signed_RSAKeyValue.xml") + xmlFile, err := os.Open("../testdata/own/pacs008_signed.xml") + if err != nil { + fmt.Println("Error opening file:", err) + return + } + defer xmlFile.Close() + + xmlBytes, _ := ioutil.ReadAll(xmlFile) + + validator, err := signedxml.NewValidator(string(xmlBytes)) + if err != nil { + fmt.Printf("Validation Error: %s", err) + } else { + err = validator.Validate() + if err != nil { + fmt.Printf("Validation Error: %s", err) + } else { + fmt.Println("Example Validation Succeeded") + } + } +} diff --git a/examples/examples.go b/examples/examples.go old mode 100644 new mode 100755 index 98196f0..da176e8 --- a/examples/examples.go +++ b/examples/examples.go @@ -1,17 +1,19 @@ -package main +package examples import ( "fmt" "io/ioutil" "os" - "github.com/ma314smith/signedxml" + "github.com/daugminas/signedxml" ) -func main() { - testValidator() - testExclCanon() -} +// func main() { +// // testValidator() +// // testExclCanon() +// // testLBmsgValidator() +// testOwnSignedDocValidator() +// } func testValidator() { xmlFile, err := os.Open("../testdata/valid-saml.xml") diff --git a/exclusivecanonicalization.go b/exclusivecanonicalization.go old mode 100644 new mode 100755 diff --git a/go.mod b/go.mod new file mode 100755 index 0000000..bb890fe --- /dev/null +++ b/go.mod @@ -0,0 +1,17 @@ +module github.com/daugminas/signedxml + +go 1.17 + +require ( + github.com/beevik/etree v1.1.0 + github.com/lestrrat-go/libxml2 v0.0.0-20201123224832-e6d9de61b80d + github.com/smartystreets/goconvey v1.7.2 +) + +require ( + github.com/gopherjs/gopherjs v0.0.0-20181017120253-0766667cb4d1 // indirect + github.com/jtolds/gls v4.20.0+incompatible // indirect + github.com/pkg/errors v0.9.1 // indirect + github.com/smartystreets/assertions v1.2.0 // indirect + github.com/stretchr/testify v1.7.0 // indirect +) diff --git a/go.sum b/go.sum new file mode 100755 index 0000000..efdb781 --- /dev/null +++ b/go.sum @@ -0,0 +1,29 @@ +github.com/beevik/etree v1.1.0 h1:T0xke/WvNtMoCqgzPhkX2r4rjY3GDZFi+FjpRZY2Jbs= +github.com/beevik/etree v1.1.0/go.mod h1:r8Aw8JqVegEf0w2fDnATrX9VpkMcyFeM0FhwO62wh+A= +github.com/davecgh/go-spew v1.1.0 h1:ZDRjVQ15GmhC3fiQ8ni8+OwkZQO4DARzQgrnXU1Liz8= +github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= +github.com/gopherjs/gopherjs v0.0.0-20181017120253-0766667cb4d1 h1:EGx4pi6eqNxGaHF6qqu48+N2wcFQ5qg5FXgOdqsJ5d8= +github.com/gopherjs/gopherjs v0.0.0-20181017120253-0766667cb4d1/go.mod h1:wJfORRmW1u3UXTncJ5qlYoELFm8eSnnEO6hX4iZ3EWY= +github.com/jtolds/gls v4.20.0+incompatible h1:xdiiI2gbIgH/gLH7ADydsJ1uDOEzR8yvV7C0MuV77Wo= +github.com/jtolds/gls v4.20.0+incompatible/go.mod h1:QJZ7F/aHp+rZTRtaJ1ow/lLfFfVYBRgL+9YlvaHOwJU= +github.com/lestrrat-go/libxml2 v0.0.0-20201123224832-e6d9de61b80d h1:7uUkdtm6TC3olmG0I9lIAwBJQianl8YT5H8zcw6Mkpk= +github.com/lestrrat-go/libxml2 v0.0.0-20201123224832-e6d9de61b80d/go.mod h1:fy/ZVbgyB83mtricxwSW3zqIRXWOVpKG2PvdUDFeC58= +github.com/pkg/errors v0.9.1 h1:FEBLx1zS214owpjy7qsBeixbURkuhQAwrK5UwLGTwt4= +github.com/pkg/errors v0.9.1/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0= +github.com/pmezard/go-difflib v1.0.0 h1:4DBwDE0NGyQoBHbLQYPwSUPoCMWR5BEzIk/f1lZbAQM= +github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4= +github.com/smartystreets/assertions v1.2.0 h1:42S6lae5dvLc7BrLu/0ugRtcFVjoJNMC/N3yZFZkDFs= +github.com/smartystreets/assertions v1.2.0/go.mod h1:tcbTF8ujkAEcZ8TElKY+i30BzYlVhC/LOxJk7iOWnoo= +github.com/smartystreets/goconvey v1.7.2 h1:9RBaZCeXEQ3UselpuwUQHltGVXvdwm6cv1hgR6gDIPg= +github.com/smartystreets/goconvey v1.7.2/go.mod h1:Vw0tHAZW6lzCRk3xgdin6fKYcG+G3Pg9vgXWeJpQFMM= +github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME= +github.com/stretchr/testify v1.7.0 h1:nwc3DEeHmmLAfoZucVR881uASk0Mfjw8xYJ99tb5CcY= +github.com/stretchr/testify v1.7.0/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg= +golang.org/x/crypto v0.0.0-20190308221718-c2843e01d9a2/go.mod h1:djNgcEr1/C05ACkg1iLfiJU5Ep61QUkGW8qpdssI0+w= +golang.org/x/net v0.0.0-20190311183353-d8887717615a/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg= +golang.org/x/sys v0.0.0-20190215142949-d0b11bdaac8a/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= +golang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ= +golang.org/x/tools v0.0.0-20190328211700-ab21143f2384/go.mod h1:LCzVGOaR6xXOjkQ3onu1FJEFr0SW1gC7cKk1uF8kGRs= +gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= +gopkg.in/yaml.v3 v3.0.0-20200313102051-9f266ea9e77c h1:dUUwHk2QECo/6vqA44rthZ8ie2QXMNeKRTHCNY2nXvo= +gopkg.in/yaml.v3 v3.0.0-20200313102051-9f266ea9e77c/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM= diff --git a/signedxml.go b/signedxml.go old mode 100644 new mode 100755 index f14578c..7753696 --- a/signedxml.go +++ b/signedxml.go @@ -47,6 +47,16 @@ func init() { "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}, + + // xmllib2 canonicalizers, added: + // "http://www.w3.org/TR/xml-c14n": C14N10Canonicalizer{}, + // "http://www.w3.org/TR/xml-c14n#WithComments": C14N10Canonicalizer{WithComments: true}, + "http://www.w3.org/TR/2001/REC-xml-c14n-20010315": C14N10Canonicalizer{}, + "http://www.w3.org/TR/2001/REC-xml-c14n-20010315#WithComments": C14N10Canonicalizer{WithComments: true}, + // "http://www.w3.org/TR/xml-exc-c14n": C14N10ExclusiveCanonicalizer{}, + // "http://www.w3.org/TR/xml-exc-c14n#WithComments": C14N10ExclusiveCanonicalizer{WithComments: true}, + "http://www.w3.org/2006/12/xml-c14n11": C14N11Canonicalizer{}, + "http://www.w3.org/2006/12/xml-c14n11#WithComments": C14N11Canonicalizer{WithComments: true}, } } @@ -117,20 +127,21 @@ func (s *signatureData) parseSignedInfo() error { } // 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) + // value will match up I.e: check if SignedInfo prefix is defined in Signature, copy it to SignInfo attrs + if s.signedInfo.Space != "" { // if SignedInfo has a prefix + attr := s.signature.SelectAttr(s.signedInfo.Space) // find prefix definition in Signature if attr != nil { - s.signedInfo.Attr = []etree.Attr{*attr} + s.signedInfo.Attr = []etree.Attr{*attr} // copy the definition to SignedInfo } - } else { - attr := s.signature.SelectAttr("xmlns") + } else { // if no prefix + attr := s.signature.SelectAttr("xmlns") // select any attribute with root namespace, if there is such 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 + // i.e. check if SignedInfo prefix is defined in root, copy it to SignedInfo attrs root := s.xml.Root() if root != nil { @@ -142,6 +153,15 @@ func (s *signatureData) parseSignedInfo() error { } } + // It is adding tag namespaces, even if it wasn't used in SignedInfo - mistake. + // Solution: add all namespaces, which are used in the SignedInfo child tags + // signedInfoDoc, err := populateElementWithNameSpaces(s.signedInfo, s.xml.Copy()) + // if err != nil { + // return err + // } + // s.signedInfo.Parent().AddChild(signedInfoDoc.Root()) + // s.signedInfo.Parent().RemoveChildAt(0) // old signedInfo + return nil } @@ -211,14 +231,30 @@ func (s *signatureData) getReferencedXML(reference *etree.Element, inputDoc *etr // populate doc with the referenced xml from the Reference URI if uri == "" { outputDoc = inputDoc + + // // the above does not remove XML declarations from the root doc, + // // this fixes it, though it should be done by canonicalization: + // outputDoc = etree.NewDocument() + // outputDoc.SetRoot(inputDoc.Root()) + } else { refIDAttribute := "ID" if s.refIDAttribute != "" { refIDAttribute = s.refIDAttribute } - path := fmt.Sprintf(".//[@%s='%s']", refIDAttribute, uri) - e := inputDoc.FindElement(path) - if e != nil { + + // path := fmt.Sprintf(".//[@%s='%s']", refIDAttribute, uri) + // e := inputDoc.FindElement(path) + // if e != nil { + // outputDoc = etree.NewDocument() + // outputDoc.SetRoot(e.Copy()) + if e := inputDoc.FindElement(fmt.Sprintf(".//[@%s='%s']", refIDAttribute, uri)); e != nil { + outputDoc = etree.NewDocument() + outputDoc.SetRoot(e.Copy()) + } else if e := inputDoc.FindElement(fmt.Sprintf(".//[@%s='%s']", strings.ToLower(refIDAttribute), uri)); e != nil { + outputDoc = etree.NewDocument() + outputDoc.SetRoot(e.Copy()) + } else if e := inputDoc.FindElement(fmt.Sprintf(".//[@%s='%s']", strings.Title(strings.ToLower(refIDAttribute)), uri)); e != nil { outputDoc = etree.NewDocument() outputDoc.SetRoot(e.Copy()) } else { @@ -239,15 +275,21 @@ func (s *signatureData) getReferencedXML(reference *etree.Element, inputDoc *etr return outputDoc, nil } -func getCertFromPEMString(pemString string) (*x509.Certificate, error) { - pubkey := fmt.Sprintf("-----BEGIN PUBLIC KEY-----\n%s\n-----END PUBLIC KEY-----", - pemString) - +func LoadCertFromPEMString(pemString, pubKeyType string) (*x509.Certificate, error) { + var pubkey string + switch { + case strings.EqualFold("PUBLIC KEY", pubKeyType): + pubkey = fmt.Sprintf("-----BEGIN PUBLIC KEY-----\n%s\n-----END PUBLIC KEY-----", + pemString) + case strings.EqualFold("CERTIFICATE", pubKeyType): + pubkey = fmt.Sprintf("-----BEGIN CERTIFICATE-----\n%s\n-----END CERTIFICATE-----", + 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" { + if pemBlock.Type != "PUBLIC KEY" && pemBlock.Type != "CERTIFICATE" { return &x509.Certificate{}, errors.New("Found wrong key type") } @@ -296,7 +338,48 @@ func processTransform(transform *etree.Element, return docOut, nil } -func calculateHash(reference *etree.Element, doc *etree.Document) (string, error) { +// 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 +// } + +// calculates a hash of a TargetToBeHashed (*etree.Document or []byte), detecting +// the hash algorithm in the reference element. If successful, hash digest value in +// base64 encoded string is written to the reference element/DigestValue tag. +func CalculateHashFromRef(reference *etree.Element, targetToBeHashed interface{}) (string, error) { digestMethodElement := reference.SelectElement("DigestMethod") if digestMethodElement == nil { return "", errors.New("signedxml: unable to find DigestMethod") @@ -313,23 +396,196 @@ func calculateHash(reference *etree.Element, doc *etree.Document) (string, error "algorithm for %s in hashAlgorithms", digestMethodURI) } - doc.WriteSettings.CanonicalEndTags = true - doc.WriteSettings.CanonicalText = true - doc.WriteSettings.CanonicalAttrVal = true + var targetBytes []byte + var err error + switch v := targetToBeHashed.(type) { + case *etree.Document: + v.WriteSettings.CanonicalEndTags = true + v.WriteSettings.CanonicalText = true + v.WriteSettings.CanonicalAttrVal = true + targetBytes, err = v.WriteToBytes() + if err != nil { + return "", err + } - h := digestAlgo.New() - docBytes, err := doc.WriteToBytes() - if err != nil { - return "", err + case []byte: + targetBytes = v } - // ioutil.WriteFile("C:/Temp/SignedXML/Suspect.xml", docBytes, 0644) - // s, _ := doc.WriteToString() - // logger.Println(s) + // debug + // fn := strconv.FormatInt(time.Now().UnixNano(), 10) + ".xml" // unix-time based filename + // f, err := os.Create(fn) + // if err != nil { + // panic(err) + // } + // defer f.Close() + // _, err = f.Write(targetBytes) + // if err != nil { + // panic(err) + // } + + h := digestAlgo.New() // hasher + h.Write(targetBytes) // calculate hash + d := h.Sum(nil) // digest + calculatedValue := base64.StdEncoding.EncodeToString(d) // digest in base64 - h.Write(docBytes) - d := h.Sum(nil) - calculatedValue := base64.StdEncoding.EncodeToString(d) + return calculatedValue, nil +} + +// calculates a hash of a targetToBeHashed ([]byte), detecting the hash algorithm +// by the URI string. The URI follows notation common for XML Signatures. If successfull, +// it outputs base64 encoded string of a target hash digest (fingerprint). +func CalculateHash(targetToBeHashed []byte, digestMethodURI string) (string, error) { + + digestAlgo, ok := hashAlgorithms[digestMethodURI] + if !ok { + return "", fmt.Errorf("signedxml: unable to find matching hash"+ + "algorithm for %s in hashAlgorithms", digestMethodURI) + } + + h := digestAlgo.New() // hasher + _, err := h.Write(targetToBeHashed) // calculate hash + if err != nil { + return "", fmt.Errorf("signedxml: hashing error: %s", err) + } + d := h.Sum(nil) // digest + calculatedValue := base64.StdEncoding.EncodeToString(d) // digest in base64 return calculatedValue, nil } + +// Copies all namespaces that related to the targetElement. It must have the following namespaces: +// - own namespaces (if it defines such): nothing todo here, typically, they're defined in attributes of that element; +// - if the element has a prefix, but no definition for it, then parent has this namespace defined; +// - if any of the sub-elementas have a prefix, which is different from targetElement, then some parent must define it. +// Needed before canonicalizing and calculating hash of the target Element. +// TargetElem is always a sub-tag (child) of RootDoc +func PopulateElementWithNameSpaces(targetElem *etree.Element, rootDoc *etree.Document) (err error) { //(outputDoc *etree.Document, err error) { + + // check that targetElem is a child of rootDoc + if rootDoc.FindElement(".//"+targetElem.Tag) != nil { + + // Step 1: cycle through all prefixes used in the targetElement, + // these will be namespace definitions we'll have to have in the element + nsDefinitions := getUsedPrefixes(targetElem) + + // Step1.5: check if namespace definitions has an empty value (indicicator of + // default namespace). If it doesn't exists, check if any parents above the element + // have xmlns defined - if so, add this used nsDefinitions + if _, ok := nsDefinitions[""]; !ok { // if no empty k name exists + if checkIfParentsUseDefaultNS(targetElem, rootDoc) { + nsDefinitions[""] = "" + } + } + + // Step 2: starting with the targetElem, work up the path until all + // prefix keys (namespace names) have their corresponding definitions collected + nsDefinitions = getNameSpaceDefinitions(nsDefinitions, targetElem, rootDoc) + + // Step 3: populate the targetElem with the namespaces, relevant for it + // setNSDefinitionsDynamically(targetElem, nsDefinitions, []string{}) + setNSDefinitions(targetElem, nsDefinitions) + + } else if targetElem.FullTag() == rootDoc.FullTag() { + targetElem = rootDoc.Root() + } else { + err = errors.New("targetElem is not in the rootDoc, cannot copy namespaces") + } + + return +} + +// MOD: setts namespaces on the element, given in nsdef +func setNSDefinitions(el *etree.Element, nsdef map[string]string) { + for k, v := range nsdef { + if k == "" { + el.CreateAttr("xmlns", v) + } else { + el.CreateAttr("xmlns:"+k, v) + } + } +} + +// too complext, aimed at setting namespace where it is used +func setNSDefinitionsDynamically(el *etree.Element, nsdef map[string]string, parentPrefixes []string) { + + if el.Space != "" && !isInArray(el.Space, parentPrefixes) { + el.CreateAttr("xmlns:"+el.Space, nsdef[el.Space]) + parentPrefixes = append(parentPrefixes, el.Space) + } + + for _, c := range el.ChildElements() { + setNSDefinitionsDynamically(c, nsdef, parentPrefixes) + } +} + +// checks if items is in array +func isInArray(item string, array []string) bool { + for _, i := range array { + if item == i { + return true + } + } + return false +} + +// returns a map, where its keys are the unique prefixes used in the +// element and its children +func getUsedPrefixes(el *etree.Element) (outMap map[string]string) { + // Space is element tag prefix. If it's emtpy, then this element has root namespace. + // if it's not empty, then it's defined somewhere up the element path. + + outMap = map[string]string{} + outMap[el.Space] = "" // process element prefix + for _, c := range el.ChildElements() { + childMap := getUsedPrefixes(c) // process its children prefixes + for k, v := range childMap { + outMap[k] = v + } + } + return outMap +} + +// checks if any of the parents above define default namespace (attribute "xmlns=...") +func checkIfParentsUseDefaultNS(el *etree.Element, rootDoc *etree.Document) bool { + if attr := el.SelectAttr("xmlns"); attr != nil { + return true + } + upNext := rootDoc.FindElement(".//" + el.Tag).Parent() + if upNext != nil { + return checkIfParentsUseDefaultNS(upNext, rootDoc) + } + return false +} + +// takes a map of prefixes and cycles up the path from the element to +// collect its definitions +func getNameSpaceDefinitions(prefixMap map[string]string, el *etree.Element, rootDoc *etree.Document) (outMap map[string]string) { + var weHaveUnfilledValues bool + + for k, v := range prefixMap { + if v == "" { + weHaveUnfilledValues = true + if attr := el.SelectAttr(k); attr != nil { + prefixMap[k] = attr.Value + } else if attr := el.SelectAttr("xmlns:" + k); attr != nil { + prefixMap[k] = attr.Value + } else if attr := el.SelectAttr("xmlns"); attr != nil { // root NS + if _, ok := prefixMap[""]; ok { // make sure non-prefixed tags were in element + prefixMap[""] = attr.Value + } + } + } + } + upNext := rootDoc.FindElement(".//" + el.Tag).Parent() + if weHaveUnfilledValues && upNext != nil { + parentMap := getNameSpaceDefinitions(prefixMap, upNext, rootDoc) + for k, v := range parentMap { + if prefixMap[k] == "" && v != "" { + prefixMap[k] = v + } + } + } + outMap = prefixMap + return +} diff --git a/signedxml_test.go b/signedxml_test.go old mode 100644 new mode 100755 diff --git a/signer.go b/signer.go old mode 100644 new mode 100755 index d9abe47..3c77b90 --- a/signer.go +++ b/signer.go @@ -64,7 +64,7 @@ func (s *Signer) Sign(privateKey interface{}) (string, error) { return "", err } } - if err := s.parseSignedInfo(); err != nil { + if err := s.parseSignedInfo(); err != nil { // changes to SignedInfo tag return "", err } if err := s.parseSigAlgorithm(); err != nil { @@ -73,7 +73,7 @@ func (s *Signer) Sign(privateKey interface{}) (string, error) { if err := s.parseCanonAlgorithm(); err != nil { return "", err } - if err := s.setDigest(); err != nil { + if err := s.setDigest(); err != nil { // changes to Document, SignedProperties return "", err } if err := s.setSignature(); err != nil { @@ -96,20 +96,66 @@ 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) + // transforms := ref.SelectElement("Transforms") + // if transforms != nil { + // for _, transform := range transforms.SelectElements("Transform") { + // doc, err = processTransform(transform, doc) + // if err != nil { + // return err + // } + // } + // } + + // targetDoc, err := s.getReferencedXML(ref, doc) + // if err != nil { + // return err + // } + + // MOD: 1. change order: 1st find the ref, then transform + // 2. if not root doc, add namespaces + targetDoc, err := s.getReferencedXML(ref, doc) + if err != nil { + return err + } + + // copy relevant namespaces if the targetDoc is not the root document + if targetDoc.Root().Tag != s.xml.Root().Tag { + + // if targetDoc element is not root (i.e, root sub-tag or child) being "digested", + // then populate with relevant namespaces + err = PopulateElementWithNameSpaces(targetDoc.Root(), s.xml.Copy()) if err != nil { return err } } - doc, err := s.getReferencedXML(ref, doc) + transforms := ref.SelectElement("Transforms") + if transforms != nil { + for _, transform := range transforms.SelectElements("Transform") { + targetDoc, err = processTransform(transform, targetDoc) + if err != nil { + return err + } + } + } + + // MOD: canonicalization, defined at the signature level is mandatory for each + // reference before calculating the hash. This is to avoid situations when canonicalization + // is not explicitly defined in the of the Reference (it's implied). + // source: https://www.di-mgt.com.au/xmldsig2.html + targetDocStr, err := targetDoc.WriteToString() + if err != nil { + return err + } + targetDocStr, err = s.canonAlgorithm.Process(targetDocStr, "") if err != nil { return err } + targetDoc2 := etree.NewDocument() + targetDoc2.ReadFromString(targetDocStr) - calculatedValue, err := calculateHash(ref, doc) + // calculatedValue, err := calculateHash(ref, doc) + calculatedValue, err := CalculateHashFromRef(ref, targetDoc2) if err != nil { return err } @@ -136,7 +182,7 @@ func (s *Signer) setSignature() error { return err } - var hashed, signature []byte + var digest, signature []byte //var h1, h2 *big.Int signingAlgorithm, ok := signingAlgorithms[s.sigAlgorithm] if !ok { @@ -145,16 +191,17 @@ func (s *Signer) setSignature() error { hasher := signingAlgorithm.hash.New() hasher.Write([]byte(canonSignedInfo)) - hashed = hasher.Sum(nil) + digest = hasher.Sum(nil) switch signingAlgorithm.algorithm { case "rsa": - signature, err = rsa.SignPKCS1v15(rand.Reader, s.privateKey.(*rsa.PrivateKey), signingAlgorithm.hash, hashed) + // "RSASSA-PKCS1-v1_5" as in Section 8.2 of RFC8017 (https://tools.ietf.org/html/rfc8017) + signature, err = rsa.SignPKCS1v15(rand.Reader, s.privateKey.(*rsa.PrivateKey), signingAlgorithm.hash, digest) /* case "dsa": - h1, h2, err = dsa.Sign(rand.Reader, s.privateKey.(*dsa.PrivateKey), hashed) + h1, h2, err = dsa.Sign(rand.Reader, s.privateKey.(*dsa.PrivateKey), digest) case "ecdsa": - h1, h2, err = ecdsa.Sign(rand.Reader, s.privateKey.(*ecdsa.PrivateKey), hashed) + h1, h2, err = ecdsa.Sign(rand.Reader, s.privateKey.(*ecdsa.PrivateKey), digest) */ } if err != nil { diff --git a/testdata/LB/cert.pem b/testdata/LB/cert.pem new file mode 100755 index 0000000..a30f519 --- /dev/null +++ b/testdata/LB/cert.pem @@ -0,0 +1,3 @@ +-----BEGIN CERTIFICATE----- +MIIFiDCCBHCgAwIBAgIKSZtmdgAAAAAA9TANBgkqhkiG9w0BAQUFADBdMQswCQYDVQQGEwJMVDEQMA4GA1UEBxMHVmlsbml1czEYMBYGA1UEChMPTGlldHV2b3MgYmFua2FzMQwwCgYDVQQLEwNNU0QxFDASBgNVBAMTC0xCLUxJVEFTLUNBMB4XDTExMDUyNjExMjMyOFoXDTQwMDgyNzA3NDMzM1owga8xMDAuBgoJkiaJk/IsZAEBDCBBNDI3QjFGM0M0MDFBMEQ0RTA0MzBBQzIwMzI5QTBENDEUMBIGA1UEBRMLMDAxMC8wMzUvMDExCzAJBgNVBAYTAkxUMRgwFgYDVQQKDA9MaWV0dXZvcyBiYW5rYXMxKTAnBgNVBAsMIE1va8SXamltbyBzaXN0ZW3FsyBkZXBhcnRhbWVudGFzMRMwEQYDVQQDDApURVNUIExJVEFTMIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEAl/bRLV1F9k1CCHPEedLbKVH3CDmrSlmNUfczG94tBabRdumiHTH14lopRkr6tS/2bN4xHU94Gd3Y9CdZ/lIBScJJVLlHJkZID8B7o4NyJXSSPOIg43fCbDO9q3eC/6WCTGklfmzhHxIOu5qqXetaWCtxDZKsLGJz625fRC/80Kw1JADMgQVl+mavj48LBQmkqfcHq5KDJQfHc1sthW+BGBdbNPE9wdGpmHQaCbt4fM3QczEhrHcDLSEvuFxUE9Z8WWLDs8GzrT4KPa5Y2MWZ5bdi2Rf3NiO46OLVuF97OcjsPQU5dx9jykd/ONTB8OOX1nEzK43d19zC8HkrlpFiYQIDAQABo4IB9TCCAfEwDgYDVR0PAQH/BAQDAgbAMIIBGwYDVR0gBIIBEjCCAQ4wggEKBgYEAI96AQIwgf8wgfwGCCsGAQUFBwICMIHvHoHsAFMAZQByAHQAaQBmAGkAawBhAHQAYQBzACAAbgBhAHUAZABvAGoAYQBtAGEAcwAgAHQAaQBrACAATABpAGUAdAB1AHYAbwBzACAAYgBhAG4AawBvACAAaQBuAGYAbwByAG0AYQBjAGkAbgEXAHMAZQAgAHMAaQBzAHQAZQBtAG8AcwBlAC4AIABGAG8AcgAgAHUAcwBhAGcAZQAgAGkAbgAgAHQAaABlACAASQBTACAAbwBmACAAdABoAGUAIABCAGEAbgBrACAAbwBmACAATABpAHQAaAB1AGEAbgBpAGEAIABvAG4AbAB5AC4wHQYDVR0OBBYEFCCU5ADFpsIgTc0MdhbwMr6oIAowMB8GA1UdIwQYMBaAFGCYgBf9iIkVmk22OnVwjPYOoFhzMDkGA1UdHwQyMDAwLqAsoCqGKGh0dHA6Ly93d3cubGIubHQvcGtpL2NybC9MQi1MSVRBUy1DQS5jcmwwRQYIKwYBBQUHAQEEOTA3MDUGCCsGAQUFBzAChilodHRwOi8vd3d3LmxiLmx0L3BraS9jZXJ0L0xCLUxJVEFTLUNBLmNydDANBgkqhkiG9w0BAQUFAAOCAQEArS4jZ0TeVLGJmwYMsNJNSzlMZ5GlnDzOHfNm/6/Dx1v0RfYJhL57V6H5REDCJzzdzemXsWLtYgjnP2UN1wTMcFcnYdRdbf7qrgSWdRCCQQlb8UOHct02bLyfOG4YzIsTqpYvcsmMu4dePquOabgLplGnSVWSHYsSgtkFXv7CR9e9bJ7QNvxj9hHQEtdyDOySEzkca784EqWS7x9R7m8Cyjj5EcZIgVN7s31kadZN3hoyMoqEeVc07z5SbKYDKxX7JHigzQeXlKYxQScJYov0JdzwKuH5LSy5+8kNruqz7y/KHRXiscrq6wEDmgfSx8NWW8KSj3ng75Nr+ZFx4AVSQg== +-----END CERTIFICATE----- diff --git a/testdata/LB/ilpnot.xml b/testdata/LB/ilpnot.xml new file mode 100755 index 0000000..4bd056b --- /dev/null +++ b/testdata/LB/ilpnot.xml @@ -0,0 +1,124 @@ + + + +
+ LIABLT2XMSD + UAAMLT21XXX + 55 + S192910006283297 + 2019-10-18T08:04:44 + LITAS-INST +
+ + +
+ S192910006283297 + + FormDoc + 2019-10-18T08:04:44 + + ILPNOT + 55 + SEPAINST +
+ + + + + O192910019289527 + 2019-10-18T08:04:44 + + + 96487 + 2019-10-18T08:04:44 + + + + LT721020103010035400 + + + + + LIABLT2XMSD + + + + + + + INFO + + + 55 + CRDT +
+
2019-10-18
+ +
+
+
+
+
+
+
+
+ + + + + + + + + + UR71lqxT1AqiiroWOcxhT7IxSnx2kkHbgi0TEXJSl7k= + + + + R/wFOwKSleB9viuWSe/TZ1PrHY9+0K755hfVNCOQDYg= + + + kBI4DTDeOcGmhCn38nwGtpI+dHt9Qji2YEdDy4y9eRlKkYInEtoM1jwzp/PhMN5mm8kHM+CSHB1jFFt6JFSofGjWCpIW+wlDE8woi1JdMytsXpprIaxTp0IxkMM7gBB7CuJWpagqnP9NkD6Hd4qR6JQqSncpss/WqLfd5HBOlc2COym0V9yHoke9OUUdTi97Dx+wMqLaRw6bGl440So9A/YsQ9W7yUwdPDmdzBqxt2o2Ea4IAEflfsPXTjxh5exnN+0rcbGWTzJ9YuXwlUgbXRdT0Od+N+ibW+c2H1bZ4VlVBX39DA6NqaTTrX3Erg4f8tt1TkP9dbp5ApRiNhFpRg== + + + + + + + 2019-10-18T08:06:12+03:00 + + + + + 8qgOegrfKqAjvODPkq7ctJm4YHc= + + + C=LT, L=Vilnius, O=Lietuvos bankas, OU=MSD, CN=LB-LITAS-CA + 347599381669548201607413 + + + + + + + + + + + + + + + ancestor-or-self::dsig:SignatureValue + + + + MIICZwYJKoZIhvcNAQcCoIICWDCCAlQCAQMxCzAJBgUrDgMCGgUAMGkGCyqGSIb3DQEJEAEEoFoEWDBWAgEBBgYEAM4PAQEwITAJBgUrDgMCGgUABBQOAcijvfNWRXoxMrg7fL0yHYOYPAIQFmyl2zcGDKJMyaC2/fKJuBgPMjAxOTEwMTgwNTA2MTJaMAMCAQExggHVMIIB0QIBATBrMF0xCzAJBgNVBAYTAkxUMRAwDgYDVQQHEwdWaWxuaXVzMRgwFgYDVQQKEw9MaWV0dXZvcyBiYW5rYXMxDDAKBgNVBAsTA01TRDEUMBIGA1UEAxMLTEItTElUQVMtQ0ECCkmb8KUAAAAAAPYwCQYFKw4DAhoFAKBBMBoGCSqGSIb3DQEJAzENBgsqhkiG9w0BCRABBDAjBgkqhkiG9w0BCQQxFgQUaJaKvHeN49uPY6dd1w9dkjKy1hEwDQYJKoZIhvcNAQEBBQAEggEAQYh4Of+9HNnI5wUEVhCBy0Cgyn9q6yEjg7qIKu3LALpUQrw1kWZf8EJH6S40qfH8zl53vpBYc/uMPso50BPFHGMHpj0j4wTsNNtqlAzFypFE2qayjYyk7kWr+MHVy6V9WqD4suyRucb00S1qsRtrxcs2XT26eOf06LlLeMHXTkPdkndniy0g2fT+Beqej3mwG40qfFTA8JiUacRRNUz8hBEhbU6acyXPIF4zhL9nGLPnwq1v8/h+3/Ks64pImbtPLXVOAVpFplBnzJchajT460dMRZLy15cMruPTw3FDEoPbgunj66VC4r+13JEObSFnMyoO67EZ2matMHx7kTt/Sw== + + + + + + +
diff --git a/testdata/LB/iltsoinf.xml b/testdata/LB/iltsoinf.xml new file mode 100755 index 0000000..afeb9c5 --- /dev/null +++ b/testdata/LB/iltsoinf.xml @@ -0,0 +1,2 @@ + +
LIABLT2XMSDTRYULT21XXX55S1928400062777132019-10-11T07:59:54LITAS-PHA
S192840006277713FormDoc2019-10-11T07:59:54ILTSOINF55SEPAINST
S1928400062777132019-10-11T07:59:5412.5O192840019280677TRYULT21XXXLT831020103000037800O192840019280674O1928400192806742.52019-10-11LIABLT2XINSLT061020103000210100
8rqSzUtkmYsoi+gidJOKS/k2B4M=UPxuddCeQoBr97hRj8bY5RZoerE=ApJR4siWoLVqvmIiIs9yFn+71gnWX6d5000Xkqa6WbJ8AymQOrF/SEfapPeVEGdBSYLOZWYASHNwUq77qFOSoDyCbsRN9pCnqJQWUP0BT9eOEXeDNDLr8P8o56gR3OAEoGrMABY5vPBGzqnOCh0Rzr6nBDwmZJInIhnI2IuL7Hm5K77d/EUMWz0ry9b8eGls0h/5osGPRKbYucIolV2184f3TkxF52lgoUjT1e+T6sHUFsAkU2p2roezSZLoeIXuuTadNH5WfeAZhSplcQ0xb+Y12Mqm+071WXBbhci+L9VNsUoY8748vbO1javmjKpEEXLm/r+xLa2uuJbGr4xrhg==2019-10-11T08:00:23+03:008qgOegrfKqAjvODPkq7ctJm4YHc=C=LT, L=Vilnius, O=Lietuvos bankas, OU=MSD, CN=LB-LITAS-CA347599381669548201607413ancestor-or-self::dsig:SignatureValueMIICaAYJKoZIhvcNAQcCoIICWTCCAlUCAQMxCzAJBgUrDgMCGgUAMGoGCyqGSIb3DQEJEAEEoFsEWTBXAgEBBgYEAM4PAQEwITAJBgUrDgMCGgUABBSSsMfbqaAsZnGbBx3kcRFXDetW+gIRANYSQBeS/s6NScF2GsjJ+8YYDzIwMTkxMDExMDUwMDIzWjADAgEBMYIB1TCCAdECAQEwazBdMQswCQYDVQQGEwJMVDEQMA4GA1UEBxMHVmlsbml1czEYMBYGA1UEChMPTGlldHV2b3MgYmFua2FzMQwwCgYDVQQLEwNNU0QxFDASBgNVBAMTC0xCLUxJVEFTLUNBAgpJm/ClAAAAAAD2MAkGBSsOAwIaBQCgQTAaBgkqhkiG9w0BCQMxDQYLKoZIhvcNAQkQAQQwIwYJKoZIhvcNAQkEMRYEFBCeO79048MXXY2QdJDgsXhysRD4MA0GCSqGSIb3DQEBAQUABIIBAGaKtqXndSfa4XqaSIu/OZv6zfLUpsI4fVbiqBOKrtUvEXQgt8XUdQBaHJifasnalsOmQ38LiQfO4W5nXa1w1OmjRDPN/F5Q9faFF0rcKNwYICWxX1OxKURkDOLuxCdmQ5rwE5Zv5c3XgfdkaEad2NLJijH3xlW1CJreCP089CGOVVgT6pWbxc+AgOBWIYLBdc8H33Yzetd7+wIjoizLHdwLuVJ+evHlHTXJmDTZaTdCAF39evDa5GCL8NjbMEoCWuRCQHmYIVO7bluzF+KKh44qPUEx2OjQjO9xPPqRh0Rg2Mw6VuV+6CLSEo54HsJDFNP7rfb6p9IPFMobmdZralE=
diff --git a/testdata/LB/intermediate.pem b/testdata/LB/intermediate.pem new file mode 100755 index 0000000..542a0a4 --- /dev/null +++ b/testdata/LB/intermediate.pem @@ -0,0 +1,3 @@ +-----BEGIN CERTIFICATE----- +MIIFYDCCA0igAwIBAgIKYWSxSAAAAAAAAzANBgkqhkiG9w0BAQUFADBiMQswCQYDVQQGEwJMVDEQMA4GA1UEBxMHVmlsbml1czEYMBYGA1UEChMPTGlldHV2b3MgYmFua2FzMQwwCgYDVQQLEwNNU0QxGTAXBgNVBAMTEExCLUxJVEFTLVJPT1QtQ0EwHhcNMTAwODI3MDczMzMzWhcNNDAwODI3MDc0MzMzWjBdMQswCQYDVQQGEwJMVDEQMA4GA1UEBxMHVmlsbml1czEYMBYGA1UEChMPTGlldHV2b3MgYmFua2FzMQwwCgYDVQQLEwNNU0QxFDASBgNVBAMTC0xCLUxJVEFTLUNBMIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEAs5+VO3eKDFB1dVGkmFvAtGLyXLrTCdPfTd6hvm5qGQ5hjC0gx/SgBp2QSPnRXWWccoGCWYYuWgc900B0ln+boBjzfLqFKMxIhabrwS7vJIQX2imM1SNSq65PYA1xlC7GVYxpmn407f2TnXJJtjvMSCkCZVVoVqR97Qe5LQ+Qtq8+AugyVBPGeM15yBy65iuIhgpw55u4RNU1TtQ/6ECaH+0Hv0HAmoSRmT1MUYd4IRgC+jMRLUEnXYYMjEomVngVL5Peb3MN6tPxSlkK75TXADJkUwwxTP6pwU0+83UmWSb9EPxKvD+VpdP2s7YT/Saav3w0x7qCJoQC57/VJ4CtEQIDAQABo4IBGzCCARcwEAYJKwYBBAGCNxUBBAMCAQAwHQYDVR0OBBYEFGCYgBf9iIkVmk22OnVwjPYOoFhzMBkGCSsGAQQBgjcUAgQMHgoAUwB1AGIAQwBBMAsGA1UdDwQEAwIBhjAPBgNVHRMBAf8EBTADAQH/MB8GA1UdIwQYMBaAFFl0eSGRBuXzGc6uCf4GzSCF9+VMMD4GA1UdHwQ3MDUwM6AxoC+GLWh0dHA6Ly93d3cubGIubHQvcGtpL2NybC9MQi1MSVRBUy1ST09ULUNBLmNybDBKBggrBgEFBQcBAQQ+MDwwOgYIKwYBBQUHMAKGLmh0dHA6Ly93d3cubGIubHQvcGtpL2NlcnQvTEItTElUQVMtUk9PVC1DQS5jcnQwDQYJKoZIhvcNAQEFBQADggIBAINaMR7OGai71ULxTgtEkcxF4O0reivcFNUh2UddxadWDN1st8YTC1HSOu43NGWo+V77dxe7aP8e6p9OiEIgdzJWOvDxoA6JVFz1AN0JPMpXzwj+M19Y+kM5ncqdCjrB4NcXYxJvcjyOlc1UNfIdAGG8o15H4HEWzreIE3o0JxlZD/JIjBzkhNYVJxvJyK7ErO0YuDycOvYNGiOG521nz7UoPYJY/lb03rPjZ3wC0ayrcEPFRobbYAmSqLOAK9S6DHPVgWNNVIowbbmfOQT/vNIEZ1mmWvCx1U83mOrvZiMpmbHy02AS21k0yvWZq/yHQd/+CXKtSldldE5Z+IaQl4n0Ll6aV2UjbVu79yd9JW1py0bh4lciEXp7M3lQQ/4m/EiMoMEf31Tjqh9k+8i6AinN1fVmb5BnYeCtB6r9Pz9ppNC8/ezFdlrebdJNUZFEDAt5XucTHUQ8jYjrdeimqjQyRfKqiTmJ0BRN7zJyq2U1dWC3rL8vQbvwIUjModFEk9Bo6PZSJ8CrN4h85awSewRwqPTZMEQxYOWsNJzxfIFrCIH7O8LrRRcRXITb5yyYibWvpJ+5sqpeeVdtwR4uPk9LSJE62PCs3g7JDYnYdXYgSfxooFaJlYS0TlatXW3xW4G4eJEkowM1YMzr10E2r9/fNi08QqdEgW9+N9H8jqpv +-----END CERTIFICATE----- diff --git a/testdata/LB/roinvstg.xml b/testdata/LB/roinvstg.xml new file mode 100755 index 0000000..7810d76 --- /dev/null +++ b/testdata/LB/roinvstg.xml @@ -0,0 +1,178 @@ + + + +
+ LIABLT2XMSD + BANDLT21XXX + 51 + S182550005953387 + 2018-09-12T11:37:17 + LITAS-RLS +
+ + +
+ S182550005953387 + + FormDoc + 2018-09-12T11:37:17 + + ROINVSTG + 51 + SEPASCT +
+ + + + + S182550005953387 + + + + LIABLT2XMSD + + + + + + + BANDLT21XXX + + + + 2018-09-12T11:37:17 + + + RJCR + + + + RT01536741376723 + + FFCCTRNS + pacs.008.001.02 + + NOTPROVIDED + va001 + RJCR + + + + + VAUALT21XXX + + + + + AC04 + + + + 11.00 + 2018-09-12 + + CLRG + + LITAS-RLS + + + + + SEPA + + + + Bandomasis + + + + LT950100100000123456 + + + + + BANDLT21XXX + + + + + VAUALT21XXX + + + + Frodo Baggins + + + + LT183070020100000030 + + + + + + + + +
+
+
+ + + + + + + + + + E6QSUeOPHp+E6USDMrN37nL9iVQ= + + + + WvY17Gcsp7aHX9M6pwl5Fgbam1k= + + + Pr62NMjfDtZnzRFgJq9jpZdm0230y0sYM2nv9zzs2hczTcJ3deUCfjYhlScEG3Xep9+joASRTyJLcELlxSgMFyXVxI5pTzwDbFcnH6HH1RW1K7szazL4SmLS3grcNxKY/2erhnxS/84vILVE2zRGE8rOLTenZwmy+bcKw9RTblaGQ6xTh3MKN2JkhGAOPPQAmR8G8ADb1vgpklRDaDL0GG65xq3wUOWeHkiI4LDTfEFj+YyNf1IBpdPKPecvhk3yTkX9xoHzdwNKgdXvEkDlAQv0Azxu3110+y2HAJ1EjL51UrRDuJwrSdZeSUZ6OSP7kwqA8iovlL3hIaV7ehDLQQ== + + + + + + + 2018-09-12T11:37:43+03:00 + + + + + 8qgOegrfKqAjvODPkq7ctJm4YHc= + + + C=LT, L=Vilnius, O=Lietuvos bankas, OU=MSD, CN=LB-LITAS-CA + 347599381669548201607413 + + + + + + + + + + + + + + + ancestor-or-self::dsig:SignatureValue + + + + MIICaAYJKoZIhvcNAQcCoIICWTCCAlUCAQMxCzAJBgUrDgMCGgUAMGoGCyqGSIb3DQEJEAEEoFsEWTBXAgEBBgYEAM4PAQEwITAJBgUrDgMCGgUABBStpG85eOnt0jfxeHARhVrpWMbMXgIRAJi42HyeGOiFQlFK6yW/FFgYDzIwMTgwOTEyMDgzNzQzWjADAgEBMYIB1TCCAdECAQEwazBdMQswCQYDVQQGEwJMVDEQMA4GA1UEBxMHVmlsbml1czEYMBYGA1UEChMPTGlldHV2b3MgYmFua2FzMQwwCgYDVQQLEwNNU0QxFDASBgNVBAMTC0xCLUxJVEFTLUNBAgpJm/ClAAAAAAD2MAkGBSsOAwIaBQCgQTAaBgkqhkiG9w0BCQMxDQYLKoZIhvcNAQkQAQQwIwYJKoZIhvcNAQkEMRYEFKf7Xxi5gkNFNNl/3vc0Zl6CL5TAMA0GCSqGSIb3DQEBAQUABIIBAMH2FrDenbnZ0qKJ8wqnkd2BAHX3bDm6//ILu5eERwrU5tIQSJqAZmufvzbIy06xvkNTCFCNdy/UM9wIv7oCFOJn+RGfSRD1SqdJ6o+VVXb5KdFHMOoIg9wyBDRGeJa/2MaAKC5SmbyDhlmtfy44NpOaJdW77kBI5VBmgvigh4IJsYy1T9Rjf4fTpW605Y5dA8hmMiY43tVMO1kSLdpyMe68v8OLNxp6wWhoxisLvKrgEM3dZmpXxEskzlD5xxhhsHkkYHEFUMRPPCow0fx/xpaSTNvnY+n5IYpLMG3lvzQmsd8Ct59m93z2Gvxz8eYwaAVKBV1Cnjc+AUz4Ku1APDo= + + + + + + +
diff --git a/testdata/LB/root-ca.pem b/testdata/LB/root-ca.pem new file mode 100755 index 0000000..9478c03 --- /dev/null +++ b/testdata/LB/root-ca.pem @@ -0,0 +1,3 @@ +-----BEGIN CERTIFICATE----- +MIIFoTCCA4mgAwIBAgIQOPX+M4cXw69LDS0bYk/z9DANBgkqhkiG9w0BAQUFADBiMQswCQYDVQQGEwJMVDEQMA4GA1UEBxMHVmlsbml1czEYMBYGA1UEChMPTGlldHV2b3MgYmFua2FzMQwwCgYDVQQLEwNNU0QxGTAXBgNVBAMTEExCLUxJVEFTLVJPT1QtQ0EwIBcNMTAwODI3MDU1MDEwWhgPMjA3MDA4MjcwNTU5NTBaMGIxCzAJBgNVBAYTAkxUMRAwDgYDVQQHEwdWaWxuaXVzMRgwFgYDVQQKEw9MaWV0dXZvcyBiYW5rYXMxDDAKBgNVBAsTA01TRDEZMBcGA1UEAxMQTEItTElUQVMtUk9PVC1DQTCCAiIwDQYJKoZIhvcNAQEBBQADggIPADCCAgoCggIBAMqlQVbTKw8lDj2EKwlFv5iIOAGaRV02/W3Z2CApbv4E7W7nfry0nEgeLe/JzBDQJpXB0ABrP7YMGC6J2q1PbViVdG3uLFtbxWTCaHGw5eJELKtxQSj+xzaQQDzGlNQvDzcgm/tQPnh0+J86ZMSiW1c88yhXX7rMEoQIaSEsaQInnB4Jwe5/ADGPrvE939+hrIYSRLIlLjBRAdvwTkcXMPZE79vBYWumVsxmYjcLpzm0q69146VFeREyPHVg0FhVK6YOF5H7ckTq8/e7Lw1jgcV9ozDJTgW9sqVwY5EQhK9V+YFr0oLAmDCIDI+OGDl+JDwktMv4kkk8YmWgcWH541XIyoM8J2j2CAH5I2pGEY87saaV1JROSzgg8wF5OZMauFiKZ099FGqhIsfrBJLnurI1AekS2SdL3AH8NFzJ+bV+A+uzOmDCbmY0O67TPPuZv1SNavUABbB3p0jc22z7obqRAvfOq00ctlEYTBygmE1Xr3D68UvEAFq30ZyS3twBi/HIoGl4gG2ygQv7EmDhCkNu+tLLWGb4bIVWIjJpUJdJrEhq3xBqFY4fiWb8tIxy4/iPGdFkQQ5rvjCHORLwi71ZR7nCFLLebdG+a3SbRSGpG9q/vRIYw31IynzEGzo4vEfmDVHyVib5YQqcLA7pGGnvYHQncan7laOM20x892nxAgMBAAGjUTBPMAsGA1UdDwQEAwIBhjAPBgNVHRMBAf8EBTADAQH/MB0GA1UdDgQWBBRZdHkhkQbl8xnOrgn+Bs0ghfflTDAQBgkrBgEEAYI3FQEEAwIBADANBgkqhkiG9w0BAQUFAAOCAgEAMjDbJYVemx/EJqlHEOCozq2pQUjEaGNRx0Z3IcdR3i6ARayg3ITvM38TKP6rqC8T3mdstPs5/Vuf6NacKiOgLJjSEEyc6FNPah+EN7aNjHJucfr2PsyfxKtxMdrEHakf3J02D8DK+5/+3iQTp/iT4jAxSsrGI3cn1bwCtbBEEElXXBRerQglZITmcrf7Qf5nAxZ9OFVZT1AmUGTZNz45dts3QXd8OGs9UopV3LFCuXerbElOZFx2lK2X0PT+pZknfDvDIhi2vCpMSegbGLClt0/Wgsc7dPRNUBYx+aPsaZv1/pYCsI2muHiYTemBt9gm/d4yVrb0IqExc0xWa3CYS68QWyLJFDvuVIu6AtI/Oo7WS4pGKbfI+D3pJquaSwPZYFnzHg3Cix/LL9dpOe1HqBotiDmP4Z+eDPGlWLj/0RB19Y5oxVUR10AwTSE6qk9aWgtDeT//cqyPEIaqrpDpfQ01/FiisEfT/ZCpiMEFI9ruIEnGIprJM520AdJwCzz4UzQUWpO6KuOAVBz0gz2vBeEo5/e/Yex/XLciPmHArHkMahNR2zDKYU3aRGbPPHZgUB1pZRPN4AfOUxQU9mcGoM7RSa9Ky164GOOhYTnxSpXIWdIgAufj3OUV1edlHuYl8w/Bk51isZVweew4FpQ40/k0yVfk+yo5Kcup7Vi4Tio= +-----END CERTIFICATE----- diff --git a/testdata/LB/rsltnofinvstgtn.xml b/testdata/LB/rsltnofinvstgtn.xml new file mode 100755 index 0000000..0baebf3 --- /dev/null +++ b/testdata/LB/rsltnofinvstgtn.xml @@ -0,0 +1,107 @@ + + +
+ ABLPLT21XXX + LIABLT2XMSD + 51 + 01191003000004B9 + 2019-10-03T09:39:20 + LITAS-MIG +
+ + +
+ 01191003000004B9 + + FormDoc + 2019-10-03T09:39:20 + + RESINV + 51 + SEPAQuery +
+ + + + + 01191003000004B9 + + + + ABLPLT21XXX + + + + + + + LIABLT2XMSD + + + + 2019-10-03T09:39:20 + + + 01191003000004B9 + + + + ABLPLT20XXX + + + + + + MODI + + + 4S1927400062680804 + + S192740006268080 + pacs.008.001.02 + + NOTPROVIDED + txid.2019-09-30.14:52:11.45d4dd8 + + + CLRG + + ST2 + + + + + SEPA + + + + + FR7612345678900001234567858 + + + + + SATPFRP0 + + + + + ABLPLT20XXX + + + + + LT023030000000123456 + + + + + + 2019-10-02 + + + + +
+
+
AeljTX3XdhMEVFaHunp2OA0o958WIxPBz7RJlUoXVhU=Pj1h8jNnLa144y5RM7dvi4qhlwJbskyj+h/vE4XOzv0=rY9qedkv6z31RdLGyCuSjJpODrJzwtb/8vOxxufHlw7rnPaPimXjgf/C3H1uL4Xexlyelv5oEmpZ7SQsxQqhESC6lP91tvA5w5N2exu9EitVnu7xGHUx9hQ1TbfOGZohbXczU3xhaJaNh+EL463JEoux/GOkVJnhRJH4egvdI5s=2019-10-03T09:44:18+03:00J03XIAgLz6aH0GAETXqeC1Gn/Nk=C=LT, L=Vilnius, O=Lietuvos bankas, OU=MSD, CN=LB-LITAS-CA109513179832538871370210ancestor-or-self::dsig:SignatureValueMIICZwYJKoZIhvcNAQcCoIICWDCCAlQCAQMxCzAJBgUrDgMCGgUAMGkGCyqGSIb3DQEJEAEEoFoEWDBWAgEBBgYEAM4PAQEwITAJBgUrDgMCGgUABBTyZdUrqlqkyxmuA+YSk10FZRVwNAIQCKK3NVOMUZlIPeEzgl0K+RgPMjAxOTEwMDMwNjQ0MjBaMAMCAQExggHVMIIB0QIBATBrMF0xCzAJBgNVBAYTAkxUMRAwDgYDVQQHEwdWaWxuaXVzMRgwFgYDVQQKEw9MaWV0dXZvcyBiYW5rYXMxDDAKBgNVBAsTA01TRDEUMBIGA1UEAxMLTEItTElUQVMtQ0ECCkmb8KUAAAAAAPYwCQYFKw4DAhoFAKBBMBoGCSqGSIb3DQEJAzENBgsqhkiG9w0BCRABBDAjBgkqhkiG9w0BCQQxFgQULELyJSS6MUN4GuzDv4IogBH98FIwDQYJKoZIhvcNAQEBBQAEggEAjYmT2yxBGwlhQS6lFvCRfHznqJQsXTWvj+ylt1RkyOL5vxHPLSnZgFDXMB409pN3qRrFbOmWN1ic5xmVy4pg+Zpgjm8OAROUVAsu2Up58+Gn4jZeCMTpikOx+RpnCzfZqVOrqZBEDpZHYVv945TGxVYvY0VU9VUXWILwv4j2/Fv2UibmdWSABP7MA3EwbzeKMt0ZBuhnv2wz/Si15qbwFUdVDNqM/bJaCDYyrBDgXJH1rpkcfLPRn/ffGNsS52i5nMXCuca+JhjXoMsI/JW+t7YzxLEq9N0xffeupVG4OlS7cJsNQnFilYDjiyoKcfnHtVclyh4wyE049cSXptOC+A==
diff --git a/testdata/bbauth-metadata.xml b/testdata/bbauth-metadata.xml old mode 100644 new mode 100755 diff --git a/testdata/invalid-signature-changed content.xml b/testdata/invalid-signature-changed content.xml old mode 100644 new mode 100755 diff --git a/testdata/invalid-signature-non-existing-reference.xml b/testdata/invalid-signature-non-existing-reference.xml old mode 100644 new mode 100755 diff --git a/testdata/invalid-signature-signature-value.xml b/testdata/invalid-signature-signature-value.xml old mode 100644 new mode 100755 diff --git a/testdata/nosignature-custom-reference-id-attribute.xml b/testdata/nosignature-custom-reference-id-attribute.xml old mode 100644 new mode 100755 diff --git a/testdata/nosignature.xml b/testdata/nosignature.xml old mode 100644 new mode 100755 diff --git a/testdata/own/minimal_signed.xml b/testdata/own/minimal_signed.xml new file mode 100755 index 0000000..5a42040 --- /dev/null +++ b/testdata/own/minimal_signed.xml @@ -0,0 +1,54 @@ + + + + + + + + + + + 3qPQF56HYcF7PZ//TPc3tSiN9PCV254ycN83zciMqHo= + + + + + + + izo8g0o8ks1271nYYzovz9+DTU/W+yXL5F3hogsfZXg= + + + MzKzMy9y7nTN5lHkPKaJPVIISf5XRzvmq7RoaJtpA7VWR5lctBVeO+c8EXPlfad5gKy3V/bzAtCgNcBbGoiVHu1giJd3Bo+NLt0sXHaXebLSbHnI0g/AvKD2GEGgGqrdviLrBNck9OV4tVZYtkV42kSXaZoTAEUxWNLZ01yhVZJrgn65XrcLmhZKViCtGS4JRvbA7r7ER6KVb6k3aP0VPFargRx+mBKTlv5jIK7Lkn0lBkN9IwvyVt+Xl2/A2PHVzWXMLEh09D64W4O/NyInWuZyUqGiJXi3AhhGUXTKm1Z0X+Sq2geLuHEDZ+WBgSm2f3gHZkbYK5ls6sAdjGZgFA== + MIID2TCCAsGgAwIBAgIUEEnabJkXk0Cflcvmaw/J1dZqct8wDQYJKoZIhvcNAQELBQAwTjELMAkGA1UEBhMCVVMxEzARBgNVBAgMCkNhbGlmb3JuaWExEjAQBgNVBAcMCVRoZSBDbG91ZDEWMBQGA1UECgwNTXkgQ29tcGFueSBDQTAeFw0yMTExMjExMjQxMThaFw0yMjAzMjExMjQxMThaMIGNMQswCQYDVQQGEwJMVDESMBAGA1UECAwJTGl0aHVhbmlhMRIwEAYDVQQHDAlUaGUgQ2xvdWQxDTALBgNVBAoMBERlbW8xFTATBgNVBAMMDFNpZ25pbmcgY2VydDEwMC4GCgmSJomT8ixkARkWIGxvY2FsaG9zdC9lbWFpbEFkZHJlc3M9c2RAc2RmLmZmMIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEAwLBaiVFQg8xTW7NTcCre7hy2yEKgZmGeESsUMOufOd0Hp7xraYp/CuTj8fptHHqFcIqdI520Br17WESWe5WM5XxkA/eWAmN3hBbi5lUM5b9w/KwAkq8RHBZaq+vHwVAFMnlZMBWRz2R5PcQ2NyJj0YBgImJImhm7KllkKvuLLXNqjYYp7S4n/lXbPHOYA28PeJlPLYe4Wbpe18aGoGRaag9PK4oR4tX1rHjtcAWf8u7NmJX56/6xRl15qsMngTNJKu6mxMDd5i9X1xDNRzVN7uTlK9CBqmq+aflDLCnSpiZX/W3AazOpJm5Cm1f8vqOX8XnJ6DIWUREnZ/IU2CT7dwIDAQABo28wbTAMBgNVHRMBAf8EAjAAMB0GA1UdDgQWBBRd2sKVueUBgVKqiiwOOZ93E7Kw+jALBgNVHQ8EBAMCBeAwMQYDVR0lBCowKAYIKwYBBQUHAwIGCCsGAQUFBwMJBggrBgEFBQcDAwYIKwYBBQUHAwgwDQYJKoZIhvcNAQELBQADggEBAIKoE6AXe2akNOPeVvGWc4BaATm8M/E+UqjkD0j6hPlQJeJBEPZfFYxqSG1egK6LkvA7cZei1gFqrKbHwyrnda8fiLB+0TkgQKJGU427GFSSaAHtqY1nSsXcDTTQCsjvIY9BzYzcfdD7grYzvBdCuhwU7Wtki6RX6cVKK08HlO+k+CN8/bo0SAns8STGluFLP7RwR6U0bHXOGQJoFetJZ2+jj1NfuCkolrV1qWzhdznxvrD9j/BAaPi8kiMHbApF2NpQkP/vLWqYVPp0z4WEZ2UpFUWPAfVXOnRDSFpTGEdmeXdbPsR2/QsM41WZqSe6zDv+zISMpwgmuKbkhthK7gc= + + + + + 2021-12-19T21:43:39.656Z + + + + + z6Te8U9MUPFnKUKBB1IiYAxfWD0= + + + O=My Company CA,L=The Cloud,ST=California,C=US + 92990834164997211889227204231006993513378902751 + + + + + + + + + + + + + + what not + + + + \ No newline at end of file diff --git a/testdata/own/minimal_signed_RSAKeyValue.xml b/testdata/own/minimal_signed_RSAKeyValue.xml new file mode 100755 index 0000000..5f35cc6 --- /dev/null +++ b/testdata/own/minimal_signed_RSAKeyValue.xml @@ -0,0 +1,5 @@ + + + what not + +IkKICQEn7PJZJQzT/v99xf7kXg8C/TxVoF8vcX0pk3Q=YCvhYGsMf37qNi7lejSOy/FvP+DkGXjjolIqpUpAppg=mRAGkoy1Hy17ZatGEQjbPdohceYImv9S+KTHe2W7B8JCquEZ+P3zYQnZP4VTPMnhJCn+8VyO5HhYa52QDiwzErRxLSOaNzmdn2KZBccmb/ZvKws5/8LkUGbUaFJqwiQr7/qsLyuvfjXZ9qEmMjxg3OMZNJFt/f9l1U11ucTvoY5ZDCtUFYiztvAWgucjrboeYD/51q8+5RpaasYMt9WEyh+UYR1jyDSIHoq14gl4K0FbzCHBV7ufOz6hTPTlOY8revCl9/PBh6QMG7nlzAVaeZNh+m3OG0IVfCSWvBNHLuNxWdWAF6crawzYZi+nRpWXzZ/tA22S4q3ROh0NgtYYGw==wLBaiVFQg8xTW7NTcCre7hy2yEKgZmGeESsUMOufOd0Hp7xraYp/CuTj8fptHHqFcIqdI520Br17WESWe5WM5XxkA/eWAmN3hBbi5lUM5b9w/KwAkq8RHBZaq+vHwVAFMnlZMBWRz2R5PcQ2NyJj0YBgImJImhm7KllkKvuLLXNqjYYp7S4n/lXbPHOYA28PeJlPLYe4Wbpe18aGoGRaag9PK4oR4tX1rHjtcAWf8u7NmJX56/6xRl15qsMngTNJKu6mxMDd5i9X1xDNRzVN7uTlK9CBqmq+aflDLCnSpiZX/W3AazOpJm5Cm1f8vqOX8XnJ6DIWUREnZ/IU2CT7dw==AQAB2021-12-19T21:52:18.422Zz6Te8U9MUPFnKUKBB1IiYAxfWD0=O=My Company CA,L=The Cloud,ST=California,C=US92990834164997211889227204231006993513378902751 \ No newline at end of file diff --git a/testdata/own/pacs008_signed.xml b/testdata/own/pacs008_signed.xml new file mode 100755 index 0000000..89e869e --- /dev/null +++ b/testdata/own/pacs008_signed.xml @@ -0,0 +1,55 @@ + + + + + 3408f3caf90d4fceafac322ab3f3f870 + 2021-04-20T18:21:43 + 1 + 170.06 + 2021-04-20 + + INGA + + INTRAGROUP + + + + + + NOTPROVIDED + 5bab4907dc98463a8b305f68812bad42 + + 170.06 + SLEV + + Tadeush Blindewski + + + + LT372140000000000001 + + + + + AGBLLT2XXXX + + + + + CBSBLT26XXX + + + + Иван Царевич + + + + LT497181600000000002 + + + + А, уж ка тас паведимас? Вистэк ачу. + + + +R/ueo9oM8J+1P8QWh4W6FS/zvqKbnyWDh9ndALABIMI=6ObG98/6Hdcv2QRp/RGmV1iI6xkkgzOpYVJFmbKYK3A=fiiMKGjmr7oUTvuEHeKdh50Oc3pu8MS75XMYVUHBE1nI/vOW5xF7Xvl1/hqU87R178q2NzmEBl9gOnl4d3Q3xsE4wFGV7xMqfoYrHIj3yrqiAzd88qnAd3QPJVuKLLwhT7qaadqle0qP+gmytIX5nFFFCuwgWgk47LCWHSa8pkbgxZsJDBTtox8OPcGnUFSkJMwKeld8oiicjMKXZDYqjUdASeI60bFWcLi8PNbDrnHye8LlrqGARVCSPpqQUdH3QtWO3YLLM2bq8oSCWxAcL604y2B29yDroSbvpj/w5vMwy/K09KMA8OvE0DaWlAA/xUqq+ajqzYCDOC7IJOKVow==MIID2TCCAsGgAwIBAgIUEEnabJkXk0Cflcvmaw/J1dZqct8wDQYJKoZIhvcNAQELBQAwTjELMAkGA1UEBhMCVVMxEzARBgNVBAgMCkNhbGlmb3JuaWExEjAQBgNVBAcMCVRoZSBDbG91ZDEWMBQGA1UECgwNTXkgQ29tcGFueSBDQTAeFw0yMTExMjExMjQxMThaFw0yMjAzMjExMjQxMThaMIGNMQswCQYDVQQGEwJMVDESMBAGA1UECAwJTGl0aHVhbmlhMRIwEAYDVQQHDAlUaGUgQ2xvdWQxDTALBgNVBAoMBERlbW8xFTATBgNVBAMMDFNpZ25pbmcgY2VydDEwMC4GCgmSJomT8ixkARkWIGxvY2FsaG9zdC9lbWFpbEFkZHJlc3M9c2RAc2RmLmZmMIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEAwLBaiVFQg8xTW7NTcCre7hy2yEKgZmGeESsUMOufOd0Hp7xraYp/CuTj8fptHHqFcIqdI520Br17WESWe5WM5XxkA/eWAmN3hBbi5lUM5b9w/KwAkq8RHBZaq+vHwVAFMnlZMBWRz2R5PcQ2NyJj0YBgImJImhm7KllkKvuLLXNqjYYp7S4n/lXbPHOYA28PeJlPLYe4Wbpe18aGoGRaag9PK4oR4tX1rHjtcAWf8u7NmJX56/6xRl15qsMngTNJKu6mxMDd5i9X1xDNRzVN7uTlK9CBqmq+aflDLCnSpiZX/W3AazOpJm5Cm1f8vqOX8XnJ6DIWUREnZ/IU2CT7dwIDAQABo28wbTAMBgNVHRMBAf8EAjAAMB0GA1UdDgQWBBRd2sKVueUBgVKqiiwOOZ93E7Kw+jALBgNVHQ8EBAMCBeAwMQYDVR0lBCowKAYIKwYBBQUHAwIGCCsGAQUFBwMJBggrBgEFBQcDAwYIKwYBBQUHAwgwDQYJKoZIhvcNAQELBQADggEBAIKoE6AXe2akNOPeVvGWc4BaATm8M/E+UqjkD0j6hPlQJeJBEPZfFYxqSG1egK6LkvA7cZei1gFqrKbHwyrnda8fiLB+0TkgQKJGU427GFSSaAHtqY1nSsXcDTTQCsjvIY9BzYzcfdD7grYzvBdCuhwU7Wtki6RX6cVKK08HlO+k+CN8/bo0SAns8STGluFLP7RwR6U0bHXOGQJoFetJZ2+jj1NfuCkolrV1qWzhdznxvrD9j/BAaPi8kiMHbApF2NpQkP/vLWqYVPp0z4WEZ2UpFUWPAfVXOnRDSFpTGEdmeXdbPsR2/QsM41WZqSe6zDv+zISMpwgmuKbkhthK7gc=2021-12-25T19:34:57.021Zz6Te8U9MUPFnKUKBB1IiYAxfWD0=O=My Company CA,L=The Cloud,ST=California,C=US92990834164997211889227204231006993513378902751 \ No newline at end of file diff --git a/testdata/rootxmlns.crt b/testdata/rootxmlns.crt old mode 100644 new mode 100755 diff --git a/testdata/rootxmlns.xml b/testdata/rootxmlns.xml old mode 100644 new mode 100755 diff --git a/testdata/rsa.crt b/testdata/rsa.crt old mode 100644 new mode 100755 diff --git a/testdata/rsa.key b/testdata/rsa.key old mode 100644 new mode 100755 diff --git a/testdata/saml-external-ns.xml b/testdata/saml-external-ns.xml old mode 100644 new mode 100755 diff --git a/testdata/signature-with-inclusivenamespaces.xml b/testdata/signature-with-inclusivenamespaces.xml old mode 100644 new mode 100755 diff --git a/testdata/valid-saml.xml b/testdata/valid-saml.xml old mode 100644 new mode 100755 diff --git a/testdata/windows-store-signature.xml b/testdata/windows-store-signature.xml old mode 100644 new mode 100755 diff --git a/testdata/wsfed-metadata.xml b/testdata/wsfed-metadata.xml old mode 100644 new mode 100755 diff --git a/validator.go b/validator.go old mode 100644 new mode 100755 index eb13580..4eb9d38 --- a/validator.go +++ b/validator.go @@ -1,19 +1,25 @@ package signedxml import ( + "crypto" + "crypto/rsa" "crypto/x509" "encoding/base64" "errors" "fmt" "log" + "math/big" + "regexp" + "time" "github.com/beevik/etree" ) // Validator provides options for verifying a signed XML document type Validator struct { - Certificates []x509.Certificate - signingCert x509.Certificate + Certificates []x509.Certificate + RSAPublicKeys []*rsa.PublicKey + signingCert x509.Certificate signatureData } @@ -54,6 +60,7 @@ func (v *Validator) SigningCert() x509.Certificate { // Deprecated: Use ValidateReferences instead func (v *Validator) Validate() error { _, err := v.ValidateReferences() + // TODO: validate certificate digest at the SignedProperties - maybe elsewhere? return err } @@ -70,12 +77,14 @@ func (v *Validator) ValidateReferences() ([]string, error) { return nil, err } + // referenced is array of cannonicalized reference elements whose digest is calculated + // and compared to original ones. If err is nil, then all digests of references match. referenced, err := v.validateReferences() if err != nil { return nil, err } - var ref []string + var ref []string // referenced elements -> ref string array for _, doc := range referenced { docStr, err := doc.WriteToString() if err != nil { @@ -84,6 +93,8 @@ func (v *Validator) ValidateReferences() ([]string, error) { ref = append(ref, docStr) } + // checks SignatureValue using x509.Certificate{}.CheckSignature function. + // Function params - canonicalized SignedInfo block, and the SignatureValue err = v.validateSignature() return ref, err } @@ -116,20 +127,65 @@ func (v *Validator) validateReferences() (referenced []*etree.Document, err erro 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) + // transforms := ref.SelectElement("Transforms") + // for _, transform := range transforms.SelectElements("Transform") { + // doc, err = processTransform(transform, doc) + // if err != nil { + // return nil, err + // } + // } + + // doc, err = v.getReferencedXML(ref, doc) + // if err != nil { + // return nil, err + // } + + // MOD: 1. change order: 1st find the ref, then 3. transform + // 2. if not root doc, add namespaces + targetDoc, err := v.getReferencedXML(ref, doc) + if err != nil { + return nil, err + } + + // 2. copy relevant namespaces if the targetDoc is not the root document + if targetDoc.Root().Tag != v.xml.Root().Tag { + + // if targetDoc element is not root (i.e, root sub-tag or child) being "digested", + // then populate with relevant namespaces + err = PopulateElementWithNameSpaces(targetDoc.Root(), v.xml.Copy()) if err != nil { return nil, err } } - doc, err = v.getReferencedXML(ref, doc) + // 3. do the transforms + transforms := ref.SelectElement("Transforms") + if transforms != nil { + for _, transform := range transforms.SelectElements("Transform") { + targetDoc, err = processTransform(transform, targetDoc) + if err != nil { + return nil, err + } + } + } + + // 4. canonicalization, defined at the signature level is mandatory for each + // reference before calculating the hash. This is to avoid situations when canonicalization + // is not explicitly defined in the of the Reference (it's implied). + // source: https://www.di-mgt.com.au/xmldsig2.html + targetDocStr, err := targetDoc.WriteToString() if err != nil { return nil, err } + targetDocStr, err = v.canonAlgorithm.Process(targetDocStr, "") + if err != nil { + return nil, err + } + targetDoc2 := etree.NewDocument() + targetDoc2.ReadFromString(targetDocStr) - referenced = append(referenced, doc) + // continue with the old code: + referenced = append(referenced, targetDoc2) digestValueElement := ref.SelectElement("DigestValue") if digestValueElement == nil { @@ -137,7 +193,8 @@ func (v *Validator) validateReferences() (referenced []*etree.Document, err erro } digestValue := digestValueElement.Text() - calculatedValue, err := calculateHash(ref, doc) + // calculatedValue, err := calculateHash(ref, doc) + calculatedValue, err := CalculateHashFromRef(ref, targetDoc2) if err != nil { return nil, err } @@ -163,21 +220,70 @@ func (v *Validator) validateSignature() error { return err } - b64, err := base64.StdEncoding.DecodeString(v.sigValue) + // debug + // fn := strconv.FormatInt(time.Now().UnixNano(), 10) + ".xml" // unix-time based filename + // f, err := os.Create(fn) + // if err != nil { + // panic(err) + // } + // defer f.Close() + // _, err = f.Write([]byte(canonSignedInfo)) + // if err != nil { + // panic(err) + // } + + signatureBytes, err := base64.StdEncoding.DecodeString(v.sigValue) if err != nil { return err } - sig := []byte(b64) + // sig := []byte(b64) // useless double conversion from bytes to bytes v.signingCert = x509.Certificate{} for _, cert := range v.Certificates { - err := cert.CheckSignature(v.sigAlgorithm, []byte(canonSignedInfo), sig) + err := cert.CheckSignature(v.sigAlgorithm, []byte(canonSignedInfo), signatureBytes) if err == nil { v.signingCert = cert return nil } } + // MOD: added RSA PublicKey checking of the signature + if v.RSAPublicKeys != nil { + signingAlgorithm, ok := signingAlgorithms[v.sigAlgorithm] + if !ok { + return errors.New("signedxml: unsupported algorithm") + } + hasher := signingAlgorithm.hash.New() + hasher.Write([]byte(canonSignedInfo)) + digest := hasher.Sum(nil) + // fmt.Println(base64.StdEncoding.EncodeToString(digest)) // debug + + for _, pubKey := range v.RSAPublicKeys { + // err := rsa.VerifyPKCnotAfterv15(pubKey, crypto.Hash(v.sigAlgorithm), digest, signatureBytes) + + var cryptoHashId crypto.Hash + switch v.sigAlgorithm.String() { + case "MD5-RSA": + cryptoHashId = crypto.MD5 + case "SHA1-RSA": + cryptoHashId = crypto.SHA1 + case "SHA256-RSA", "SHA256-RSAPSS": + cryptoHashId = crypto.SHA256 + case "SHA384-RSA", "SHA384-RSAPSS": + cryptoHashId = crypto.SHA384 + case "SHA512-RSA", "SHA512-RSAPSS": + cryptoHashId = crypto.SHA512 + default: + return errors.New("unknown signature hash type") + } + + err := rsa.VerifyPKCS1v15(pubKey, cryptoHashId, digest, signatureBytes) + if err == nil { + return nil // signature validated + } + } + } + return errors.New("signedxml: Calculated signature does not match the " + "SignatureValue provided") } @@ -186,20 +292,193 @@ 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 { + switch { + case len(v.xml.FindElements(".//X509Certificate")) >= 1: + keydata := v.xml.FindElements(".//X509Certificate") + for _, key := range keydata { + cert, err := LoadCertFromPEMString(key.Text(), "CERTIFICATE") + if err != nil { + log.Printf("signedxml: Unable to load certificate: (%s). "+ + "Looking for another cert.", err) + continue // don't append current cert: it will be nil due to error + } + + // if certificate digest and digest method are present, validate the certificate + // TODO: what if there are multiple certificates in the ? + var certDigest, digestMethodURI string + if el := v.xml.FindElement(".//CertDigest/DigestMethod"); el != nil { + digestMethodURI = el.SelectAttrValue("Algorithm", "") + } + if el := v.xml.FindElement(".//CertDigest/DigestValue"); el != nil { + certDigest = el.Text() + } + err = ValidateCertificate(cert, certDigest, digestMethodURI, "", "") + if err != nil { + log.Printf("signedxml: certificate validation failed: (%s). "+ + "Looking for another cert.", err) + continue // don't append current cert: it will be nil due to error + } + v.Certificates = append(v.Certificates, *cert) } + + case len(v.xml.FindElements(".//RSAKeyValue")) >= 1: + keydata := v.xml.FindElements(".//RSAKeyValue") + for _, key := range keydata { + modulus := key.SelectElement("Modulus") + if modulus == nil { + log.Printf("signedxml: RSA Modulus not found, cannot load certificate. Looking for another cert.") + continue + } + modulusBytes, err := base64.StdEncoding.DecodeString(modulus.Text()) + if err != nil { + log.Printf("signedxml: can't b64 decode RSA modulus (%s). "+ + "Looking for another cert.", err) + continue + } + exponent := key.SelectElement("Exponent") + if exponent == nil { + log.Printf("signedxml: RSA Exponent not found, cannot load certificate. Looking for another cert.") + continue + } + + // source: https://stackoverflow.com/questions/41127019/go-language-convert-modulus-exponent-to-x-509-certificate + e := 65537 + // The default exponent is usually 65537, so just compare the + // base64 for [1,0,1] or [0,1,0,1] + if exponent.Text() != "AQAB" && exponent.Text() != "AAEAAQ" { + // still need to decode the big-endian int + log.Printf("signedxml: unusual RSA exponent ('%s', base64). "+ + "still need to decode it, looking for another cert.", exponent.Text()) + continue + } + pubKey := &rsa.PublicKey{ + N: new(big.Int).SetBytes(modulusBytes), + E: e, + } + v.RSAPublicKeys = append(v.RSAPublicKeys, pubKey) + + // TODO: not sure if certificate validation is possible with RSA key + + // pubKeyDERBytes := pem.EncodeToMemory(&pem.Block{ + // Type: "RSA PUBLIC KEY", + // Bytes: x509.MarshalPKCnotAfterPublicKey(pub), + // }) + // cert, err := x509.ParseCertificate(pubKeyDERBytes) + + // exponentBytes, err := base64.StdEncoding.DecodeString(exponent.Text()) + // if err != nil { + // log.Printf("signedxml: can't b64 decode RSA exponent (%s). "+ + // "Looking for another cert.", err) + // continue + // } + + // // conversion to BigEndian + // if len(exponentBytes) < 4 { + // ndata := make([]byte, 4) + // copy(ndata[4-len(exponentBytes):], exponentBytes) + // exponentBytes = ndata + // } + + // pubKey := &rsa.PublicKey{ + // N: new(big.Int).SetBytes(modulusBytes), + // // E: int(binary.BigEndian.Uint32(exponentBytes[:])), + // E: 65537, + // } + + } } } - if len(v.Certificates) < 1 { - return errors.New("signedxml: a certificate is required, but was not found") + if len(v.Certificates) < 1 && v.RSAPublicKeys == nil { + return errors.New("signedxml: a X509 certificate or a RSA public key is required, but was not found") } return nil } + +func (v *Validator) SetValidationCertFromPEMString(certPEM string) error { + cert, err := LoadCertFromPEMString(certPEM, "CERTIFICATE") + if err != nil { + return fmt.Errorf("signedxml: Unable to load certificate: (%s). ", err) + } + v.Certificates = append(v.Certificates, *cert) + return nil +} + +func (v *Validator) SetValidationCert(cert *x509.Certificate) { + v.Certificates = append(v.Certificates, *cert) +} + +// Validates certificate: +// 1. checks if it hasn't expired, +// 2. calculates certificate hash digest and compares to supplied certificate digest value. +// Params 'notBefore', 'notAfter' are optional, just for setting validity dates separately, else +// X509.Certificate container equivalent values are used +func ValidateCertificate(cert *x509.Certificate, certDigest, digestMethodURI, notBefore, notAfter string) (err error) { + + // setup of custom NotBefore and NotAfter dates + if notBefore != "" && notAfter != "" { + + var layout string + var t0, t1 time.Time + + r1 := regexp.MustCompile(`^\d{4}-\d{2}-\d{2}T\d{2}:\d{2}:\d{2}Z$`) + r3 := regexp.MustCompile(`^\d{4}-\d{2}-\d{2}T\d{2}:\d{2}:\d{2}[+-]\d{2}:\d{2}$`) + r4 := regexp.MustCompile(`^\d{4}-\d{2}-\d{2}T\d{2}:\d{2}:\d{2}$`) + r5 := regexp.MustCompile(`^\d{4}-\d{2}-\d{2}T\d{2}:\d{2}:\d{2}.\d{1,9}$`) + r6 := regexp.MustCompile(`^\d{4}-\d{2}-\d{2}$`) + + switch { + case r1.Match([]byte(notBefore)) || r3.Match([]byte(notBefore)): + layout = time.RFC3339 // "2006-01-02T15:04:05+HH:MM" + case r4.Match([]byte(notBefore)) || r5.Match([]byte(notBefore)): + layout = "2006-01-02T15:04:05" // local tz + case r6.Match([]byte(notBefore)): + layout = "2006-01-02" // local tz + } + if r4.Match([]byte(notBefore)) || r5.Match([]byte(notBefore)) || r6.Match([]byte(notBefore)) { + t0, err = time.ParseInLocation(layout, notBefore, time.UTC) + } else if r1.Match([]byte(notBefore)) || r3.Match([]byte(notBefore)) { + t0, err = time.Parse(layout, notBefore) + } + if err != nil { + return fmt.Errorf("error parsing date string %s, error: %s", t0, err) + } + // assume that t1 is in the same format as t0 + if r4.Match([]byte(notAfter)) || r5.Match([]byte(notAfter)) || r6.Match([]byte(notAfter)) { + t1, err = time.ParseInLocation(layout, notAfter, time.UTC) + } else if r1.Match([]byte(notAfter)) || r3.Match([]byte(notAfter)) { + t1, err = time.Parse(layout, notAfter) + } + if err != nil { + return fmt.Errorf("error parsing date string %s, error: %s", t1, err) + } + + cert.NotBefore = t0 + cert.NotAfter = t1 + } + + // checking if certificate expired + if time.Now().Before(cert.NotBefore) { + return fmt.Errorf("certificate is not valid until %s; now is %s", + cert.NotBefore.UTC().Format("2006-01-02T15:04:05Z"), time.Now().UTC().Format("2006-01-02T15:04:05Z")) + } + if time.Now().After(cert.NotAfter) { + return fmt.Errorf("certificate has expired in %s; now is %s", + cert.NotAfter.UTC().Format("2006-01-02T15:04:05Z"), time.Now().UTC().Format("2006-01-02T15:04:05Z")) + } + + // calculate certificate hash + certDigestB64, err := CalculateHash(cert.Raw, digestMethodURI) + if err != nil { + return err + } + + // check if hash matches + if certDigest != certDigestB64 { + return fmt.Errorf("certificate hash digest mismatch: xml uses certificate hash "+ + "digest '%s', while cert digest in db is '%s'", certDigest, certDigestB64) + } + + return +}