Chapter 5. Secure authentication with Kerberos

This chapter covers

  • Alternatives to password-based authentication
  • Kerberos and Java GSS API
  • WS-Security with Kerberos

In the previous chapter, we showed you how to claim your identity using passwords. We discussed two schemes: one that requires you to submit your password in clear text and another that helps you guard your password from snoopers—people who intend to steal it while it is on the wire. Both schemes required you to first register a username and password with your service providers. Like most users, you probably reuse the same username and password when registering with several services. This makes you susceptible to repurposing attacks. Administrators of a service you are registered with can steal your username and password and spoof your identity on other services. Is there a way out of this mess without burdening yourself with the inhuman task of creating and remembering unique username and password combinations for each service? Kerberos is the security technology that first provided an answer to this question.

In fact, Kerberos answers several other significant questions related to SOA security. How do we ensure the confidentiality of messages while they are in transit? Can we detect tampering of messages by a man in the middle? Kerberos answers these questions as well.

In this chapter, we will introduce Kerberos and describe how it can be used for secure authentication in SOA. We will not delve into how Kerberos can be used for addressing other SOA security concerns such as protecting message confidentiality and detecting message tampering. Later chapters will describe how you can address these issues using PKI, a more scalable solution to the problems Kerberos was designed to address.

Given our reference to PKI as a superior technology compared to Kerberos, you might be wondering if you still need to learn about Kerberos. In our opinion, Kerberos is still a technology you will want to understand for a couple of reasons.

First, Kerberos was the first full-fledged security framework that tackled most of the security requirements we discuss in this book. In fact, several ideas in SOA security standards can be traced to Kerberos. A good understanding of Kerberos will help you better appreciate the more recent SOA security technologies we will introduce in the rest of the book.

The second reason why Kerberos is important is a practical one. Microsoft uses Kerberos (with its own extensions) for security in its server offerings. Therefore, if you are working with any enterprise solutions, it is likely that you will have to deal with Kerberos. This chapter will help you understand and develop the right solution in those cases.

In this chapter, we will first establish Kerberos as a viable security solution for SOA. We do that by identifying the requirements of an authentication system. Next, we will show how to put Kerberos into practice through an example. This example will show you how to use Java Generic Security Service (JGSS), a standard Java API for authentication with Kerberos. By the end of this chapter, you will see how WS-Security supports Kerberos and how you can implement this support in your web services and clients.

5.1. Authentication requirements in SOA

We concluded the previous chapter with a rant about how password-based authentication is inherently insecure. In particular, we pointed out that:

  • The weakest link in a password-based authentication mechanism is the password itself. A password is just too easy to steal and use.
  • Enterprises can go to great lengths to protect passwords by enforcing password policies, but these are not good solutions because they make the lives of users difficult in the name of security. Worse still, there is no policy that can make people guard their passwords well.
  • No password policy can effectively deal with one menace: misuse of passwords by service providers themselves. Users tend to reuse their easy-to-remember passwords for multiple services, many of which cannot be trusted not to steal the password.

These are just the tip of the iceberg. In fact, we can describe several other ways that a password-based system fails to meet the requirements of an authentication system for SOA. Now may be the time to describe these requirements so that we can see how other alternatives fare. Table 5.1 shows these requirements.

Table 5.1. Requirements an authentication system should satisfy if it is to be appropriate for use in SOA

Requirement

Description

Multifactor authentication The authentication system must allow users to claim identity in multiple ways. For example, it must be possible to authenticate based on what the user has or what the user is in addition to, or instead of, what the user knows. What the user knows is his password. He may also have a smart card that can prove his identity. What the user is can be proven with a biometric device such as a digital fingerprint or a retina scan. All these are valid forms of identification and should be supported by themselves or in combination with the rest.
Protection against service-provider abuse The authentication system must not require the user to send a shared secret such as a password or its variation (such as a digest) to a service in order to prove his identity. For example, the authentication system can challenge the caller with a question that can only be answered correctly by someone who has access to the caller’s password. This will better protect the caller’s secret in case the same secret is being used to contact multiple services, not all of which are administered by trusted parties.
SSO to multiple services The authentication system must allow the possibility of using a single set of user credentials to authenticate with a variety of services including legacy services that demand custom passwords.
Delegatable authentication If the user wants to authorize somebody else to invoke a service on his behalf, he should be able to do that without revealing the secret credentials he uses to prove his identity to the authentication system. For example, when visiting a hospital, you may digitally grant your consent to let the hospital’s applications contact applications from other hospitals to obtain your medical history.
Mutual authentication It is not just the servers that are interested in knowing who exactly they are talking to; clients are often as interested knowing for sure that the server they are talking to is indeed a genuine one. For example, when submitting your credentials to a bank, you would like to know for sure that you are indeed communicating with the bank’s application. The authentication system must support mutual authentication of parties involved in a transaction.
Protection against replay attacks Whatever the authentication strategy, it should be possible to protect against the possibility of someone else capturing our requests and simply replaying them.

As you can see, SOA imposes a long list of onerous requirements on authentication systems. For those who are not very familiar with modern security technologies, these requirements might even seem far too ambitious. Fortunately, we live in an age when most of these requirements can be met by not one, but two popular technologies: Kerberos and PKI. Throughout the rest of this chapter, we’ll discuss Kerberos. PKI is introduced in the next two chapters, where we will discuss it in conjunction with encryption and/or signatures.

In the next section, we will introduce the basic set of ideas behind Kerberos and how they all can come together to meet the requirements listed in table 5.1. We will begin our discussion by describing the specific security problem Kerberos was originally meant to address.

5.2. Introduction to Kerberos

