Package net.sf.jml.protocol.soap

Source Code of net.sf.jml.protocol.soap.SSO

/*
* Copyright 2004-2005 the original author or authors.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
*      http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package net.sf.jml.protocol.soap;

import net.sf.jml.util.Base64;
import net.sf.jml.util.XmlUtils;
import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;
import org.apache.http.HttpHost;
import org.apache.http.HttpResponse;
import org.apache.http.HttpVersion;
import org.apache.http.impl.DefaultHttpClientConnection;
import org.apache.http.message.BasicHttpEntityEnclosingRequest;
import org.apache.http.params.BasicHttpParams;
import org.apache.http.params.HttpParams;
import org.apache.http.params.HttpProtocolParams;
import org.apache.http.protocol.*;
import org.apache.http.util.EntityUtils;
import org.w3c.dom.Document;
import org.w3c.dom.Element;

import javax.crypto.Cipher;
import javax.crypto.Mac;
import javax.crypto.spec.IvParameterSpec;
import javax.crypto.spec.SecretKeySpec;
import javax.net.ssl.SSLSocketFactory;
import javax.xml.parsers.DocumentBuilder;
import javax.xml.parsers.DocumentBuilderFactory;
import java.io.ByteArrayInputStream;
import java.net.Socket;
import java.net.URL;
import java.security.InvalidKeyException;
import java.security.NoSuchAlgorithmException;
import java.util.regex.Matcher;
import java.util.regex.Pattern;

/**
* Manages MSN15 Single Sign-On
*
* @author Damian Minkov
*/
public class SSO
{
    private static final Log logger = LogFactory.getLog(SSO.class);

    private String userName = null;
    private String password = null;
    private String policy = null;
    private String nonce = null;

    private String webTicket = null;
    private String contactTicket = null;
    private String oimTicket = null;
    private String spaceTicket = null;
    private String storageTicket = null;

    Pattern redirectPattern = Pattern.compile("<psf:redirectUrl>(.*)</psf:redirectUrl>");

    public SSO(String userName, String password, String policy, String nonce)
    {
        this.userName = userName;
        this.password = password;
        this.policy = policy;
        this.nonce = nonce;
    }

    public String getTicket()
    {
        return getTicket(null);
    }

