Skip to content

Commit

Permalink
#192: Support multiple IdP signing certificates
Browse files Browse the repository at this point in the history
  • Loading branch information
Stephen Kress authored and crewjam committed May 1, 2019
1 parent d99784d commit 727f495
Showing 1 changed file with 25 additions and 19 deletions.
44 changes: 25 additions & 19 deletions service_provider.go
Original file line number Diff line number Diff line change
Expand Up @@ -205,48 +205,54 @@ func (sp *ServiceProvider) GetSSOBindingLocation(binding string) string {
return ""
}

// getIDPSigningCert returns the certificate which we can use to verify things
// getIDPSigningCerts returns the certificates which we can use to verify things
// signed by the IDP in PEM format, or nil if no such certificate is found.
func (sp *ServiceProvider) getIDPSigningCert() (*x509.Certificate, error) {
certStr := ""
func (sp *ServiceProvider) getIDPSigningCerts() ([]*x509.Certificate, error) {
var certStrs []string
for _, idpSSODescriptor := range sp.IDPMetadata.IDPSSODescriptors {
for _, keyDescriptor := range idpSSODescriptor.KeyDescriptors {
if keyDescriptor.Use == "signing" {
certStr = keyDescriptor.KeyInfo.Certificate
break
certStrs = append(certStrs, keyDescriptor.KeyInfo.Certificate)
}
}
}

// If there are no explicitly signing certs, just return the first
// non-empty cert we find.
if certStr == "" {
if len(certStrs) == 0 {
for _, idpSSODescriptor := range sp.IDPMetadata.IDPSSODescriptors {
for _, keyDescriptor := range idpSSODescriptor.KeyDescriptors {
if keyDescriptor.Use == "" && keyDescriptor.KeyInfo.Certificate != "" {
certStr = keyDescriptor.KeyInfo.Certificate
certStrs = append(certStrs, keyDescriptor.KeyInfo.Certificate)
break
}
}
}
}

if certStr == "" {
if len(certStrs) == 0 {
return nil, errors.New("cannot find any signing certificate in the IDP SSO descriptor")
}

var certs []*x509.Certificate

// cleanup whitespace
certStr = regexp.MustCompile(`\s+`).ReplaceAllString(certStr, "")
certBytes, err := base64.StdEncoding.DecodeString(certStr)
if err != nil {
return nil, fmt.Errorf("cannot parse certificate: %s", err)
}
regex := regexp.MustCompile(`\s+`)
for _, certStr := range certStrs {
certStr = regex.ReplaceAllString(certStr, "")
certBytes, err := base64.StdEncoding.DecodeString(certStr)
if err != nil {
return nil, fmt.Errorf("cannot parse certificate: %s", err)
}

parsedCert, err := x509.ParseCertificate(certBytes)
if err != nil {
return nil, err
parsedCert, err := x509.ParseCertificate(certBytes)
if err != nil {
return nil, err
}
certs = append(certs, parsedCert)
}
return parsedCert, nil

return certs, nil
}

// MakeAuthenticationRequest produces a new AuthnRequest object for idpURL.
Expand Down Expand Up @@ -627,13 +633,13 @@ func (sp *ServiceProvider) validateSigned(responseEl *etree.Element) error {

// validateSignature returns nill iff the Signature embedded in the element is valid
func (sp *ServiceProvider) validateSignature(el *etree.Element) error {
cert, err := sp.getIDPSigningCert()
certs, err := sp.getIDPSigningCerts()
if err != nil {
return err
}

certificateStore := dsig.MemoryX509CertificateStore{
Roots: []*x509.Certificate{cert},
Roots: certs,
}

validationContext := dsig.NewDefaultValidationContext(&certificateStore)
Expand Down

1 comment on commit 727f495

@BryceDFisher
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This is great! I just ran into this issue today.

Please sign in to comment.