How To Configure Browser-based SSO with Kerberos/SPNEGO

by Abhijit Patil - Published May 2012

Oracle WebLogic Server offers a complete solution for single sign-on with Microsoft clients using Kerberos.

This article describes how to enable Microsoft clients (browsers in this case), authenticated in a Windows domain, using Kerberos, to be transparently authenticated in a Oracle WebLogic Server (Oracle WebLogic Server) domain, based on the same credentials, and without the need to type in a password again.

The purpose of this feature is to enable a client browser to access a protected resource on Oracle WebLogic Server, and to transparently provide Oracle WebLogic Server with authentication information from the Kerberos database via a SPNEGO ticket. Note that this feature also works for Java SE clients. Oracle WebLogic Server will be able to recognize the ticket, and extract the information from it. The server will then use the information for authentication and grant access to the resource if the authenticated user is authorized to access it. (Kerberos is responsible for authentication only; authorization is still handled by Oracle WebLogic Server.)

Following configuration is used to demonstrate this scenario:

  • Browser Client (MACHINEA): Windows 7 Enterprise (IE 9.0/Firefox 7.0.1/Chrome 17 installed)
  • Oracle WebLogic Server (MACHINEB) – Linux version 2.6.18-238.0.0.0.1.el5xen with Oracle WebLogic Server 12c (Oracle JDK 1.6) installed.
  • KDC (MACHINEC) - Windows Server 2008 R2 Enterprise SP1

Note that although above configuration is used for this scenario, SPNEGO should work for older versions of browsers, Oracle WebLogic Server, JDK, and so on.

Figure 1: Machine Configuration for SPNEGO/Kerberos scenario

The following list of steps are a detailed breakdown of the cross-platform authentication design shown above.

  • When the logged-on user (MACHINEA) requests a resource from Oracle WebLogic Server (MACHINEB), it sends the initial HTTP GET verb.
  • Oracle WebLogic Server (MACHINEB), running the SPNEGO Token Handler code, requires authentication and issues a 401 Access Denied, WWW-Authenticate: Negotiate response.
  • The client (Browser on MACHINEA) then requests the session ticket from the TGS/KDC (MACHINEC).
  • The TGS/KDC (MACHINEC) supplies the client with the necessary Kerberos Ticket (assuming the client is authorized) wrapped in a SPNEGO Token.
  • The client re-sends the HTTP GET request + the Negotiate SPNEGO Token in an Authorization: Negotiate base64(token) header.
  • Oracle WebLogic Server's SPNEGO Token Handler code accepts and processes the token through GSS API, authenticates the user and responds with the requested URL.

KDC Configuration

A Windows 2008 Server domain controller can serve as the Kerberos Key Distribution Center (KDC) server for Kerberos-based client and host systems.

Create an Account for Oracle WebLogic Server Server

In this step, a Kerberos Principal representing Oracle WebLogic Server is created on the Active Directory. The principal name would be something like name@REALM.NAME, while the REALM.NAME is the administrative name of the realm. In our example, the principal name will be negotiatetestserver@SECURITYQA.COM. The machine hosting Oracle WebLogic Server doesn't have to be part of SECURITYQA.com domain. In this case it’s part of OTHERDOM.DOM domain. The account type should be "User", not a "Computer" in the AD.

Create a User “negotiatetestserver” in Active Directory for Your Oracle WebLogic Server instance

Figure 2: Account tab showing properties for “negotiatetestserver” user on KDC

  • Launch Programs/Administrative Tools/Active Directory Users and Computers tool.
  • Right click on theUsersnode and selectNew/User. (Do not selectMachine.)
  • Type in the user “negotiatetestserver” in the "Full Name" field and in the "Logon Name" field.
  • ClickNext, and enter a password (and of course, memorize it)
  • Verify that none of the password options are checked. ClickNext.
  • ClickFinish.
