Skip to content

Commit faf55e5

Browse files
committed
Complete security fixes, added validate certificate
1 parent 1c63148 commit faf55e5

7 files changed

Lines changed: 196 additions & 141 deletions

File tree

src/main/java/com/onelogin/AccountSettings.java

Lines changed: 39 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,16 @@
11
package com.onelogin;
22

3+
import java.io.ByteArrayInputStream;
4+
import java.security.cert.Certificate;
5+
import java.security.cert.CertificateException;
6+
import java.security.cert.CertificateFactory;
7+
8+
import org.apache.commons.codec.binary.Base64;
9+
10+
311
public class AccountSettings {
412
private String certificate;
13+
private Certificate cert;
514
private String idp_sso_target_url;
615

716
public String getCertificate() {
@@ -16,4 +25,34 @@ public String getIdp_sso_target_url() {
1625
public void setIdpSsoTargetUrl(String idp_sso_target_url) {
1726
this.idp_sso_target_url = idp_sso_target_url;
1827
}
28+
29+
/**
30+
* Loads certificate from a base64 encoded string
31+
* @param certificate an base64 encoded string.
32+
*/
33+
public void loadCertificate() throws CertificateException {
34+
CertificateFactory fty = CertificateFactory.getInstance("X.509");
35+
ByteArrayInputStream bais = new ByteArrayInputStream(Base64.decodeBase64(certificate.getBytes()));
36+
cert = fty.generateCertificate(bais);
37+
}
38+
39+
40+
public Certificate getCert() throws CertificateException {
41+
if(cert == null){
42+
loadCertificate();
43+
}
44+
return cert;
45+
}
46+
47+
/**
48+
* load and get a certificate from a encoded base64 byte array.
49+
* @param certificate an encoded base64 byte array.
50+
* @throws CertificateException In case it can't load the certificate.
51+
*/
52+
public Certificate getCert(byte[] certificate) throws CertificateException {
53+
CertificateFactory fty = CertificateFactory.getInstance("X.509");
54+
ByteArrayInputStream bais = new ByteArrayInputStream(Base64.decodeBase64(certificate));
55+
cert = fty.generateCertificate(bais);
56+
return cert;
57+
}
1958
}

src/main/java/com/onelogin/saml/Certificate.java

Lines changed: 0 additions & 37 deletions
This file was deleted.

src/main/java/com/onelogin/saml/Response.java

Lines changed: 17 additions & 50 deletions
Original file line numberDiff line numberDiff line change
@@ -1,24 +1,17 @@
11
package com.onelogin.saml;
22

3-
import java.lang.reflect.Method;
3+
import java.security.cert.Certificate;
44
import java.security.cert.CertificateException;
5-
import java.security.cert.X509Certificate;
65
import java.util.ArrayList;
76
import java.util.Calendar;
87
import java.util.HashMap;
9-
import java.util.Iterator;
108
import java.util.LinkedHashSet;
119
import java.util.Map;
1210
import java.util.Set;
1311
import java.util.TimeZone;
1412

1513
import javax.xml.crypto.dsig.XMLSignature;
16-
import javax.xml.crypto.dsig.XMLSignatureFactory;
17-
import javax.xml.crypto.dsig.dom.DOMValidateContext;
18-
import javax.xml.namespace.NamespaceContext;
19-
import javax.xml.xpath.XPath;
2014
import javax.xml.xpath.XPathExpressionException;
21-
import javax.xml.xpath.XPathFactory;
2215

2316
import org.apache.commons.codec.binary.Base64;
2417
import org.slf4j.Logger;
@@ -40,10 +33,8 @@ public class Response {
4033
*/
4134
private Document document;
4235

43-
private NodeList assertions;
4436
private Element rootElement;
4537
private final AccountSettings accountSettings;
46-
private final Certificate certificate;
4738
private String response;
4839
private String currentUrl;
4940
private StringBuffer error;
@@ -55,8 +46,6 @@ public class Response {
5546
public Response(AccountSettings accountSettings) throws CertificateException {
5647
error = new StringBuffer();
5748
this.accountSettings = accountSettings;
58-
certificate = new Certificate();
59-
certificate.loadCertificate(this.accountSettings.getCertificate());
6049
}
6150

6251
public Response(AccountSettings accountSettings, String response) throws Exception {
@@ -98,10 +87,10 @@ public boolean isValid(String... requestId){
9887
throw new Exception("SAML Response must contain 1 Assertion.");
9988
}
10089

101-
NodeList nodes = document.getElementsByTagName("Signature");
90+
NodeList signNodes = document.getElementsByTagNameNS(XMLSignature.XMLNS, "Signature");
10291
ArrayList<String> signedElements = new ArrayList<String>();
103-
for (int i = 0; i < nodes.getLength(); i++) {
104-
signedElements.add(nodes.item(i).getParentNode().getLocalName());
92+
for (int i = 0; i < signNodes.getLength(); i++) {
93+
signedElements.add(signNodes.item(i).getParentNode().getLocalName());
10594
}
10695
if (!signedElements.isEmpty()) {
10796
if(!this.validateSignedElements(signedElements)){
@@ -208,24 +197,17 @@ public boolean isValid(String... requestId){
208197
throw new Exception("A valid SubjectConfirmation was not found on this Response");
209198
}
210199

211-
212-
// ------------ working validations until here!
213-
//TODO: more validations
214-
215-
216-
217-
218-
219-
// if (setIdAttributeExists()) {
220-
// tagIdAttributes(xmlDoc);
221-
// }
222-
223-
X509Certificate cert = certificate.getX509Cert();
224-
DOMValidateContext ctx = new DOMValidateContext(cert.getPublicKey(), nodes.item(0));
225-
XMLSignatureFactory sigF = XMLSignatureFactory.getInstance("DOM");
226-
XMLSignature xmlSignature = sigF.unmarshalXMLSignature(ctx);
200+
if(signedElements.isEmpty()){
201+
throw new Exception("No Signature found. SAML Response rejected");
202+
}else{
203+
Certificate cert = this.accountSettings.getCert();
227204

228-
return xmlSignature.validate(ctx);
205+
// Only validates the first signed element
206+
if (!Utils.validateSign(signNodes.item(0), cert)) {
207+
throw new Exception("Signature validation failed. SAML Response rejected");
208+
}
209+
}
210+
return true;
229211
}catch (Error e) {
230212
error.append(e.getMessage());
231213
return false;
@@ -245,15 +227,15 @@ public String getNameId() throws Exception {
245227
}
246228

247229
public String getAttribute(String name) {
248-
HashMap attributes = getAttributes();
230+
HashMap<String, ArrayList<String>> attributes = getAttributes();
249231
if (!attributes.isEmpty()) {
250232
return attributes.get(name).toString();
251233
}
252234
return null;
253235
}
254236

255-
public HashMap getAttributes() {
256-
HashMap<String, ArrayList> attributes = new HashMap<String, ArrayList>();
237+
public HashMap<String, ArrayList<String>> getAttributes() {
238+
HashMap<String, ArrayList<String>> attributes = new HashMap<String, ArrayList<String>>();
257239
NodeList nodes = document.getElementsByTagNameNS("urn:oasis:names:tc:SAML:2.0:assertion", "Attribute");
258240

259241
if (nodes.getLength() != 0) {
@@ -435,21 +417,6 @@ private boolean validateTimestamps()
435417
return true;
436418
}
437419

438-
439-
440-
private boolean setIdAttributeExists() {
441-
for (Method method : Element.class.getDeclaredMethods()) {
442-
if (method.getName().equals("setIdAttribute")) {
443-
return true;
444-
}
445-
}
446-
return false;
447-
}
448-
449-
private void tagIdAttributes(Document xmlDoc) {
450-
throw new UnsupportedOperationException("Not supported yet.");
451-
}
452-
453420
public void setDestinationUrl(String urld){
454421
currentUrl = urld;
455422
}

0 commit comments

Comments
 (0)