Skip to content

Commit f9cbf62

Browse files
committed
v2.0 Support EC/DSA crypto
1 parent 113f39a commit f9cbf62

33 files changed

Lines changed: 1990 additions & 1337 deletions

.rubocop_todo.yml

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -204,12 +204,12 @@ Metrics/CyclomaticComplexity:
204204
# Offense count: 58
205205
# Configuration parameters: CountComments, CountAsOne, AllowedMethods, AllowedPatterns.
206206
Metrics/MethodLength:
207-
Max: 63
207+
Max: 80
208208

209209
# Offense count: 1
210210
# Configuration parameters: CountComments, CountAsOne.
211211
Metrics/ModuleLength:
212-
Max: 244
212+
Max: 300
213213

214214
# Offense count: 2
215215
# Configuration parameters: Max, CountKeywordArgs.

README.md

Lines changed: 6 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -176,7 +176,7 @@ In the above there are a few assumptions, one being that `response.nameid` is an
176176
This is all handled with how you specify the settings that are in play via the `saml_settings` method.
177177
That could be implemented along the lines of this:
178178
179-
```
179+
```ruby
180180
response = RubySaml::Response.new(params[:SAMLResponse])
181181
response.settings = saml_settings
182182
```
@@ -759,6 +759,11 @@ Note the following:
759759
inactive/expired certificates. This avoids validation errors when the IdP reads the SP
760760
metadata.
761761
762+
#### Key Algorithm Support
763+
764+
Ruby SAML supports RSA, DSA, and ECDSA keys for both SP and IdP certificates.
765+
JRuby cannot support ECDSA due to a [known issue](https://github.com/jruby/jruby-openssl/issues/257).
766+
762767
#### Audience Validation
763768
764769
A service provider should only consider a SAML response valid if the IdP includes an <AudienceRestriction>

UPGRADING.md

Lines changed: 20 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -8,7 +8,15 @@
88

99
Before attempting to upgrade to `2.0.0`:
1010
- Upgrade your project to minimum Ruby 3.0, JRuby 9.4, or TruffleRuby 22.
11-
- Upgrade RubySaml to `1.17.x`. Note that RubySaml `1.17.x` is compatible with up to Ruby 3.3.
11+
- Upgrade RubySaml to `1.17.x`.
12+
- In RubySaml `1.17.x`, if you were using the SHA-1 default behavior, change your settings to use SHA-256 as per below:
13+
14+
```ruby
15+
# Set this in RubySaml 1.17.x, can be removed when upgrading to 2.0.0
16+
settings.idp_cert_fingerprint_algorithm = XMLSecurity::Document::SHA256
17+
settings.security[:signature_method] = XMLSecurity::Document::RSA_SHA256
18+
settings.security[:digest_method] = XMLSecurity::Document::SHA256
19+
```
1220

1321
### Root "OneLogin" namespace changed to "RubySaml"
1422

@@ -38,16 +46,17 @@ For security reasons, RubySaml version `2.0.0` uses SHA-256 as its default hashi
3846
instead of the now-obsolete SHA-1. This affects:
3947
- The default signature and digest algorithms used when generating SP metadata.
4048
- The default signature algorithm used when generating SP messages such as AuthnRequests.
41-
- The default fingerprint of IdP metadata (`:idp_cert_fingerprint` as generated by `RubySaml::IdpMetadataParser`)
49+
- The `:idp_cert_fingerprint` of IdP metadata as generated by `RubySaml::IdpMetadataParser`.
4250

43-
To preserve the old insecure SHA-1 behavior *(not recommended)*, you may set `RubySaml::Settings` as follows:
51+
If you see any signature or fingerprint mismatch errors after upgrading to RubySaml `2.0.0`,
52+
this change is likely the reason. To preserve the old insecure SHA-1 behavior *(not recommended)*,
53+
you may set `RubySaml::Settings` as follows:
4454

4555
```ruby
4656
# Preserve RubySaml 1.x insecure SHA-1 behavior
47-
settings = RubySaml::Settings.new
48-
settings.idp_cert_fingerprint_algorithm = RubySaml::XML::Document::SHA1
49-
settings.security[:digest_method] = RubySaml::XML::Document::SHA1
50-
settings.security[:signature_method] = RubySaml::XML::Document::RSA_SHA1
57+
settings.idp_cert_fingerprint_algorithm = RubySaml::XML::Crypto::SHA1
58+
settings.security[:digest_method] = RubySaml::XML::Crypto::SHA1
59+
settings.security[:signature_method] = RubySaml::XML::Crypto::RSA_SHA1
5160
```
5261

5362
### Removal of embed_sign setting
@@ -94,13 +103,13 @@ The following parameters in `RubySaml::Settings` are deprecated and will be remo
94103

95104
### Minor changes to Util#format_cert and #format_private_key
96105

97-
Version 2.0.0 standardizes how RubySaml reads and formats certificate and private key
98-
PEM strings. In general, version 2.0.0 is more permissive than 1.x, and the changes
106+
Version `2.0.0` standardizes how RubySaml reads and formats certificate and private key
107+
PEM strings. In general, version `2.0.0` is more permissive than `1.x`, and the changes
99108
are not anticipated to affect most users. Please note the change affects parameters
100109
such `#idp_cert` and `#certificate`, as well as the `RubySaml::Util#format_cert`
101110
and `#format_private_key` methods. Specifically:
102111