Kerberos was invented in the ’80s to solve a security issue for a university campus network. In a typical university, students themselves administer most of the lab machines. According to the Kerberos legend, some of the students found it interesting and funny to capture each other’s passwords by abusing the administrative privileges. The university was forced to come up with a solution to this menace without withdrawing the administrative functions available to the students. Thus, the Kerberos authentication scheme was born. The main problem that Kerberos solved was authentication in multiple applications that did not trust each other. It also tackled the problems of SSO and delegatable authentication described in the previous section.

The modern-day Internet is not too different from the university campus network for which Kerberos was designed. As shown in figure 5.1, there are just too many unknown service providers we rely on without knowing if we can really trust them.

Figure 5.1. Example of service provider abuse Kerberos was designed to eliminate. Users often tend to reuse passwords across services. A malicious service provider can easily abuse the passwords registered by users to spoof user identity on a different service.

In other words, the core problem of protecting one’s identity credentials such as passwords from abuse by malicious service providers is still a relevant one. In the rest of this section, we will describe how Kerberos solves this problem. In particular, we will describe:

  • The basics of encryption that underlie Kerberos
  • How encryption can be leveraged to do authentication
  • Basic concepts in Kerberos such as Key Distribution Center (KDC), long-term key, session key, and ticket
  • The sequence of steps in authenticating a client to a service
  • How Kerberos supports mutual authentication and delegatable authentication

5.2.1. Basic ideas behind Kerberos

Kerberos solves the problem of service provider abuse with a basic set of ideas that are all clever but simple refinements of the ideas that underlie encryption. To understand Kerberos, you need to understand some principles of encryption. Even though we are going to discuss encryption in depth in the next chapter, we will provide here just enough background to understand Kerberos.

First, a quick introduction to encryption.

Encryption protects the secrecy or confidentiality of data passing over the wire by transforming it in such a way that only the intended receiver can recover and read the original data. You have already seen in action the idea of transforming data before sending it on the wire, albeit with a slightly different goal.

Recall the password digest mechanism we discussed in chapter 4. The goal behind the password digest is to enable a client to protect its password from a man in the middle and still prove to a server that it indeed is in possession of the password. A client using a password digest mechanism uses a transformation function (a digest function, to be more specific) f to transform its password p[1] into f(p) before sending it over the wire.

1 Password digest functions transform not just the password p, but also a nonce n and timestamp t to thwart replay attacks, but we will ignore that detail here to keep the discussion simple.

The challenge in digest computation is to pick a specially designed transformation function f that will make it extremely difficult, if not impossible, to do the inverse transformation. As what is passed over the wire is f(p) and not p, a man in the middle cannot steal the password unless he can compute the inverse of f. This means that the intended recipient cannot compute the inverse either. The intended recipient is assumed to already be in possession of the original password p so that it can simply recompute f(p) and match the result against the value transmitted by the sender.

The needs of encryption are clearly different. In encryption, we want the intended recipient (and the intended recipient alone) to recover and read the original data. In other words, the intended recipient must be able to compute the inverse of the transformation function.

So, each encryption function must come with a decryption function also. The encryption function encrypts a secret and the decryption function can decrypt the encrypted secret. To put it mathematically: if f is the encryption function, and g is the decryption function, then g(f(s))= s. Figure 5.2 shows how this works:

Figure 5.2. Relationship between encryption and decryption functions: the decryption function can recover a secret encrypted by the encryption function.

So, if two computer programs want to exchange confidential information, one of them needs to know the encryption function and the other needs to know the decryption function. This is easy enough until we start having multiple senders and receivers. Since no receiver should be in a position to compromise the information being sent to the other, we will need as many encryption functions as there are receivers.

Coming up with a different encryption function for every receiver is not an easy task. A common trick is to simply parameterize a well-known encryption function f with a key k in such a way that only the receiver who knows the key value used can decrypt the message. The sender sends f(s,k) and only the designated receiver who knows the key value k can get back s. Figure 5.3 shows this in action:

Figure 5.3. To protect a secret even when using a well-known encryption function, the sender can use a secret key k that is only known to the intended receiver. The intended recipient uses its knowledge of the secret key to match the modification of the encryption function in the decryption function.

Now that you understand the basics of encryption, the next few subsections describe how Kerberos employs encryption to create an authentication mechanism that does not expose user identity to service provider abuse.

Leveraging encryption to do authentication

Let us take a look again at figure 5.3 and see if we can spot a simple way to leverage encryption to accomplish authentication.

Let’s say each user is assigned a distinctive key that he can use for encryption. Each user can authenticate himself to a server by sending his identity claim (which may, for example, consist simply of a username) both in plain text as well as in an encrypted form. As a server knows the key for each user, it can authenticate users by decrypting the encrypted claim and comparing it with the claim in plain text. Thus, we can use encryption as a way for authentication.

Is this mechanism less susceptible to service provider abuse? The answer is no. Just as with password-based authentication, a user will have to register a secret (which in this case is the decryption key) with every server in order for the servers to authenticate the client. Just as with the password, the server can abuse this knowledge by pretending to be the user.

Yet, there is a way to make sure that the service provider does not need to know the key, thereby eliminating the risk of password or key misuse. Kerberos offers some clever mechanisms to do that, which we will describe in the next subsection.

Basic concepts in Kerberos

To solve the problem of service provider abuse, the designers of Kerberos needed to eliminate the need for users to register a secret with every server. They met this requirement by saying that clients only need to register a secret with a central authority called the KDC and not with every server they wish to interact with.

Under the Kerberos scheme, every user registers a long-term key that can be computed from the user’s password using a well-known function. The KDC holds the long-term keys for all the users. The only two parties that know a user’s long-term key are the user and the KDC itself. Note that the KDC has no knowledge of the user’s password. It only knows the user’s long-term key.

Servers register long-term keys, too. Each server registers a long-term key with the KDC, and that key is once again known only to two parties: the server and the KDC.

Armed with the knowledge of long-term keys for every user and server, the KDC can facilitate secure authentication (and communication) in the following way. When a user wants to authenticate and communicate with a server, she simply requests what is known as a session key. Figure 5.4 shows how a KDC responds to such a request.