    public String getTicket(String urlStr)
    {
        try
        {
            if(urlStr == null)
                urlStr = "http://login.live.com/RST.srf";

            URL url = new URL(urlStr);

            StringBuilder mess = new StringBuilder();

            mess.append("<?xml version=\"1.0\" encoding=\"UTF-8\"?>\r\n");
            mess.append("<Envelope xmlns=\"http://schemas.xmlsoap.org/soap/envelope/\"\r\n");
            mess.append(" xmlns:wsse=\"http://schemas.xmlsoap.org/ws/2003/06/secext\"\r\n");
            mess.append(" xmlns:saml=\"urn:oasis:names:tc:SAML:1.0:assertion\"\r\n");
            mess.append(" xmlns:wsp=\"http://schemas.xmlsoap.org/ws/2002/12/policy\"\r\n");
            mess.append(" xmlns:wsu=\"http://docs.oasis-open.org/wss/2004/01/oasis-200401-wss-wssecurity-utility-1.0.xsd\"\r\n");
            mess.append(" xmlns:wsa=\"http://schemas.xmlsoap.org/ws/2004/03/addressing\"\r\n");
            mess.append(" xmlns:wssc=\"http://schemas.xmlsoap.org/ws/2004/04/sc\"\r\n");
            mess.append(" xmlns:wst=\"http://schemas.xmlsoap.org/ws/2004/04/trust\">\r\n");
            mess.append("<Header>\r\n");
            mess.append("  <ps:AuthInfo xmlns:ps=\"http://schemas.microsoft.com/Passport/SoapServices/PPCRL\" Id=\"PPAuthInfo\">\r\n");
            mess.append("    <ps:HostingApp>{7108E71A-9926-4FCB-BCC9-9A9D3F32E423}</ps:HostingApp>\r\n");
            mess.append("    <ps:BinaryVersion>4</ps:BinaryVersion>\r\n");
            mess.append("    <ps:UIVersion>1</ps:UIVersion>\r\n");
            mess.append("    <ps:Cookies></ps:Cookies>\r\n");
            mess.append("    <ps:RequestParams>AQAAAAIAAABsYwQAAAAxMDMz</ps:RequestParams>\r\n");
            mess.append("  </ps:AuthInfo>\r\n");
            mess.append("  <wsse:Security>\r\n");
            mess.append("    <wsse:UsernameToken Id=\"user\">\r\n");
            mess.append("      <wsse:Username>" + userName + "</wsse:Username>\r\n");
            mess.append("      <wsse:Password>" + password + "</wsse:Password>\r\n");
            mess.append("    </wsse:UsernameToken>\r\n");
            mess.append("  </wsse:Security>\r\n");
            mess.append("</Header>\r\n");
            mess.append("<Body>\r\n");

            mess.append("<ps:RequestMultipleSecurityTokens xmlns:ps=\"http://schemas.microsoft.com/Passport/SoapServices/PPCRL\" Id=\"RSTS\">\r\n");
            mess.append("  <wst:RequestSecurityToken Id=\"RST0\">\r\n");
            mess.append("    <wst:RequestType>http://schemas.xmlsoap.org/ws/2004/04/security/trust/Issue</wst:RequestType>\r\n");
            mess.append("    <wsp:AppliesTo>\r\n");
            mess.append("      <wsa:EndpointReference>\r\n");
            mess.append("        <wsa:Address>http://Passport.NET/tb</wsa:Address>\r\n");
            mess.append("      </wsa:EndpointReference>\r\n");
            mess.append("    </wsp:AppliesTo>\r\n");
            mess.append("  </wst:RequestSecurityToken>\r\n");

            mess.append("<wst:RequestSecurityToken Id=\"RST1\">\r\n");
            mess.append("  <wst:RequestType>http://schemas.xmlsoap.org/ws/2004/04/security/trust/Issue</wst:RequestType>\r\n");
            mess.append("  <wsp:AppliesTo>\r\n");
            mess.append("    <wsa:EndpointReference>\r\n");
            mess.append("      <wsa:Address>messengerclear.live.com</wsa:Address>\r\n");
            mess.append("     </wsa:EndpointReference>\r\n");
            mess.append("    </wsp:AppliesTo>\r\n");
            mess.append("   <wsse:PolicyReference URI=\"" + policy + "\"></wsse:PolicyReference>\r\n");
            mess.append("</wst:RequestSecurityToken>\r\n");

            mess.append("<wst:RequestSecurityToken Id=\"RST2\">\r\n");
            mess.append("  <wst:RequestType>http://schemas.xmlsoap.org/ws/2004/04/security/trust/Issue</wst:RequestType>\r\n");
            mess.append("  <wsp:AppliesTo>\r\n");
            mess.append("   <wsa:EndpointReference>\r\n");
            mess.append("     <wsa:Address>messenger.msn.com</wsa:Address>\r\n");
            mess.append("   </wsa:EndpointReference>\r\n");
            mess.append("  </wsp:AppliesTo>\r\n");
            mess.append("  <wsse:PolicyReference URI=\"?id=507\"></wsse:PolicyReference>\r\n");
            mess.append("</wst:RequestSecurityToken>\r\n");

            mess.append("<wst:RequestSecurityToken Id=\"RST3\">\r\n");
            mess.append("  <wst:RequestType>http://schemas.xmlsoap.org/ws/2004/04/security/trust/Issue</wst:RequestType>\r\n");
            mess.append("   <wsp:AppliesTo>\r\n");
            mess.append("        <wsa:EndpointReference>\r\n");
            mess.append("          <wsa:Address>contacts.msn.com</wsa:Address>\r\n");
            mess.append("        </wsa:EndpointReference>\r\n");
            mess.append("      </wsp:AppliesTo>\r\n");
            mess.append("      <wsse:PolicyReference URI=\"MBI\"></wsse:PolicyReference>\r\n");
            mess.append("    </wst:RequestSecurityToken>\r\n");

            mess.append("    <wst:RequestSecurityToken Id=\"RST4\">\r\n");
            mess.append("      <wst:RequestType>http://schemas.xmlsoap.org/ws/2004/04/security/trust/Issue</wst:RequestType>\r\n");
            mess.append("      <wsp:AppliesTo>\r\n");
            mess.append("        <wsa:EndpointReference>\r\n");
            mess.append("          <wsa:Address>messengersecure.live.com</wsa:Address>\r\n");
            mess.append("        </wsa:EndpointReference>\r\n");
            mess.append("      </wsp:AppliesTo>\r\n");
            mess.append("      <wsse:PolicyReference URI=\"MBI_SSL\"></wsse:PolicyReference>\r\n");
            mess.append("    </wst:RequestSecurityToken>\r\n");

            mess.append("    <wst:RequestSecurityToken Id=\"RST5\">\r\n");
            mess.append("      <wst:RequestType>http://schemas.xmlsoap.org/ws/2004/04/security/trust/Issue</wst:RequestType>\r\n");
            mess.append("      <wsp:AppliesTo>\r\n");
            mess.append("        <wsa:EndpointReference>\r\n");
            mess.append("          <wsa:Address>spaces.live.com</wsa:Address>\r\n");
            mess.append("        </wsa:EndpointReference>\r\n");
            mess.append("      </wsp:AppliesTo>\r\n");
            mess.append("      <wsse:PolicyReference URI=\"MBI\"></wsse:PolicyReference>\r\n");
            mess.append("    </wst:RequestSecurityToken>\r\n");

            mess.append("    <wst:RequestSecurityToken Id=\"RST6\">\r\n");
            mess.append("      <wst:RequestType>http://schemas.xmlsoap.org/ws/2004/04/security/trust/Issue</wst:RequestType>\r\n");
            mess.append("      <wsp:AppliesTo>\r\n");
            mess.append("        <wsa:EndpointReference>\r\n");
            mess.append("          <wsa:Address>storage.msn.com</wsa:Address>\r\n");
            mess.append("        </wsa:EndpointReference>\r\n");
            mess.append("      </wsp:AppliesTo>\r\n");
            mess.append("      <wsse:PolicyReference URI=\"MBI\"></wsse:PolicyReference>\r\n");
            mess.append("    </wst:RequestSecurityToken>\r\n");
            mess.append("  </ps:RequestMultipleSecurityTokens>\r\n");
            mess.append("</Body>\r\n");
            mess.append("</Envelope>");

            DefaultHttpClientConnection conn = new DefaultHttpClientConnection();
            HttpParams params = null;
            BasicHttpProcessor httpproc = null;

            params = new BasicHttpParams();
            HttpProtocolParams.setVersion(params, HttpVersion.HTTP_1_1);
            HttpProtocolParams.setContentCharset(params, "UTF-8");
            HttpProtocolParams.setUserAgent(params, "MSN Explorer/9.0 (MSN 8.0; TmstmpExt)");
            HttpProtocolParams.setUseExpectContinue(params, false);

            httpproc = new BasicHttpProcessor();
            // Required protocol interceptors
            httpproc.addInterceptor(new RequestContent());
            httpproc.addInterceptor(new RequestTargetHost());
            // Recommended protocol interceptors
            httpproc.addInterceptor(new RequestConnControl());
            httpproc.addInterceptor(new RequestUserAgent());
            httpproc.addInterceptor(new RequestExpectContinue());

            HttpRequestExecutor httpexecutor = new HttpRequestExecutor();

            HttpContext context = new BasicHttpContext(null);

            HttpHost host = new HttpHost(url.getHost(), 443, "https");

            context.setAttribute(ExecutionContext.HTTP_CONNECTION, conn);
            context.setAttribute(ExecutionContext.HTTP_TARGET_HOST, host);


            SSLSocketFactory factory = (SSLSocketFactory) SSLSocketFactory.getDefault();
            Socket socket = factory.createSocket(host.getHostName(), host.getPort());

            conn.bind(socket, params);

            BasicHttpEntityEnclosingRequest request = new BasicHttpEntityEnclosingRequest(
                "POST",
                url.getPath());
            request.setEntity(new XmlEntity(mess.toString()));

            context.setAttribute(ExecutionContext.HTTP_REQUEST, request);
            request.setParams(params);

            request.addHeader("Host", url.getHost());

            httpexecutor.preProcess(request, httpproc, context);

            HttpResponse response = httpexecutor.execute(request, conn, context);
            httpexecutor.postProcess(response, httpproc, context);

            logger.debug(response.getStatusLine());

            String responseStr = EntityUtils.toString(response.getEntity());

            logger.debug(response.getStatusLine() + " / " + responseStr);

            conn.close();

            int code = response.getStatusLine().getStatusCode();

            if(code > -1 && code != 200)
            {
                if(responseStr.indexOf("<faultcode>psf:Redirect</faultcode>") == -1)
                {
                    logger.error("*** Can't get passport ticket! http code = " + code);
                    return null;
                }


                Matcher m = redirectPattern.matcher(responseStr);
                if(!m.find())
                {
                    logger.error("*** redirect, but can't get redirect URL!");
                    return null;
                }
                else
                {
                    String redirectUrl = m.group(1);

                    if(urlStr.equals(redirectUrl))
                    {
                        logger.error("*** redirect, but redirect to same URL!");
                        return null;
                    }

                    return getTicket(redirectUrl);
                }
            }
            else
            {
                if(responseStr.indexOf("<faultcode>psf:Redirect</faultcode>") != -1)
                {
                    Matcher m = redirectPattern.matcher(responseStr);
                    if(m.find())
                    {
                        String redirectUrl = m.group(1);

                        if(urlStr.equals(redirectUrl))
                        {
                            logger.error("*** redirect, but redirect to same URL!");
                            return null;
                        }

                        return getTicket(redirectUrl);
                    }
                }
            }

            if(response.getStatusLine().getStatusCode() != 200)
            {
                logger.error("something wrong!", new Exception());
                return null;
            }

            return getTicketFromResponseXml(responseStr);
        }catch (Exception e)
        {
            logger.error("Login error ", e);
            // fire login error
        }

        return null;
    }

