Skip to content

Commit 460bb6f

Browse files
author
Christian Pedersen
committed
first version
1 parent 13f8790 commit 460bb6f

7 files changed

Lines changed: 321 additions & 0 deletions

File tree

com/onelogin/AccountSettings.java

Lines changed: 19 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,19 @@
1+
package com.onelogin;
2+
3+
public class AccountSettings {
4+
private String certificate;
5+
private String idp_sso_target_url;
6+
7+
public String getCertificate() {
8+
return certificate;
9+
}
10+
public void setCertificate(String certificate) {
11+
this.certificate = certificate;
12+
}
13+
public String getIdp_sso_target_url() {
14+
return idp_sso_target_url;
15+
}
16+
public void setIdp_sso_target_url(String idp_sso_target_url) {
17+
this.idp_sso_target_url = idp_sso_target_url;
18+
}
19+
}

com/onelogin/AppSettings.java

Lines changed: 20 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,20 @@
1+
package com.onelogin;
2+
3+
public class AppSettings {
4+
private String assertionConsumerServiceUrl;
5+
private String issuer;
6+
7+
public String getAssertionConsumerServiceUrl() {
8+
return assertionConsumerServiceUrl;
9+
}
10+
public void setAssertionConsumerServiceUrl(String assertionConsumerServiceUrl) {
11+
this.assertionConsumerServiceUrl = assertionConsumerServiceUrl;
12+
}
13+
public String getIssuer() {
14+
return issuer;
15+
}
16+
public void setIssuer(String issuer) {
17+
this.issuer = issuer;
18+
}
19+
20+
}

com/onelogin/saml/AuthRequest.java