Figure 5.4. A KDC can act as a “broker” that supplies a session key.

The KDC dynamically generates a session key and sends two copies of it—one to the user (the client) and one to the server. Each copy of the session key is encrypted using a well-known encryption function that is parameterized by the long-term key of the intended recipient. This protects the session key from all users other than the user and server involved. Each party can use its long-term key to decrypt the session key copy provided by KDC. Subsequently, the client and the server use the session key to encrypt all communication between them.

There is one subtle issue here. For any client and server to establish secure communication, the KDC needs to send a copy of the session key to both of them. But, this scheme imposes an undue burden on the KDC and makes it the bottleneck. An easy way out would be to have the KDC send both copies of the session key to the client. The client can then send the server’s copy of the session key along with the first message.

Figure 5.5 shows this idea pictorially.

Figure 5.5. Refinement of the KDC idea.

This picture is not complete because we have not yet addressed one important aspect. Looking at the session key, there is no way to ascertain which client it is coming from. In other words, we can see how a session key can be used to encrypt all communication between the client and the sender, but we cannot yet see how the session key can be used to do authentication. Kerberos addresses this problem by introducing the concept of a ticket.

A ticket consists of the client’s identity information and the session key granted to the client by the KDC for a particular service, and is encrypted using the service’s long-term key. As the service’s long-term key is not known to anyone except the KDC and the service itself, a ticket cannot be forged. That is, when the server decrypts the ticket, it knows that it can only come from the KDC.

The use of ticket for authentication is shown in figure 5.6. The only difference between figure 5.5 and figure 5.6 is that the server’s copy of the session key is enclosed in a ticket so that the server can determine the identity of the client.

Figure 5.6. Further refinements to combine client identity details and session key as part of a ticket.

Finally, this is what we have: We have a ticket that cannot be made up by any one other than the KDC because only the KDC has knowledge of the target service’s long-term key. The ticket cannot be captured and used by any MIM, as he has no way to decipher the session key needed to communicate with the service. Plus, the ticket can be used for authenticating the client as well as securing the communications between the client and the target service.

You are now familiar with all the basic ideas that Kerberos introduced to eliminate the possibility of service providers abusing knowledge of users’ credentials. A user no longer needs to give out the password that forms the basis for his identity claim; instead, KDC gives out a session key that can only be used by a genuine user who is in possession of his secret. Remember the user’s password never needs to be transmitted—not to service providers and not even to the KDC.

Now that you have an informal understanding of how Kerberos works, we will next describe the formal sequence of steps involved in Kerberos-based authentication.

5.2.2. Authentication sequence

Figure 5.7 shows the sequence of steps involved in Kerberos-based authentication.

Figure 5.7. Authentication sequence between a Kerberos-enabled client and a service. The client application authenticates to the service without revealing the user’s password to the service. The user password is not submitted to the KDC either. Logon Session Key returned by KDC AS can only be used by the client if it is in possession of the user’s long-term key, computable from the user’s password.

In this figure, the KDC is divided into two parts: Authentication Service and Ticket Granting Service. We will describe the functionality of these two parts next. Once you understand the functionality of both parts, you will see that this division helps make Kerberos more efficient.

Authentication Service (AS)

The Authentication Service, as the name indicates, authenticates the clients. The end result of this authentication is that the client gets credentials to talk to a Ticket Granting Service (TGS), which we will describe later.

Staying true to the idea of never asking a user to transmit his password over the network, AS does not demand the password from the client. Instead, AS takes the client’s authentication request at its face value and supplies the credentials the client can in turn use to contact the TGS. The credentials supplied by AS are encrypted with the client’s long-term key; only the real only the real client can make use of the credentials.

Kerberos treats TGS like any other service. To talk to TGS, the client needs a ticket and a session key, and these are exactly what AS provides. Since these are used by the client to contact the TGS that is part of the KDC, these credentials are specially referred to as the Ticket Granting Ticket (TGT) and Logon Session Key, respectively.

Ticket Granting Service (TGS)

The purpose of TGS is to grant a ticket so that a secure session between a client and server can be established. Once the client has a TGT and the Logon Session Key, it can contact the TGS and request a ticket and a session key to talk to the target service. The TGS uses its own long-term key to verify the TGT and, if successful, provides the client with a ticket and session key to use with the target service.

The client contacts the service and just like the TGS, the service can verify the ticket presented using its own long-term key. These steps complete the authentication of the client by the service.

At the start of this chapter, we identified various requirements an authentication mechanism must support in order to satisfy the security needs of SOA. We argued at the time that an authentication mechanism must provide more than just client authentication. For example, we pointed out that mutual authentication and delegatable authentication are also required. In the next section, we will see how Kerberos supports these additional requirements.

5.2.3. Beyond client authentication

Kerberos is designed to do a lot more than authenticate clients. It can provide mutual authentication, delegatable authentication, message confidentiality and integrity guarantees, message sequencing, and replay detection. Of these, we will restrict the discussion here to a very brief introduction of mutual authentication and delegatable authentication, as the rest of the topics will be covered in later chapters.

Mutual authentication

Just as clients authenticate themselves to a service, the service can in turn authenticate itself to the clients. For example, the service can extract a part of the client information embedded in the service ticket and send it back encrypted using the session key. This information proves the service’s identity to the client, as client information and session key can only be extracted from the service ticket by those who have access to the service’s long term key.

Delegatable authentication

A client can ask the KDC to provide it with a ticket that it can then share with a third party to use on its behalf. This request can be made in two ways. If the client is willing to let the third party use all the services on its behalf, the client can ask the AS for a TGT that can be used by the third party in turn to get service tickets from the TGS. If, on the other hand, the client only wishes to grant the third party its privileges to invoke a particular service, it can ask the TGS for a proxy ticket that can be used by the third party to invoke a particular service.

