Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Able to pass more elements to the createsignature method #144

Open
wants to merge 1 commit into
base: main
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
67 changes: 46 additions & 21 deletions etreeutils/namespace.go
Original file line number Diff line number Diff line change
Expand Up @@ -90,34 +90,40 @@ func (ctx NSContext) declare(prefix, namespace string) etree.Attr {
}

func (ctx NSContext) SubContext(el *etree.Element) (NSContext, error) {
return ctx.SubContexts([]*etree.Element{el})
}

func (ctx NSContext) SubContexts(els []*etree.Element) (NSContext, error) {
// The subcontext should inherit existing declared prefixes
newCtx := ctx.Copy()

// Merge new namespace declarations on top of existing ones.
for _, attr := range el.Attr {
if attr.Space == xmlnsPrefix {
// This attribute is a namespace declaration of the form "xmlns:<prefix>"
for _, el := range els {
// Merge new namespace declarations on top of existing ones.
for _, attr := range el.Attr {
if attr.Space == xmlnsPrefix {
// This attribute is a namespace declaration of the form "xmlns:<prefix>"

// The 'xml' namespace may only be re-declared with the name 'http://www.w3.org/XML/1998/namespace'
if attr.Key == xmlPrefix && attr.Value != XMLNamespace {
return ctx, ErrReservedNamespace
}
// The 'xml' namespace may only be re-declared with the name 'http://www.w3.org/XML/1998/namespace'
if attr.Key == xmlPrefix && attr.Value != XMLNamespace {
return ctx, ErrReservedNamespace
}

// The 'xmlns' namespace may not be re-declared
if attr.Key == xmlnsPrefix {
return ctx, ErrReservedNamespace
}
// The 'xmlns' namespace may not be re-declared
if attr.Key == xmlnsPrefix {
return ctx, ErrReservedNamespace
}

newCtx.declare(attr.Key, attr.Value)
} else if attr.Space == defaultPrefix && attr.Key == xmlnsPrefix {
// This attribute is a default namespace declaration
newCtx.declare(attr.Key, attr.Value)
} else if attr.Space == defaultPrefix && attr.Key == xmlnsPrefix {
// This attribute is a default namespace declaration

// The xmlns namespace value may not be declared as the default namespace
if attr.Value == XMLNSNamespace {
return ctx, ErrInvalidDefaultNamespace
}
// The xmlns namespace value may not be declared as the default namespace
if attr.Value == XMLNSNamespace {
return ctx, ErrInvalidDefaultNamespace
}

newCtx.declare(defaultPrefix, attr.Value)
newCtx.declare(defaultPrefix, attr.Value)
}
}
}

Expand Down Expand Up @@ -413,13 +419,32 @@ func NSFindOneChildCtx(ctx NSContext, el *etree.Element, namespace, tag string)
// NSBuildParentContext recurses upward from an element in order to build an NSContext
// for its immediate parent. If the element has no parent DefaultNSContext
// is returned.
func NSBuildParentContexts(els []*etree.Element) (NSContext, error) {

ctx := NewDefaultNSContext()
for _, el := range els {
dctx, err := nSBuildParentContext(ctx, el)
ctx = dctx
if err != nil {
return NewDefaultNSContext(), err
}
}
return ctx, nil
}

func NSBuildParentContext(el *etree.Element) (NSContext, error) {

return NSBuildParentContexts([]*etree.Element{el})
}

func nSBuildParentContext(ctx NSContext, el *etree.Element) (NSContext, error) {
parent := el.Parent()
if parent == nil {
return NewDefaultNSContext(), nil
}

ctx, err := NSBuildParentContext(parent)
ctx, err := nSBuildParentContext(ctx, parent)

if err != nil {
return ctx, err
}
Expand Down
87 changes: 52 additions & 35 deletions sign.go
Original file line number Diff line number Diff line change
Expand Up @@ -148,7 +148,7 @@ func (ctx *SigningContext) getCerts() ([][]byte, error) {
}
}

func (ctx *SigningContext) constructSignedInfo(el *etree.Element, enveloped bool) (*etree.Element, error) {
func (ctx *SigningContext) constructSignedInfo(els []*etree.Element, enveloped bool) (*etree.Element, error) {
digestAlgorithmIdentifier := ctx.GetDigestAlgorithmIdentifier()
if digestAlgorithmIdentifier == "" {
return nil, errors.New("unsupported hash mechanism")
Expand All @@ -159,11 +159,6 @@ func (ctx *SigningContext) constructSignedInfo(el *etree.Element, enveloped bool
return nil, errors.New("unsupported signature method")
}

digest, err := ctx.digest(el)
if err != nil {
return nil, err
}

signedInfo := &etree.Element{
Tag: SignedInfoTag,
Space: ctx.Prefix,
Expand All @@ -177,38 +172,55 @@ func (ctx *SigningContext) constructSignedInfo(el *etree.Element, enveloped bool
signatureMethod := ctx.createNamespacedElement(signedInfo, SignatureMethodTag)
signatureMethod.CreateAttr(AlgorithmAttr, signatureMethodIdentifier)

// /SignedInfo/Reference
reference := ctx.createNamespacedElement(signedInfo, ReferenceTag)

dataId := el.SelectAttrValue(ctx.IdAttribute, "")
if dataId == "" {
reference.CreateAttr(URIAttr, "")
} else {
reference.CreateAttr(URIAttr, "#"+dataId)
}

// /SignedInfo/Reference/Transforms
transforms := ctx.createNamespacedElement(reference, TransformsTag)
if enveloped {
envelopedTransform := ctx.createNamespacedElement(transforms, TransformTag)
envelopedTransform.CreateAttr(AlgorithmAttr, EnvelopedSignatureAltorithmId.String())
}
canonicalizationAlgorithm := ctx.createNamespacedElement(transforms, TransformTag)
canonicalizationAlgorithm.CreateAttr(AlgorithmAttr, string(ctx.Canonicalizer.Algorithm()))
for _, el := range els {
digest, err := ctx.digest(el)
if err != nil {
return nil, err
}

// /SignedInfo/Reference
reference := ctx.createNamespacedElement(signedInfo, ReferenceTag)

dataId := el.SelectAttrValue(ctx.IdAttribute, "")
if dataId == "" {
reference.CreateAttr(URIAttr, "")
} else {
reference.CreateAttr(URIAttr, "#"+dataId)
}

// /SignedInfo/Reference/DigestMethod
digestMethod := ctx.createNamespacedElement(reference, DigestMethodTag)
digestMethod.CreateAttr(AlgorithmAttr, digestAlgorithmIdentifier)
// /SignedInfo/Reference/Transforms
transforms := ctx.createNamespacedElement(reference, TransformsTag)
if enveloped {
envelopedTransform := ctx.createNamespacedElement(transforms, TransformTag)
envelopedTransform.CreateAttr(AlgorithmAttr, EnvelopedSignatureAltorithmId.String())
}
canonicalizationAlgorithm := ctx.createNamespacedElement(transforms, TransformTag)
canonicalizationAlgorithm.CreateAttr(AlgorithmAttr, string(ctx.Canonicalizer.Algorithm()))

// /SignedInfo/Reference/DigestMethod
digestMethod := ctx.createNamespacedElement(reference, DigestMethodTag)
digestMethod.CreateAttr(AlgorithmAttr, digestAlgorithmIdentifier)

// /SignedInfo/Reference/DigestValue
digestValue := ctx.createNamespacedElement(reference, DigestValueTag)
digestValue.SetText(base64.StdEncoding.EncodeToString(digest))
// /SignedInfo/Reference/DigestValue
digestValue := ctx.createNamespacedElement(reference, DigestValueTag)
digestValue.SetText(base64.StdEncoding.EncodeToString(digest))
}

return signedInfo, nil
}

func (ctx *SigningContext) ConstructSignatures(els []*etree.Element, enveloped bool) (*etree.Element, error) {
return ctx.ConstructSignatureRef(els, nil, enveloped)
}

func (ctx *SigningContext) ConstructSignature(el *etree.Element, enveloped bool) (*etree.Element, error) {
signedInfo, err := ctx.constructSignedInfo(el, enveloped)
return ctx.ConstructSignatures([]*etree.Element { el }, enveloped)
}

func (ctx *SigningContext) ConstructSignatureRef(els []*etree.Element, keyRefElementDecorator func(*etree.Element), enveloped bool) (*etree.Element, error) {
signedInfo, err := ctx.constructSignedInfo(els, enveloped)
if err != nil {
return nil, err
}
Expand All @@ -232,13 +244,13 @@ func (ctx *SigningContext) ConstructSignature(el *etree.Element, enveloped bool)
// a series of cascading NSContexts to capture namespace declarations:

// First get the context surrounding the element we are signing.
rootNSCtx, err := etreeutils.NSBuildParentContext(el)
rootNSCtx, err := etreeutils.NSBuildParentContexts(els)
if err != nil {
return nil, err
}

// Then capture any declarations on the element itself.
elNSCtx, err := rootNSCtx.SubContext(el)
elNSCtx, err := rootNSCtx.SubContexts(els)
if err != nil {
return nil, err
}
Expand Down Expand Up @@ -275,10 +287,15 @@ func (ctx *SigningContext) ConstructSignature(el *etree.Element, enveloped bool)
signatureValue.SetText(base64.StdEncoding.EncodeToString(rawSignature))

keyInfo := ctx.createNamespacedElement(sig, KeyInfoTag)
x509Data := ctx.createNamespacedElement(keyInfo, X509DataTag)
for _, cert := range certs {
x509Certificate := ctx.createNamespacedElement(x509Data, X509CertificateTag)
x509Certificate.SetText(base64.StdEncoding.EncodeToString(cert))

if (keyRefElementDecorator == nil) {
x509Data := ctx.createNamespacedElement(keyInfo, X509DataTag)
for _, cert := range certs {
x509Certificate := ctx.createNamespacedElement(x509Data, X509CertificateTag)
x509Certificate.SetText(base64.StdEncoding.EncodeToString(cert))
}
} else {
keyRefElementDecorator(keyInfo)
}

return sig, nil
Expand Down
39 changes: 39 additions & 0 deletions sign_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -138,6 +138,45 @@ func TestSignNonDefaultID(t *testing.T) {
require.Equal(t, refURI, "#"+id)
}

func TestSignSoapRequest(t *testing.T) {
// Given
//bs, err := ioutil.ReadFile("./testdata/soaprequest_result.xml")

ks := RandomKeyStoreForTest()
ctx := &SigningContext{
Hash: crypto.SHA256,
KeyStore: ks,
IdAttribute: "wsu:Id",
Prefix: DefaultPrefix,
Canonicalizer: MakeC14N11Canonicalizer(),
}

doc := etree.NewDocument()
err := doc.ReadFromFile("./testdata/soaprequest.xml")

bodyPath, err := etree.CompilePath("./soap:Envelope/soap:Body")
bodyElement := doc.FindElementPath(bodyPath)
require.NotNil(t, bodyElement)

actionPath, err := etree.CompilePath("./soap:Envelope/soap:Header/Action")
actionElement := doc.FindElementPath(actionPath)
require.NotNil(t, actionElement)

securityPath, err := etree.CompilePath("./soap:Envelope/soap:Header/wsse:Security")
securityElement := doc.FindElementPath(securityPath)
require.NotNil(t, securityElement)

// When
sig, err := ctx.ConstructSignatures([]*etree.Element{bodyElement, actionElement}, true)
require.NoError(t, err)
require.NotNil(t, sig)
securityElement.AddChild(sig)

// Then
// str, err := doc.WriteToString()
// require.NoError(t, err)
}

func TestIncompatibleSignatureMethods(t *testing.T) {
// RSA
randomKeyStore := RandomKeyStoreForTest().(*MemoryX509KeyStore)
Expand Down
1 change: 1 addition & 0 deletions testdata/soaprequest.xml
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
<?xml version="1.0" encoding="UTF-8"?><soap:Envelope xmlns:wsse="http://docs.oasis-open.org/wss/2004/01/oasis-200401-wss-wssecurity-secext-1.0.xsd" xmlns:wsu="http://docs.oasis-open.org/wss/2004/01/oasis-200401-wss-wssecurity-utility-1.0.xsd" xmlns:ds="http://www.w3.org/2000/09/xmldsig#" xmlns:adr="http://www.w3.org/2005/08/addressing" xmlns:soap="http://schemas.xmlsoap.org/soap/envelope/"><soap:Header><adr:Action xmlns:wsse="http://docs.oasis-open.org/wss/2004/01/oasis-200401-wss-wssecurity-secext-1.0.xsd" xmlns:wsu="http://docs.oasis-open.org/wss/2004/01/oasis-200401-wss-wssecurity-utility-1.0.xsd" xmlns:ds="http://www.w3.org/2000/09/xmldsig#" xmlns:adr="http://www.w3.org/2005/08/addressing" xmlns:soap="http://schemas.xmlsoap.org/soap/envelope/" wsu:Id="_2451b4b1-38d6-4395-9a28-372560725c59">http://docs.oasis-open.org/ws-sx/ws-trust/200512/RST/Issue</adr:Action><MessageID xmlns="http://www.w3.org/2005/08/addressing" xmlns:wsu="http://docs.oasis-open.org/wss/2004/01/oasis-200401-wss-wssecurity-utility-1.0.xsd" wsu:Id="_04d30812-c0cc-46c4-9069-293e71d3b183">urn:uuid:3f58046c-0a79-4aa3-887d-81e8bd47108d</MessageID><To xmlns="http://www.w3.org/2005/08/addressing" xmlns:wsu="http://docs.oasis-open.org/wss/2004/01/oasis-200401-wss-wssecurity-utility-1.0.xsd" wsu:Id="_811c93ac-5639-4e4d-80e7-3c0a1d38015d">https://sts.test-vdxapi.vconf.dk/sts/service/sts</To><ReplyTo xmlns="http://www.w3.org/2005/08/addressing" xmlns:wsu="http://docs.oasis-open.org/wss/2004/01/oasis-200401-wss-wssecurity-utility-1.0.xsd" wsu:Id="_7ef42676-9114-434c-929d-eea3dbe9aeda"><Address>http://www.w3.org/2005/08/addressing/anonymous</Address></ReplyTo><wsse:Security xmlns:wsse="http://docs.oasis-open.org/wss/2004/01/oasis-200401-wss-wssecurity-secext-1.0.xsd" xmlns:wsu="http://docs.oasis-open.org/wss/2004/01/oasis-200401-wss-wssecurity-utility-1.0.xsd" soap:mustUnderstand="1"><wsu:Timestamp wsu:Id="TS-dee96fa3-77a7-43bf-b3cf-6f43f0fc123d"><wsu:Created>2019-05-13T06:45:42.701Z</wsu:Created><wsu:Expires>2019-05-13T06:50:42.701Z</wsu:Expires></wsu:Timestamp></wsse:Security></soap:Header><soap:Body xmlns:wsse="http://docs.oasis-open.org/wss/2004/01/oasis-200401-wss-wssecurity-secext-1.0.xsd" xmlns:wsu="http://docs.oasis-open.org/wss/2004/01/oasis-200401-wss-wssecurity-utility-1.0.xsd" xmlns:ds="http://www.w3.org/2000/09/xmldsig#" xmlns:adr="http://www.w3.org/2005/08/addressing" xmlns:soap="http://schemas.xmlsoap.org/soap/envelope/" xmlns:wsu="http://docs.oasis-open.org/wss/2004/01/oasis-200401-wss-wssecurity-utility-1.0.xsd" wsu:Id="_a7dd77e4-586d-47b5-9b83-2ed20ff0441c"><wst:RequestSecurityToken xmlns:wst="http://docs.oasis-open.org/ws-sx/ws-trust/200512"><wst:RequestType>http://docs.oasis-open.org/ws-sx/ws-trust/200512/Issue</wst:RequestType><wsp:AppliesTo xmlns:wsp="http://www.w3.org/ns/ws-policy"><wsa:EndpointReference xmlns:wsa="http://www.w3.org/2005/08/addressing"><wsa:Address>urn:medcom:videoapi</wsa:Address></wsa:EndpointReference></wsp:AppliesTo><wst:TokenType>http://docs.oasis-open.org/wss/oasis-wss-saml-token-profile-1.1#SAMLV2.0</wst:TokenType><wst:KeyType>http://docs.oasis-open.org/ws-sx/ws-trust/200512/PublicKey</wst:KeyType><wst:UseKey><ds:KeyInfo xmlns:ds="http://www.w3.org/2000/09/xmldsig#"><ds:KeyValue><ds:RSAKeyValue><ds:Modulus>rXApxxjCWlsEfeKgUPOl1mJC9aqkkWooyUgOU+KsrH9qRCoK9xVdI7YJebwr5+TJtBbWkKkuD926SMxJV1LY6IT8tCflomIl4E5IZdRZPci1N71lQDV6SfNuGPHNpFpLssdSY34+t4/vuGeTZ2lJB5IP4sDvjAxJ+nXECcHmcupEEQu3wI2nijcWl4hRRSdhUuKDB/AiaZvsPKcdFj4WTlRdewJS4v5m1khwce6Zj1jw6N7PSQPHaisIxqx2SMHvKiepPuESgEpqP+sGRaL2ESJWuB1kTsNHmer6cJ+ba/pvJy3xraY7mrgRv/zWa+6Of9LSVw2hfFx3pEjBgYHhhw==</ds:Modulus><ds:Exponent>AQAB</ds:Exponent></ds:RSAKeyValue></ds:KeyValue></ds:KeyInfo></wst:UseKey><wst:Renewing/></wst:RequestSecurityToken></soap:Body></soap:Envelope>