103-
| # | Input value | RubySaml 2.0.0 | RubySaml 1.x |
112+
| # | Input value | RubySaml 2.0.0 | RubySaml 1.17.x |
104113
|---|------------------------------------------------------|---------------------------------------------------------|---------------------------|
105114
| 1 | Input contains a bad (e.g. non-base64) PEM | Skip PEM formatting | Return a bad PEM |
106115
| 2 | Input contains `\r` character(s) | Strip out all `\r` character(s) and format as PEM | Skip PEM formatting |
@@ -113,7 +122,7 @@ and `#format_private_key` methods. Specifically:
113122
**Notes**
114123
- Case 3: For example, `-----BEGIN TRUSTED X509 CERTIFICATE-----` is now
115124
considered a valid header as an input, but it will be formatted to
116-
`-----BEGIN CERTIFICATE-----` in the output. As a special case, in both 2.0.0
125+
`-----BEGIN CERTIFICATE-----` in the output. As a special case, in both `2.0.0`
117126
and 1.x, if `RSA PRIVATE KEY` is present in the input string, the `RSA` prefix will
118127
be preserved in the output.
119128
- Case 5: When formatting multiple certificates in one string (i.e. a certificate chain),

lib/ruby_saml/authrequest.rb

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -77,14 +77,14 @@ def create_params(settings, params={})
7777
sp_signing_key = settings.get_sp_signing_key
7878

7979
if binding_redirect && settings.security[:authn_requests_signed] && sp_signing_key
80-
params['SigAlg'] = settings.security[:signature_method]
80+
params['SigAlg'] = settings.get_sp_signature_method
8181
url_string = RubySaml::Utils.build_query(
8282
type: 'SAMLRequest',
8383
data: base64_request,
8484
relay_state: relay_state,
8585
sig_alg: params['SigAlg']
8686
)
87-
sign_algorithm = RubySaml::XML::BaseDocument.new.algorithm(settings.security[:signature_method])
87+
sign_algorithm = RubySaml::XML::Crypto.hash_algorithm(settings.get_sp_signature_method)
8888
signature = sp_signing_key.sign(sign_algorithm.new, url_string)
8989
params['Signature'] = encode(signature)
9090
end
@@ -185,7 +185,7 @@ def create_xml_document(settings)
185185
def sign_document(document, settings)
186186
cert, private_key = settings.get_sp_signing_pair
187187
if settings.idp_sso_service_binding == Utils::BINDINGS[:post] && settings.security[:authn_requests_signed] && private_key && cert
188-
document.sign_document(private_key, cert, settings.security[:signature_method], settings.security[:digest_method])
188+
document.sign_document(private_key, cert, settings.get_sp_signature_method, settings.get_sp_digest_method)
189189
end
190190

191191
document