So far, we have seen how Kerberos operates, and what it can do. The rest of the chapter focuses on how to use Kerberos to secure web services, in particular to authenticate users. Here, we get into a lot of the nitty-gritty details of Kerberos, and the material can be difficult to navigate. In the next subsection, we will provide you with a roadmap to the rest of the chapter so that you will know where you are headed and how each of the remaining sections ties into the big picture.

5.2.4. Roadmap for the rest of the chapter

There are two top-level sections in the rest of the chapter. The scope for each of these two sections is:

  • Section 5.3 illustrates through an annotated example how to use Kerberos in a generic client server application. Here, we are not going to deal with any WS-Security–specific details. Instead, we will show how we can communicate with AS and TGS to get a ticket and session key. As such, this part is independent of web services.
  • Section 5.4 covers the WS-Security–specific details we omit in Section 5.3. In particular, it describes the mechanism specified in the WS-Security Kerberos Token Profile to communicate the service ticket from a SOAP client to a SOAP service.

Together, these two sections will show you how to implement Kerberos-based authentication in a web service invocation. Table 5.2 maps each of the steps in the authentication process (shown in figure 5.7) to the subsections where they are discussed. This table also briefly describes the implementation strategy used in each step so that you know what APIs, libraries, and specifications you will learn about in different subsections.

Table 5.2. Implementation roadmap for Kerberos-based authentication of a web service client

Step (as shown in Figure 5.7)

Action

Section describing the implementation

Implementation strategy

Step A to Step D Client obtains TGT and Logon Session Key from KDC AS. Obtains password from the user, computes his long-term key, and uses it to decrypt the Logon Session Key. 5.3.1 Use the JAAS login module for Kerberos available in Sun’s JDK.
Steps E and F Client uses the TGT and Logon Session Key to obtain a service ticket and session key from the KDC TGS. 5.3.1 Use Java GSS API.
Step G Client invokes the Service using service ticket and session key. 5.4.25.4.4 Use a JAX-RPC handler to add Service Ticket to WS-Security headers as described in the Kerberos Token Profile for WS-Security.
    In a full implementation, the session key should be used for protecting the confidentiality and integrity of messages, as described by WS-Security and Kerberos Token Profile for WS-Security. This step is not demonstrated by the examples in this book. If you follow the examples in chapters 6 and 7 that show how to accomplish the same goals using PKI instead of Kerberos, you will be able to read the Kerberos Token Profile for WS-Security and teach yourself how to implement this step. N/A.
Step H Server reads the service ticket submitted by the client, decrypts it using its own long-term key, and validates it. 5.4.5 Extract service ticket from WS-Security header.
    5.3.2 Decrypt and validate the service ticket using Java GSS API.

To summarize the details presented in the table 5.2, section 5.3 will demonstrate the use of JAAS and GSS APIs for obtaining TGT, Logon Session Key, service ticket, and session key on the client-side and validating the service ticket on the server-side. We leave the details of adding a service ticket to a web services call for a later section (section 5.4). As we mentioned earlier, you will not see in this chapter how to use the session keys to protect confidentiality and integrity of messages. You will have to wait until chapters 6 and 7 to see how to take care of those concerns.

5.3. Implementing Kerberos with JAAS and GSS APIs

In this section, we are going to illustrate how a generic client and a server communicate with KDC. In the client part, we will show how a client gets a session key and a ticket. In the server part, we will show how a server understands the ticket. For these purposes, we will use the JAAS API, introduced in the previous chapter, with a JAAS login module that is Kerberos-specific. In addition, we will introduce a new API (GSS) that is used to contact KDC.

5.3.1. Client-side implementation

As shown in figure 5.7, a client needs to do the following to use Kerberos-based authentication:

  1. Get a TGT and a Logon Session Key from KDC AS, and decrypt Logon Session Key using the long-term key computed from the password.
  2. Get a service ticket from KDC TGS.
  3. Use the service ticket to authenticate when contacting the service.

In this section, we are not going to show how to do the third step, as that is the subject of the next section. Because of interdependencies, first we will show the code for step 2, and where it requires the TGT and Logon Session Key, we will show the code for step 1.

Introduction to GSS

To perform step 2—that is, to get a service token once we have the TGT and Logon Session Key—we use the General Security Services (GSS) API defined in the org.ietf.jgss standard Java package. GSS provides a generic API to use mechanisms such as Kerberos and digital certificates for authentication, guarding message confidentiality and integrity, sequencing messages, and guarding against replay attacks. In this chapter, we are only interested in using GSS to get a service ticket from TGS on the client-side and verify service tickets presented by clients on the service-side. Figure 5.8 shows how GSS can be used for this purpose.

Figure 5.8. Use of GSS API for the limited purpose of obtaining a Kerberos service ticket on the client-side and verifying it on the server-side.

Client-side activities B and C in figure 5.8 are the subject of this subsection. Steps A and E are server-side activities and are described in the next subsection (5.3.2). Step D varies based on the client-server protocol, and its implementation in the context of web services is described in section 5.4.

Identifying the target service to GSS

Let’s now look at the code needed to implement steps B and C in figure 5.8. In step B, we should provide two arguments to a method named createContext: The name of the service we wish to get a ticket for and our credentials (TGT). Let’s first see how we can provide the first of these two arguments.

GSS provides a special data structure, GSSName, to identify clients or services. Listing 5.1 shows how we can create an instance of this data structure to identify our target service.

Listing 5.1. Identifying the target service

We start off by getting hold of GSSManager , a factory for creating all other GSS-related objects. We use the retrieved GSSManager instance to identify the target service. As you can see, this is done using the createName method in GSSManager. This method takes two parameters: a name string and a namespace identifier. The namespace identifier has to be specified using an OID. (See the callout regarding the definition of an OID.) In this example, we are using a predefined OID to identify the namespace for host-based services.

 

