Skip to content

Commit 1d79874

Browse files
committed
Set SHA-256 as default alg in settings. Support Alg Deprecated rejection. Notify with Logger.info if sha-1 alg used on Signature
1 parent e756d62 commit 1d79874

File tree

13 files changed

+333
-11
lines changed

13 files changed

+333
-11
lines changed

README.md

Lines changed: 14 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -282,7 +282,7 @@ onelogin.saml2.idp.x509cert =
282282
# let the toolkit know which Algorithm was used. Possible values: sha1, sha256, sha384 or sha512
283283
# 'sha1' is the default value.
284284
# onelogin.saml2.idp.certfingerprint =
285-
# onelogin.saml2.idp.certfingerprint_algorithm = sha1
285+
# onelogin.saml2.idp.certfingerprint_algorithm = sha256
286286

287287
# Security settings
288288
#
@@ -342,7 +342,18 @@ onelogin.saml2.security.want_xml_validation = true
342342
# 'http://www.w3.org/2001/04/xmldsig-more#rsa-sha256'
343343
# 'http://www.w3.org/2001/04/xmldsig-more#rsa-sha384'
344344
# 'http://www.w3.org/2001/04/xmldsig-more#rsa-sha512'
345-
onelogin.saml2.security.signature_algorithm = http://www.w3.org/2000/09/xmldsig#rsa-sha1
345+
onelogin.saml2.security.signature_algorithm = http://www.w3.org/2001/04/xmldsig-more#rsa-sha256
346+
347+
# Algorithm that the toolkit will use on digest process. Options:
348+
# 'http://www.w3.org/2000/09/xmldsig#sha1'
349+
# 'http://www.w3.org/2001/04/xmlenc#sha256'
350+
# 'http://www.w3.org/2001/04/xmldsig-more#sha384'
351+
# 'http://www.w3.org/2001/04/xmlenc#sha512'
352+
onelogin.saml2.security.digest_algorithm = http://www.w3.org/2001/04/xmlenc#sha256
353+
354+
355+
# Reject Signatures with deprecated algorithms (sha1)
356+
onelogin.saml2.security.reject_deprecated_alg = true
346357