lib/ruby_saml/idp_metadata_parser.rb

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -398,7 +398,7 @@ def fingerprint(certificate, fingerprint_algorithm = RubySaml::XML::Document::SH
398398

399399
cert = OpenSSL::X509::Certificate.new(Base64.decode64(certificate))
400400

401-
fingerprint_alg = RubySaml::XML::BaseDocument.new.algorithm(fingerprint_algorithm).new
401+
fingerprint_alg = RubySaml::XML::Crypto.hash_algorithm(fingerprint_algorithm).new
402402
fingerprint_alg.hexdigest(cert.to_der).upcase.scan(/../).join(":")
403403
end
404404
end

lib/ruby_saml/logoutrequest.rb

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -75,14 +75,14 @@ def create_params(settings, params={})
7575
sp_signing_key = settings.get_sp_signing_key
7676

7777
if binding_redirect && settings.security[:logout_requests_signed] && sp_signing_key
78-
params['SigAlg'] = settings.security[:signature_method]
78+
params['SigAlg'] = settings.get_sp_signature_method
7979
url_string = RubySaml::Utils.build_query(
8080
type: 'SAMLRequest',
8181
data: base64_request,
8282
relay_state: relay_state,
8383
sig_alg: params['SigAlg']
8484
)
85-
sign_algorithm = RubySaml::XML::BaseDocument.new.algorithm(settings.security[:signature_method])
85+
sign_algorithm = RubySaml::XML::Crypto.hash_algorithm(settings.get_sp_signature_method)
8686
signature = settings.get_sp_signing_key.sign(sign_algorithm.new, url_string)
8787
params['Signature'] = encode(signature)
8888
end
@@ -144,7 +144,7 @@ def sign_document(document, settings)
144144
# embed signature
145145
cert, private_key = settings.get_sp_signing_pair
146146
if settings.idp_slo_service_binding == Utils::BINDINGS[:post] && settings.security[:logout_requests_signed] && private_key && cert
147-
document.sign_document(private_key, cert, settings.security[:signature_method], settings.security[:digest_method])
147+
document.sign_document(private_key, cert, settings.get_sp_signature_method, settings.get_sp_digest_method)
148148
end
149149

150150
document

lib/ruby_saml/metadata.rb

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -142,7 +142,7 @@ def embed_signature(meta_doc, settings)
142142
cert, private_key = settings.get_sp_signing_pair
143143
return unless private_key && cert
144144

145-
meta_doc.sign_document(private_key, cert, settings.security[:signature_method], settings.security[:digest_method])
145+
meta_doc.sign_document(private_key, cert, settings.get_sp_signature_method, settings.get_sp_digest_method)
146146
end
147147

148148
def output_xml(meta_doc, pretty_print)

lib/ruby_saml/pem_formatter.rb

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -106,7 +106,7 @@ def format_pem_body(pem)
106106

107107
# Returns a regexp which can be used to loosely match unformatted PEM(s) in a string.
108108
def pem_scan_regexp(label = nil)
109-
base64 = '[A-Za-z\d+/\s]*[A-Za-z\d+]+[A-Za-z\d+/\s]*=?\s*=?\s*'
109+
base64 = '[A-Za-z\d+/\s]*[A-Za-z\d+][A-Za-z\d+/\s]*=?\s*=?\s*'
110110
/#{pem_scan_header('BEGIN', label)}#{base64}#{pem_scan_header('END', label)}/m
111111
end
112112

lib/ruby_saml/response.rb

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -861,8 +861,8 @@ def validate_signature
861861

862862
if fingerprint && doc.validate_document(fingerprint, @soft, opts)
863863
if settings.security[:check_idp_cert_expiration] && RubySaml::Utils.is_cert_expired(idp_cert)
864-
error_msg = "IdP x509 certificate expired"
865-
return append_error(error_msg)
864+
error_msg = "IdP x509 certificate expired"
865+
return append_error(error_msg)
866866
end
867867
else
868868
return append_error(error_msg)

lib/ruby_saml/saml_message.rb

Lines changed: 1 addition & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -133,11 +133,7 @@ def decode(string)
133133
# @return [String] The encoded string
134134
#
135135
def encode(string)
136-
if Base64.respond_to?(:strict_encode64)
137-
Base64.strict_encode64(string)
138-
else
139-
Base64.encode64(string).gsub(/\n/, "")
140-
end
136+
Base64.strict_encode64(string)
141137
end
142138

143139
# Check if a string is base64 encoded

0 commit comments

Comments
 (0)