Note: What is an OID?

Object Identifiers (OIDs) are identifiers assigned by IANA, a standards organization, in compliance with the guidelines laid down by a standard named ASN.1. OIDs are defined in hierarchical namespaces. For example, Kerberos V5 mechanism is identified by the OID 1.2.840.113554.1.2.2, which corresponds to iso.member-body.United States.mit.infosys.gssapi.krb5. GSS uses OIDs to identify security mechanisms and name formats.

 

Providing the client credentials (TGT) to GSS

You have seen how to identify the target service. Now, let’s shift our attention to providing the second argument to the createContext method shown in step B of figure 5.8. Listing 5.2 shows the code required to provide client credentials to GSS.

Listing 5.2. Creating client credentials

In this listing, we first create a GSSName to identify the client name just like we identified the service in the previous example. Notice the predefined OID we use to indicate the namespace. We then create the client credentials we want to provide to GSS. What we really want to do here is encapsulate the TGT in a GSS data structure that can be later used when getting the service token using GSS. Instead, what do we see here? We are simply providing the client name and specifying that we want to create a Kerberos ticket that can be used to initiate a service invocation. How does GSS get to the TGT required to obtain a service ticket from the TGS? The answer is that GSS obtains the TGT automatically from the Subject in the current access control context.

 

Note: What is current access control context in Java?

The Java security model controls access to resources based on (a) what code is requesting access to a resource and (b) the user executing that code.

The first of these checks is required because an application may sometimes attempt to run code that it downloaded at runtime from untrusted sources. For example, a browser may run an applet downloaded from a web site. Clearly, such code should have restricted permissions.

The second check is required because an application may need to utilize the privileges granted to a user if it has to act on the user’s request. For example, in our case, we need to utilize the privileges granted to a user in order to request a service ticket using his TGT.

The combination of the results of these two checks constitutes an access control context. The access control context that is in effect when executing a piece of code is said to be current access control context. Applications can switch the current access control context when needed. One way of doing so is provided by the doAs static method in javax.security.auth.Subject.

 

If GSS is to get the TGT by looking up the Subject in the current access control context, we need to do the following activities first:

  1. Create a Subject instance that represents the user’s identity.
  2. Obtain a TGT and Logon Session Key from the KDC and store them as private credentials of the Subject instance created in step 1.
  3. Associate the Subject instance with current access control context in the Java application.

The first two steps can be carried out using Krb5LoginModule, a JAAS login module for Kerberos that is shipped as part of Sun’s implementation of JDK. The last step is carried out using the doAs static method in javax.security.auth.Subject.

Listing 5.3. Obtaining TGT via JAAS login and passing it via current access control context to ServiceTicketGrabber

In this code listing, we first do the JAAS-based login that we are so familiar with by now. We assume here that JAAS has been configured to use the Krb5LoginModule for the named application. The Krb5LoginModule contacts the KDC on the host given by the java.security.krb5.kdc system property and obtains a TGT and a Logon Session Key from the KDC AS. It adds the obtained TGT and Logon Session Key to the private credentials of the Subject associated with the LoginContext.

In order to make GSS use the TGT we obtained using Krb5LoginModule, we need to set the Subject associated with the current access control context when executing the GSS calls. So, we get the logged-in Subject from the JAAS Login-Context and use the doAs static method in javax.security.auth.Subject to switch the Subject in the current access control context when executing Service-TicketGrabber. The doAs method takes two arguments: the subject to set in the current access control context and an instance of a class that implements the java.security.PrivilegedAction interface. To implement the PrivilegedAction interface, all a class needs to do is implement a run() method that returns any Object. We will next look at how our ServiceTicketGrabber class implements this method.

Obtaining the service ticket

What should be in the run() method of ServiceTicketGrabber? As the name suggests, we would like to grab a service ticket from the KDC using GSS APIs. So, as shown in steps B and C of figure 5.8, the run() method must make createContext and initSecContext calls. These calls require the gssManager, serviceName, and clientCredentials created in listings 5.1 and 5.2. Listing 5.4 shows selected parts of the run() method.

Listing 5.4. ServiceTicketGrabber implementation

We first identify the target service and provide GSS with the client credentials . This step provides us with a GSSContext instance upon which we can call the initSecContext(...) method to obtain a service ticket . The initSecContext method takes three arguments: a byte array representing the token generated by the peer (service), the offset into the byte array where the token begins, and the length of the token. All three arguments are given dummy values here, as there is no token that the service provides to the client before the client asks KDC for a service ticket.

Strictly speaking, the byte array returned by gssContext.initSecContext is a little more than a Kerberos service ticket in two ways. First, Kerberos itself specifies that the client should wrap the service ticket in a data structure named KRB_AP_REQ when sending the ticket to the server. In addition to a service ticket, a KRB_AP_REQ structure consists of a protocol version number, message type, application options, and what is called an authenticator to defend against replay-of-service tickets. Second, GSS wraps KRB_AP_REQ into another data structure that also contains a mechanism identifier. The mechanism identifier is required because GSS is not restricted to Kerberos and can be used with many other security mechanisms. What is returned by gssContext.initSecContext is a KRB_AP_REQ wrapped by GSS.

Phew! We finally have a service ticket we can use. Observe that GSS stays independent of the client-server protocol by leaving it to the client to figure out how the service token should be sent to the service. We will soon see how we can add a Kerberos service ticket to the WS-Security header, but first, let us complete the task we set for ourselves in this section: understanding how we can implement Kerberos-based authentication in generic client-server interaction. We still have to understand how we verify a service ticket submitted by a client on the service side.

5.3.2. Service-side implementation