    private String getTicketFromResponseXml(String xml)
    {
        try
        {
            DocumentBuilderFactory dbfactory = DocumentBuilderFactory.newInstance();
            dbfactory.setIgnoringComments(true);
            DocumentBuilder docBuilder;
            docBuilder = dbfactory.newDocumentBuilder();

            ByteArrayInputStream in = new ByteArrayInputStream(xml.getBytes());
            Document doc = docBuilder.parse(in);

            Element el1 = XmlUtils.locateElement(doc.getDocumentElement(),
                "wsse:BinarySecurityToken", "Id", "Compact1");

            Element p1 = (Element) el1.getParentNode().getParentNode();
            Element p2 = XmlUtils.findChild(p1, "wst:RequestedProofToken");
            Element el2 = XmlUtils.findChild(p2, "wst:BinarySecret");

            String ticket = XmlUtils.getText(el1);
            String binSecret = XmlUtils.getText(el2).trim();

            Element webTicketEl = XmlUtils.locateElement(doc.getDocumentElement(),
                "wsse:BinarySecurityToken", "Id", "PPToken2");
            webTicket = XmlUtils.getText(webTicketEl);

            Element contactTicketEl = XmlUtils.locateElement(doc.getDocumentElement(),
                "wsse:BinarySecurityToken", "Id", "Compact3");
            contactTicket = XmlUtils.getText(contactTicketEl);

            Element oimTicketEl = XmlUtils.locateElement(doc.getDocumentElement(),
                "wsse:BinarySecurityToken", "Id", "Compact4");
            oimTicket = XmlUtils.getText(oimTicketEl);

            Element spaceTicketEl = XmlUtils.locateElement(doc.getDocumentElement(),
                "wsse:BinarySecurityToken", "Id", "Compact5");
            spaceTicket = XmlUtils.getText(spaceTicketEl);

            Element storageTicketEl = XmlUtils.locateElement(doc.getDocumentElement(),
                "wsse:BinarySecurityToken", "Id", "Compact6");
            storageTicket = XmlUtils.getText(storageTicketEl);

            SSOticket SSOTicket = new SSOticket(binSecret, nonce.trim());

            //return the final ticket
            return ticket.trim() + " " + SSOTicket.value;
        } catch (Exception e)
        {
            logger.error("Login error ", e);
        }

        return null;
    }

