From 727f495679e8131786c4852853b80a66dc00a5d7 Mon Sep 17 00:00:00 2001 From: Stephen Kress Date: Wed, 1 May 2019 09:45:55 -0500 Subject: [PATCH] #192: Support multiple IdP signing certificates --- service_provider.go | 44 +++++++++++++++++++++++++------------------- 1 file changed, 25 insertions(+), 19 deletions(-) diff --git a/service_provider.go b/service_provider.go index 6061b04a..3ade8ff8 100644 --- a/service_provider.go +++ b/service_provider.go @@ -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. @@ -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)