Figure 5.7 tells us that we need the service’s long-term key in order to verify a service ticket (step H). If we do a JAAS login using the Krb5LoginModule, in the process of decrypting the Logon Session Key provided by the AS, it will automatically compute the long-term key and store it in the Subject associated with the LoginContext. So, just like on the client-side, the first thing to do on the server-side is a JAAS login using the Krb5LoginModule. We will not show the code for doing this, as you should be well-versed with it by now.

Once we have the service’s long-term key, we can verify the service ticket using the createContext and acceptSecContext methods shown in steps A and E of figure 5.8, respectively. Listing 5.5 shows these calls in action.

Listing 5.5. Validation of a service ticket received from a client

Just as on the client-side, in this listing, we first get a handle to the GSSManager instance and use it to identify the service and create its credentials . The credentials mention that the service will simply accept calls instead of initiating calls like the client.

We create a GSSContext next and it is at this time that GSS looks up the server’s long-term key from the Subject associated with the current access control context. For this reason, this listing must be executed as the Subject that logged in using JAAS Krb5LoginModule. For brevity, we have not shown that here, but you have already seen in listing 5.3 how to do it.

Finally, we verify the ticket received from the client .

If we want to do mutual authentication, we should capture the bytes returned by acceptSecContext and return them to the client along with the service response. GSS even allows for security mechanisms that require multiple round-trips to initialize and accept security context. Kerberos of course does not require multiple round-trips, as we saw here. See the javadocs for GSS APIs for more such advanced details.

In this section, you have seen how a generic client and a server communicate with the KDC in order to use Kerberos-based authentication. What you have not seen yet is how a client presents its service ticket when invoking a SOAP-based web service (step D in figure 5.8). This is the topic of the next section.

5.4. Using Kerberos with WS-Security

The Kerberos token profile for WS-Security standardizes the usage of Kerberos in SOAP-based message exchanges. In this section, we will illustrate the use of this standard through an example. We will first tell you how you can set up the environment required for running the example and run it. Using the request message you see in the example, we will explain how WS-Security allows Kerberos tickets to be included in the Security header entry of a SOAP message. Then we will show you the code required on the client- and server-sides to construct and parse a Security header with a Kerberos ticket.

5.4.1. Running the Kerberos example

To run the Kerberos example, you will need a KDC instance. The instructions given here assume a KDC instance available with Active Directory in Microsoft Windows Server 2003. It is outside the scope of this book to provide instructions on installing an Active Directory instance. Table 5.3 provides the instructions to run the example.

Table 5.3. Steps to run the example that illustrates how WS-Security supports Kerberos-based authentication

Step

Action

How To

1 Set up your environment. If you have not already set up the environment required to run the examples in this book, please refer to chapter 2 to do so. ant deploy should install all the examples.
2 Create two user accounts—one for client and one for server—in Active Directory. Use the Active Directory Users and Computers utility available under Administrative Tools in the Windows Control Panel to create the required user accounts.
3 Export the server’s account (also called the service principal) information into a file (called the keytab file). The info in this file can be used by the server to validate service tickets submitted by clients. Install the tools available under the SupportTools folder of your Windows CD. This gets you a tool named ktpass that you can use to generate the keytab file. The command to generate the keytab file is: ktpass -princ soasecimpl/myhost.myorg.com@REALM -mapuser username@REALM -pass mypass -out c: empsoasecimpl.keytab In this command, replace myhost.myorg.com with the name of the host you have deployed the example web services on, REALM with your KDC realm name, username with the name of the user account you created in Active Directory for use by the server, mypass with the password you wish to assign to the service, and c: empsoasecimpl.keytab with the path to the file in which you want the service principal information to be stored. Note that the REALM name is always an uppercase version of the windows domain name (for example MYORG.COM)
4 Customize the JAAS configuration file to match the set up you have done in previous steps. The JAAS configuration file, example3-jaas.conf, can be found in the conf/ directory of the example archive you downloaded in Step 1. You will see that this file lists JAAS modules for three applications: soasecimpl: The server-side uses the JAAS modules listed for this app to authenticate client requests. GSSContextAcceptanceJAASModule, the second module listed for this app, uses GSS API to validate service ticket shown in listing 5.5. You will need to set the serviceGSSName option for this module to [email protected], where myhost.myorg.com is the name of the host you have deployed the example web services on. soasecimplclient: The client-side uses the Krb5LoginModule listed here to obtain the TGT and service ticket as shown in listings 5.3 and 5.4. soasecimplserver: The server-side uses the Krb5LoginModule to read the keytab file generated in step 3 and compute the long-term key needed to validate the service tickets submitted by the clients. You will need to customize the keyTab and principal options of Krb5LoginModule. Set the former to the path to the keyTab file produced in step 3 and the latter to soasecimpl/myhost.myorg.com, where myhost.myorg.com is the name of the host you have deployed the example web services on.
5 Customize JVM system properties required by the client-side to match the setup you have done in previous steps. As we will be using JAAS to obtain the TGT and service tickets, we need to set a system property in the client’s JVM to point to the JAAS configuration file we set up in step 4. The Krb5LoginModule invoked by JAAS in turn looks up KDC information from JVM system properties. Our example client code also looks up JVM system properties for information such as the client user name, client user password, and service name. All these system properties needed by the client-side are set up by the Ant script we use to run the example client. This script gets the values required for these system properties from a file named build.properties. Hence, you need to customize a few values in build.properties as described next. kerberos.kdc: Active Directory hostname kerberos.realm: Active Directory realm name kerberos.testclient.user: Username of the client-side user you have created in step 2 kerberos.testclient.password: Password of the client-side user you have created in step 2 kerberos.testservice.gssname: [email protected], where myhost.myorg.com is the name of the host you have deployed the example web services on. jaas.config: Absolute path of the JAAS configuration file you have customized in step 3.
6 Customize JVM system properties on the server-side to match the setup you have done in previous steps. Set the JAVA_OPTS environment variable to -Djava. security.auth.login.config=path-to-jaas. conf-file -Djava.security.krb5.realm=active-directory-realm-name -Djava.security.krb5. kdc=active-directory-realm-name.
7 Restart Tomcat server. Run shutdown and startup scripts (.bat files if you are using Windows and .sh files if you are on Linux/Solaris/OS X) found in Tomcat’s bin directory.
8 If it is not already running, start TCP monitor Run ant tcpmon so that you can observe the conversation. Check the XML Format check box to allow tcpmon to format shown requests and responses.
9 Run the example. Run ant demo –Dexample.id=3. You should be able to view the request-response pairs going through the tcpmon console.