Lines changed: 103 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,103 @@
1+
package com.onelogin.saml;
2+
3+
import java.io.ByteArrayOutputStream;
4+
import java.nio.charset.Charset;
5+
import java.text.SimpleDateFormat;
6+
import java.util.Date;
7+
import java.util.UUID;
8+
9+
import javax.xml.stream.XMLOutputFactory;
10+
import javax.xml.stream.XMLStreamException;
11+
import javax.xml.stream.XMLStreamWriter;
12+
13+
import org.apache.commons.codec.binary.Base64;
14+
15+
import com.onelogin.AccountSettings;
16+
import com.onelogin.AppSettings;
17+
18+
public class AuthRequest {
19+
20+
private String id;
21+
private String issueInstant;
22+
private AppSettings appSettings;
23+
public static final int base64 = 1;
24+
25+
public AuthRequest(AppSettings appSettings, AccountSettings accountSettings){
26+
this.appSettings = appSettings;
27+
id="_"+UUID.randomUUID().toString();
28+
SimpleDateFormat simpleDf = new SimpleDateFormat("yyyy-MM-dd'T'H:mm:ss");
29+
issueInstant = simpleDf.format(new Date());
30+
}
31+
32+
public String getRequest(int format) throws XMLStreamException {
33+
ByteArrayOutputStream baos = new ByteArrayOutputStream();
34+
XMLOutputFactory factory = XMLOutputFactory.newInstance();
35+
XMLStreamWriter writer = factory.createXMLStreamWriter(baos);
36+
37+
writer.writeStartElement("samlp", "AuthnRequest", "urn:oasis:names:tc:SAML:2.0:protocol");
38+
writer.writeNamespace("samlp","urn:oasis:names:tc:SAML:2.0:protocol");
39+
40+
writer.writeAttribute("ID", id);
41+
writer.writeAttribute("Version", "2.0");
42+
writer.writeAttribute("IssueInstant", this.issueInstant);
43+
writer.writeAttribute("ProtocolBinding", "urn:oasis:names:tc:SAML:2.0:bindings:HTTP-POST");
44+
writer.writeAttribute("AssertionConsumerServiceURL", this.appSettings.getAssertionConsumerServiceUrl());
45+
46+
writer.writeStartElement("saml","Issuer","urn:oasis:names:tc:SAML:2.0:assertion");
47+
writer.writeNamespace("saml","urn:oasis:names:tc:SAML:2.0:assertion");
48+
writer.writeCharacters(this.appSettings.getIssuer());
49+
writer.writeEndElement();
50+
51+
writer.writeStartElement("samlp", "NameIDPolicy", "urn:oasis:names:tc:SAML:2.0:protocol");
52+
53+
writer.writeAttribute("Format", "urn:oasis:names:tc:SAML:2.0:nameid-format:unspecified");
54+
writer.writeAttribute("AllowCreate", "true");
55+
writer.writeEndElement();
56+
57+
writer.writeStartElement("samlp","RequestedAuthnContext","urn:oasis:names:tc:SAML:2.0:protocol");
58+
59+
writer.writeAttribute("Comparison", "exact");
60+
writer.writeEndElement();
61+
62+
writer.writeStartElement("saml","AuthnContextClassRef","urn:oasis:names:tc:SAML:2.0:assertion");
63+
writer.writeNamespace("saml", "urn:oasis:names:tc:SAML:2.0:assertion");
64+
writer.writeCharacters("urn:oasis:names:tc:SAML:2.0:ac:classes:PasswordProtectedTransport");
65+
writer.writeEndElement();
66+
67+
writer.writeEndElement();
68+
writer.flush();
69+
70+
if (format == base64) {
71+
byte [] encoded = Base64.encodeBase64Chunked(baos.toByteArray());
72+
String result = new String(encoded,Charset.forName("UTF-8"));
73+
74+
return result;
75+
}
76+
77+
return null;
78+
}
79+
80+
public static String getRidOfCRLF(String what) {
81+
String lf = "%0D";
82+
String cr = "%0A";
83+
String now = lf;
84+
85+
int index = what.indexOf(now);
86+
StringBuffer r = new StringBuffer();
87+
88+
while (index!=-1) {
89+
r.append(what.substring(0,index));
90+
what = what.substring(index+3,what.length());
91+
92+
if (now.equals(lf)) {
93+
now = cr;
94+
} else {
95+
now = lf;
96+
}
97+
98+
index = what.indexOf(now);
99+
}
100+
return r.toString();
101+
}
102+
103+
}

com/onelogin/saml/Certificate.java

Lines changed: 37 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,37 @@
1+
package com.onelogin.saml;
2+
3+
import java.io.ByteArrayInputStream;
4+
import java.security.cert.CertificateException;
5+
import java.security.cert.CertificateFactory;
6+
import java.security.cert.X509Certificate;
7+
8+
import org.apache.commons.codec.binary.Base64;
9+
10+
public class Certificate {
11+
12+
private X509Certificate x509Cert;
13+
14+
/**
15+
* Loads certificate from a base64 encoded string
16+
*/
17+
public void loadCertificate(String certificate) throws CertificateException {
18+
CertificateFactory fty = CertificateFactory.getInstance("X.509");
19+
ByteArrayInputStream bais = new ByteArrayInputStream(Base64.decodeBase64(certificate.getBytes()));
20+
x509Cert = (X509Certificate)fty.generateCertificate(bais);
21+
}
22+
23+
/**
24+
* Loads a certificate from a encoded base64 byte array.
25+
* @param certificate an encoded base64 byte array.
26+
* @throws CertificateException In case it can't load the certificate.
27+
*/
28+
public void loadCertificate(byte[] certificate) throws CertificateException {
29+
CertificateFactory fty = CertificateFactory.getInstance("X.509");
30+
ByteArrayInputStream bais = new ByteArrayInputStream(Base64.decodeBase64(certificate));
31+
x509Cert = (X509Certificate)fty.generateCertificate(bais);
32+
}
33+
34+
public X509Certificate getX509Cert() {
35+
return x509Cert;
36+
}
37+
}