    static class SSOticket
    {
        public String value;    // the ticket in form of a string

        private byte[] beginning;

        public SSOticket(String key, String nonce)
            throws Exception
        {
            // First of all, we need to create a structure of information, which elements' size is 4 bytes
            // To do that, we use an array which we can turn into a string, later...
            beginning = new byte[28];

            //StructHeaderSize = 28
            beginning[0] = 0x1c;
            beginning[1] = 0x00;
            beginning[2] = 0x00;
            beginning[3] = 0x00;

            //CryptMode = 1
            beginning[4] = 0x01;
            beginning[5] = 0x00;
            beginning[6] = 0x00;
            beginning[7] = 0x00;

            //CipherType = 0x6603
            beginning[8] = 0x03;
            beginning[9] = 0x66;
            beginning[10] = 0x00;
            beginning[11] = 0x00;

            //HashType = 0x8004
            beginning[12] = 0x04;
            beginning[13] = (byte)0x80;
            beginning[14] = 0x00;
            beginning[15] = 0x00;

            //IV length = 8
            beginning[16] = 0x08;
            beginning[17] = 0x00;
            beginning[18] = 0x00;
            beginning[19] = 0x00;

            //hash length = 20
            beginning[20] = 0x14;
            beginning[21] = 0x00;
            beginning[22] = 0x00;
            beginning[23] = 0x00;

            //cipher length = 72
            beginning[24] = 0x48;
            beginning[25] = 0x00;
            beginning[26] = 0x00;
            beginning[27] = 0x00;

            // now, we have to create a first, base64 decoded key, which we get from the input key
            byte[] key1 = Base64.decode(key);

            // then we calculate a second key through a specific algorithm (see function DeriveKey())
            byte[] key2 = deriveKey(key1, "WS-SecureConversationSESSION KEY HASH");

            // ...and a third key with the same algorithm...
            byte[] key3 = deriveKey(key1, "WS-SecureConversationSESSION KEY ENCRYPTION");

            // compute the hash
            byte[] hash = HMAC(key2, nonce.getBytes());

            // now, we will use TrippleDES algorithm to transform the nonce to a block of 72 bytes...
            // create the initialization vector (which number's are not important, but better use random ;-))
            byte[] iv = { 0, 1, 2, 3, 4, 5, 6, 7 };

            // fill random iv
            //byte[] iv = new byte[8];
            //for (int i = 0; i < 8; i++)
            //{
            //    byte rand = (byte) Math.floor(Math.random() * 256);
            //    iv[i] = rand;
            //}

            // we have to fill the nonce with 8*8
            byte[] restOfNonce = { 0x08, 0x08, 0x08, 0x08, 0x08, 0x08, 0x08, 0x08 };

            byte[] output = DES3(key3, combine(nonce.getBytes(), restOfNonce), iv);

            // the final key will be a base64 encoded structure, composed by the beginning of the structure, the initialization vector, the SHA1 - Hash and the transformed block
            // string struc = Encoding.Default.GetString(Beginning) + Encoding.Default.GetString(iv) + Encoding.Default.GetString(hash) + Encoding.Default.GetString(output);
            String struc = new String(beginning, "ISO-8859-1") + new String(iv, "ISO-8859-1") + new String(hash, "ISO-8859-1") + new String(output, "ISO-8859-1");


            value = new String(Base64.encode(struc.getBytes("ISO-8859-1")));
        }