After these steps, you should be seeing the execution of a number of web service calls, as shown in figure 4.1. The last of these calls is the one that relies on Kerberos for authentication. Take a look at how the request looks in this call to understand how Kerberos can be used with WS-Security.

In the next section, we will describe how to add a Kerberos ticket to the WS-Security header.

5.4.2. Adding a Kerberos ticket to a WS-Security header

In section 5.3, you saw how a generic client can get a service ticket from a KDC. The client needs to submit the service ticket to the server, along with its requests. In this section, we will describe how this can be done in the case of SOAP-based web services using standard elements, attributes, and attribute values defined by WS-Security and the Kerberos Token Profile for WS-Security.

WS-Security defines a BinarySecurityToken element that can be included in the Security header to carry binary tokens such as Kerberos tickets and digital certificates. Listing 5.6 shows an example request to see how this looks in practice.

Listing 5.6. Use of BinarySecurityToken to carry a Kerberos service ticket.

As you can see, to use Kerberos-based authentication, a SOAP client takes the GSS-wrapped KRB_AP_REQ token (computed using the code in listing 5.4), encodes it in base64, and sends it as a BinarySecurityToken in the Security header. On the server-side, the server needs to extract the service ticket from the BinarySecurityToken in the Security header and validate it as shown in listing 5.5 to complete authentication. Quite simple, isn’t it?

5.4.3. Using a Kerberos ticket for authentication

Let us now look at sample implementations of a SOAP client and service that use Kerberos-based authentication. Figure 5.9 provides an overview of the approach we take.

Figure 5.9. Overview of the implementation of a Kerberos-based authentication scheme

The client-side and server-side components you see in figure 5.9 are exactly the ones you saw in figure 4.2. Of course, the components have a little more code in them now to support Kerberos-based authentication. The ClientSideWSSecurityHandler on the client adds a BinarySecurityToken to the Security header. The first handler on the server-side, WSSecurityUsernameHandler, parses the Security header, extracts the Kerberos token and saves it in the message context. The second handler, JAASAuthenticationHandler, validates this token and populates the authenticated subject information in the message context. The end service makes use of the authenticated subject information in carrying out its business logic.

In the next two subsections, we walk you through the additional code that goes into client-side and server-side components to support Kerberos-based authentication. Let’s start with the client-side.

5.4.4. Adding a Kerberos ticket on the client-side

We extend the ClientSideWSSecurityHandler initialization code you have seen in previous chapter to take an additional configuration parameter, authMode, to choose between clear-text/digest passwords and Kerberos.

Listing 5.7. Extract from init method of ClientSideWSSecurityHandler

In case Kerberos is configured as the authentication mode, we use JAAS and GSS APIs as shown earlier to get a service ticket. Once we get the service ticket, adding it as a BinarySecurityToken in the WS-Security header is easy, as shown in listing 5.8.

Listing 5.8. Adding a Kerberos service ticket as a BinarySecurityToken in the Security header

That is all there is to it on the client-side. Let us now look at the code on the server-side that extracts the BinarySecurityToken from the Security header and validates it.

5.4.5. Processing a Kerberos ticket on the service-side

Just as we did with usernames and passwords, we extract a Kerberos ticket from the WS-Security header in WSSecurityUsernameHandler, store it in MessageContext, and validate it in JAASAuthenticationHandler. Figure 5.9 illustrates our approach. Use it as your guide to understanding the overall flow as you read through the code presented in this section.

Copying a Kerberos ticket from Security header to message context

Listing 5.9 shows the code in WSSecurityUsernameHandler to copy the Kerberos service ticket from the Security header to message context.

Listing 5.9. Code to copy a Kerberos service ticket in Security header to message context

In this code, we first locate a BinarySecurityToken element in the Security header . We assume here that the Kerberos token is in the first Binary-SecurityToken element. This is only a simplifying assumption on our part. There is no restriction that a Kerberos token should be the first BinarySecurityToken in a WS-Security header.

Once we locate a BinarySecurityToken element, we look up whether it has an attribute named EncodingType . If there is none, then, as specified by WS-Security, we assume that the token is base64-encoded and decode it. Of course, if the encodingType attribute does indeed exist, we inspect its value and apply base64 decoding only if appropriate.

We inspect the ValueType attribute of the BinarySecurityToken element to see if the value of the token is a GSS-wrapped KRB_AP_REQ . If it is, we copy the decoded token to MessageContext and make it available to the downstream handlers and the service.

Validating a Kerberos ticket available in message context

To complete the authentication, we need to modify the JAASAuthenticationHandler you saw in chapter 4 to validate the Kerberos ticket available in message context. To do this, we will enhance MessageContextBackedCallbakHandler to handle a GSSTokenCallback and simply code a new JAAS module, GSSContextAcceptanceJAASModule, that JAASAuthenticationHandler can use to validate the ticket.

In listing 5.5, you saw the code needed to verify service tickets on the service-side. You also saw in the previous chapter how to write a JAAS LoginModule. So, the only new ground we are breaking in GSSContextAcceptanceJAASModule is in the commit() and logout()/abort() methods. Let’s first describe the commit() method, where we need to save the authenticated client’s information in the subject of the login context. Listing 5.10 shows how we do this.

Listing 5.10. Code to populate subject information post-login in GSSContextAcceptanceJAASModule