com/onelogin/saml/Response.java

Lines changed: 74 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,74 @@
1+
package com.onelogin.saml;
2+
3+
import java.io.ByteArrayInputStream;
4+
import java.io.IOException;
5+
import java.security.cert.CertificateException;
6+
import java.security.cert.X509Certificate;
7+
8+
import javax.xml.crypto.dsig.XMLSignature;
9+
import javax.xml.crypto.dsig.XMLSignatureFactory;
10+
import javax.xml.crypto.dsig.dom.DOMValidateContext;
11+
import javax.xml.parsers.DocumentBuilder;
12+
import javax.xml.parsers.DocumentBuilderFactory;
13+
import javax.xml.parsers.ParserConfigurationException;
14+
15+
import org.apache.commons.codec.binary.Base64;
16+
import org.w3c.dom.Document;
17+
import org.w3c.dom.NodeList;
18+
import org.xml.sax.SAXException;
19+
20+
import com.onelogin.AccountSettings;
21+
22+
public class Response {
23+
24+
private Document xmlDoc;
25+
private AccountSettings accountSettings;
26+
private Certificate certificate;
27+
28+
public Response(AccountSettings accountSettings) throws CertificateException {
29+
this.accountSettings = accountSettings;
30+
certificate = new Certificate();
31+
certificate.loadCertificate(this.accountSettings.getCertificate());
32+
}
33+
34+
public void loadXml(String xml) throws ParserConfigurationException, SAXException, IOException {
35+
DocumentBuilderFactory fty = DocumentBuilderFactory.newInstance();
36+
fty.setNamespaceAware(true);
37+
DocumentBuilder builder = fty.newDocumentBuilder();
38+
ByteArrayInputStream bais = new ByteArrayInputStream(xml.getBytes());
39+
xmlDoc = builder.parse(bais);
40+
}
41+
42+
43+
public void loadXmlFromBase64(String response) throws ParserConfigurationException, SAXException, IOException {
44+
Base64 base64 = new Base64();
45+
byte [] decodedB = base64.decode(response);
46+
String decodedS = new String(decodedB);
47+
loadXml(decodedS);
48+
}
49+
50+
public boolean isValid() throws Exception {
51+
NodeList nodes = xmlDoc.getElementsByTagNameNS(XMLSignature.XMLNS, "Signature");
52+
53+
if(nodes==null || nodes.getLength()==0){
54+
throw new Exception("Can't find signature in document.");
55+
}
56+
57+
X509Certificate cert = certificate.getX509Cert();
58+
DOMValidateContext ctx = new DOMValidateContext(cert.getPublicKey() , nodes.item(0));
59+
XMLSignatureFactory sigF = XMLSignatureFactory.getInstance("DOM");
60+
XMLSignature xmlSignature = sigF.unmarshalXMLSignature(ctx);
61+
62+
return xmlSignature.validate(ctx);
63+
}
64+
65+
public String getNameId() throws Exception {
66+
NodeList nodes = xmlDoc.getElementsByTagNameNS("urn:oasis:names:tc:SAML:2.0:assertion", "NameID");
67+
68+
if(nodes.getLength()==0){
69+
throw new Exception("No name id found in document");
70+
}
71+
72+
return nodes.item(0).getTextContent();
73+
}
74+
}

sample/consume.jsp

