Skip to content

Commit

Permalink
Add support for hardware security module (HSM) signing.
Browse files Browse the repository at this point in the history
goxmldsig was recently updated to support HSMs. This is done by supporting
signing using the `crypto.Signer` directly, which is how HSMs are able to
sign things in golang.

This commit adds a new field `Signer` to the `IdentityProvider` which takes
precedence over the `Key` field when creating the `SigningContext`. The `Key`
field has been left in place to maintain backwards compatibility.

Tests have been updated to verify that this functions, and the `samlidp`
example identity provider has been updated such that it now supports a
`Signer` option. Additionally, `NewIdentifyProviderTest` has been corrected to
`NewIdentityProviderTest`.
  • Loading branch information
Mike Wilson committed Mar 8, 2023
1 parent 2aeb2ef commit 80d4329
Show file tree
Hide file tree
Showing 7 changed files with 122 additions and 65 deletions.
2 changes: 1 addition & 1 deletion go.mod
Original file line number Diff line number Diff line change
Expand Up @@ -11,7 +11,7 @@ require (
github.com/kr/pretty v0.3.1
github.com/mattermost/xml-roundtrip-validator v0.1.0
github.com/pkg/errors v0.9.1 // indirect
github.com/russellhaering/goxmldsig v1.2.0
github.com/russellhaering/goxmldsig v1.3.0
github.com/stretchr/testify v1.8.1
github.com/zenazn/goji v1.0.1
golang.org/x/crypto v0.0.0-20220128200615-198e4374d7ed
Expand Down
4 changes: 2 additions & 2 deletions go.sum
Original file line number Diff line number Diff line change
Expand Up @@ -35,8 +35,8 @@ github.com/rogpeppe/go-internal v1.6.1/go.mod h1:xXDCJY+GAPziupqXw64V24skbSoqbTE
github.com/rogpeppe/go-internal v1.8.0/go.mod h1:WmiCO8CzOY8rg0OYDC4/i/2WRWAB6poM+XZ2dLUbcbE=
github.com/rogpeppe/go-internal v1.9.0 h1:73kH8U+JUqXU8lRuOHeVHaa/SZPifC7BkcraZVejAe8=
github.com/rogpeppe/go-internal v1.9.0/go.mod h1:WtVeX8xhTBvf0smdhujwtBcq4Qrzq/fJaraNFVN+nFs=
github.com/russellhaering/goxmldsig v1.2.0 h1:Y6GTTc9Un5hCxSzVz4UIWQ/zuVwDvzJk80guqzwx6Vg=
github.com/russellhaering/goxmldsig v1.2.0/go.mod h1:gM4MDENBQf7M+V824SGfyIUVFWydB7n0KkEubVJl+Tw=
github.com/russellhaering/goxmldsig v1.3.0 h1:DllIWUgMy0cRUMfGiASiYEa35nsieyD3cigIwLonTPM=
github.com/russellhaering/goxmldsig v1.3.0/go.mod h1:gM4MDENBQf7M+V824SGfyIUVFWydB7n0KkEubVJl+Tw=
github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME=
github.com/stretchr/objx v0.4.0/go.mod h1:YvHI0jy2hoMjB+UWwv71VJQ9isScKT/TqJzVSSt89Yw=
github.com/stretchr/objx v0.5.0/go.mod h1:Yh+to48EsGEfYuaHDzXPcE3xhTkx73EhmCGUpEOglKo=
Expand Down
80 changes: 44 additions & 36 deletions identity_provider.go
Original file line number Diff line number Diff line change
Expand Up @@ -97,6 +97,7 @@ type AssertionMaker interface {
// and password).
type IdentityProvider struct {
Key crypto.PrivateKey
Signer crypto.Signer
Logger logger.Interface
Certificate *x509.Certificate
Intermediates []*x509.Certificate
Expand Down Expand Up @@ -831,24 +832,8 @@ const canonicalizerPrefixList = ""

// MakeAssertionEl sets `AssertionEl` to a signed, possibly encrypted, version of `Assertion`.
func (req *IdpAuthnRequest) MakeAssertionEl() error {
keyPair := tls.Certificate{
Certificate: [][]byte{req.IDP.Certificate.Raw},
PrivateKey: req.IDP.Key,
Leaf: req.IDP.Certificate,
}
for _, cert := range req.IDP.Intermediates {
keyPair.Certificate = append(keyPair.Certificate, cert.Raw)
}
keyStore := dsig.TLSCertKeyStore(keyPair)

signatureMethod := req.IDP.SignatureMethod
if signatureMethod == "" {
signatureMethod = dsig.RSASHA1SignatureMethod
}

signingContext := dsig.NewDefaultSigningContext(keyStore)
signingContext.Canonicalizer = dsig.MakeC14N10ExclusiveCanonicalizerWithPrefixList(canonicalizerPrefixList)
if err := signingContext.SetSignatureMethod(signatureMethod); err != nil {
signingContext, err := req.signingContext()
if err != nil {
return err
}

Expand Down Expand Up @@ -1049,24 +1034,8 @@ func (req *IdpAuthnRequest) MakeResponse() error {

// Sign the response element (we've already signed the Assertion element)
{
keyPair := tls.Certificate{
Certificate: [][]byte{req.IDP.Certificate.Raw},
PrivateKey: req.IDP.Key,
Leaf: req.IDP.Certificate,
}
for _, cert := range req.IDP.Intermediates {
keyPair.Certificate = append(keyPair.Certificate, cert.Raw)
}
keyStore := dsig.TLSCertKeyStore(keyPair)

signatureMethod := req.IDP.SignatureMethod
if signatureMethod == "" {
signatureMethod = dsig.RSASHA1SignatureMethod
}

signingContext := dsig.NewDefaultSigningContext(keyStore)
signingContext.Canonicalizer = dsig.MakeC14N10ExclusiveCanonicalizerWithPrefixList(canonicalizerPrefixList)
if err := signingContext.SetSignatureMethod(signatureMethod); err != nil {
signingContext, err := req.signingContext()
if err != nil {
return err
}

Expand All @@ -1084,3 +1053,42 @@ func (req *IdpAuthnRequest) MakeResponse() error {
req.ResponseEl = responseEl
return nil
}

// signingContext will create a signing context for the request.
func (req *IdpAuthnRequest) signingContext() (signingContext *dsig.SigningContext, err error) {
// Create a cert chain based off of the IDP cert and its intermediates.
certificates := [][]byte{req.IDP.Certificate.Raw}
for _, cert := range req.IDP.Intermediates {
certificates = append(certificates, cert.Raw)
}

// If signer is set, use it instead of the private key.
if req.IDP.Signer != nil {
signingContext, err = dsig.NewSigningContext(req.IDP.Signer, certificates)
if err != nil {
return
}
} else {
keyPair := tls.Certificate{
Certificate: certificates,
PrivateKey: req.IDP.Key,
Leaf: req.IDP.Certificate,
}
keyStore := dsig.TLSCertKeyStore(keyPair)

signingContext = dsig.NewDefaultSigningContext(keyStore)
}

// Default to using SHA1 if the signature method isn't set.
signatureMethod := req.IDP.SignatureMethod
if signatureMethod == "" {
signatureMethod = dsig.RSASHA1SignatureMethod
}

signingContext.Canonicalizer = dsig.MakeC14N10ExclusiveCanonicalizerWithPrefixList(canonicalizerPrefixList)
if err = signingContext.SetSignatureMethod(signatureMethod); err != nil {
return
}

return
}
2 changes: 1 addition & 1 deletion identity_provider_go116_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -18,7 +18,7 @@ import (
)

func TestIDPHTTPCanHandleSSORequest(t *testing.T) {
test := NewIdentifyProviderTest(t)
test := NewIdentityProviderTest(t, applyKey)
w := httptest.NewRecorder()

const validRequest = `lJJBayoxFIX%2FypC9JhnU5wszAz7lgWCLaNtFd5fMbQ1MkmnunVb%2FfUfbUqEgdhs%2BTr5zkmLW8S5s8KVD4mzvm0Cl6FIwEciRCeCRDFuznd2sTD5Upk2Ro42NyGZEmNjFMI%2BBOo9pi%2BnVWbzfrEqxY27JSEntEPfg2waHNnpJ4JtcgiWRLfoLXYBjwDfu6p%2B8JIoiWy5K4eqBUipXIzVRUwXKKtRK53qkJ3qqQVuNPUjU4TIQQ%2BBS5EqPBzofKH2ntBn%2FMervo8jWnyX%2BuVC78FwKkT1gopNKX1JUxSklXTMIfM0gsv8xeeDL%2BPGk7%2FF0Qg0GdnwQ1cW5PDLUwFDID6uquO1Dlot1bJw9%2FPLRmia%2BzRMCYyk4dSiq6205QSDXOxfy3KAq5Pkvqt4DAAD%2F%2Fw%3D%3D`
Expand Down
2 changes: 1 addition & 1 deletion identity_provider_go117_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -18,7 +18,7 @@ import (
)

func TestIDPHTTPCanHandleSSORequest(t *testing.T) {
test := NewIdentifyProviderTest(t)
test := NewIdentityProviderTest(t, applyKey)
w := httptest.NewRecorder()

const validRequest = `lJJBayoxFIX%2FypC9JhnU5wszAz7lgWCLaNtFd5fMbQ1MkmnunVb%2FfUfbUqEgdhs%2BTr5zkmLW8S5s8KVD4mzvm0Cl6FIwEciRCeCRDFuznd2sTD5Upk2Ro42NyGZEmNjFMI%2BBOo9pi%2BnVWbzfrEqxY27JSEntEPfg2waHNnpJ4JtcgiWRLfoLXYBjwDfu6p%2B8JIoiWy5K4eqBUipXIzVRUwXKKtRK53qkJ3qqQVuNPUjU4TIQQ%2BBS5EqPBzofKH2ntBn%2FMervo8jWnyX%2BuVC78FwKkT1gopNKX1JUxSklXTMIfM0gsv8xeeDL%2BPGk7%2FF0Qg0GdnwQ1cW5PDLUwFDID6uquO1Dlot1bJw9%2FPLRmia%2BzRMCYyk4dSiq6205QSDXOxfy3KAq5Pkvqt4DAAD%2F%2Fw%3D%3D`
Expand Down
Loading

0 comments on commit 80d4329

Please sign in to comment.