Configure Your User to Comply with the Kerberos Protocol
  • Locate your newly created user in the Users tree in the left hand pane and double click it.
  • On the "Account" tab for user “negotiatestserver”,

  • For AES128-SHA1 cipher strength, make sure This account supports AES 128 bit encryption is checked; all others (except password never expires) are unchecked.
  • For AES256-SHA1 cipher strength, make sure This account supports AES 256 bit encryption is checked; all others (except password never expires) are unchecked.
  • For RC4-HMAC-NT cipher strength, make sure all options (except password never expires) are unchecked.
  • For DES-CBC-CRC cipher strength, make sure Use Kerberos DES encryption types for this account and make sure all options (except password never expires) are unchecked.
  • Click OK.
Define a Service Principal Name and Create a Keytab for the Service

An SPN (Service Principal Name) is a unique name that identifies an instance of a service and is associated with the logon account under which the service instance runs. The SPN is used in the process of mutual authentication between the client and the server hosting a particular service. The client finds a computer account based on the SPN of the service to which it is trying to connect.

The ktpass command-line tool enables an administrator to configure a non-Windows Server Kerberos service as a security principal in the Windows Server Active Directory. Ktpass configures the server principal name for the service in Active Directory and generates an MIT-style Kerberos "keytab" file containing the shared secret key of the service. The tool allows UNIX-based services that support Kerberos authentication to use the interoperability features provided by the Windows Server Kerberos KDC service.

Use the following command to configure SPN (for AES128 cipher strength) and generate keytab file:



C:\Users\bt>ktpass -out negotiatetestserver_keytab -princ negotiatetestserver@SECURITYQA.COM -mapUser negotiatetestserver -kvno 0 -crypto  AES128-SHA1 -pass  -p type KRB5_NT_PRINCIPAL

	  

Save generated keytab file (negotiatetestserver_keytab) in a secure location, and export it to the domain directory of your Oracle WebLogic Server. (In our example, we will transfer this file to MachineB.) This file is reference by a JAAS (Java Authentication and Authorization Service) configuration file explained later.

Oracle WebLogic Server Server Configuration

The important requirements for the configuration of this server are:

  • The server has to be represented in the Kerberos realm via a Kerberos principal (which we defined in the previous section).
  • The server needs to be able to access the KDC.
  • The Oracle WebLogic Server process needs to have access to the credentials of its account in Kerberos.
  • Oracle WebLogic Server must be configured to recognize a spnego token in a request.
Create JAAS Login File

JAAS allows dynamic configuration of login modules. We need to specify a JAAS configuration file that specifies the login modules to use.

Create a file namedkrb5Login.confin the Oracle WebLogic Server domain directory with the following contents:

For Oracle WebLogic Server using Oracle JDK:


com.sun.security.jgss.initiate {
  com.sun.security.auth.module.Krb5LoginModule required principal="negotiatetestserver@SECURITYQA.COM" 
useKeyTab=true keyTab=negotiatetestserver_keytab
   storeKey=true debug=true;
};
com.sun.security.jgss.krb5.accept {
  com.sun.security.auth.module.Krb5LoginModule required principal="negotiatetestserver@SECURITYQA.COM" 
useKeyTab=true keyTab=negotiatetestserver_keytab 
   storeKey=true debug=true;
};
For Oracle WebLogic Server using IBM JDK:


com.ibm.security.jgss.initiate {
  com.ibm.security.auth.module.Krb5LoginModule required principal="negotiatetestserver@SECURITYQA.COM"
useKeyTab=true keyTab=negotiatetestserver_keytab storeKey=true debug=true;
};
com.ibm.security.jgss.accept {
  com.ibm.security.auth.module.Krb5LoginModule required principal="negotiatetestserver@SECURITYQA.COM" 
useKeyTab=true keyTab=negotiatetestserver_keytab storeKey=true debug=true;
};
Using Startup Arguments for Kerberos Authentication with Oracle WebLogic Server

This assumes you have transferred the keytab file “negotiatetestserver_keytab” generated in step 2 to your domain directory on Oracle WebLogic Server. If Oracle WebLogic Server is using Oracle JDK, specify following options in the Oracle WebLogic Server java command line:



-Dcom.ibm.security.jgss.debug=all -Djava.security.krb5.realm=SECURITYQA.COM -Djava.security.krb5.kdc=MACHINEC -Djava.security.auth.login.config= krb5Login.conf -Djavax.security.auth.useSubjectCredsOnly=false
Configure Identity Assertion provider

