Skip to content
This repository has been archived by the owner on Jun 6, 2023. It is now read-only.

Verifying response without depending on an external binary #6

Open
wants to merge 2 commits into
base: master
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
47 changes: 47 additions & 0 deletions authnrequest_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -47,3 +47,50 @@ func TestGetUnsignedRequest(t *testing.T) {
assert.NoError(err)
assert.NotEmpty(authnRequest)
}

func TestGetUnsignedRequestWithEmbededCert(t *testing.T) {
assert := assert.New(t)
sp := ServiceProviderSettings{
IDPSSOURL: "http://www.onelogin.net",
IDPSSODescriptorURL: "http://www.onelogin.net",
AssertionConsumerServiceURL: "http://localhost:8000/auth/saml/name",
IDPPublicCertContent: `-----BEGIN CERTIFICATE-----
MIIFYTCCA0mgAwIBAgIJAI1a1evtQYDkMA0GCSqGSIb3DQEBBQUAME8xCzAJBgNV
BAYTAkZSMQ4wDAYDVQQHEwVQYXJpczEOMAwGA1UEChMFRWtpbm8xDzANBgNVBAsT
BkRldk9wczEPMA0GA1UEAxMGZ29zYW1sMB4XDTE1MDcyMDIyNDE1OFoXDTI1MDcx
NzIyNDE1OFowTzELMAkGA1UEBhMCRlIxDjAMBgNVBAcTBVBhcmlzMQ4wDAYDVQQK
EwVFa2lubzEPMA0GA1UECxMGRGV2T3BzMQ8wDQYDVQQDEwZnb3NhbWwwggIiMA0G
CSqGSIb3DQEBAQUAA4ICDwAwggIKAoICAQDoo/DTqWoyJyXR0K+hF4pw4qBkaLL0
gbbKoiKH+7wvdzHONOoFBfF5NQj02M4JJyeOQ6+hHYV4QjtUG41zMf1XoH/U6Ey/
oURkuCJJCGhW9AyD+A4WP4YS4Ag/uN7o0P3nuj7hJipefY1Bzmg2n89iHDcpHvwK
TtVWZYdj6Dgbwh9ZH9QiRRRp+GZHXu7nW+VCZM0mE+9qjxK4Mw+KEDD6LIgSOAzR
LWLyUmb2Kwvc++DhwDtIoThVHYoNd4Sk9j6/4B3DmPa83i/1dZKyFaMCDUn7+i6K
hwIWbGfg6uQMM8G6XzF4V5x5agmg8DK24VXs3yb1lOIUczNVq4ZHkApc4jwHWiXn
cab88UnDPG7pVm87whaMghWNwrYAt//QEInExkxjNhWwxNFlelg/8b9fUsdH58Fe
ZiZ+mNnwACXnggmZEE+lUX5Fh8l79bke+dnQbJAhQfi+OhmNlqmc+ouKDPYqk0/I
C9q/3Tg65Ej9Miq918IAvQAVtlwwwp6I5/02Aa5iqZozBTUXYqWE/qXixlpWh2tP
5ljecgGazuw58tGj2+nXS9DA9wVgGUAl4xJFO/s8emna52lSPzwvcr6j+BMifXHr
0WBIEcTbtzXhxUpfC6IC14yfPOf8g4WKKgg1Wq3H4dGiE11y66ceYeh1RZlWXq/J
EtJ1FVLoGq4qLwIDAQABo0AwPjA8BgNVHREENTAzghBsb2dzLmV4YW1wbGUuY29t
ghNtZXRyaWNzLmV4YW1wbGUuY29thwTAqAABhwQKAAAyMA0GCSqGSIb3DQEBBQUA
A4ICAQAcaLdziL6dNZ3lXtm3nsI9ceSVwp2yKfpsswjs524bOsLK97Ucf4hhlh1b
q5hywWbm85N7iuxdpBuhSmeJ94ryFAPDUkhR1Mzcl48c6R8tPbJVhabhbfg+uIHi
4BYUA0olesdsyTOsRHprM4iV+PlKZ85SQT04ZNyaqIDzmNEP7YXDl/Wl3Q0N5E1U
yGfDTBxo07srqrAM2E5X7hN9bwdZX0Hbo/C4q3wgRHAts/wJXXWSSTe1jbIWYXem
EkwAEd01BiMBj1LYK/sJ8s4fONdLxIyKqLUh1Ja46moqpgl5AHuPbqnwPdgGGvEd
iBzz5ppHs0wXFopk+J4rzYRhya6a3BMXiDjg+YOSwFgCysmWmCrxoImmfcQWUZJy
5eMow+hBBiKgT2DxggqVzReN3C7uwsFZLZCsv8+MjvFQz52oEp/GWqFepggFQiRI
K7/QmwcsDdz6zBobZJaJstq3R2mHYkhaVUIOqEuqyD2N7qms8bek7xzq6F9KkYLk
PK/d2Crkxq1bnvM7oO8IsA6vHdTexfZ1SRPf7Mxpg8DMV788qE09BDZ5mLFOkRbw
FY7MHRX6Mz59gfnAcRwK/0HnG6c8EZCJH8jMStzqA0bUjzDiyN2ZgzFkTUA9Cr8j
kq8grtVMsp40mjFnSg/FR+O+rG32D/rbfvNYFCR8wawOcYrGyA==
-----END CERTIFICATE-----`,
}
err := sp.Init()
assert.NoError(err)

// Construct an AuthnRequest
authnRequest := sp.GetAuthnRequest()
assert.NoError(err)
assert.NotEmpty(authnRequest)
}
13 changes: 10 additions & 3 deletions authnresponse.go
Original file line number Diff line number Diff line change
Expand Up @@ -75,9 +75,16 @@ func (r *Response) Validate(s *ServiceProviderSettings) error {
return errors.New("subject recipient mismatch, expected: " + s.AssertionConsumerServiceURL + " not " + r.Assertion.Subject.SubjectConfirmation.SubjectConfirmationData.Recipient)
}