        // combine two byte arrays
        private byte[] combine(byte[] a, byte[] b)
        {
            byte[] c = new byte[a.length + b.length];
            System.arraycopy(a, 0, c, 0, a.length);
            System.arraycopy(b, 0, c, a.length, b.length);

            return c;
        }

        // specific algorithm to calculate a key...
        private byte[] deriveKey(byte[] key, String magic)
            throws Exception
        {
            byte hash1[] = HMAC(key, magic.getBytes());
            byte hash2[] = HMAC(key, combine(hash1, magic.getBytes()));
            byte hash3[] = HMAC(key, hash1);
            byte hash4[] = HMAC(key, combine(hash3, magic.getBytes()));
            byte out[] = new byte[4];
            out[0] = hash4[0];
            out[1] = hash4[1];
            out[2] = hash4[2];
            out[3] = hash4[3];
            return combine(hash2, out);
        }

        private byte[] HMAC(byte[] key, byte[] subject)
        {
            try
            {
                Mac mac = Mac.getInstance("HmacSHA1");
                SecretKeySpec sk = new SecretKeySpec(key, "HmacSHA1");

                mac.init(sk);
                return mac.doFinal(subject);
            }
            catch (NoSuchAlgorithmException ex)
            {
                ex.printStackTrace();
            }
            catch (InvalidKeyException ex)
            {
                ex.printStackTrace();
            }

            return null;
        }

        private byte[] DES3(byte[] key, byte[] subject, byte[] iv)
        {
            try
            {
                Cipher cipher = Cipher.getInstance("DESede/CBC/NoPadding");
                SecretKeySpec sk = new SecretKeySpec(key, "DESede");

                IvParameterSpec sr = new IvParameterSpec(iv);
                cipher.init(Cipher.ENCRYPT_MODE, sk, sr);
                return cipher.doFinal(subject);
            }
            catch (Exception ex)
            {
                ex.printStackTrace();
            }
            return null;
        }
    }

    public String getContactTicket()
    {
        return contactTicket;
    }

    public String getOimTicket()
    {
        return oimTicket;
    }

    public String getSpaceTicket()
    {
        return spaceTicket;
    }

    public String getStorageTicket()
    {
        return storageTicket;
    }

    public String getWebTicket()
    {
        return webTicket;
    }
}
TOP

Related Classes of net.sf.jml.protocol.soap.SSO

TOP
Copyright © 2018 www.massapi.com. All rights reserved.
All source code are property of their respective owners. Java is a trademark of Sun Microsystems, Inc and owned by ORACLE Inc. Contact coftware#gmail.com.