WebLogic Server includes a security provider, the Negotiate Identity Assertion provider, to support single sign-on (SSO) with Microsoft clients. This identity assertion provider decodes Simple and Protected Negotiate (SPNEGO) tokens to obtain Kerberos tokens, validates the Kerberos tokens, and maps Kerberos tokens to WebLogic users. You need to configure a Negotiate Identity Assertion provider in your WebLogic security realm in order to enable SSO with Microsoft clients. See Configuring a Negotiate Identity Assertion Provider .

Install Java Cryptography Extension (JCE) Unlimited Strength Jurisdiction Policy Files

(This step is applicable only if you plan to use AES256-SHA1 cipher strength. Skip this step for all other cipher strengths). You need to download and install this bundle which provides "unlimited strength" policy files which contain no restrictions on cryptographic strengths.

  • For Oracle JDK 6: Download Java Cryptography Extension (JCE) Unlimited Strength Jurisdiction Policy Files 6 here.
  • For Oracle JDK 7: Download Java Cryptography Extension (JCE) Unlimited Strength Jurisdiction Policy Files 7 here. Overwrite 2 jar files under “ /jre/lib/security” directory with 2 jar files inside downloaded zip file.
  • For IBM JDK 6 and above: Download Java Cryptography Extension (JCE) Unlimited Strength Jurisdiction Policy Files 7 here and follow installation instructions
Define Security Constraint in Web Application

In order for authentication to take place, the resource (JSP or Servlet) being accessed must be protected, and for the web application to participate in Single Sign On with the client.