err := VerifyResponseSignature(r.originalString, s.IDPPublicCertPath)
if err != nil {
return err
if s.IDPPublicCertContent != "" {
err := VerifyResponseSignatureMem([]byte(r.originalString), []byte(s.IDPPublicCertContent))
if err != nil {
return err
}
} else {
err := VerifyResponseSignature(r.originalString, s.IDPPublicCertPath)
if err != nil {
return err
}
}

//CHECK TIMES
Expand Down
29 changes: 20 additions & 9 deletions saml.go
Original file line number Diff line number Diff line change
@@ -1,6 +1,10 @@
package saml

import "github.com/RobotsAndPencils/go-saml/util"
import (
"errors"

"github.com/RobotsAndPencils/go-saml/util"
)

// ServiceProviderSettings provides settings to configure server acting as a SAML Service Provider.
// Expect only one IDP per SP in this configuration. If you need to configure multipe IDPs for an SP
Expand All @@ -10,9 +14,10 @@ type ServiceProviderSettings struct {
PrivateKeyPath string
IDPSSOURL string
IDPSSODescriptorURL string
IDPPublicCertContent string
IDPPublicCertPath string
AssertionConsumerServiceURL string
SPSignRequest bool
SPSignRequest bool

hasInit bool
publicCert string
Expand All @@ -27,25 +32,31 @@ func (s *ServiceProviderSettings) Init() (err error) {
if s.hasInit {
return nil
}
s.hasInit = true

if s.SPSignRequest {
if s.SPSignRequest {
s.publicCert, err = util.LoadCertificate(s.PublicCertPath)
if err != nil {
panic(err)
return err
}

s.privateKey, err = util.LoadCertificate(s.PrivateKeyPath)
if err != nil {
panic(err)
return err
}
}

s.iDPPublicCert, err = util.LoadCertificate(s.IDPPublicCertPath)
if err != nil {
panic(err)
if s.IDPPublicCertPath != "" {
s.iDPPublicCert, err = util.LoadCertificate(s.IDPPublicCertPath)
if err != nil {
return err
}
} else if s.IDPPublicCertContent != "" {
s.iDPPublicCert = util.SanitizeCertificate(s.IDPPublicCertContent)
} else {
return errors.New("missing idp public cert or its path")
}

s.hasInit = true
return nil
}

Expand Down
12 changes: 8 additions & 4 deletions util/loadCertificate.go
Original file line number Diff line number Diff line change
Expand Up @@ -6,18 +6,22 @@ import (
"strings"
)

var (
re = regexp.MustCompile("---(.*)CERTIFICATE(.*)---")
)

// LoadCertificate from file system
func LoadCertificate(certPath string) (string, error) {
b, err := ioutil.ReadFile(certPath)
if err != nil {
return "", err
}
cert := string(b)
return SanitizeCertificate(string(b)), nil
}

re := regexp.MustCompile("---(.*)CERTIFICATE(.*)---")
func SanitizeCertificate(cert string) string {
cert = re.ReplaceAllString(cert, "")
cert = strings.Trim(cert, " \n")
cert = strings.Replace(cert, "\n", "", -1)

return cert, nil
return cert
}
27 changes: 27 additions & 0 deletions xmlsec.go
Original file line number Diff line number Diff line change
Expand Up @@ -6,13 +6,28 @@ import (
"os"
"os/exec"
"strings"

"github.com/RobotsAndPencils/go-saml/xmlsec"
)

const (
xmlResponseID = "urn:oasis:names:tc:SAML:2.0:protocol:Response"
xmlRequestID = "urn:oasis:names:tc:SAML:2.0:protocol:AuthnRequest"
)

var (
xmlResponseIDAttr = xmlsec.IDAttr{
Name: "ID",
NodeName: "Response",
NsHref: "urn:oasis:names:tc:SAML:2.0:protocol",
}
xmlRequestIDAttr = xmlsec.IDAttr{
Name: "ID",
NodeName: "AuthnRequest",
NsHref: "urn:oasis:names:tc:SAML:2.0:protocol",
}
)

// SignRequest sign a SAML 2.0 AuthnRequest
// `privateKeyPath` must be a path on the filesystem, xmlsec1 is run out of process
// through `exec`
Expand Down Expand Up @@ -70,13 +85,25 @@ func VerifyResponseSignature(xml string, publicCertPath string) error {
return verify(xml, publicCertPath, xmlResponseID)
}

func VerifyResponseSignatureMem(doc []byte, publicCert []byte) error {
return xmlsec.Verify(doc, publicCert, xmlsec.Options{
IDAttrs: []xmlsec.IDAttr{xmlResponseIDAttr},
})
}

// VerifyRequestSignature verify signature of a SAML 2.0 AuthnRequest document
// `publicCertPath` must be a path on the filesystem, xmlsec1 is run out of process
// through `exec`
func VerifyRequestSignature(xml string, publicCertPath string) error {
return verify(xml, publicCertPath, xmlRequestID)
}

func VerifyRequestSignatureMem(doc []byte, publicCert []byte) error {
return xmlsec.Verify(doc, publicCert, xmlsec.Options{
IDAttrs: []xmlsec.IDAttr{xmlRequestIDAttr},
})
}

func verify(xml string, publicCertPath string, id string) error {
//Write saml to
samlXmlsecInput, err := ioutil.TempFile(os.TempDir(), "tmpgs")
Expand Down
Loading