347358
# Organization
348359
onelogin.saml2.organization.name = SP Java
@@ -362,6 +373,7 @@ onelogin.saml2.contacts.support.email_address = support@example.com
362373
# onelogin.saml2.unique_id_prefix = _
363374
```
364375

376+
365377
##### KeyStores
366378

367379
The Auth constructor supports the ability to read SP public cert/private key from a KeyStore. A KeyStoreSettings object must be provided with the KeyStore, the Alias and the KeyEntry password.

core/src/main/java/com/onelogin/saml2/authn/SamlResponse.java

Lines changed: 4 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -323,12 +323,14 @@ public boolean isValid(String requestId) {
323323
String fingerprint = settings.getIdpCertFingerprint();
324324
String alg = settings.getIdpCertFingerprintAlgorithm();
325325

326-
if (hasSignedResponse && !Util.validateSign(samlResponseDocument, certList, fingerprint, alg, Util.RESPONSE_SIGNATURE_XPATH)) {
326+
Boolean rejectDeprecatedAlg = settings.getRejectDeprecatedAlg();
327+
328+
if (hasSignedResponse && !Util.validateSign(samlResponseDocument, certList, fingerprint, alg, Util.RESPONSE_SIGNATURE_XPATH, rejectDeprecatedAlg)) {
327329
throw new ValidationError("Signature validation failed. SAML Response rejected", ValidationError.INVALID_SIGNATURE);
328330
}
329331

330332
final Document documentToCheckAssertion = encrypted ? decryptedDocument : samlResponseDocument;
331-
if (hasSignedAssertion && !Util.validateSign(documentToCheckAssertion, certList, fingerprint, alg, Util.ASSERTION_SIGNATURE_XPATH)) {
333+
if (hasSignedAssertion && !Util.validateSign(documentToCheckAssertion, certList, fingerprint, alg, Util.ASSERTION_SIGNATURE_XPATH, rejectDeprecatedAlg)) {
332334
throw new ValidationError("Signature validation failed. SAML Response rejected", ValidationError.INVALID_SIGNATURE);
333335
}
334336
}

core/src/main/java/com/onelogin/saml2/logout/LogoutRequest.java

Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -456,6 +456,17 @@ public Boolean isValid() throws Exception {
456456
if (signAlg == null || signAlg.isEmpty()) {
457457
signAlg = Constants.RSA_SHA1;
458458
}
459+
460+
if (signAlg.equals(Constants.RSA_SHA1)) {
461+
Boolean rejectDeprecatedAlg = settings.getRejectDeprecatedAlg();
462+
if (rejectDeprecatedAlg) {
463+
LOGGER.error("A deprecated algorithm (RSA_SHA1) found in the Signature element, rejecting it");
464+
return false;
465+
} else {
466+
LOGGER.info("RSA_SHA1 alg found in a Signature element, consider request a more robust alg");
467+
}
468+
}
469+
459470
String relayState = request.getEncodedParameter("RelayState");
460471

461472
String signedQuery = "SAMLRequest=" + request.getEncodedParameter("SAMLRequest");

core/src/main/java/com/onelogin/saml2/logout/LogoutResponse.java

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -258,6 +258,16 @@ public Boolean isValid(String requestId) {
258258
signAlg = Constants.RSA_SHA1;
259259
}
260260

261+
if (signAlg.equals(Constants.RSA_SHA1)) {
262+
Boolean rejectDeprecatedAlg = settings.getRejectDeprecatedAlg();
263+
if (rejectDeprecatedAlg) {
264+
LOGGER.error("A deprecated algorithm (RSA_SHA1) found in the Signature element, rejecting it");
265+
return false;
266+
} else {
267+
LOGGER.info("RSA_SHA1 alg found in a Signature element, consider request a more robust alg");
268+
}
269+
}
270+
261271
String signedQuery = "SAMLResponse=" + request.getEncodedParameter("SAMLResponse");
262272

263273
String relayState = request.getEncodedParameter("RelayState");

core/src/main/java/com/onelogin/saml2/settings/Saml2Settings.java

Lines changed: 19 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -76,6 +76,7 @@ public class Saml2Settings {
7676
private String digestAlgorithm = Constants.SHA1;
7777
private boolean rejectUnsolicitedResponsesWithInResponseTo = false;
7878
private boolean allowRepeatAttributeName = false;
79+
private boolean rejectDeprecatedAlg = false;
7980
private String uniqueIDPrefix = null;
8081

8182
// Compress
@@ -140,10 +141,17 @@ public final String getSpNameIDFormat() {
140141
/**
141142
* @return the allowRepeatAttributeName setting value
142143
*/
143-
public boolean isAllowRepeatAttributeName () {
144+
public boolean isAllowRepeatAttributeName() {
144145
return allowRepeatAttributeName;
145146
}
146147

148+
/**
149+
* @return the rejectDeprecatedAlg setting value
150+
*/
151+
public boolean getRejectDeprecatedAlg() {
152+
return rejectDeprecatedAlg;
153+
}
154+
147155
/**
148156
* @return the spX509cert setting value
149157
*/
@@ -478,6 +486,16 @@ public void setAllowRepeatAttributeName (boolean allowRepeatAttributeName) {
478486
this.allowRepeatAttributeName = allowRepeatAttributeName;
479487
}
480488

489+
/**
490+
* Set the rejectDeprecatedAlg setting value
491+
*
492+
* @param rejectDeprecatedAlg
493+
* the rejectDeprecatedAlg value to be set
494+
*/
495+
public void setRejectDeprecatedAlg (boolean rejectDeprecatedAlg) {
496+
this.rejectDeprecatedAlg = rejectDeprecatedAlg;
497+
}
498+
481499
/**
482500
* Set the spX509cert setting value provided as X509Certificate object
483501
*

core/src/main/java/com/onelogin/saml2/settings/SettingsBuilder.java

Lines changed: 8 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -101,6 +101,7 @@ public class SettingsBuilder {
101101
public final static String SECURITY_DIGEST_ALGORITHM = "onelogin.saml2.security.digest_algorithm";
102102
public final static String SECURITY_REJECT_UNSOLICITED_RESPONSES_WITH_INRESPONSETO = "onelogin.saml2.security.reject_unsolicited_responses_with_inresponseto";
103103
public final static String SECURITY_ALLOW_REPEAT_ATTRIBUTE_NAME_PROPERTY_KEY = "onelogin.saml2.security.allow_duplicated_attribute_name";
104+
public final static String SECURITY_REJECT_DEPRECATED_ALGORITHM = "onelogin.saml2.security.reject_deprecated_alg";
104105

105106
// Compress
106107
public final static String COMPRESS_REQUEST = "onelogin.saml2.compress.request";
@@ -376,8 +377,14 @@ private void loadSecuritySetting() {
376377
}
377378

378379
Boolean allowRepeatAttributeName = loadBooleanProperty(SECURITY_ALLOW_REPEAT_ATTRIBUTE_NAME_PROPERTY_KEY);
379-
if (allowRepeatAttributeName != null)
380+
if (allowRepeatAttributeName != null) {
380381
saml2Setting.setAllowRepeatAttributeName(allowRepeatAttributeName);
382+
}
383+
384+
Boolean rejectDeprecatedAlg = loadBooleanProperty(SECURITY_REJECT_DEPRECATED_ALGORITHM);
385+
if (rejectDeprecatedAlg != null) {
386+
saml2Setting.setRejectDeprecatedAlg(rejectDeprecatedAlg);
387+
}
381388
}
382389

383390
/**

core/src/main/java/com/onelogin/saml2/util/Util.java

Lines changed: 91 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -923,13 +923,30 @@ public static boolean validateSign(final Document doc, final X509Certificate cer
923923
*/
924924
public static boolean validateSign(final Document doc, final List<X509Certificate> certList, final String fingerprint,
925925
final String alg, final String xpath) {
926+
return validateSign(doc, certList, fingerprint,alg, xpath, false);
927+
}
928+
929+
/**
930+
* Validate the signature pointed to by the xpath
931+
*
932+
* @param doc The document we should validate
933+
* @param certList The public certificates
934+
* @param fingerprint The fingerprint of the public certificate
935+
* @param alg The signature algorithm method
936+
* @param xpath the xpath of the ds:Signture node to validate
937+
* @param rejectDeprecatedAlg Flag to invalidate or not Signatures with deprecated alg
938+
*
939+
* @return True if the signature exists and is valid, false otherwise.
940+
*/
941+
public static boolean validateSign(final Document doc, final List<X509Certificate> certList, final String fingerprint,
942+
final String alg, final String xpath, final Boolean rejectDeprecatedAlg) {
926943
try {
927944
final NodeList signatures = query(doc, xpath);
928945

929946
if (signatures.getLength() == 1) {
930947
final Node signNode = signatures.item(0);
931948

932-
Map<String,Object> signatureData = getSignatureData(signNode, alg);
949+
Map<String,Object> signatureData = getSignatureData(signNode, alg, rejectDeprecatedAlg);
933950
if (signatureData.isEmpty()) {
934951
return false;
935952
}
@@ -984,6 +1001,26 @@ public static boolean validateSign(final Document doc, final List<X509Certificat
9841001
* @return True if the sign is valid, false otherwise.
9851002
*/
9861003
public static Boolean validateMetadataSign(Document doc, X509Certificate cert, String fingerprint, String alg) {
1004+
return validateMetadataSign(doc, cert, fingerprint, alg, false);
1005+
}
1006+
1007+
/**
1008+
* Validate signature (Metadata).
1009+
*
1010+
* @param doc
1011+
* The document we should validate
1012+
* @param cert
1013+
* The public certificate
1014+
* @param fingerprint
1015+
* The fingerprint of the public certificate
1016+
* @param alg
1017+
* The signature algorithm method
1018+
* @param rejectDeprecatedAlg
1019+
* Flag to invalidate or not Signatures with deprecated alg
1020+
*
1021+
* @return True if the sign is valid, false otherwise.
1022+
*/
1023+
public static Boolean validateMetadataSign(Document doc, X509Certificate cert, String fingerprint, String alg, Boolean rejectDeprecatedAlg) {
9871024
NodeList signNodesToValidate;
9881025
try {
9891026
signNodesToValidate = query(doc, "/md:EntitiesDescriptor/ds:Signature");
@@ -999,7 +1036,7 @@ public static Boolean validateMetadataSign(Document doc, X509Certificate cert, S
9991036
if (signNodesToValidate.getLength() > 0) {
10001037
for (int i = 0; i < signNodesToValidate.getLength(); i++) {
10011038
Node signNode = signNodesToValidate.item(i);
1002-
if (!validateSignNode(signNode, cert, fingerprint, alg)) {
1039+
if (!validateSignNode(signNode, cert, fingerprint, alg, rejectDeprecatedAlg)) {
10031040
return false;
10041041
}
10051042
}
@@ -1026,6 +1063,26 @@ public static Boolean validateMetadataSign(Document doc, X509Certificate cert, S
10261063
* @return True if the sign is valid, false otherwise.
10271064
*/
10281065
private static Map<String,Object> getSignatureData(Node signNode, String alg) {
1066+
return getSignatureData(signNode, alg, false);
1067+
}
1068+
1069+
/**
1070+
* Validate signature (Metadata).
1071+
*
1072+
* @param doc
1073+
* The document we should validate
1074+
* @param cert
1075+
* The public certificate
1076+
* @param fingerprint
1077+
* The fingerprint of the public certificate
1078+
* @param alg
1079+
* The signature algorithm method
1080+
* @param rejectDeprecatedAlg
1081+
* Flag to invalidate or not Signatures with deprecated alg
1082+
*
1083+
* @return True if the sign is valid, false otherwise.
1084+
*/
1085+
private static Map<String,Object> getSignatureData(Node signNode, String alg, Boolean rejectDeprecatedAlg) {
10291086
Map<String,Object> signatureData = new HashMap<>();
10301087
try {
10311088
Element sigElement = (Element) signNode;
@@ -1036,6 +1093,15 @@ private static Map<String,Object> getSignatureData(Node signNode, String alg) {
10361093
throw new Exception(sigMethodAlg + " is not a valid supported algorithm");
10371094
}
10381095

1096+
if (sigMethodAlg.equals(Constants.RSA_SHA1)) {
1097+
if (rejectDeprecatedAlg) {
1098+
LOGGER.error("A deprecated algorithm (RSA_SHA1) found in the Signature element, rejecting it");
1099+
return signatureData;
1100+
} else {
1101+
LOGGER.info("RSA_SHA1 alg found in a Signature element, consider request a more robust alg");
1102+
}
1103+
}
1104+
10391105
signatureData.put("signature", signature);
10401106

10411107
String extractedFingerprint = null;
@@ -1073,7 +1139,29 @@ private static Map<String,Object> getSignatureData(Node signNode, String alg) {
10731139
* @throws Exception
10741140
*/
10751141
public static Boolean validateSignNode(Node signNode, X509Certificate cert, String fingerprint, String alg) {
1076-
Map<String,Object> signatureData = getSignatureData(signNode, alg);
1142+
return validateSignNode(signNode, cert, fingerprint, alg, false);
1143+
}
1144+
1145+
/**
1146+
* Validate signature of the Node.
1147+
*
1148+
* @param signNode
1149+
* The document we should validate
1150+
* @param cert
1151+
* The public certificate
1152+
* @param fingerprint
1153+
* The fingerprint of the public certificate
1154+
* @param alg
1155+
* The signature algorithm method
1156+
* @param rejectDeprecatedAlg
1157+
* Flag to invalidate or not Signatures with deprecated alg
1158+
*
1159+
* @return True if the sign is valid, false otherwise.
1160+
*
1161+
* @throws Exception
1162+
*/
1163+
public static Boolean validateSignNode(Node signNode, X509Certificate cert, String fingerprint, String alg, Boolean rejectDeprecatedAlg) {
1164+
Map<String,Object> signatureData = getSignatureData(signNode, alg, rejectDeprecatedAlg);
10771165
if (signatureData.isEmpty()) {
10781166
return false;
10791167
}

core/src/test/java/com/onelogin/saml2/test/authn/AuthnResponseTest.java

Lines changed: 49 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -5,6 +5,7 @@
55
import com.onelogin.saml2.exception.SettingsException;
66
import com.onelogin.saml2.exception.ValidationError;
77
import com.onelogin.saml2.http.HttpRequest;
8+
import com.onelogin.saml2.logout.LogoutRequest;
89
import com.onelogin.saml2.model.SamlResponseStatus;
910
import com.onelogin.saml2.settings.Saml2Settings;
1011
import com.onelogin.saml2.settings.SettingsBuilder;
@@ -2409,6 +2410,54 @@ public void testIsValid_signedEncryptedAssertion() throws Exception {
24092410
assertResponseValid(settings, samlResponseEncoded, true, false, "The Message of the Response is not signed and the SP requires it");
24102411
}
24112412

2413+
/**
2414+
* Tests the isValid method of SamlResponse
2415+
* Case: Signed with deprecated method and flag enabled
2416+
*
2417+
* @throws ValidationError
2418+
* @throws SettingsException
2419+
* @throws IOException
2420+
* @throws SAXException
2421+
* @throws ParserConfigurationException
2422+
* @throws XPathExpressionException
2423+
* @throws Error
2424+
*
2425+
* @see com.onelogin.saml2.authn.SamlResponse#isValid
2426+
*/
2427+
@Test
2428+
public void testIsInValidSignWithDeprecatedAlg() throws IOException, Error, XPathExpressionException, ParserConfigurationException, SAXException, SettingsException, ValidationError {
2429+
Saml2Settings settings = new SettingsBuilder().fromFile("config/config.my.properties").build();
2430+
settings.setWantAssertionsSigned(false);
2431+
settings.setWantMessagesSigned(false);
2432+
String samlResponseEncoded = Util.getFileAsString("data/responses/signed_message_response.xml.base64");
2433+
2434+
settings.setStrict(true);
2435+
SamlResponse samlResponse = new SamlResponse(settings, newHttpRequest(samlResponseEncoded));
2436+
assertTrue(samlResponse.isValid());
2437+
2438+
settings.setRejectDeprecatedAlg(true);
2439+
SamlResponse samlResponse2 = new SamlResponse(settings, newHttpRequest(samlResponseEncoded));
2440+
assertFalse(samlResponse2.isValid());
2441+
2442+
settings.setRejectDeprecatedAlg(false);
2443+
samlResponseEncoded = Util.getFileAsString("data/responses/signed_assertion_response.xml.base64");
2444+
SamlResponse samlResponse3 = new SamlResponse(settings, newHttpRequest(samlResponseEncoded));
2445+
assertTrue(samlResponse3.isValid());
2446+
2447+
settings.setRejectDeprecatedAlg(true);
2448+
SamlResponse samlResponse4 = new SamlResponse(settings, newHttpRequest(samlResponseEncoded));
2449+
assertFalse(samlResponse4.isValid());
2450+
2451+
settings.setRejectDeprecatedAlg(false);
2452+
samlResponseEncoded = Util.getFileAsString("data/responses/double_signed_response.xml.base64");
2453+
SamlResponse samlResponse5 = new SamlResponse(settings, newHttpRequest(samlResponseEncoded));
2454+
assertTrue(samlResponse5.isValid());
2455+
2456+
settings.setRejectDeprecatedAlg(true);
2457+
SamlResponse samlResponse6 = new SamlResponse(settings, newHttpRequest(samlResponseEncoded));
2458+
assertFalse(samlResponse6.isValid());
2459+
}
2460+
24122461
/**
24132462
* Tests the isValid method of SamlResponse
24142463
* Case: valid sign response / sign assertion / both signed

0 commit comments

Comments
 (0)