Here’s the servlet code used in our case (SimpleTestServlet.java):


  package wlstest.functional.security.negotiate.servlet;
  
  import java.io.IOException;
  import java.io.PrintWriter;
  import java.util.Enumeration;
  
  import javax.servlet.ServletException;
  import javax.servlet.http.HttpServlet;
  import javax.servlet.http.HttpServletRequest;
  import javax.servlet.http.HttpServletResponse;
  import javax.servlet.http.HttpUtils;
  
  public class SimpleTestServlet extends HttpServlet
  {
  public void service(HttpServletRequest req, HttpServletResponse res)
  throws ServletException, IOException
  {
  res.setContentType("text/html");
  PrintWriter out = res.getWriter();
  
  out.println("
  ");
out.println("

Simple Test Servlet


                              
");
out.println("

");
out.println("

Requested URL:

"); out.println("
");

        out.println(HttpUtils.getRequestURL(req).toString());
        out.println("
"); Enumeration theEnum = getServletConfig().getInitParameterNames(); if (theEnum != null) { boolean first = true; while (theEnum.hasMoreElements()) { if (first) { out.println("

Init Parameters

"); out.println("
");

                    first = false;
                }
                String param = (String) theEnum.nextElement();
                out.println(" "+param+": "+getInitParameter(param));
            }
            out.println("
"); } out.println("

Request information:

"); out.println("
");


        print(out, "Request method", req.getMethod());
        print(out, "Request URI", req.getRequestURI());
        print(out, "Request protocol", req.getProtocol());
        print(out, "Servlet path",     req.getServletPath());
        print(out, "Path info",        req.getPathInfo());
        print(out, "Path translated",  req.getPathTranslated());
        print(out, "Query string",     req.getQueryString());
        print(out, "Content length",   req.getContentLength());
        print(out, "Content type",     req.getContentType());
        print(out, "Server name",      req.getServerName());
        print(out, "Server port",      req.getServerPort());
        print(out, "Remote user",      req.getRemoteUser());
        print(out, "Remote address",   req.getRemoteAddr());
        print(out, "Remote host",      req.getRemoteHost());
        print(out, "Scheme",           req.getScheme());
        print(out, "Authorization scheme", req.getAuthType());
        print(out, "Request scheme", req.getScheme());
        out.println("
"); Enumeration e = req.getHeaderNames(); if (e.hasMoreElements()) { out.println("

Request headers:

"); out.println("
");

            while (e.hasMoreElements())
            {
                String name = (String)e.nextElement();
                out.println(" " + name + ": " + req.getHeader(name));
            }
            out.println("
"); } e = req.getParameterNames(); if (e.hasMoreElements()) { out.println("

Servlet parameters (Single Value style):

"); out.println("
");

            while (e.hasMoreElements())
            {
                String name = (String)e.nextElement();
                out.println(" " + name + " = " + req.getParameter(name));
            }
            out.println("
"); } e = req.getParameterNames(); if (e.hasMoreElements()) { out.println("

Servlet parameters (Multiple Value style):

"); out.println("
");

            while (e.hasMoreElements())
            {
                String name = (String)e.nextElement();
                String vals[] = (String []) req.getParameterValues(name);
                if (vals != null)
                {
                    out.print(" " + name + " = "); 
                    out.println(vals[0]);
                    for (int i = 1; i");
            }
            out.println("
"); } out.println("

Request Attributes:

"); e = req.getAttributeNames(); if (e.hasMoreElements()) { out.println("
");

            while (e.hasMoreElements())
            {
                String name = (String)e.nextElement();
                Object o = req.getAttribute(name);
                if (o == null) continue;
                out.println(" " + name + ": type=" + o.getClass().getName() + " str='" + o.toString() + "'");
            }
            out.println("
"); } out.println(" "); } private void print (PrintWriter out, String name, String value) { out.print(" " + name + ": "); out.println(value == null ? "<none>" : value); } private void print (PrintWriter out, String name, int value) { out.print(" " + name + ": "); if (value == -1) out.println("<none>"); else out.println(value); } }
The web.xml file looks like this:




    Simple Test Servlet (Basic Auth)

    
        
            BasicAuthSimpleTestServlet
            /*
            /
            POST
            GET
        
        
            negotiateAdminRole
        
        
            no description
            NONE
        
    

    
        negotiateAdminRole
    

    
        BASIC
        default
    

    
        BasicAuthSimpleTestServlet
        wlstest.functional.security.negotiate.servlet.SimpleTestServlet
    

    
        /BasicAuthSimpleTestServlet
    

    
        BasicAuthSimpleTestServlet
        /
    


The role negotiateAdminRole is defined in weblogic.xml, as follows:




  
    negotiateAdminRole
    negotiateAdmin
    Administrators
  

Client Configuration

For Single Sign On to occur you will need an authenticated Microsoft client, belonging to the domain controlled by your realm, and requesting access to the Oracle WebLogic Server service

Configuring Internet Explorer Browser

To configure an Internet Explorer browser to use Windows authentication, follow these procedures in Internet Explorer.

Configure Local Intranet Domains
  • In Internet Explorer, select Tools > Internet Options.
  • Select the Security tab.

python-nopool-gif

Figure 3: Local Intranet Dialog Box for Internet Explorer

  • Select Local intranet and click Sites.
  • In the Local intranet popup, ensure that the Include all sites that bypass the proxy server and Include all local (intranet) sites not listed in other zones options are checked.
  • Click Advanced.
  • In the Local intranet (Advanced) dialog box, add all relative domain names that will be used for Oracle WebLogic Server instances participating in the SSO configuration (for example, myhost.example.com) and click OK.

python-nopool-gif

Figure 5: Configure Intranet Authentication

Configure Intranet Authentication
  • Select Tools > Internet Options.
  • Select the Security tab.
  • Select Local intranet and click Custom Level... .
  • In the Security Settings dialog box, scroll to the User Authentication section.
  • Select Automatic logon only in Intranet zone. This option prevents users from having to re-enter logon credentials, which is a key piece to this solution.
  • Click OK.
Verify Proxy Settings

If you have a proxy server enabled:

  • Select Tools > Internet Options.
  • Select the Connections tab and click LAN Settings.
  • Verify that the proxy server address and port number are correct.
  • Click Advanced.
  • In the Proxy Settings dialog box, ensure that all desired domain names are entered in the Exceptions field.
  • Click OK to close the Proxy Settings dialog box.

Configuring Mozilla Firefox Browser

To configure a Firefox browser to use Windows Integrated authentication, complete the following steps:

  • Start Firefox.
  • Enter about:config in the Location Bar.
  • Enter the filter string network.negotiate.
  • Set the preferences as shown in Figure below:

python-nopool-gif

Figure 6: Preferences Required in Firefox for Windows Integrated Authentication

Configuring Google Chrome Browser

No special configuration needed for Chrome Browser.

python-nopool-gif

Figure 7: Using klist to view and purge tickets

Verifying Configuration
  • Login to MachineA (Browser Client) as user “SECURITYQA.COM\
  • Open command prompt and run 'klist purge'. This is to purge any existing tickets.
  • Open browser and access url of the web application. In this case, we are accessing a servlet which provides basic HTTP header information:

python-nopool-gif

Figure 8: Servlet displaying HTTP info after SPNEGO authentication

  • If the SSO was unsuccessful you will be prompted for username/password by the browser. In this case you need to check wls server logs for exception (Check Troubleshooting section below).

python-nopool-gif

Figure 9: Browser prompting for username/password after SPNEGO failure

  • Confirm if browser is sending SPNEGO tokens. Look for message “"Authorization: Negotiate YII…”. This means SPNEGO token is being passed by browser to Oracle WebLogic Server.


User-Agent: Mozilla/5.0 (Windows NT 6.1; WOW64; rv:7.0.1) Gecko/20100101 Firefox/7.0.1
Accept: text/html,application/xhtml+xml,application/xml;q=0.9,*/*;q=0.8
Accept-Language: en-us,en;q=0.5
Accept-Encoding: gzip, deflate
Accept-Charset: ISO-8859-1,utf-8;q=0.7,*;q=0.7
Connection: keep-alive
Authorization: Negotiate YIIGzQYGKwYBBQUCoIIGwTCCBr2gMDAuBgkqhkiC9xIBAgIGCSqGSIb3EgECAgYKKwYBBAGCNwICHgYKKwYBBAGCNwICCqKCB
ocEggaDYIIGfwYJKoZIhvcSAQICAQBuggZuMIIGaqADAgEFoQMCAQ6iBwMFACAAAACjggUCYYIE/jCCBPqgAwIBBaEQGw5TRUNVUklUWVFBLkNPTaIrMCmgAwIBA
qEiMCAbBEhUVFAbGGFkYzIxNzA3MTkudXMub3JhY2xlLmNvbaOCBLIwggSuoAMCARGhAwIBJKKCBKAEggSc8v4RphGvP7CinPf4mhiBzyfZWQG …
  • You can also check if Oracle WebLogic Server is using correct cipher in the Oracle WebLogic Server log.
    For Oracle JDK:


>>>Pre-Authentication Data:
       PA-DATA type = 19
       PA-ETYPE-INFO2 etype = 17
>>>Pre-Authentication Data:
       PA-DATA type = 2
       PA-ENC-TIMESTAMP
>>>Pre-Authentication Data:
       PA-DATA type = 16
>>>Pre-Authentication Data:
       PA-DATA type = 15
AcquireTGT: PREAUTH FAILED/REQUIRED, re-send AS-REQ
Updated salt from pre-auth = SECURITYQA.COMnegotiatetestserver
>>>KrbAsReq salt is SECURITYQA.COMnegotiatetestserver
Pre-Authenticaton: find key for etype = 17
AS-REQ: Add PA_ENC_TIMESTAMP now
>>> EType: sun.security.krb5.internal.crypto.Aes128CtsHmacSha1EType
>>> KrbAsReq calling createMessage
>>> KrbAsReq in createMessage
>>> KrbKdcReq send: kdc=rno05089 UDP:88, timeout=30000, number of retries =3, #bytes=241
>>> KDCCommunication: kdc=rno05089 UDP:88, timeout=30000,Attempt =1, #bytes=241
>>> KrbKdcReq send: #bytes read=100
>>> KrbKdcReq send: #bytes read=100
>>> KdcAccessibility: remove rno05089
>>> KDCRep: init() encoding tag is 126 req type is 11
For IBM JDK:


JGSS_DBG_PROV] getMechs: Mechanism(s) supported by provider IBMJGSSProvider
[JGSS_DBG_PROV]   1.3.6.1.5.5.2
[JGSS_DBG_PROV] getMechs: Mechanism(s) supported by provider IBMJGSSProvider
[JGSS_DBG_PROV]   1.2.840.113554.1.2.2
[JGSS_DBG_PROV] getMechs: Mechanism(s) supported by provider IBMSPNEGO
[JGSS_DBG_PROV]   1.3.6.1.5.5.2
[JGSS_DBG_PROV] getMechs: 2 unique mechanism(s) found
[JGSS_DBG_PROV]   [0]: 1.3.6.1.5.5.2
[JGSS_DBG_PROV]   [1]: 1.2.840.113554.1.2.2
[JGSS_DBG_CTX] Default list of negotiable mechs:
            1.2.840.113554.1.2.2
                           
[JGSS_DBG_CTX] AuthenticatorCache, scope of bucket122
[JGSS_DBG_CTX] ticket enc type = rc4-hmac
[JGSS_DBG_CTX] Successfully decrypted ticket
[JGSS_DBG_CTX] Put authz info in cache
[JGSS_DBG_CTX] Session key type = rc4-hmac
[JGSS_DBG_CTX] Successfully decrypted authenticator
[JGSS_DBG_CTX] Remote subkey type = rc4-hmac
[JGSS_DBG_CTX] No delegated creds from peer
[JGSS_DBG_CTX] Received channel binding checksum

Troubleshooting

Here are typical exceptions you might encounter during SPNEGO setup along with solutions:

  • kinit: Cannot contact any KDC for requested realm while getting initial credentials. Your computer successfully sent out a request, but the KDC never responded. The network is probably down between your host and the KDC, or you are behind a firewall.
  • javax.security.auth.login.LoginException: KrbException:: Pre-authentication information was invalid (24) - Preauthentication failed. The principal exists in kerberos but the password is wrong. This is a password problem. Double check the validity of your keytab, or of the password that you have entered.
  • Exception: krb_error 0 Cannot retrieve key from keytab for principal xxx No error. The keytab file you have provided was not created for that principal, or there is no such file (this will be easy to check) . If the file does exist, the principal xxx might exist in the AD server, but this keytab is not for it. On the other hand, principal might not exist at all.
  • javax.security.auth.login.LoginException: KrbException: KDC has no support for encryption type (14) - KDC has no support for encryption type. Your KDC does not support the encryption type requested. Please choose an encryption type that is supported by the KDC you are using.
  • <Debug> <SecurityDebug> <000000> <Found NTLM token when expecting SPNEGO>. This is a very common error. It means that the Oracle WebLogic Server was ready to extract a SPNEGO token but could not find one in the request sent by the browser. The browser is not set up correctly to send a spnego token, go back to the client configuration, and double check the browser configuration. Something is wrong in your SPN definition: Either no SPN was defined for this service, or you have duplicate SPNs, which means that the SPN resolved in more than one principal associated with it.
  • Exception: weblogic.security.providers.utils.NegotiateTokenException: GSSException: No valid credentials provided (Mechanism level: Attempt to obtain new ACCEPT credentials failed!). This is very common exception that covers anything that might have gone wrong during the process of the Oracle WebLogic Server loading the JAAS configuration from the krb5Login.conf file to reading the keytab and successfully decoding the SPNEGO ticket. The krb5Login.conf file could not be found or opened - double-check the way you have specified it to Oracle WebLogic Server, double check existence and permissions. There could be some wrong syntax inside the krb5Login.conf
  • java.lang.SecurityException at javax.security.auth.login.Configuration.getConfiguration. There was a problem processing the JAAS login configuration file, possibly due to a syntax error in the file. Check the configuration file carefully for errors.
  • GSSException: No valid credentials provided (Mechanism level: Attempt to obtain new INITIATE credentials failed! (null)) . . . Caused by: javax.security.auth.login.LoginException: Clock skew too great. Kerberos requires the time on the KDC and on the client to be loosely synchronized. (The default is within 5 minutes.) If that's not the case, you will get this error. Synchronize the clocks (or have a system administrator do so).

Conclusion

SSO Cross-platform authentication is achieved by emulating the negotiate behavior of native Windows-to-Windows authentication services that use the Kerberos protocol. In order for cross-platform authentication to work, Oracle WebLogic Server can be used to parse SPNEGO tokens in order to extract Kerberos tokens which are then used for authentication thus providing transparent authentication to the end user.