Skip to content

Commit

Permalink
Merge pull request RobotsAndPencils#14 from RanadeepPolavarapu/master
Browse files Browse the repository at this point in the history
feat: add support to read cert and keys from files
  • Loading branch information
mayankagwl authored Oct 2, 2020
2 parents 9a06a8e + 9943846 commit 227f64b
Show file tree
Hide file tree
Showing 5 changed files with 182 additions and 27 deletions.
27 changes: 27 additions & 0 deletions examples/certs/idp.key
Original file line number Diff line number Diff line change
@@ -0,0 +1,27 @@
-----BEGIN RSA PRIVATE KEY-----
MIIEpAIBAAKCAQEA0EOsaFAUiVn41jrDUtBzDlpPiZCqXsAHjaroLjGS2mOgcKs6
3eISdcRZMy0k4prk80Qi9LQ73dEZDrBJepeotT9ZifO8goRZ2omqUjoghdkcGXCC
56jQWk5dBi10AtGtg7LyGncE3KS2OHqWLwz28lnCE2hj1oROuZ1VGgzJSPrGaI9Q
MlaC6PK/r2xTS3GaGVoAOZ9eMjYM3bgEpmGNWuHHK5Tlsi89vaOynUVe4GcMRa7u
RhF52E0HskLsNBu1Dsm41GOw8/WG7is/jmjyN8ZZ74l51EqZ7jhPSHxgTjhisvOa
C49pmlXZ30/+SPJa2gXcf7JlRv93eCoR/mwiTQIDAQABAoIBAQDCoNu4SE5I5fo0
SopLFF7R5Pg7uSRek9r7O+LreIyi50QCxyKmRSW3RR2PQ/sagmLALMb2JJFmBIkE
Px8MIDsb1u4Yi915o7JNFyZC3euS6ojmBU1wrUS5gE5S/ZtTHxym3U6ACQZUJOHd
+kHsCpOlfNhwRrg2u0LPGAd15skdFv8cHRRJWTiuQH8rGtqOujKkzVaM1s1OzT+9
7ef9qbMJ36CVmNhT00AejFHfICA8SKizFoi3ckfDYRAT4XiuTwQSQHN4suBteZMA
PRBPrnF7KlTu+1UD4YcFPwa4tEAa3dyHINmZRwxphtUYw91DYA16FlwYn95Yr/Kp
/NyQBh/BAoGBAO3tPg1CyMI0wLKhbM1hVkjZ9qp/IkxJzGHPW7RKq5tNrfs3kqWD
Bv4xqKRxKZhJAVocTpKpDsGrOGRfsGnu19M8ca8dsutn49HqqViePFEnr/4TbXjj
IiyzqcXM8Z7rRbGUsBDS6Hs9CzupS3UW4Qat9pje2QT/si6AQde9Q0lJAoGBAOAV
nLb3jliefR2M0tER+JofoSZAuTNsdfJEiHqLEeQo1BWQVo8UneliR9AJaEL3Or1S
qxYYCV5k3rhReH/Y38obsKKxHxlCj+MI6FTh2dij1GIoVRE0ef9FHWvy48qgsXA3
rhPme0lITaiJdeTL2yvAkibVUsN/cdHb9mlmUvTlAoGAN64CjIXph5Fi2yrt1G6I
C8p+cE4KT8Ihg25Mbrfeyxx1r8nEltlABLLIXZth+ZJ0L4taU/YeTvJr4wmFtnS/
q16E2E9h9Lc+WHzLHsMBKJjaFeKkBttYvw9YlsKsdN1oVPOP9I/2+ghCHbczwnYV
OUPy5rmYHhRctYcsYXmYSokCgYBvyZhSlpEGmJomAbix39s7WAgWgzdg9fypDZNg
PryAK/YRg94KpuhYI1Vb7TV8IsXE6Na8wOT7mCqbnNkWPmT1+HZX0zCGc3RAeJMe
tUTuFFzBUwmM+bbap+oTjiohTaT3LG/FMbfTSSSkrV6I80G1Q+4lPUp6iLuJS8hT
sF42GQKBgQDl10lGbzefFSx/97GbLpZk0BEV7CRUDEEy9tWcmVfINkJ+YtZOT1os
P5aHDmT/n+4yKVNM4ZPJ1sEcByWpIy85UqBHH0wTnkRoM45AV1gXn29gnylCBhMp
YrFioyChfIcY2+K/h8c+VwCY+uYyJfmrVQFJo6aKbwvKh4x2sBsRPg==
-----END RSA PRIVATE KEY-----
19 changes: 19 additions & 0 deletions examples/certs/idp.pem
Original file line number Diff line number Diff line change
@@ -0,0 +1,19 @@
-----BEGIN CERTIFICATE-----
MIIDITCCAgmgAwIBAgIUCnWJffzG7ehWg5DLtwYvhTR9oTIwDQYJKoZIhvcNAQEL
BQAwIDEeMBwGA1UEAwwVaWRlbnRpdHktcHJvdmlkZXIuY29tMB4XDTIwMDkxNzIz
NDQwNVoXDTIxMDkxNzIzNDQwNVowIDEeMBwGA1UEAwwVaWRlbnRpdHktcHJvdmlk
ZXIuY29tMIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEA0EOsaFAUiVn4
1jrDUtBzDlpPiZCqXsAHjaroLjGS2mOgcKs63eISdcRZMy0k4prk80Qi9LQ73dEZ
DrBJepeotT9ZifO8goRZ2omqUjoghdkcGXCC56jQWk5dBi10AtGtg7LyGncE3KS2
OHqWLwz28lnCE2hj1oROuZ1VGgzJSPrGaI9QMlaC6PK/r2xTS3GaGVoAOZ9eMjYM
3bgEpmGNWuHHK5Tlsi89vaOynUVe4GcMRa7uRhF52E0HskLsNBu1Dsm41GOw8/WG
7is/jmjyN8ZZ74l51EqZ7jhPSHxgTjhisvOaC49pmlXZ30/+SPJa2gXcf7JlRv93
eCoR/mwiTQIDAQABo1MwUTAdBgNVHQ4EFgQU7XI0rfRcWI9//x28oHcfpTHXLzMw
HwYDVR0jBBgwFoAU7XI0rfRcWI9//x28oHcfpTHXLzMwDwYDVR0TAQH/BAUwAwEB
/zANBgkqhkiG9w0BAQsFAAOCAQEATIfDtbTeRRCJNTvlbH9dQPk9W/UNCHoVWfF/
lYhvQmUkkvXA/xoALOQqPH6xjrb9/jMnq89FsM7p7wPph+dnCEp1b3+ZQ5BFepYX
PNDTT2u08EN20b6QDb+EWDiFx3vMDRPvwByGAWHwbORF/AR3JwaUF7ElcWN48+pt
qEGBV9MmR6mM4icRTwOVWcSlE4D1OHPvjBcfFzrAg1KS/J4r7+vWbCAdtKLe2bKm
WklVrOJ3OhOeY5SFefaZ969uFPHhK7C0ntUNhJjm5losUrFPczG6twLvZdmrbcya
v3xrxdzGxa0jAQysOzB+CMsjYWjWAZA8wOtd/pDQ/K2n6+7ABA==
-----END CERTIFICATE-----
26 changes: 26 additions & 0 deletions examples/certs/sp.pem
Original file line number Diff line number Diff line change
@@ -0,0 +1,26 @@
-----BEGIN CERTIFICATE-----
MIIEdTCCA12gAwIBAgIOAW1mLqPmAAAAAC+KcbswDQYJKoZIhvcNAQELBQAwfzEX
MBUGA1UEAwwOZGV2Y2VydGlmaWNhdGUxGDAWBgNVBAsMDzAwRDdGMDAwMDA0Qnlz
YzEXMBUGA1UECgwOU2FsZXNmb3JjZS5jb20xFjAUBgNVBAcMDVNhbiBGcmFuY2lz
Y28xCzAJBgNVBAgMAkNBMQwwCgYDVQQGEwNVU0EwHhcNMTkwOTI1MDIwOTU1WhcN
MjAwOTI1MDAwMDAwWjB/MRcwFQYDVQQDDA5kZXZjZXJ0aWZpY2F0ZTEYMBYGA1UE
CwwPMDBEN0YwMDAwMDRCeXNjMRcwFQYDVQQKDA5TYWxlc2ZvcmNlLmNvbTEWMBQG
A1UEBwwNU2FuIEZyYW5jaXNjbzELMAkGA1UECAwCQ0ExDDAKBgNVBAYTA1VTQTCC
ASIwDQYJKoZIhvcNAQEBBQADggEPADCCAQoCggEBAI9HcJmAFUAjQaY4WiIVMcIX
v5vRJ+jefBUwVtU4qBD5FGqO33F7dLGcdvQcJwnMVFIXG+PJ67XkwApXOs5e6ViG
xWmKa40kMYnzYKH/q47VIf+3mTxIpAr37AYkBzHhcSTnjOXpmgfLe7eaYVXYQKCL
rO+ilSF7T3+pC8vUcgSFRokhJ9V5bUOf2EqEl89AGHCddzNa49u2a5xTl2i0+4FS
V+r4H/naDZpTaTwEPmZ5xHHLn74znyqYb776Tmxo57HVG8jyeKvjHhoaaFBb+RQK
kOj8JcvnNnT4Hd6iYQxZ3or58iC0nPnSjVrCYiYTOZSElEZ2C5esHKB50uGTZ1kC
AwEAAaOB7jCB6zAdBgNVHQ4EFgQUtEZepqOQLwHToZGuf/WuRrRBvjowDwYDVR0T
AQH/BAUwAwEB/zCBuAYDVR0jBIGwMIGtgBS0Rl6mo5AvAdOhka5/9a5GtEG+OqGB
hKSBgTB/MRcwFQYDVQQDDA5kZXZjZXJ0aWZpY2F0ZTEYMBYGA1UECwwPMDBEN0Yw
MDAwMDRCeXNjMRcwFQYDVQQKDA5TYWxlc2ZvcmNlLmNvbTEWMBQGA1UEBwwNU2Fu
IEZyYW5jaXNjbzELMAkGA1UECAwCQ0ExDDAKBgNVBAYTA1VTQYIOAW1mLqPmAAAA
AC+KcbswDQYJKoZIhvcNAQELBQADggEBACsiskSO0vNW2peA4h1IXZlbnLCmc8jO
qbh5bAAG+Vak9lWk24oa2KEco7fX2M/9N259Z3GJVcsULXMpYYgMECXKVzxb8k6v
+wcr9vCqA+FVId6qle6qy2zXHsO40NgG5DJ2catvDH19YWEJcnzNqbXoNOqZgGE/
EKv76hpQVH+35aRPBFC6AKU/4Ix4YTIqpntyRc15vdtlKSAPLGxXRSL+m3KDMCMR
qFyauy7mI6RmOyJvY7cgICNmPWLGcjb6YiVS9eEjRZhF17X8PeFb0/5cIC776vwr
Z9LcnL+ebNUyc8yS7JkYMjSihyB4POHSzCdZviyJqtCoOMD+9GraRDo=
-----END CERTIFICATE-----
134 changes: 107 additions & 27 deletions idp.go
Original file line number Diff line number Diff line change
Expand Up @@ -25,8 +25,8 @@ func (idp *IdentityProvider) NewSignedLoginResponse() (string, *Reject) {
}
response := lib.NewResponse()
response.SetIdpCertificate(idp.x509IdpCertificate)
resposne.SetSignatureAlgorithm(idp.signatureAlgorithm())
resposne.SetDigestAlgorithm(idp.digestAlgorithm())
response.SetSignatureAlgorithm(idp.signatureAlgorithm())
response.SetDigestAlgorithm(idp.digestAlgorithm())
response.SetIssuer(idp.Issuer)
response.SetDestination(idp.ACSLocation)
response.SetNameId(idp.NameIdentifierFormat, idp.NameIdentifier)
Expand Down Expand Up @@ -60,8 +60,8 @@ func (idp *IdentityProvider) NewSignedLogoutResponse() (string, *Reject) {
if idp.samlRequestParam != nil {
response.InResponseTo = idp.samlRequestParam.LogoutRequest.ID
}
resposne.SetSignatureAlgorithm(idp.signatureAlgorithm())
resposne.SetDigestAlgorithm(idp.digestAlgorithm())
response.SetSignatureAlgorithm(idp.signatureAlgorithm())
response.SetDigestAlgorithm(idp.digestAlgorithm())
signedXml, err := response.SignedXml(idp.idpPrivateKey)
if err != nil {
return "", &Reject{err, "SIGNED_XML_ERROR"}
Expand All @@ -70,9 +70,21 @@ func (idp *IdentityProvider) NewSignedLogoutResponse() (string, *Reject) {
}

func (idp *IdentityProvider) MetaDataResponse() (string, *Reject) {
var idpCert string
if idp.IDPCertFilePath != "" {
// IDP Cert is provided by file path.
buf, err := ioutil.ReadFile(idp.IDPCertFilePath)
if err != nil {
return "", &Reject{err, "SAML Configuration: IDP Certificate file could not be read"}
}
idpCert = util.GetRawCertificate(string(buf))
} else {
idpCert = util.GetRawCertificate(idp.IDPCert)
}

metadata := lib.GetIdpEntityDescriptor()
metadata.EntityId = idp.Issuer
metadata.IDPSSODescriptor.SigningKeyDescriptor.KeyInfo.X509Data.X509Certificate.Cert = util.GetRawCertificate(idp.IDPCert)
metadata.IDPSSODescriptor.SigningKeyDescriptor.KeyInfo.X509Data.X509Certificate.Cert = idpCert
if len(idp.SingleSignOnService) > 0 {
for i := 0; i < len(idp.SingleSignOnService); i++ {
metadata.IDPSSODescriptor.SingleSignOnService = append(metadata.IDPSSODescriptor.SingleSignOnService, lib.SingleSignOnService{
Expand All @@ -99,8 +111,7 @@ func (idp *IdentityProvider) MetaDataResponse() (string, *Reject) {
}
b, err := xml.MarshalIndent(metadata, "", " ")
if err != nil {
return "", &Reject{err, "XML_ENCODE_ERROR",
}
return "", &Reject{err, "XML_ENCODE_ERROR"}
}
newMetadata := fmt.Sprintf("<?xml version='1.0' encoding='UTF-8'?>\n%s", b)
return string(newMetadata), nil
Expand Down Expand Up @@ -180,24 +191,50 @@ func (idp *IdentityProvider) ResponseHtml(signedXML string, requestType string)
}

func (idp *IdentityProvider) validateIDPX509Certificate() *Reject {
if idp.IDPCert == "" {
if idp.IDPCert == "" && idp.IDPCertFilePath == "" {
return &Reject{errors.New("SAML Configuration: IDP Certificate is empty"), "EMPTY_IDP_CERTIFICATE"}
}
err := util.ValidateCertificatePem(idp.IDPCert)
if err != nil {
return &Reject{err, "INVALID_CERTIFICATE_PEM"}

if idp.IDPCertFilePath != "" {
// IDP Cert is provided by file path.
buf, err := ioutil.ReadFile(idp.IDPCertFilePath)
if err != nil {
return &Reject{errors.New("SAML Configuration: IDP Certificate is empty"), "EMPTY_IDP_CERTIFICATE"}
}
if err := util.ValidateCertificatePem(string(buf)); err != nil {
return &Reject{err, "INVALID_CERTIFICATE_PEM"}
}
} else {
// IDP Cert is provided by string.
if err := util.ValidateCertificatePem(idp.IDPCert); err != nil {
return &Reject{err, "INVALID_CERTIFICATE_PEM"}
}
}

return nil
}

func (idp *IdentityProvider) validateSPX509Certificate() *Reject {
if idp.SPCert == "" {
if idp.SPCert == "" && idp.SPCertFilePath == "" {
return &Reject{errors.New("SAML Configuration: SP Certificate is empty"), "EMPTY_IDP_CERTIFICATE"}
}
err := util.ValidateCertificatePem(idp.SPCert)
if err != nil {
return &Reject{err, "INVALID_CERTIFICATE_PEM"}

if idp.SPCertFilePath != "" {
// SP Cert is provided by file path.
buf, err := ioutil.ReadFile(idp.SPCertFilePath)
if err != nil {
return &Reject{err, "INVALID_CERTIFICATE_PEM_FILE_PATH"}
}
if err := util.ValidateCertificatePem(string(buf)); err != nil {
return &Reject{err, "INVALID_CERTIFICATE_PEM"}
}
} else {
// SP Cert is provided by string.
if err := util.ValidateCertificatePem(idp.SPCert); err != nil {
return &Reject{err, "INVALID_CERTIFICATE_PEM"}
}
}

return nil
}

Expand All @@ -206,7 +243,18 @@ func (idp *IdentityProvider) rawIdpX509Certificate() *Reject {
if err != nil {
return err
}
idp.x509IdpCertificate = util.GetRawCertificate(idp.IDPCert)

if idp.IDPCertFilePath != "" {
// IDP Cert is provided by file path.
buf, err := ioutil.ReadFile(idp.IDPCertFilePath)
if err != nil {
return &Reject{err, "INVALID_CERTIFICATE_PEM_FILE_PATH"}
}
idp.x509IdpCertificate = util.GetRawCertificate(string(buf))
} else {
idp.x509IdpCertificate = util.GetRawCertificate(idp.IDPCert)
}

return nil
}

Expand All @@ -215,7 +263,18 @@ func (idp *IdentityProvider) rawSPX509Certificate() *Reject {
if err != nil {
return err
}
idp.x509SpCertificate = util.GetRawCertificate(idp.SPCert)

if idp.SPCertFilePath != "" {
// SP Cert is provided by file path.
buf, err := ioutil.ReadFile(idp.SPCertFilePath)
if err != nil {
return &Reject{err, "INVALID_CERTIFICATE_PEM_FILE_PATH"}
}
idp.x509SpCertificate = util.GetRawCertificate(string(buf))
} else {
idp.x509SpCertificate = util.GetRawCertificate(idp.SPCert)
}

return nil
}

Expand All @@ -238,14 +297,35 @@ func (idp *IdentityProvider) parseSpX509Certificate() *Reject {
}

func (idp *IdentityProvider) parsePrivateKey() *Reject {
if idp.IDPKey == "" {
if idp.IDPKey == "" && idp.IDPKeyFilePath == "" {
return &Reject{errors.New("SAML Configuration: IDP Private Key is empty"), "EMPTY_IDP_PRIVATE_KEY"}
}
privateKey, err := util.ParseRsaPrivateKeyPem(idp.IDPKey)
if err != nil {
return &Reject{err, "PARSE_PRIVATE_KEY_ERROR"}
}
idp.idpPrivateKey = privateKey

if idp.IDPKeyFilePath != "" {
// IDP Private Key is provided by file path.
buf, err := ioutil.ReadFile(idp.IDPKeyFilePath)
if err != nil {
return &Reject{err, "INVALID_PRIVATE_KEY_FILE_PATH"}
}
privateKey, err := util.ParseRsaPrivateKeyPem(string(buf))
if err != nil {
return &Reject{err, "PARSE_PRIVATE_KEY_ERROR"}
}
idp.idpPrivateKey = privateKey
} else {
// IDP Private Key is provided by string.
privateKey, err := util.ParseRsaPrivateKeyPem(idp.IDPKey)
if err != nil {
return &Reject{err, "PARSE_PRIVATE_KEY_ERROR"}
}
idp.idpPrivateKey = privateKey
}

return nil
}

Expand All @@ -272,38 +352,38 @@ func (idp *IdentityProvider) validate() *Reject {
}

if idp.ACSBinging == "" {
return &Reject{errors.New("SAML Configuration: ACSBinging is empty"),"SAML_CONFIG_ACSBINGING_EMPTY"}
return &Reject{errors.New("SAML Configuration: ACSBinging is empty"), "SAML_CONFIG_ACSBINGING_EMPTY"}
}

if idp.ACSBinging != "urn:oasis:names:tc:SAML:2.0:bindings:HTTP-POST" {
return &Reject{errors.New("SAML Configuration: ACSBinging is invalid"),"SAML_CONFIG_ACSBINGING_EMPTY"}
return &Reject{errors.New("SAML Configuration: ACSBinging is invalid"), "SAML_CONFIG_ACSBINGING_EMPTY"}
}

if idp.NameIdentifierFormat == "" {
return &Reject{errors.New("SAML Configuration: NameID Format is empty"),"SAML_CONFIG_NAMEID_FORMAT_EMPTY"}
return &Reject{errors.New("SAML Configuration: NameID Format is empty"), "SAML_CONFIG_NAMEID_FORMAT_EMPTY"}
}
if idp.NameIdentifier == "" {
return &Reject{errors.New("SAML Configuration: NameID value is empty"),"SAML_CONFIG_NAMEID_VALUE_EMPTY"}
return &Reject{errors.New("SAML Configuration: NameID value is empty"), "SAML_CONFIG_NAMEID_VALUE_EMPTY"}
}
if idp.SessionIndex == "" {
return &Reject{errors.New("SAML Configuration: SessionIndex is empty"),"SAML_CONFIG_SESSIONINDEX_EMPTY"}
return &Reject{errors.New("SAML Configuration: SessionIndex is empty"), "SAML_CONFIG_SESSIONINDEX_EMPTY"}
}
if len(idp.Audiences) == 0 {
return &Reject{errors.New("SAML Configuration: Audience is empty"),"SAML_CONFIG_AUDIENCE_EMPTY"}
return &Reject{errors.New("SAML Configuration: Audience is empty"), "SAML_CONFIG_AUDIENCE_EMPTY"}
}
if len(idp.Attributes) > 0 {
for _, attr := range idp.Attributes {
attrName, attrNameOk := attr["Name"]
attrFormat, attrFormatOk := attr["Format"]
_, attrValueOk := attr["Value"]
if !(attrNameOk && attrName != "") {
return &Reject{errors.New("SAML Configuration: Attributes Name is not defined or empty"),"SAML_CONFIG_NAME_UNDEFINED"}
return &Reject{errors.New("SAML Configuration: Attributes Name is not defined or empty"), "SAML_CONFIG_NAME_UNDEFINED"}
}
if !(attrFormatOk && attrFormat != "") {
return &Reject{errors.New("SAML Configuration: Attributes Format is not defined or empty"),"SAML_CONFIG_FORMAT_UNDEFINED"}
return &Reject{errors.New("SAML Configuration: Attributes Format is not defined or empty"), "SAML_CONFIG_FORMAT_UNDEFINED"}
}
if !(attrValueOk) {
return &Reject{errors.New("SAML Configuration: Attributes Value is not defined"),"SAML_CONFIG_VALUE_UNDEFINED"}
return &Reject{errors.New("SAML Configuration: Attributes Value is not defined"), "SAML_CONFIG_VALUE_UNDEFINED"}
}
}
}
Expand Down
3 changes: 3 additions & 0 deletions saml.go
Original file line number Diff line number Diff line change
Expand Up @@ -34,6 +34,9 @@ type IdentityProvider struct {
IDPCert string
IDPKey string
SPCert string
IDPCertFilePath string
IDPKeyFilePath string
SPCertFilePath string
Attributes []map[string]string
SignatureAlgorithm string // RSA-SHA256 is the default
SignaturePrefix string
Expand Down

0 comments on commit 227f64b

Please sign in to comment.