In this code, we create a UsernamePrincipal from the client name available through GSSContext and add it to the subject .

The GSSContext established during login should be disposed of at the time of logout() or if the login process is aborted.

Listing 5.11. Code to dispose of GSSContext during logout
public boolean logout() throws LoginException {
    try {
        gssContext.dispose();
    } catch (GSSException e) {
        logger.warn(e);
    }
    logger.debug("aborted login process");
    return true;
}

You now know how to implement Kerberos-based authentication in SOAP-based web services and clients. In the previous chapter, you saw how to implement two variants of password-based authentication. In chapter 7, you will see how to use digital signatures for authentication. Which of these methods should you use for any given web service call? We will discuss this question in the next section.

5.5. What authentication scheme to use?

In these first five chapters, we have seen three different kinds of authentication systems. We are sure to see more in the coming chapters. There is no one solution that is suitable for all business situations; which is why we are covering so many different schemes.

For example, consider the case of our running example, the brokerage firm. How does the brokerage firm authenticate trade requests coming in from end users? Users may be submitting trades using a web page or a desktop application. The web page can use HTTPS and desktop applications can use SSL to encrypt all traffic exchanged, including the username and password submitted along with the request.

In such a scenario, a simple username and password scheme works well. It is fully protected by transport-level security in SSL. Besides, the data transmitted is small enough not to impose a performance penalty. Also, we can design it in such a way that username and password are only seen by one trusted application on the server-side. That is, these applications have only point-to-point communication.

Brokerage user profiles may be stored in a single database or LDAP directory. In either case, we can directly program without using JAAS.

Let us consider the employees of the brokerage firm. They too can use applications over HTTPS. These applications tend to be complex. Communication may not be point-to-point like in the previous case. In such a case, transport-level security is not sufficient. The right way would be to secure the communication using a password digest. Since it is likely that all the employee profiles are stored in a single source such as LDAP, our applications understand what kind of transformation to apply to the password before generating the digest.

Even in this scenario, one can make a case for not using JAAS and directly validating the credentials within the application code. If another brokerage company merges with our brokerage firm, suddenly we need to grant permission to a whole new set of employees. We can make profiles for the other firm’s employees also be part of the same LDAP server, but there may be reasons why this is not desirable. If we use JAAS, it is easy to set up a stack of login modules in the configuration without too much of a code change.

Now, suppose the brokerage firm already standardized on Active Directory. In that case, using Kerberos for web services authentication deserves strong consideration. For one thing, there will be in-house expertise in dealing with Kerberos. It is likely that will be other applications that are “Kerberized.” So, it would not be too much to set up and configure the web services to use Kerberos.

On the other hand, consider the case of a brokerage firm that does not already have Kerberos set up in any form. In such a case, it may not make sense to introduce Kerberos just because the firm wants to adopt SOA. It is important to recognize that the power of Kerberos comes at the cost of substantial complexity. We have seen in this chapter that developing client-server applications that rely on Kerberos for authentication is a far more complex task than username/password authentication. When deploying Kerberos in a large enterprise, getting all the network applications to work with Kerberos is an expensive proposition due to its complexity.

Of course, in organizations that need a very high level of security, the expense is well worth it, as Kerberos provides a security mechanism far superior to traditional username/password authentication. For instance, traditional password mechanisms cannot support delegatable authentication and mutual authentication, but Kerberos can.

But even in cases where complexity is not a concern, Kerberos may not be the best choice. Kerberos does not scale outside an enterprise, as it depends on a central KDC or a chain of KDCs with established trust relationships. For example, it is unlikely that a brokerage will be able to secure its communications with exchanges using Kerberos. PKI, introduced in the next chapter, provides a better option most of the time. PKI is more widely deployed and understood than Kerberos. With commercial vendors marketing digital certificates, there is much more momentum behind PKI than behind Kerberos. For example, while the Kerberos token profile for WS-Security took a long while to come out of draft stage, the X.509 Certificate token profile was fairly quickly adopted as a standard.

Still, there are several legitimate uses for Kerberos, the most important reason being, of course, that it is used in Microsoft server software. There is another reason—it can be the basis for a full-fledged security solution. Indeed, we can use the lessons from Kerberos in SOA security frameworks. You will learn about these frameworks in chapter 8.

5.6. Summary

We started this chapter by going one step ahead of what we did at the end of the last chapter. While the previous chapter ended by simply identifying what’s wrong with password-based authentication in SOA, we started this chapter by drawing up all the requirements we can think of for an ideal authentication mechanism. Once we identified all the requirements, we claimed that the requirements we drew up are not as unattainable as they may first seem. We are sure you agree with us now that you have learned about Kerberos.

Kerberos was primarily designed to prevent abuse of user credentials by malicious service providers. Kerberos accomplishes this goal by cleverly leveraging the ideas behind encryption to eliminate the need for transmitting a password over the network. Anyone can ask the KDC, a central authority, for a ticket to contact a service, but only a genuine client will be able to use the ticket provided by KDC to establish its identity to the service.

The Kerberos Token Profile for WS-Security specifies how to use Kerberos for authentication in web services. You have learned the details of this specification in this chapter. You also learned how to implement Kerberos-based authentication in web services using the Kerberos JAAS module available with Sun’s JDK and Java GSS APIs.

Kerberos is a complex topic. Whole books have been written about it. In this chapter, we limited ourselves to explaining how Kerberos can be used for authentication in web service implementations.

Kerberos can also be used for protecting the confidentiality and verifying the integrity of messages. The next two chapters discuss these aspects of security in depth. Although we will not be showing any Kerberos-related examples in the next two chapters, interested readers will not find it difficult to construct such examples on their own by combining the material presented in this book with Kerberos Token Profile for WS-Security.

Suggestions for further reading

..................Content has been hidden....................

You can't read the all page of ebook, please click here login for view all page.
Reset
3.144.15.43