Lines changed: 45 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,45 @@
1+
<%@ page language="java" contentType="text/html; charset=UTF-8" pageEncoding="UTF-8"%>
2+
<%@ page import="com.onelogin.*,com.onelogin.saml.*,org.apache.log4j.Logger" %>
3+
<!DOCTYPE html PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN" "http://www.w3.org/TR/html4/loose.dtd">
4+
<html>
5+
<head>
6+
<meta http-equiv="Content-Type" content="text/html; charset=UTF-8">
7+
<title>SAML Assertion Page</title>
8+
</head>
9+
<body>
10+
<%
11+
String certificateS ="MIIBrTCCAaGgAwIBAgIBATADBgEAMGcxCzAJBgNVBAYTAlVTMRMwEQYDVQQIDApD"+
12+
"YWxpZm9ybmlhMRUwEwYDVQQHDAxTYW50YSBNb25pY2ExETAPBgNVBAoMCE9uZUxv"+
13+
"Z2luMRkwFwYDVQQDDBBhcHAub25lbG9naW4uY29tMB4XDTExMDMxMTAwMjc0OVoX"+
14+
"DTE2MDMxMDAwMjc0OVowZzELMAkGA1UEBhMCVVMxEzARBgNVBAgMCkNhbGlmb3Ju"+
15+
"aWExFTATBgNVBAcMDFNhbnRhIE1vbmljYTERMA8GA1UECgwIT25lTG9naW4xGTAX"+
16+
"BgNVBAMMEGFwcC5vbmVsb2dpbi5jb20wgZ8wDQYJKoZIhvcNAQEBBQADgY0AMIGJ"+
17+
"AoGBALfqtQf59sdh8AqMtrTMv1wONW53Rj29zzCPy1di3qJZ/PzHvYurTf9GVBz9"+
18+
"ZyNLBfkt16dhv/dLTzTxJVAqnKDRcnwhskLvWp+8nPnqnNDsousAYzMs63rYO+/f"+
19+
"qdt/19BZrpm7zZI6BWzQKimTLS0gYpgkqbreoKK+AdbbwtyXAgMBAAEwAwYBAAMB"+
20+
"AA==";
21+
22+
AccountSettings accountSettings = new AccountSettings();
23+
accountSettings.setCertificate(certificateS);
24+
25+
Response samlResponse = new Response(accountSettings);
26+
samlResponse.loadXmlFromBase64(request.getParameter("SAMLResponse"));
27+
28+
if(samlResponse.isValid()){
29+
30+
java.io.PrintWriter writer = response.getWriter();
31+
writer.write("OK!");
32+
String nameId = samlResponse.getNameId();
33+
writer.write(nameId);
34+
writer.flush();
35+
36+
} else {
37+
38+
java.io.PrintWriter writer = response.getWriter();
39+
writer.write("Failed");
40+
writer.flush();
41+
42+
}
43+
%>
44+
</body>
45+
</html>

sample/index.jsp

Lines changed: 23 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,23 @@
1+
<%@page import="java.net.URLEncoder,org.apache.log4j.Logger"%>
2+
<%@ page language="java" contentType="text/html; charset=UTF-8" pageEncoding="UTF-8"%>
3+
<%@ page import="com.onelogin.saml.*,com.onelogin.*" %>
4+
<!DOCTYPE html PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN" "http://www.w3.org/TR/html4/loose.dtd">
5+
<html>
6+
<head>
7+
<meta http-equiv="Content-Type" content="text/html; charset=UTF-8">
8+
<title>Auth Request</title>
9+
<%
10+
AppSettings appSettings = new AppSettings();
11+
appSettings.setAssertionConsumerServiceUrl("http://68.169.49.120:8080/testOneLogin/consume.jsp");
12+
appSettings.setIssuer("user@onelogin.com");
13+
AccountSettings accSettings = new AccountSettings();
14+
accSettings.setIdp_sso_target_url("https://app.onelogin.com/saml/signon/20956");
15+
AuthRequest authReq = new AuthRequest(appSettings, accSettings);
16+
String reqString = accSettings.getIdp_sso_target_url()+"?SAMLRequest=" + AuthRequest.getRidOfCRLF(URLEncoder.encode(authReq.getRequest(AuthRequest.base64),"UTF-8"));
17+
response.sendRedirect(reqString);
18+
%>
19+
</head>
20+
<body>
21+
22+
</body>
23+
</html>

0 commit comments

Comments
 (0)