diff --git a/.gitignore b/.gitignore
index 0d8c5ba..077c171 100644
--- a/.gitignore
+++ b/.gitignore
@@ -14,3 +14,8 @@
example/example
.vscode
+/.idea/.gitignore
+/.idea/dbnavigator.xml
+/.idea/modules.xml
+/.idea/spid-go.iml
+/.idea/vcs.xml
diff --git a/example/service.go b/example/service.go
index ded89f2..dd5f80c 100644
--- a/example/service.go
+++ b/example/service.go
@@ -43,6 +43,11 @@ func main() {
Attributes: []string{"fiscalNumber", "name", "familyName", "dateOfBirth"},
},
},
+ Organization: spidsaml.SPOrganization{
+ OrganizationName: "Example Srl",
+ OrganizationDisplayName: "Example S.r.l.",
+ OrganizationURL: "https://www.example-sp.it",
+ },
}
// Load Identity Providers from their XML metadata
diff --git a/example/sp.pem b/example/sp.pem
index e5c0847..1bc67eb 100644
--- a/example/sp.pem
+++ b/example/sp.pem
@@ -1,16 +1,17 @@
-----BEGIN CERTIFICATE-----
-MIICljCCAX4CCQDP2/0y0d0/dzANBgkqhkiG9w0BAQsFADANMQswCQYDVQQGEwJJ
-VDAeFw0xODA2MjgwOTM2MDRaFw0xOTA2MjgwOTM2MDRaMA0xCzAJBgNVBAYTAklU
-MIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEAo4i2HsG/+Qm3qe0gqwEO
-h4wlBYK181WCn3RTuyFNQh6mdn6pLdv/dYXY22zER2ufY227FFto3vqtSdT306lp
-IoVuXKBAVoWwcrzO0dadM4dyHX7KRmWSDT51GBqkP6Hj1UoUywbXp5q9GXP5uVL8
-U93caT11VZaalHhEjKxtSYJHDP7ZP/2k9p54JgVIonMF0DJVhx0smPZ3QdHX+my/
-JNevsnuXTZIyyu0KjcXlflKSldngVDjv9D6cGE4wGOa5Vz5M+z4tjKnJtfj/xacI
-Wcj/4Ukuu6CDyQ8+YNCaE9YjitRmdi5ZqTDOoGKonmlbhCcfqPeRvGwfWXJcVR+q
-UwIDAQABMA0GCSqGSIb3DQEBCwUAA4IBAQBVO9pGMnJ3X5D6ny/32TM4fvFesdRB
-tSTnivkdssvn4o8u6570XZIpz2AFQ9eltREbobAqYuWrXIr+1x5aACsReFSjusSM
-Nb9dUCwZbpcId53WQdGikXVkLwgRw9LfYSr73EfeUIIc9R5HCbR5p2piDzw9cNpR
-9wGhhL64g1zhy7O7bdWCXZ4cg9in9N2fCMTjdNpUvG4ZiToRdUqvuMDF4gsQJOwV
-wmN6BxQFloyODpdf1XoTk9dEqPqFO5B8h+DY/26JV8QYUPKzGUJkr24GxjFj5dly
-d8++oQDBEz0WVC0uRl5nGj+MDAO0DmHNeaS05gWQpp/KpykPzKyQw7cg
------END CERTIFICATE-----
\ No newline at end of file
+MIICoTCCAYkCFF5EIcofizvlSV+cMzaM458OR9HgMA0GCSqGSIb3DQEBCwUAMA0x
+CzAJBgNVBAYTAklUMB4XDTIxMDQxNTEzNTM0OFoXDTMxMDQxMzEzNTM0OFowDTEL
+MAkGA1UEBhMCSVQwggEiMA0GCSqGSIb3DQEBAQUAA4IBDwAwggEKAoIBAQCjiLYe
+wb/5Cbep7SCrAQ6HjCUFgrXzVYKfdFO7IU1CHqZ2fqkt2/91hdjbbMRHa59jbbsU
+W2je+q1J1PfTqWkihW5coEBWhbByvM7R1p0zh3IdfspGZZINPnUYGqQ/oePVShTL
+Btenmr0Zc/m5UvxT3dxpPXVVlpqUeESMrG1JgkcM/tk//aT2nngmBUiicwXQMlWH
+HSyY9ndB0df6bL8k16+ye5dNkjLK7QqNxeV+UpKV2eBUOO/0PpwYTjAY5rlXPkz7
+Pi2Mqcm1+P/FpwhZyP/hSS67oIPJDz5g0JoT1iOK1GZ2LlmpMM6gYqieaVuEJx+o
+95G8bB9ZclxVH6pTAgMBAAEwDQYJKoZIhvcNAQELBQADggEBAH2fN9TBKtzcfmaT
+GQ/hAmTur2BAU9vbMRJizgpQR9hSO7ty1FSVfS/rzTGOKr/oRfVI3SaTjo+ZYUjc
+rVMw3gn/3JfJGTnZmZ6f1yqu0Cm7a7wbvyqWQxbCZzXEqHG13xYq64aYP/aKV8eS
+zs1VTw7ShhfhrSYNlQWlr5OUMTgy6WN7ENAr8aCLCKyj+CR4et3hGyo66WPmKGuJ
+eD97BYxP/No0S47Z6hUO+W8AgF1Qp52oAMQQ6N6EBVtViT34XMhpK1/7RnmTYusc
+gj13zX2qZGt+HpOEOLqA2N/5PtLfEenxPO6vs6ZEPwngelpOQST8St2DmpSBwRjN
+PObavq0=
+-----END CERTIFICATE-----
diff --git a/spidsaml/authnrequest_out.go b/spidsaml/authnrequest_out.go
index baba20d..2603577 100644
--- a/spidsaml/authnrequest_out.go
+++ b/spidsaml/authnrequest_out.go
@@ -23,7 +23,7 @@ func (sp *SP) NewAuthnRequest(idp *IDP) *AuthnRequest {
req := new(AuthnRequest)
req.SP = sp
req.IDP = idp
- req.ID = generateMessageID()
+ req.ID = GenerateRandomID()
req.AcsIndex = -1
req.AttrIndex = -1
req.Level = 1
@@ -32,6 +32,7 @@ func (sp *SP) NewAuthnRequest(idp *IDP) *AuthnRequest {
}
// XML generates the XML representation of this AuthnRequest
+// Applicato il bugfix di saml:Issuer
func (authnreq *AuthnRequest) XML(binding SAMLBinding) []byte {
var signatureTemplate string
if binding == HTTPPost {
@@ -71,26 +72,24 @@ func (authnreq *AuthnRequest) XML(binding SAMLBinding) []byte {
ForceAuthn="{{ if gt .Level 1 }}true{{ else }}false{{ end }}">
-
Login with SPID ({{ $entityID }})
-{{ end }} -` - -// GetButton returns the rendered HTML of the SPID button. -func (sp *SP) GetButton(pattern string) string { - items := make(map[string]string) // entityID: URL - - for entityID := range sp.IDP { - items[entityID] = fmt.Sprintf(pattern, url.QueryEscape(entityID)) - } - - t := template.Must(template.New("button").Parse(tmplButton)) - var button bytes.Buffer - t.Execute(&button, items) - return button.String() -} diff --git a/spidsaml/go.mod b/spidsaml/go.mod index 8c301b7..30651a0 100644 --- a/spidsaml/go.mod +++ b/spidsaml/go.mod @@ -1,7 +1,15 @@ -module github.com/italia/spid-go/spidsaml +module github.com/nicolasvac/spid-go/spidsaml + +go 1.17 require ( - github.com/beevik/etree v1.0.1 - github.com/crewjam/errset v0.0.0-20160219153700-f78d65de925c // indirect + github.com/beevik/etree v1.1.0 github.com/crewjam/go-xmlsec v0.0.0-20170116132012-1aa2f9374afa + github.com/ma314smith/signedxml v0.0.0-20210628192057-abc5b481ae1c +) + +require ( + github.com/crewjam/errset v0.0.0-20160219153700-f78d65de925c // indirect + github.com/smartystreets/goconvey v1.7.2 // indirect + gopkg.in/check.v1 v1.0.0-20201130134442-10cb98267c6c // indirect ) diff --git a/spidsaml/go.sum b/spidsaml/go.sum index 5fc5f07..a12436c 100644 --- a/spidsaml/go.sum +++ b/spidsaml/go.sum @@ -1,6 +1,30 @@ github.com/beevik/etree v1.0.1 h1:lWzdj5v/Pj1X360EV7bUudox5SRipy4qZLjY0rhb0ck= github.com/beevik/etree v1.0.1/go.mod h1:r8Aw8JqVegEf0w2fDnATrX9VpkMcyFeM0FhwO62wh+A= +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/crewjam/errset v0.0.0-20160219153700-f78d65de925c h1:dCJ9oZ0VgnzJHR5BjkSrwkXA1USu483qlxBd0u29P8s= github.com/crewjam/errset v0.0.0-20160219153700-f78d65de925c/go.mod h1:XhiWL7J86xoqJ8+x2OA+AM2l9skQP2DZ0UOXQYVg7uI= github.com/crewjam/go-xmlsec v0.0.0-20170116132012-1aa2f9374afa h1:P3H7u0mfn9zEniDEKN50Yos1m60AM2PNOcyrInUjRFM= github.com/crewjam/go-xmlsec v0.0.0-20170116132012-1aa2f9374afa/go.mod h1:M9eHnKpImgRwzOFdlFQnbgJRqFwW/eX1cKAVobv03uE= +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/kr/pretty v0.2.1 h1:Fmg33tUaq4/8ym9TJN1x7sLJnHVwhP33CNkpYV/7rwI= +github.com/kr/pretty v0.2.1/go.mod h1:ipq/a2n7PKx3OHsz4KJII5eveXtPO4qwEXGdVfWzfnI= +github.com/kr/pty v1.1.1/go.mod h1:pFQYn66WHrOpPYNljwOMqo10TkYh1fy3cYio2l3bCsQ= +github.com/kr/text v0.1.0 h1:45sCR5RtlFHMR4UwH9sdQ5TC8v0qDQCHnXt+kaKSTVE= +github.com/kr/text v0.1.0/go.mod h1:4Jbv+DJW3UT/LiOwJeYQe1efqtUx/iVham/4vfdArNI= +github.com/ma314smith/signedxml v0.0.0-20210628192057-abc5b481ae1c h1:UPJygtyk491bJJ/DnRJFuzcq9Dl9NSeFrJ7VdiRzMxc= +github.com/ma314smith/signedxml v0.0.0-20210628192057-abc5b481ae1c/go.mod h1:KEgVcb43+f5KFUH/x6Vd3NROG0AIL2CuKMrIqYsmx6E= +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= +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 v1.0.0-20201130134442-10cb98267c6c h1:Hei/4ADfdWqJk1ZMxUNpqntNwaWcugrBjAiHlqqRiVk= +gopkg.in/check.v1 v1.0.0-20201130134442-10cb98267c6c/go.mod h1:JHkPIbrfpd72SG/EVd6muEfDQjcINNoR0C8j2r3qZ4Q= diff --git a/spidsaml/idp.go b/spidsaml/idp.go index 503faaf..6617251 100644 --- a/spidsaml/idp.go +++ b/spidsaml/idp.go @@ -31,9 +31,6 @@ func NewIDPFromXML(xml []byte) *IDP { panic(err) } - // TODO: if metadata is signed, validate /md:EntityDescriptor/dsig:Signature - // against a known CA - idp := new(IDP) idp.EntityID = doc.FindElement("/EntityDescriptor").SelectAttr("entityID").Value @@ -78,6 +75,8 @@ func NewIDPFromXML(xml []byte) *IDP { panic("failed to parse certificate: " + err.Error()) } + idp.XML, _ = doc.WriteToString() + return idp } diff --git a/spidsaml/logoutrequest_in.go b/spidsaml/logoutrequest_in.go index bee13c7..52ca6dc 100644 --- a/spidsaml/logoutrequest_in.go +++ b/spidsaml/logoutrequest_in.go @@ -53,7 +53,7 @@ func (logoutreq *LogoutRequestIn) validate(r *http.Request) error { } } if !knownDestination { - return fmt.Errorf("Invalid Destination: '%s'", destination) + return fmt.Errorf("invalid Destination: '%s'", destination) } return nil diff --git a/spidsaml/logoutrequest_out.go b/spidsaml/logoutrequest_out.go index e015588..4d1fd3f 100644 --- a/spidsaml/logoutrequest_out.go +++ b/spidsaml/logoutrequest_out.go @@ -25,7 +25,7 @@ func (sp *SP) NewLogoutRequest(session *Session) (*LogoutRequestOut, error) { if err != nil { return nil, err } - req.ID = generateMessageID() + req.ID = GenerateRandomID() req.Session = session return req, nil } @@ -78,7 +78,10 @@ func (logoutreq *LogoutRequestOut) XML(binding SAMLBinding) []byte { t := template.Must(template.New("req").Parse(tmpl)) var metadata bytes.Buffer - t.Execute(&metadata, data) + if t.Execute(&metadata, data) != nil { + return nil + } + return metadata.Bytes() } diff --git a/spidsaml/logoutresponse_in.go b/spidsaml/logoutresponse_in.go index c37cb0e..6e00f78 100644 --- a/spidsaml/logoutresponse_in.go +++ b/spidsaml/logoutresponse_in.go @@ -43,7 +43,7 @@ func (logoutres *LogoutResponseIn) validate(r *http.Request, inResponseTo string } if inResponseTo != logoutres.InResponseTo() { - return fmt.Errorf("Invalid InResponseTo: '%s' (expected: '%s')", + return fmt.Errorf("invalid InResponseTo: '%s' (expected: '%s')", logoutres.InResponseTo(), inResponseTo) } @@ -58,7 +58,7 @@ func (logoutres *LogoutResponseIn) validate(r *http.Request, inResponseTo string } } if !knownDestination { - return fmt.Errorf("Invalid Destination: '%s'", destination) + return fmt.Errorf("invalid Destination: '%s'", destination) } return nil diff --git a/spidsaml/logoutresponse_out.go b/spidsaml/logoutresponse_out.go index 29a223b..f0fc285 100644 --- a/spidsaml/logoutresponse_out.go +++ b/spidsaml/logoutresponse_out.go @@ -34,7 +34,7 @@ func (sp *SP) NewLogoutResponse(logoutreq *LogoutRequestIn, status LogoutStatus) if err != nil { return nil, err } - res.ID = generateMessageID() + res.ID = GenerateRandomID() res.InResponseTo = logoutreq.ID() return res, nil } @@ -92,7 +92,11 @@ func (logoutres *LogoutResponseOut) XML(binding SAMLBinding) []byte { t := template.Must(template.New("req").Parse(tmpl)) var metadata bytes.Buffer - t.Execute(&metadata, data) + + if t.Execute(&metadata, data) != nil { + return nil + } + return metadata.Bytes() } diff --git a/spidsaml/messages.go b/spidsaml/messages.go index 7e9df5a..4681541 100644 --- a/spidsaml/messages.go +++ b/spidsaml/messages.go @@ -11,6 +11,7 @@ import ( "encoding/base64" "errors" "fmt" + "github.com/ma314smith/signedxml" "io/ioutil" "net/http" "net/url" @@ -19,7 +20,7 @@ import ( "time" "github.com/beevik/etree" - xmlsec "github.com/crewjam/go-xmlsec" + "github.com/crewjam/go-xmlsec" ) // protocolMessage is the base class for all SAML messages @@ -43,16 +44,6 @@ type inMessage struct { RelayState string } -func generateMessageID() string { - id := make([]byte, 16) - if _, err := rand.Reader.Read(id); err != nil { - panic(err) - } - - // first character must not be a digit - return fmt.Sprintf("_%x", id) -} - func (msg *outMessage) IssueInstant() *time.Time { if msg.issueInstant == nil { t := time.Now().UTC() @@ -70,7 +61,7 @@ func (msg *outMessage) RedirectURL(baseurl string, xml []byte, param string) str w := &bytes.Buffer{} w1 := base64.NewEncoder(base64.StdEncoding, w) w2, _ := flate.NewWriter(w1, 9) - w2.Write([]byte(xml)) + w2.Write(xml) w2.Close() w1.Close() @@ -105,24 +96,29 @@ func (msg *outMessage) RedirectURL(baseurl string, xml []byte, param string) str func (msg *outMessage) PostForm(url string, xml []byte, param string) []byte { // We need to get the name of the root element doc := etree.NewDocument() - doc.ReadFromBytes(xml) - - signedDoc, err := xmlsec.Sign(msg.SP.KeyPEM(), xml, xmlsec.SignatureOptions{ - XMLID: []xmlsec.XMLIDOption{ - { - ElementName: doc.Root().Tag, - ElementNamespace: "", - AttributeName: "ID", - }, - }, - }) + err := doc.ReadFromBytes(xml) + + if err != nil { + panic(err) + } + + completeXML, _ := doc.WriteToString() + + // Sign the Authnrequest + signer, err := signedxml.NewSigner(completeXML) + + if err != nil { + panic(err) + } + + completeXML, err = signer.Sign(msg.SP.Key()) + if err != nil { panic(err) } - //os.Stdout.Write(signedDoc) // encode in base64 - encodedReqBuf := base64.StdEncoding.EncodeToString(signedDoc) + encodedReqBuf := base64.StdEncoding.EncodeToString([]byte(completeXML)) tmpl := template.Must(template.New("saml-post-form").Parse(` @@ -158,23 +154,23 @@ func (msg *outMessage) signatureTemplate() []byte { tmpl := template.Must(template.New("saml-post-form").Parse(`