RFC 2853
Generic Security Service API Version 2 : Java Bindings
RFC 2853: Generic Security Service API Version 2 : Java Bindings ๐
Part 1 of 18 โจ
Okay so the title block first, bestie ๐ This is RFC 2853, called "Generic Security Service API Version 2 : Java Bindings". It was written by J. Kabat of ValiCert, Inc. and M. Upadhyay of Sun Microsystems, Inc. It's a Standards Track document dated June 2000. The title block doesn't say it obsoletes or updates anything ๐ง
Status of this Memo ๐ธ
This is the official vibe check ๐ The doc specifies an Internet standards track protocol for the Internet community, and it's asking for discussion and suggestions for improvements ๐ซถ For the standardization state and status of this protocol, check the current edition of the "Internet Official Protocol Standards" (STD 1). Distribution of this memo is unlimited ๐
Copyright Notice ๐
Copyright (C) The Internet Society (2000). All Rights Reserved. โจ
Abstract ๐ฆ
Okay so the GSS-API (the Generic Security Services Application Program Interface) gives app programmers uniform access to security services, even though it sits on top of a bunch of different underlying cryptographic mechanisms ๐ฅ This doc specifies the Java bindings for GSS-API. The language-independent, conceptual version is described in RFC 2743 [GSSAPIv2-UPDATE] ๐
The GSS-API lets a caller application authenticate a principal identity, delegate rights to a peer, and apply security services like confidentiality and integrity on a per-message basis. It's giving main character energy ๐ Examples of security mechanisms defined for GSS-API are The Simple Public-Key GSS-API Mechanism [SPKM] and The Kerberos Version 5 GSS-API Mechanism [KERBV5] ๐
Table of Contents ๐ง
Bestie, the text you sent is the Table of Contents again, and we already did that in part 1 ๐ . I don't have the body of RFC 2853 for part 2 yet, so I can't rewrite it without making things up ๐.
Could you paste the actual section text for part 2, starting where part 1 stopped? Once I have it, I'll keep every MUST, SHOULD, code block, and section number exactly as the original has them โจ
1. Introduction ๐
okay so this doc specifies Java language bindings for the Generic Security Services Application Programming Interface Version 2 (GSS-API) ๐. GSS-API Version 2 is described in a language independent format in RFC 2743 [GSSAPIv2-UPDATE]. The GSS-API lets a caller application authenticate a principal identity, delegate rights to a peer, and apply security services such as confidentiality and integrity on a per-message basis โจ.
This document leverages the work the WG did in the area of RFC 2743 [GSSAPIv2-UPDATE] and the C-bindings RFC 2744 [GSSAPI-C]. Whenever appropriate, text has been used from the C-bindings RFC 2744 to explain generic concepts and provide direction to the implementors ๐ง.
The design goals of this API were to satisfy all the functionality defined in RFC 2743 and to provide those services in an object oriented way ๐ฆ. The spec also aims to satisfy the needs of both types of Java app developers: the ones who want access to a "system-wide" GSS-API implementation, and the ones who want to provide their own "custom" implementation ๐ .
A "system-wide" implementation is one that's available to all applications in the form of a library package ๐. It may be a standard package in the Java runtime environment (JRE) being used, or it may be additionally installed and accessible to any application via the CLASSPATH.
A "custom" implementation, on the other hand, is one that would, in most cases, be bundled with the application during distribution ๐. It's expected that a custom one would be meant to provide for some particular need of the application, such as support for some specific mechanism.
The design of this API also aims to provide a flexible framework to add and manage GSS-API mechanisms ๐ธ. GSS-API leverages the Java Cryptography Architecture (JCA) provider model to support the plugability of mechanisms. Mechanisms can be added on a "system-wide" basis, where all users of the framework will have them available. The specification also allows for the addition of mechanisms per-instance of the GSS-API.
Lastly, this specification presents an API that will naturally fit within the operating environment of the Java platform ๐. Readers are assumed to be familiar with both the GSS-API and the Java platform, so no hand-holding here, bestie ๐.
2. GSS-API Operational Paradigm ๐ฆ
The Generic Security Service Application Programming Interface Version 2 [GSSAPIv2-UPDATE] defines a generic security API for calling applications. It lets a communicating application authenticate the user associated with another application, delegate rights to another application, and apply security services such as confidentiality and integrity on a per-message basis ๐.
There are four stages to using GSS-API, and they go like this:
- The application acquires a set of credentials with which it may prove its identity to other processes. The application's credentials vouch for its global identity, which may or may not be related to any local username under which it may be running ๐ชช.
- A pair of communicating applications establish a joint security context using their credentials. The security context encapsulates shared state information, which is required in order that per-message security services may be provided. Examples of state information that might be shared between applications as part of a security context are cryptographic keys, and message sequence numbers ๐๏ธ. As part of the establishment of a security context, the context initiator is authenticated to the responder, and may require that the responder is authenticated back to the initiator. The initiator may optionally give the responder the right to initiate further security contexts, acting as an agent or delegate of the initiator. This transfer of rights is termed "delegation", and it's achieved by creating a set of credentials, similar to those used by the initiating application, but which may be used by the responder ๐ค.
A GSSContext object is used to establish and maintain the shared information that makes up the security context. Certain GSSContext methods will generate a token, which applications treat as cryptographically protected, opaque data. The caller of such a GSSContext method is responsible for transferring the token to the peer application, encapsulated if necessary in an application-to-application protocol. On receipt of such a token, the peer application should pass it to a corresponding GSSContext method, which will decode the token and extract the information, updating the security context state information accordingly ๐.
- Per-message services are invoked on a
GSSContextobject to apply either integrity and data origin authentication, or confidentiality, integrity and data origin authentication, to application data, which are treated by GSS-API as arbitrary octet-strings ๐. An application transmitting a message that it wishes to protect will call the appropriateGSSContextmethod (getMICorwrap) to apply protection, and send the resulting token to the receiving application. The receiver will pass the received token (and, in the case of data protected bygetMIC, the accompanying message-data) to the corresponding decoding method of theGSSContextinterface (verifyMICorunwrap) to remove the protection and validate the data โ . - At the completion of a communications session (which may extend across several transport connections), each application uses a
GSSContextmethod to invalidate the security context and release any system or cryptographic resources held ๐งน. Multiple contexts may also be used (either successively or simultaneously) within a single communications association, at the discretion of the applications ๐ฆ.
3. Additional Controls ๐
This section discusses the optional services that a context initiator may request of the GSS-API before the context establishment ๐๏ธ. Each of these services is requested by calling the appropriate mutator method in the GSSContext object before the first call to init is performed. Only the context initiator can request context flags, so the acceptor is not invited to this party ๐
โโ๏ธ.
The optional services defined are:
- Delegation: The (usually temporary) transfer of rights from initiator to acceptor, enabling the acceptor to authenticate itself as an agent of the initiator ๐งโ๐คโ๐ง.
- Mutual Authentication: In addition to the initiator authenticating its identity to the context acceptor, the context acceptor should also authenticate itself to the initiator ๐ช.
- Replay Detection: In addition to providing message integrity services,
GSSContextper-message operations ofgetMICandwrapshould include message numbering information to enableverifyMICandunwrapto detect if a message has been duplicated ๐ฏ. - Out-of-Sequence Detection: In addition to providing message integrity services,
GSSContextper-message operations (getMICandwrap) should include message sequencing information to enableverifyMICandunwrapto detect if a message has been received out of sequence ๐งฉ. - Anonymous Authentication: The establishment of the security context should not reveal the initiator's identity to the context acceptor ๐ซฅ.
Some mechanisms may not support all optional services, and some mechanisms may only support some services in conjunction with others ๐. The GSSContext interface offers query methods that let the calling application verify which services will be available from the context when the establishment phase is complete. In general, if the security mechanism is capable of providing a requested service, it should do so even if additional services must be enabled in order to provide the requested service ๐ซ. If the mechanism is incapable of providing a requested service, it should proceed without the service, leaving the application to abort the context establishment process if it considers the requested service to be mandatory ๐ฆ.
Some mechanisms may specify that support for some services is optional, and that implementors of the mechanism need not provide it. This is most commonly true of the confidentiality service, often because of legal restrictions on the use of data-encryption, but it may apply to any of the services ๐. Such mechanisms are required to send at least one token from acceptor to initiator during context establishment when the initiator indicates a desire to use such a service, so that the initiating GSS-API can correctly indicate whether the service is supported by the acceptor's GSS-API ๐จ.
3.1. Delegation ๐
The GSS-API allows delegation to be controlled by the initiating application via the requestCredDeleg method before the first call to init has been issued ๐. Some mechanisms do not support delegation, and for such mechanisms, attempts by an application to enable delegation are ignored ๐.
The acceptor of a security context, for which the initiator enabled delegation, can check whether delegation was enabled by using the getCredDelegState method of the GSSContext interface. In cases when it is, the delegated credential object can be obtained by calling the getDelegCred method ๐. The obtained GSSCredential object may then be used to initiate subsequent GSS-API security contexts as an agent or delegate of the initiator. If the original initiator's identity is "A" and the delegate's identity is "B", then, depending on the underlying mechanism, the identity embodied by the delegated credential may be either "A" or "B acting for A" ๐ญ.
For many mechanisms that support delegation, a simple boolean does not provide enough control ๐๏ธ. Examples of additional aspects of delegation control that a mechanism might provide to an application are duration of delegation, network addresses from which delegation is valid, and constraints on the tasks that may be performed by a delegate ๐๏ธ๐๐งพ. Such controls are presently outside the scope of the GSS-API. GSS-API implementations supporting mechanisms offering additional controls should provide extension routines that allow these controls to be exercised (perhaps by modifying the initiator's GSS-API credential object prior to its use in establishing a context) ๐ ๏ธ. However, the simple delegation control provided by GSS-API should always be able to override other mechanism-specific delegation controls. If the application instructs the GSSContext object that delegation is not desired, then the implementation must not permit delegation to occur. This is an exception to the general rule that a mechanism may enable services even if they are not requested: delegation may only be provided at the explicit request of the application ๐ซ๐ซถ.
3.2. Mutual Authentication โจ
okay so, usually the context acceptor is gonna demand that the context initiator proves who they are ๐ so it can decide if they get access before it does any service for them. sometimes the initiator wants the acceptor to prove itself too, which is giving double verification ๐ GSS-API lets the initiating app ask for this by calling the requestMutualAuth method of the GSSContext interface with a "true" parameter, and it has to do that before the first call to init. the initiating app then finds out whether the context acceptor actually authenticated itself ๐
ngl not every mechanism supports mutual auth, and some mechanisms always do it no matter if the app asked ๐ for some mechanisms, mutual auth is required to get replay or out-of-sequence message detection working. for those, asking for either of those services will automatically turn on mutual auth ๐ซถ
3.3. Replay and Out-of-Sequence Detection ๐
the GSS-API may be able to detect mis-ordered messages once a security context is established ๐ messages can be protected by either app by calling the getMIC or wrap methods of the GSSContext interface, and the peer app verifies them by calling verifyMIC or unwrap on its own GSSContext object ๐
getMIC works out a cryptographic checksum of an app message and gives that checksum back in a token. the app passes both the token and the message to the peer app, which hands them to verifyMIC on its GSSContext object ๐ฆ
wrap is kinda the same vibe, but it puts the checksum and the message together inside one single token ๐ the app sends that token over, and the peer hands it to unwrap on its GSSContext object, which pulls out the message and checks the checksum ๐ง
either pair of routines might be able to spot out-of-sequence delivery or duplicated messages โจ the details about those mis-ordered messages come back through the supplementary query methods of the MessageProp object that each of these routines fills in ๐
a mechanism doesn't have to keep a list of every token it has ever processed to report these status codes ๐ธ a typical mechanism might just remember the most recent "N" tokens it processed, which lets it tell apart duplicates and missing tokens inside those last "N" messages ๐ if a token shows up that's older than the most recent "N", the isOldToken method of that MessageProp instance returns "true" ๐
3.4. Anonymous Authentication ๐ต๏ธโโ๏ธ
sometimes an app wants to start authentication so it can check who its peer is, but it doesn't want to reveal its own identity ๐ picture an app that gives access to a database full of medical info and offers unrestricted access to its service ๐ฅ a client might want to verify the service (so it can trust the info it gets back), but it might not want the service to learn who the client is. maybe that's about privacy and the specific questions it asks, or maybe it just doesn't want to end up on mailing lists ๐ญ
normally in GSS-API, the initiator's identity gets handed to the acceptor during context establishment ๐ชช but initiators can ask for their identity to not be revealed to the context acceptor ๐ซฃ lots of mechanisms don't support anonymous auth, and for those the request won't be honored ๐ฌ an authentication token still gets generated anyway, but the app is always told when a requested service isn't available, and it can choose to abort context establishment if anonymity matters more to it than the other security services that would need a context ๐
in addition to telling the app that a context was set up anonymously (through the isAnonymous method of the GSSContext class), the getSrcName method of the acceptor's GSSContext object will return a reserved internal-form name for those contexts. that name is defined by the implementation ๐ง
the toString method of a GSSName object that stands for an anonymous entity will return a printable name โจ that string has to be syntactically different from any valid principal name the implementation supports ๐ the name-type object identifier is an oid representing the value of NT_ANONYMOUS. this name-type oid is defined as a public, static Oid object of the GSSName class ๐ the printable form of an anonymous name should be picked so it implies anonymity, since it might show up in stuff like audit logs ๐ for example, the string "<anonymous>" might be a good pick, as long as no valid printable names the implementation supports can start with "<" and end with ">" ๐
when you use the equal method of the GSSName interface and one of the operands is a GSSName instance for an anonymous entity, the method must return "false" ๐ซ
3.5. Confidentiality ๐
if a GSSContext supports the confidentiality service, the wrap method can be used to encrypt app messages ๐คซ the encryption is selective, and you control it with the setPrivacy method of the MessageProp object that gets used in the wrap method ๐
3.6. Inter-process Context Transfer ๐งโโ๏ธ
GSS-API V2 gives you a way to transfer a security context between processes on a single machine ๐ฅ๏ธ this is done with the export method of GSSContext and a byte array constructor of the same class. the most common use is a client-server design where the server is one process that accepts incoming security contexts, then launches child processes to handle the data on those contexts ๐ in that setup, the child processes need access to the security context object that the parent created, so they can use the per-message protection services and delete the security context when the communication session ends ๐
the security context data structure is expected to hold sequencing info, so sharing a context between processes is impractical in general ๐ that's why the GSSContext interface has an export method. the process that currently owns the context can call it to say it doesn't plan to use the context anymore, and to make an inter-process token with the info the adopting process needs to re-create the context ๐ฆ after export completes successfully, GSS-API makes the original security context inaccessible to the calling process, and any further use of that object will fail โ the originating process hands the inter-process token to the adopting process, which makes a new GSSContext object with the byte array constructor. the properties of the context match the original one ๐
the inter-process token might have sensitive data from the original security context in it, including cryptographic keys ๐ฐ apps that use inter-process tokens to transfer contexts must take appropriate steps to protect those tokens in transit ๐
implementations are not required to support inter-process transfer of security contexts ๐คทโโ๏ธ calling the isTransferable method of the GSSContext interface will tell you if the context object is transferable โ
3.7. The Use of Incomplete Contexts โณ
some mechanisms might let the per-message services get used before context establishment is finished ๐ซ for example, a mechanism might put enough info in its initial context-level tokens so the context acceptor can decode messages protected with wrap or getMIC right away. for a mechanism like that, the initiating app doesn't need to wait for later context-level tokens to be sent and received before it uses the per-message protection services ๐โโ๏ธ
an app can call the isProtReady method of the GSSContext class to check if the per-message services are available before the context is fully established ๐ apps that want per-message protection on partially established contexts should check this method before they try to call wrap or getMIC ๐ซถ
4. Calling Conventions ๐
Java gives implementors not just a syntax for the language but a whole operational environment ๐ท for example, memory is managed automatically and doesn't need app involvement ๐งน these language features made the API simpler and got rid of certain GSS-API functions ๐ฅ
on top of that, the JCA defines a provider model that gives you implementation-independent access to security services ๐ฆ with this model, apps can switch between different implementations and add new services on the fly โจ the GSS-API spec uses these ideas by relying on providers for the mechanism implementations ๐
4.1. Package Name ๐ฆ
the classes and interfaces defined in this document live in the package called "org.ietf.jgss". apps that want to use this API should import this package name, as shown in section 7 ๐
4.2. Provider Framework ๐งฉ
the Java security APIs use a provider architecture, which lets apps stay implementation independent and lets security API implementations be modular and extensible ๐ธ the java.security.Provider class is an abstract class that a vendor extends. it maps the various properties that represent the different security services available to the names of the actual vendor classes that implement those services ๐ when an app requests a service, it just names the desired provider, and the API hands the request off to the service classes that provider has ๐
using the Java security provider model shields apps from the implementation details of the services they use ๐ก๏ธ apps can switch between providers easily, and new providers can be added as needed, even at runtime ๐โโ๏ธ
the GSS-API may use providers to find components for specific underlying security mechanisms ๐ for instance, one provider might have components that let the GSS-API support the Kerberos v5 mechanism, and another might have components for the SPKM mechanism ๐ by delegating mechanism-specific functionality to components from providers, the GSS-API can be extended to support an arbitrary list of mechanisms ๐
how the GSS-API finds and queries these providers is outside the scope of this document, and it's being left to a Service Provider Interface (SPI) specification ๐ an SPI specification isn't mandatory for adopting this API spec, and using providers in a GSS-API framework implementation isn't mandatory either ๐คทโโ๏ธ but if you pair the provider framework with an SPI specification, you can build an extensible and implementation-independent GSS-API framework ๐โจ
4.3. Integer types โจ
Okay so every number in this API is declared as the Java int primitive type, and bestie, the Java spec literally guarantees it's a 32 bit two's complement signed number ๐ข๐. And anywhere a true/false value is needed or returned, the boolean primitive type shows up. Simple, clean, iconic.
4.4. Opaque Data types ๐ซถ
Byte arrays (byte[]) are how we represent opaque data types, the stuff GSS-API consumes and produces in the form of tokens ๐ฆ. Java arrays carry their own length field, so you can always tell how big they are without any drama ๐. Plus Java has automatic garbage collection, which means developers don't have to manually release memory, and buffer ownership gets way less messy ๐งนโจ.
4.5. Strings ๐
The String object is used for all textual data, period ๐
. Java's String treats every character as a two-byte Unicode character, which means it can support lots of different locales ๐๐. Every routine that returns or accepts text uses the String object, so it's consistent everywhere.
4.6. Object Identifiers ๐ฅ
The Oid object represents Universal Object Identifiers (Oids) ๐. Oids are ISO-defined, hierarchical, globally-interpretable identifiers, and GSS-API uses them to identify security mechanisms and name formats ๐. You can create an Oid from a string in dot notation, like "1.3.6.1.5.6.2", or from its ASN.1 DER encoding. There are also methods to test equality and to get the DER representation of the object ๐ฆ.
Here's the important part, bestie: Oid instances are immutable. There are no methods that let you change what's inside an Oid ๐. Because of that, you can treat them like "statics" without ever having to make copies ๐.
Certain routines let you use a default oid, and in those cases you can pass a "null" value ๐.
4.7. Object Identifier Sets ๐ง
Object identifier sets are represented as arrays of Oid objects. And since Java arrays have a length field, they're super easy to manipulate and reference ๐.
To fully support RFC 2743, the Oid class includes a method that checks whether a given Oid exists within a specified array. It works the same way as gss_test_oid_set_member ๐. Java's arrays plus automatic garbage collection mean the following routines aren't needed anymore: gss_create_empty_oid_set, gss_release_oid_set, and gss_add_oid_set_member. So Java GSS-API implementations won't include them ๐. The combo of garbage collection and immutable Oids clears up all the complicated memory management headaches from the C version ๐โจ.
Whenever a default value for an Object Identifier Set is needed, you can use a "null" value. Check the detailed method description for the specifics ๐
.
4.8. Credentials ๐
GSS-API credentials are represented by the GSSCredential interface ๐. It has a bunch of constructs that help you create the most common credential objects for both the initiator and the acceptor. You compare credentials with the interface's equals method. Here's the general description of GSS-API credentials, pulled from the C-bindings specification:
GSS-API credentials can contain mechanism-specific principal authentication data for multiple mechanisms. A GSS-API credential is composed of a set of credential-elements, each of which is applicable to a single mechanism. A credential may contain at most one credential-element for each supported mechanism. A credential-element identifies the data needed by a single mechanism to authenticate a single principal, and conceptually contains two credential-references that describe the actual mechanism-specific authentication data, one to be used by GSS-API for initiating contexts, and one to be used for accepting contexts. For mechanisms that do not distinguish between acceptor and initiator credentials, both references would point to the same underlying mechanism-specific authentication data. Credentials describe a set of mechanism-specific principals, and give their holder the ability to act as any of those principals. All principal identities asserted by a single GSS-API credential should belong to the same entity, although enforcement of this property is an implementation-specific matter. A single GSSCredential object represents all the credential elements that have been acquired.
The creation of a GSSContext object lets you pass "null" as the GSSCredential input parameter ๐ธ. That tells GSS-API the app wants to act as a default principal ๐. Individual implementations are free to decide what that default behavior looks like for their mechanism, but the following default behavior is recommended for portability ๐งธ.
For the initiator side of the context:
- If there is only a single principal capable of initiating security contexts for the chosen mechanism that the application is authorized to act on behalf of, then that principal shall be used, otherwise
- If the platform maintains a concept of a default network-identity for the chosen mechanism, and if the application is authorized to act on behalf of that identity for the purpose of initiating security contexts, then the principal corresponding to that identity shall be used, otherwise
- If the platform maintains a concept of a default local identity, and provides a means to map local identities into network-identities for the chosen mechanism, and if the application is authorized to act on behalf of the network-identity image of the default local identity for the purpose of initiating security contexts using the chosen mechanism, then the principal corresponding to that identity shall be used, otherwise
- A user-configurable default identity should be used.
And for the acceptor side of the context:
- If there is only a single authorized principal identity capable of accepting security contexts for the chosen mechanism, then that principal shall be used, otherwise
- If the mechanism can determine the identity of the target principal by examining the context-establishment token processed during the accept method, and if the accepting application is authorized to act as that principal for the purpose of accepting security contexts using the chosen mechanism, then that principal identity shall be used, otherwise
- If the mechanism supports context acceptance by any principal, and if mutual authentication was not requested, any principal that the application is authorized to accept security contexts under using the chosen mechanism may be used, otherwise
- A user-configurable default identity shall be used.
The whole point of these rules is to let both initiator and acceptor establish security contexts using default behavior whenever possible ๐ซง. Apps that request default behavior are generally more portable across mechanisms and implementations than apps that instantiate a GSSCredential for one specific identity ๐ฆโจ.
4.9. Contexts ๐ท
The GSSContext interface represents one end of a GSS-API security context ๐. It stores the state info for that end of the peer communication, including cryptographic state ๐. The initiator and acceptor create the context object in different ways ๐. Once the context exists, the initiator can set various context options that decide the characteristics of the security context it wants ๐ ๏ธ. When every desired characteristic is set, the initiator calls the initSecContext method, which produces a token for the peer's acceptSecContext method to consume ๐. Delivering those authentication tokens between the peer applications is the application's job ๐ฌ.
After context establishment finishes, both the initiator and acceptor can retrieve context attributes using the accessor methods ๐. These reflect the actual attributes of the established context ๐ฏ. At that point, the app can use the context to apply cryptographic services to its data ๐๐ .
4.10. Authentication tokens ๐๏ธ
A token is a caller-opaque type that GSS-API uses to keep both ends of a GSS-API security context in sync ๐. It's a cryptographically protected octet-string, generated by the underlying mechanism at one end of the context for use by the peer mechanism at the other end ๐ง. Encapsulating the token within the application protocol (if needed) and transferring it is the peer applications' responsibility ๐ฎ.
Java GSS-API uses byte arrays to represent authentication tokens ๐. There are also overloaded methods that let the caller supply input and output streams for reading and writing the token data ๐.
4.11. Interprocess tokens ๐ฆ
Some GSS-API routines transfer data between processes in multi-process programs ๐งฉ. These routines use a caller-opaque octet-string, generated by GSS-API in one process for use by GSS-API in another process ๐. The calling application is responsible for transferring these tokens between processes ๐.
GSS-API implementors are encouraged to avoid placing sensitive information within interprocess tokens, or to cryptographically protect them, but many implementations will be unable to avoid placing key material or other sensitive data within them. It is the application's responsibility to ensure that interprocess tokens are protected in transit, and transferred only to processes that are trustworthy.
An interprocess token is represented as a byte array emitted from the export method of the GSSContext interface ๐ค. The receiver of the interprocess token initializes a GSSContext object with this token to create a new context ๐ฅ. Once a context has been exported, the GSSContext object is invalidated and is no longer available ๐.
4.12. Error Reporting ๐จ
RFC 2743 defined how major and minor status values signal GSS-API errors ๐ฃ. The major code, also called the GSS status code, signals errors at the GSS-API level, independent of the underlying mechanism(s) ๐ง . The minor status value, or Mechanism status code, is a mechanism-defined error value that indicates a mechanism-specific error code ๐ง.
Java GSS-API uses exceptions implemented by the GSSException class to signal both minor and major error values ๐ฏ. Both mechanism-specific errors and GSS-API-level errors come through instances of this class ๐ซถ. Because exceptions exist, the API calls don't need major and minor codes at all ๐
โโ๏ธ. The GSSException class also has methods that return textual representations of both the major and minor values, which does the same job as gss_display_status ๐๐.
4.12.1. GSS status codes โจ
Okay bestie, GSS status codes are for errors that don't depend on which underlying mechanism is doing the security work ๐ . The errors you can flag with one are the generic API routine errors, the ones defined in the GSS-API specification itself ๐. These bindings lean on Java's exceptions mechanism, so there's no need for calling errors at all ๐.
A GSS status code marks a single fatal generic API error from whichever routine threw the GSSException. Throwing an exception announces that something fatal went wrong during the method ๐. The GSS-API operational model also lets per-message calls hand back supplementary status information ๐ฆ. That kind of info is warning-ish, not fatal, so exceptions are the wrong tool for it. It gets handled as return values instead ๐. The two per-message methods that can produce supplementary information are GSSContext.verifyMIC() and GSSContext.unwrap() โจ. They fill in the supplementary status codes inside the MessageProp object that gets passed in.
The GSSException object does two jobs. It handles setting the various error codes and turning them into readable text, and it also holds the definitions of all the numeric error values ๐ฅ. Here's the full lineup of error codes:
Table: GSS Status Codes
Name Value Meaning
BAD_MECH 1 An unsupported mechanism
was requested.
BAD_NAME 2 An invalid name was supplied.
BAD_NAMETYPE 3 A supplied name was of an
unsupported type.
BAD_BINDINGS 4 Incorrect channel bindings were
supplied.
BAD_STATUS 5 An invalid status code was
supplied.
BAD_MIC 6 A token had an invalid MIC.
NO_CRED 7 No credentials were supplied, or
the credentials were unavailable
or inaccessible.
NO_CONTEXT 8 Invalid context has been
supplied.
DEFECTIVE_TOKEN 9 A supplied token was invalid.
DEFECTIVE_CREDENTIAL 10 A supplied credential was
invalid.
CREDENTIALS_EXPIRED 11 The referenced credentials
have expired.
CONTEXT_EXPIRED 12 The context has expired.
FAILURE 13 Miscellaneous failure,
unspecified at the GSS-API level.
BAD_QOP 14 The quality-of-protection
requested could not be provided.
UNAUTHORIZED 15 The operation is forbidden by
local security policy.
UNAVAILABLE 16 The operation or option is
unavailable.
DUPLICATE_ELEMENT 17 The requested credential
element already exists.
NAME_NOT_MN 18 The provided name was not a
mechanism name.
OLD_TOKEN 19 The token's validity period has
expired.
DUPLICATE_TOKEN 20 The token was a duplicate of an
earlier version.
The GSS major status code FAILURE is the catch-all ๐ง. It means the underlying mechanism hit an error that doesn't have a specific GSS status code defined. The mechanism-specific status code can give you more details about what happened ๐.
The different major status codes that can show up in the GSSException object thrown by the methods in this spec are the same ones returned by the matching calls in RFC 2743 [GSSAPIv2-UPDATE] ๐.
4.12.2. Mechanism-specific status codes ๐ธ
Mechanism-specific status codes come through in two ways. They can be part of any GSSException thrown from the mechanism-specific layer to signal a fatal error. Or they can live in the MessageProp object, which the per-message calls use to signal non-fatal errors ๐.
A default value of 0 in either the GSSException object or the MessageProp object stands for "no mechanism-specific status code here" ๐.
4.12.3. Supplementary status codes ๐ฆ
Supplementary status codes stick to the per-message methods of the GSSContext interface ๐. Because they're informative, using exceptions to signal them isn't appropriate. So the per-message operations of the GSSContext interface return these values inside a MessageProp object instead โจ.
The MessageProp class has query methods that return boolean values for these supplementary states:
Table: Supplementary Status Methods
Method Name Meaning when "true" is returned
isDuplicateToken The token was a duplicate of an
earlier token.
isOldToken The token's validity period has
expired.
isUnseqToken A later token has already been
processed.
isGapToken An expected per-message token was
not received.
A "true" return value from any of the above methods means the token has that property ๐ฏ. The application has to decide what to do about these supplementary values ๐ซถ. They are not treated as errors by the GSS-API, so don't panic when you see them ๐ญ.
4.13. Names ๐
okay so a name is basically how we identify a person or entity ๐ง. the big thing is that GSS-API checks that the name and the entity actually claiming it really go together โจ. no catfishing allowed ๐ โโ๏ธ
different auth mechanisms can use totally different namespaces for their principals ๐ , so GSS-API's naming stuff gets pretty complex in multi-mechanism setups. it can get messy even in single-mechanism setups, if the underlying mechanism supports more than one namespace ๐
there are two distinct ways to represent a name ๐:
- a GSS-API form, represented by implementations of the GSSName interface. one GSSName object can hold multiple names from different namespaces, but all of those names should refer to the same entity ๐ฆ. for example, if you call the getName method of the GSSCredential interface on a credential that has credential elements for several mechanisms with different namespaces, the name you get back is an internal name like this. this GSSName object will have a separate name for the entity for each authentication mechanism ๐
for GSS-API implementations that support multiple namespaces, GSSName implementations must contain enough info to work out which namespace each primitive name belongs to ๐ฅ
- mechanism-specific contiguous byte array and string forms ๐. there are different GSSName initialization methods for byte array and string formats, so you can pick what works for your app and the kind of name you have. these forms can only hold a single name (from a single namespace) ๐ซถ. contiguous string names always come with an object identifier that says which namespace the name belongs to, and their format depends on the auth mechanism using that name ๐. string name forms are assumed to be printable, so GSS-API apps can show them to their users ๐ธ. byte array name formats are assumed to be non-printable (for example, the byte array returned by the export method of the GSSName interface) ๐
a GSSName object can be turned into a contiguous representation with the toString method โจ. this guarantees the name gets converted to a printable format ๐. the GSSName interface has several initialization methods, which means you can use multiple syntaxes for each supported namespace and pick whichever name representation you like best ๐ฆ. the toString method should use an implementation-chosen printable syntax for each supported name-type ๐. if you want to find out the printable name type, use the getStringNameType method ๐
โ ๏ธ heads up bestie: calling toString will not always give you back the same string you originally imported ๐ญ. the name might not even have come from a string in the first place. namespace identifiers might not survive unchanged after going through the internal name-form either ๐
here's an example of what I mean ๐ง. say there's a mechanism that authenticates X.500 names but also has an algorithmic mapping of Internet DNS names into X.500 ๐ . when that mechanism's GSSName implementation gets a DNS name, it might build an internal name that holds both the original DNS name and the equivalent X.500 name ๐. or it might only store the X.500 name ๐. in that second case, toString would most likely give you a printable X.500 name instead of the original DNS name ๐
the context acceptor can get a GSSName object for the entity that started the context by using the getSrcName method โจ. because a single mechanism authenticated this name, it only has one name in it, even if the internal name the context initiator gave to the GSSContext object had multiple components ๐. names like this are called internal mechanism names, or "MN"s, and the names the GSSContext interface returns from getSrcName and getTargName are always this type ๐ฆ. some apps need MNs but don't want to pay the overhead of an authentication operation ๐ซถ, so there are creation methods that take the name buffer, the name type, and also the mechanism oid the name should be created for ๐. when you already have a GSSName object, you can call the canonicalize method to turn a general internal name into an MN ๐ธ
GSSName objects can be compared with their equal method ๐ฅ, which returns "true" if the two names refer to the same entity โจ. this is the preferred way to compare names, rather than comparing the printable names a GSS-API implementation might support ๐. GSS-API assumes that all primitive names inside a given internal name refer to the same entity, so equal can return "true" if the two names share at least one primitive name ๐. if the implementation knows about equivalence relationships between names from different namespaces, that knowledge might also let it successfully compare internal names that have no overlapping primitive elements ๐ง
when you've got a big access control list, making a GSSName object for every name and calling equal on each one from the ACL could be way too expensive ๐ญ. so, as another option, GSS-API defines a special form of the contiguous byte array name that can be compared directly, byte by byte ๐ . contiguous names that are suitable for comparison are generated by the export method ๐. you can re-import exported names by using the byte array constructor and specifying NT_EXPORT_NAME as the name type object identifier ๐. the resulting GSSName will also be an MN ๐. the GSSName interface defines public static Oid objects that represent the standard name types ๐ฆ. structurally, an exported name object has a header with an OID that identifies the mechanism that authenticated the name, and a trailer with the name itself ๐ซถ. the syntax of that trailer is defined by each individual mechanism specification ๐. for a detailed description of the format, check the language-independent GSS-API specification [GSSAPIv2-UPDATE] ๐
ngl, this is a really important one ๐: the results you get from the equals method will generally be different from what you get by calling canonicalize and export, and then comparing the byte array output ๐. the first approach figures out whether two (unauthenticated) names identify the same principal ๐. the second one figures out whether a particular mechanism would authenticate them as the same principal ๐ธ. those two operations will generally only give the same results for MNs ๐
also, the stuff above is just guidelines for how GSSName implementations should behave ๐. it's not meant to be a list of specific requirements for how name objects must be built ๐ง. mechanism designers are free to decide the details of their GSSName implementations, as long as the behaviour still follows those guidelines โจ
4.14. Channel Bindings ๐ฆ
GSS-API lets you use user-specified tags to identify a particular context to the peer application ๐ . these tags are meant to identify the specific communications channel that carries the context ๐. channel bindings are passed to GSS-API using the ChannelBinding object ๐. the application can use byte arrays to specify the application data for the channel binding, and it can also use instances of InetAddress ๐ซถ. the InetAddress for the initiator and/or acceptor can be used inside a ChannelBinding instance ๐
a ChannelBinding can be set on the GSSContext object with the setChannelBinding method, but only before the first call to init or accept ๐ฅ. if setChannelBinding hasn't been used to set the ChannelBinding for a GSSContext object, then a "null" ChannelBinding is assumed ๐. InetAddress is currently the only address type defined within the Java platform, so it's the only one the ChannelBinding class supports ๐. apps that use other address types can put them in as part of the application-specific data ๐ง
conceptually, GSS-API joins the initiator and acceptor address information together with the application-supplied byte array to make an octet string โจ. the mechanism calculates a MIC over this octet string and binds the MIC to the context establishment token that the init method of the GSSContext interface emits ๐. the context acceptor sets the same bindings on its GSSContext object, and while processing the accept method, it calculates a MIC the same way ๐ธ. the calculated MIC is then compared with the one in the token ๐. if the MICs differ, accept will throw a GSSException with the major code set to BAD_BINDINGS, and the context will not be established ๐ญ
some mechanisms might include the actual channel binding data in the token (instead of just a MIC) ๐. so, applications should not use confidential data as channel-binding components ๐ โ
individual mechanisms can put extra constraints on the addresses that show up in channel bindings ๐ฆ. for example, a mechanism might check that the initiator address field of the channel binding contains the correct network address of the host system ๐ซถ. so, portable applications should make sure that they either provide correct information in the address fields, or leave out the addressing information entirely ๐
4.15. Stream Objects ๐
the context object has overloaded methods that use input and output streams to send authentication and per-message GSS-API tokens ๐ . the streams are expected to contain the usual GSS-API tokens, which would otherwise be handled through byte arrays ๐. tokens are expected to have a definite start and a definite end โจ. the callers are responsible for making sure the supplied streams won't block, or for expecting to block until a full token has been processed by the GSS-API method ๐. only one GSS-API token gets processed per invocation of a stream-based method ๐
streams give callers control over and management of the buffers they supply ๐. since streams are non-primitive objects, callers can make them as complicated or as simple as they like, just by using the streams defined in the java.io package or by creating their own through inheritance ๐ง. this gives applications the greatest flexibility ๐
4.16. Optional Parameters ๐ซถ
whenever the application wants to leave out an optional parameter, the "null" value shall be used ๐. the detailed method descriptions say which parameters are optional ๐. method overloading is also used as a technique to indicate default parameters ๐ฆ
5. Introduction to GSS-API Classes and Interfaces โจ
this section gives a brief description of the classes and interfaces that make up the GSS-API ๐. the implementations of these come from the CLASSPATH defined by the application ๐. if Java GSS becomes part of the standard Java APIs, these classes will be available by default on all systems, as part of the JRE's system classes ๐
this section also shows the RFC 2743 functionality that each of the classes implements ๐ง. detailed descriptions of these classes and their methods are in section 6 ๐ ๐ซถ
5.1. GSSManager class
okay so the GSSManager class is basically the factory ๐ญ that makes all the other GSS-API pieces for you. It also lets you ask what security mechanisms are around and what they can do, which is iconic โจ
You can grab the default one with the static method getInstance(). If you want your own version, you can just extend the abstract class yourself ๐
This class has the equivalents of these RFC 2743 routines:
gss_import_name Create an internal name from 6.1.9-
the supplied information. 6.1.12
gss_acquire_cred Acquire credential 6.1.13-
for use. 6.1.15
gss_import_sec_context Create a previously exported 6.1.18
context.
gss_indicate_mechs List the mechanisms 6.1.6
supported by this GSS-API
implementation.
gss_inquire_mechs_for_name List the mechanisms 6.1.8
supporting the
specified name type.
gss_inquire_names_for_mech List the name types 6.1.7
supported by the
specified mechanism.
5.2. GSSName interface
GSS-API names show up in the Java bindings through the GSSName interface ๐ท๏ธ Each name format gets its own universal Object Identifier (oid), and you can work out the format of a name just from its unique oid. The GSSName interface gives you these GSS-API routines:
RFC 2743 Routine Function Section(s)
gss_display_name Covert internal name 6.2.7
representation to text format.
gss_compare_name Compare two internal names. 6.2.3, 6.2.4
gss_release_name Release resources associated N/A
with the internal name.
gss_canonicalize_name Convert an internal name to a 6.1.11,
mechanism name.
gss_export_name Convert a mechanism name to 6.2.6
export format.
gss_duplicate_name Create a copy of the internal N/A
name.
The gss_release_name call isn't here because Java handles its own garbage collection ๐๏ธ The gss_duplicate_name call is extra too, since the GSSName interface has no mutator methods that can change the state of the object, so it's safe to share ๐ซถ
5.3. GSSCredential interface
The GSSCredential interface wraps up GSS-API credentials ๐ A credential identifies a single entity and carries the cryptographic info needed to create a context on that entity's behalf. One credential can hold several mechanism-specific credentials, and each of those is called a credential element. This interface gives you these GSS-API routines:
RFC 2743 Routine Function Section(s)
gss_add_cred Constructs credentials 6.3.12
incrementally.
gss_inquire_cred Obtain information about 6.3.4,6.3.5
credential.
gss_inquire_cred_by_mech Obtain per-mechanism 6.3.5-6.3.10
information about
a credential.
gss_release_cred Disposes of credentials 6.3.3
after use.
5.4. GSSContext interface
This interface handles the context-level calls for setting up and managing a security context between peers, plus the per-message services apps use ๐ฆ A context is set up between a pair of peers and lets you use security services on a per-message basis for application data. It's created over one single security mechanism. The GSSContext interface gives you these GSS-API routines:
RFC 2743 Routine Function Section(s)
gss_init_sec_context Initiate the creation of a 6.4.3,
security context with a peer. 6.4.4
gss_accept_sec_context Accept a security context 6.4.5,
initiated by a peer. 6.4.6
gss_delete_sec_context Destroy a security context. 6.4.8
gss_context_time Obtain remaining context 6.4.37
time.
gss_inquire_context Obtain context 6.4.29 to
characteristics. 6.3.42
gss_wrap_size_limit Determine token-size limit 6.4.9
for gss_wrap.
gss_export_sec_context Transfer security context 6.4.18
to another process.
gss_get_mic Calculate a cryptographic 6.4.14,
Message Integrity Code (MIC) 6.4.15
for a message.
gss_verify_mic Verify integrity on a received 6.4.16,
message. 6.4.17
gss_wrap Attach a MIC to a message and 6.4.10,
optionally encrypt the message 6.4.11
content.
gss_unwrap Obtain a previously wrapped 6.4.12,
application message verifying 6.4.13
its integrity and optionally
decrypting it.
The gss_process_context_token routine didn't make it into the Java bindings spec ๐ The gss_delete_sec_context routine got tweaked so it doesn't return any peer tokens, and that change was proposed following the recommendations stated in RFC 2743. Even so, GSSContext still lets you destroy the locally-stored context information ๐
5.5. MessageProp class
The MessageProp class is a helper used in the per-message operations on the context โ๏ธ Your app makes an instance of it and then passes it into those per-message calls. Sometimes the app tells the GSS-API implementation things through this object, and other times the GSS-API hands info back to the app by setting it on this object. For the details on the per-message operations wrap, unwrap, getMIC, and verifyMIC, check out the GSSContext interface ๐ธ
5.6. GSSException class
Exceptions are how the Java bindings signal fatal errors to the calling apps ๐จ This replaces the major and minor codes from the C-bindings spec as the way to signal failures. The GSSException class handles both minor and major codes, and it also turns them into text. Every GSS-API method is declared as throwing this exception.
RFC 2743 Routine Function Section
gss_display_status Retrieve textual 6.8.5, 6.8.6,
representation of error 6.8.8, 6.8.9
codes.
5.7. Oid class
The Oid class is a utility for representing Universal Object Identifiers and the operations that go with them ๐ข GSS-API uses object identifiers to tell security mechanisms and name types apart. Whenever you need an object identifier, you use this class, and it also gives you this GSS-API functionality:
RFC 2743 Routine Function Section
gss_test_oid_set_member Determine if the specified oid 6.7.5
is part of a set of oids.
5.8. ChannelBinding class
An instance of ChannelBinding is how you pass channel binding info to a GSSContext object before a security context gets established ๐ The app can use a byte array for application data to go into the channel binding, and it can also use instances of InetAddress. InetAddress is currently the only address type defined within the Java platform, so it's the only one ChannelBinding supports. If your app uses other address types, you can include them as part of the application data ๐ง
6. Detailed GSS-API Class Description โจ
This section goes through the detailed description of all the public methods that each GSS-API class and interface has to provide ๐
6.1. public abstract class GSSManager
GSSManager is an abstract class and a factory for three GSS interfaces: GSSName, GSSCredential, and GSSContext ๐ญ It also has methods that let apps figure out which mechanisms the GSS implementation offers and which nametypes those mechanisms support. You can get an instance of the default GSSManager subclass through the static method getInstance(), but apps are free to instantiate other GSSManager subclasses too ๐
Every method in this class is declared abstract except one. That means subclasses have to supply the full implementation for those methods. The only exception is the static method getInstance(), which has platform-specific code to return an instance of the default subclass.
Platform providers of GSS are required not to add any constructors to this class, whether private, public, or protected. This makes sure every subclass only invokes the default constructor the compiler gives the base class ๐
A subclass that extends the GSSManager abstract class can be built as a modular, provider-based layer that uses some well-known service provider specification. The GSSManager API gives the app methods for setting provider preferences on such an implementation. These methods also let the implementation throw a well-defined exception when provider-based configuration isn't supported. Apps that want to be portable should know about this and recover cleanly by catching the exception ๐ซถ
There are three most common ways providers get used:
- The application doesn't care which provider is used (the default case).
- The application wants a particular provider used preferentially, either for a particular mechanism or all the time, no matter the mechanism.
- The application wants to use the locally configured providers as far as possible, but if support is missing for one or more mechanisms, it wants to fall back on its own provider.
The GSSManager class has two methods for these modes of usage: addProviderAtFront() and addProviderAtEnd() ๐ These methods create an ordered list of <provider, oid> pairs, where each pair shows a provider preference for a given oid.
You don't need to know anything about whatever service provider specification the GSSManager subclass follows to use these methods. They're hoped to serve the needs of most applications ๐ Extended GSSManager classes might add more methods, which could become part of a service provider specification that gets standardized later.
6.1.1. Example Code ๐
okay bestie, let's see this in action ๐ here's how you'd poke at the GSSManager and set up some providers.
GSSManager mgr = GSSManager.getInstance();
// What mechs are available to us?
Oid[] supportedMechs = mgr.getMechs();
// Set a preference for the provider to be used when support is needed
// for the mechanisms "1.2.840.113554.1.2.2" and "1.3.6.1.5.5.1.1".
Oid krb = new Oid("1.2.840.113554.1.2.2");
Oid spkm1 = new Oid("1.3.6.1.5.5.1.1");
Provider p = (Provider) (new com.foo.security.Provider());
mgr.addProviderAtFront(p, krb);
mgr.addProviderAtFront(p, spkm1);
// What name types does this spkm implementation support?
Oid[] nameTypes = mgr.getNamesForMech(spkm1);
6.1.2. getInstance โจ
public static GSSManager getInstance()
this one gives you the default GSSManager implementation ๐ธ
6.1.3. getMechs ๐ฆ
public abstract Oid[] getMechs()
this hands back an array of Oid objects for the mechanisms that GSS-API callers can use ๐ if no mechanisms are available, you get null back. ngl that happens when mechanisms are configured dynamically and none are installed right now ๐
6.1.4. getNamesForMech ๐ง
public abstract Oid[] getNamesForMech(Oid mech)
throws GSSException
returns the name type Oids supported by the mechanism you pass in ๐ฅ
Parameters:
mech: TheOidobject for the mechanism to query.
6.1.5. getMechsForName ๐
public abstract Oid[] getMechsForName(Oid nameType)
this gives you an array of Oid objects for the mechanisms that support a specific name type ๐ if nothing supports that name type, it's giving null ๐
Parameters:
nameType: TheOidobject for the name type.
6.1.6. createName ๐
public abstract GSSName createName(String nameStr, Oid nameType)
throws GSSException
this is a factory method that turns a contiguous string name from the namespace you specify into a GSSName object ๐ซถ usually the GSSName you get back won't be an MN. the two exceptions are when the namespace type says NT_EXPORT_NAME, or when the GSS-API implementation isn't multi-mechanism. so main character energy, but with exceptions ๐
Parameters:
nameStr: The string representing a printable form of the name to create.nameType: TheOidspecifying the namespace of the printable name supplied. Note thatnameTypeserves to describe and qualify the interpretation of the inputnameStr, it does not necessarily imply a type for the outputGSSNameimplementation. Anullvalue can be used to specify that a mechanism specific default printable syntax should be assumed by each mechanism that examinesnameStr.
6.1.7. createName ๐ธ
public abstract GSSName createName(byte name[], Oid nameType)
throws GSSException
same vibe as 6.1.6, except this one takes a contiguous byte array containing the name from the specified namespace and turns it into a GSSName object ๐ฆ the usual exceptions still apply, so the GSSName usually won't be an MN, unless the namespace type says NT_EXPORT_NAME or the GSS-API implementation isn't multi-mechanism ๐
Parameters:
name: The byte array containing the name to create.nameType: TheOidspecifying the namespace of the name supplied in the byte array. Note thatnameTypeserves to describe and qualify the interpretation of the inputnamebyte array, it does not necessarily imply a type for the outputGSSNameimplementation. Anullvalue can be used to specify that a mechanism specific default syntax should be assumed by each mechanism that examines the byte array.
6.1.8. createName โจ
public abstract GSSName createName(String nameStr, Oid nameType,
Oid mech) throws GSSException
okay so this one is a little combo move ๐ it takes a contiguous string name from the specified namespace and gives you a GSSName that's a mechanism name (MN). basically it does two things for you: the createName from 6.1.7, and then GSSName.canonicalize() from 6.2.5 ๐ง
Parameters:
nameStr: The string representing a printable form of the name to create.nameType: TheOidspecifying the namespace of the printable name supplied. Note thatnameTypeserves to describe and qualify the interpretation of the inputnameStr, it does not necessarily imply a type for the outputGSSNameimplementation. Anullvalue can be used to specify that a mechanism specific default printable syntax should be assumed when the mechanism examinesnameStr.mech: TheOidspecifying the mechanism for which this name should be created.
6.1.9. createName ๐
public abstract createName(byte name[], Oid nameType, Oid mech)
throws GSSException
this is the byte array version of 6.1.8 ๐ it makes a GSSName that's an MN from a contiguous byte array in the specified namespace. same deal as before: it's the createName from 6.1.8 plus GSSName.canonicalize() from 6.2.5, all in one go ๐
Parameters:
name: The byte array representing the name to create.nameType: TheOidspecifying the namespace of the name supplied in the byte array. Note thatnameTypeserves to describe and qualify the interpretation of the inputnamebyte array, it does not necessarily imply a type for the outputGSSNameimplementation. Anullvalue can be used to specify that a mechanism specific default syntax should be assumed by each mechanism that examines the byte array.mech: TheOidspecifying the mechanism for which this name should be created.
6.1.10. createCredential ๐ฅ
public abstract GSSCredential createCredential (int usage)
throws GSSException
this is the factory method for grabbing default credentials ๐ it tells the GSS-API to use system specific defaults for the set of mechanisms, the name, and a DEFAULT lifetime. bestie, that's a whole lot of "figure it out for me" energy ๐ญ
Parameters:
usage: The intended usage for this credential object. The value of this parameter must be one of:GSSCredential.ACCEPT_AND_INITIATEGSSCredential.ACCEPT_ONLYGSSCredential.INITIATE_ONLY
6.1.11. createCredential ๐ฆ
public abstract GSSCredential createCredential (GSSName aName,
int lifetime, Oid mech, int usage)
throws GSSException
factory method for acquiring a single mechanism credential ๐ธ this one lets you pick the principal, lifetime, mechanism, and usage yourself, so you're in control ๐
Parameters:
aName: Name of the principal for whom this credential is to be acquired. Usenullto specify the default principal.lifetime: The number of seconds that credentials should remain valid. UseGSSCredential.INDEFINITE_LIFETIMEto request that the credentials have the maximum permitted lifetime. UseGSSCredential.DEFAULT_LIFETIMEto request default credential lifetime.mech: The oid of the desired mechanism. Use(Oid) nullto request the default mechanism(s).usage: The intended usage for this credential object. The value of this parameter must be one of:GSSCredential.ACCEPT_AND_INITIATEGSSCredential.ACCEPT_ONLYGSSCredential.INITIATE_ONLY
6.1.12. createCredential ๐
public abstract GSSCredential createCredential(GSSName aName,
int lifetime, Oid mechs[], int usage)
throws GSSException
this is the version for getting credentials over a whole set of mechanisms ๐ง it acquires credentials for each mechanism in the mechs array. to find out which mechanisms the acquisition actually worked for, call GSSCredential.getMechs() ๐ซถ
Parameters:
aName: Name of the principal for whom this credential is to be acquired. Usenullto specify the default principal.lifetime: The number of seconds that credentials should remain valid. UseGSSCredential.INDEFINITE_LIFETIMEto request that the credentials have the maximum permitted lifetime. UseGSSCredential.DEFAULT_LIFETIMEto request default credential lifetime.mechs: The array of mechanisms over which the credential is to be acquired. Use(Oid[]) nullfor requesting a system specific default set of mechanisms.usage: The intended usage for this credential object. The value of this parameter must be one of:GSSCredential.ACCEPT_AND_INITIATEGSSCredential.ACCEPT_ONLYGSSCredential.INITIATE_ONLY
6.1.13. createContext ๐
public abstract GSSContext createContext(GSSName peer, Oid mech,
GSSCredential myCred, int lifetime)
throws GSSException
factory method for making a context on the initiator's side ๐ you can tweak the context flags with the mutator methods before you call GSSContext.initSecContext() ๐
Parameters:
peer: Name of the target peer.mech: Oid of the desired mechanism. Use(Oid) nullto request default mechanism.myCred: Credentials of the initiator. Usenullto act as a default initiator principal.lifetime: The request lifetime, in seconds, for the context. UseGSSContext.INDEFINITE_LIFETIMEandGSSContext.DEFAULT_LIFETIMEto request indefinite or default context lifetime.
6.1.14. createContext ๐ฅ
public abstract GSSContext createContext(GSSCredential myCred)
throws GSSException
factory method for making a context on the acceptor side ๐ the context's properties get determined from the input token that's supplied to the accept method, so you don't set them up yourself ๐
Parameters:
myCred: Credentials for the acceptor. Usenullto act as a default acceptor principal.
6.1.15. createContext ๐ง
public abstract GSSContext createContext(byte [] interProcessToken)
throws GSSException
last one in this batch, and it's the factory method for recreating a previously exported context ๐ฆ the context properties come from the input token, and they can't be changed through the set methods after that ๐ซถ
Parameters:
interProcessToken: The token previously emitted from the export method.
6.1.16. addProviderAtFront โจ
public abstract void addProviderAtFront(Provider p, Oid mech)
throws GSSException
okay so this method lets the app tell the GSSManager "use THIS provider first, bestie" whenever it needs support for a given mechanism ๐
. If you pass null instead of an Oid for the mechanism, the GSSManager must use the provider you picked ahead of all the others, no matter what the mechanism is ๐. It only moves on to a different provider when the one you picked doesn't support the mechanism it needs ๐ซถ.
Calling this repeatedly doesn't wipe your older settings ๐ฆ. It just pushes them down in priority, so you build up an ordered list of provider and Oid pairs that keeps growing at the top ๐ธ.
Calling addProviderAtFront with a null Oid removes all previous preferences that were set for this provider in that GSSManager instance ๐. Calling it with a non-null Oid only removes the previous preference that used that same mechanism and that same provider together โจ.
If the GSSManager implementation doesn't support an SPI with a pluggable provider architecture, it should throw a GSSException with the status code GSSException.UNAVAILABLE to say the operation is unavailable ๐.
Parameters:
p: The provider instance that should be used whenever support is needed formech.mech: The mechanism for which the provider is being set.
6.1.16.1. Example Code ๐ง
Say an app wants provider A to always get checked first whenever any mechanism is needed. It would call:
GSSManager mgr = GSSManager.getInstance();
// mgr may at this point have its own pre-configured list
// of provider preferences. The following will prepend to
// any such list:
mgr.addProviderAtFront(A, null);
Now if it also wants the mechanism of Oid m1 to come from provider B before A gets checked, it would call:
mgr.addProviderAtFront(B, m1);
So the GSSManager would first check with B if m1 is needed. If B doesn't support m1, the GSSManager continues on to check with A ๐. If any mechanism m2 is needed, and m2 is different from m1, then the GSSManager skips B and checks with A directly ๐.
Later on, suppose this call is made to the same GSSManager instance:
mgr.addProviderAtFront(B, null)
Then the earlier (B, m1) setting gets subsumed by this one and should be removed. The list of preferences now effectively becomes:
{(B, null), (A, null),
... //followed by the pre-configured list.
Please note, though, that this call:
mgr.addProviderAtFront(A, m3)
does NOT subsume the previous (A, null) setting, so the list effectively becomes:
{(A, m3), (B, null), (A, null), ...}
6.1.17. addProviderAtEnd ๐
public abstract addProviderAtEnd(Provider p, Oid mech)
throws GSSException
this one is the opposite energy ๐ฅน. It tells the GSSManager that the app wants a certain provider used only if no other provider can be found that supports the given mechanism. If you pass null instead of an Oid, the GSSManager must use that provider for any mechanism ๐.
Calling this repeatedly keeps the older settings and ranks them above the newer ones. So you get an ordered list of provider and Oid pairs that grows at the bottom, and the older provider settings always get used first ๐.
If there are any previously existing preferences that conflict with the one being set here, the GSSManager should ignore this request ๐ โโ๏ธ.
If the GSSManager implementation doesn't support an SPI with a pluggable provider architecture, it should throw a GSSException with the status code GSSException.UNAVAILABLE to indicate the operation is unavailable ๐.
Parameters:
p: The provider instance that should be used whenever support is needed formech.mech: The mechanism for which the provider is being set.
6.1.17.1. Example Code ๐
Say an app wants this: whenever a mechanism of Oid m1 is needed, the system default providers get checked first, and only if they don't support m1 should provider A be checked. It would make this call:
GSSManager mgr = GSSManager.getInstance();
mgr.addProviderAtEnd(A, m1);
Now if it also wants provider B to be checked, for all mechanisms, after all the configured providers have been checked, it would call:
mgr.addProviderAtEnd(B, null);
So the list of preferences effectively becomes {..., (A, m1), (B, null)} ๐ซถ.
Later on, suppose this call is made to the same GSSManager instance:
mgr.addProviderAtEnd(B, m2)
Then the earlier (B, null) pair subsumes this one, so this request should be ignored. The same thing happens if a request is made for the pairs that already exist, (A, m1) or (B, null) ๐.
Please note, though, that this call:
mgr.addProviderAtEnd(A, null)
is NOT subsumed by the earlier (A, m1) setting, so the list effectively becomes {..., (A, m1), (B, null), (A, null)} ๐ธ.
6.2. public interface GSSName ๐ฆ
This interface wraps up a single GSS-API principal entity ๐งธ. Different name formats and their definitions are identified with universal Object Identifiers (Oids). You can work out the format of a name from the unique Oid of its namespace type ๐.
6.2.1. Example Code ๐
Here are some code examples using the GSSName interface โจ. The code below creates a GSSName, converts it to a mechanism name (MN), performs a comparison, gets a printable representation of the name, exports it, and then re-imports it to get a brand-new GSSName ๐ซง.
GSSManager mgr = GSSManager.getInstance();
// create a host based service name
GSSName name = mgr.createName("service@host",
GSSName.NT_HOSTBASED_SERVICE);
Oid krb5 = new Oid("1.2.840.113554.1.2.2");
GSSName mechName = name.canonicalize(krb5);
// the above two steps are equivalent to the following
GSSName mechName = mgr.createName("service@host",
GSSName.NT_HOSTBASED_SERVICE, krb5);
// perform name comparison
if (name.equals(mechName))
print("Names are equals.");
// obtain textual representation of name and its printable
// name type
print(mechName.toString() +
mechName.getStringNameType().toString());
// export and re-import the name
byte [] exportName = mechName.export();
// create a new name object from the exported buffer
GSSName newName = mgr.createName(exportName,
GSSName.NT_EXPORT_NAME);
6.2.2. Static Constants ๐
public static final Oid NT_HOSTBASED_SERVICE
This Oid indicates a host-based service name form ๐ฅ๏ธ. It's used to represent services associated with host computers. The name form is built from two elements, "service" and "hostname", like this:
service@hostname
Values for the "service" element are registered with the IANA. This constant represents the following value: { 1(iso), 3(org), 6(dod), 1(internet), 5(security), 6(nametypes), 2(gss-host-based-services) } ๐
public static final Oid NT_USER_NAME
This name type indicates a named user on a local system. It represents the following value: { iso(1) member-body(2) United States(840) mit(113554) infosys(1) gssapi(2) generic(1) user_name(1) } ๐ง
public static final Oid NT_MACHINE_UID_NAME
This name type indicates a numeric user identifier that corresponds to a user on a local system (e.g. Uid). It represents the following value: { iso(1) member-body(2) United States(840) mit(113554) infosys(1) gssapi(2) generic(1) machine_uid_name(2) } ๐
public static final Oid NT_STRING_UID_NAME
This name type indicates a string of digits representing the numeric user identifier of a user on a local system. It represents the following value: { iso(1) member-body(2) United States(840) mit(113554) infosys(1) gssapi(2) generic(1) string_uid_name(3) } ๐
public static final Oid NT_ANONYMOUS
This name type represents an anonymous entity ๐. It represents the following value: { 1(iso), 3(org), 6(dod), 1(internet), 5(security), 6(nametypes), 3(gss-anonymous-name) } ๐ป
public static final Oid NT_EXPORT_NAME
This name type indicates an exported name produced by the export method ๐ฆ. It represents the following value: { 1(iso), 3(org), 6(dod), 1(internet), 5(security), 6(nametypes), 4(gss-api-exported-name) } โจ
6.2.3. equals ๐
public boolean equals(GSSName another) throws GSSException
This compares two GSSName objects to figure out whether they refer to the same entity ๐ฏ. This method may throw a GSSException when the names can't be compared. If either name represents an anonymous entity, the method returns "false" ๐ โโ๏ธ.
Parameters:
another: GSSName object to compare with.
6.2.4. equals ๐ซถ
public boolean equals(Object another)
This is a variation of the equals method from 6.2.3, provided to override the Object.equals() method that the implementing class inherits ๐งฉ. The behavior is exactly the same as in 6.2.3, except no GSSException is thrown. Instead, false is returned in the situation where an error occurs. (Note that the Java language specification requires that two objects that are equal according to the equals(Object) method must return the same integer result when the hashCode() method is called on them ๐ค.)
Parameters:
another: GSSName object to compare with.
6.2.5. canonicalize ๐ธ
public GSSName canonicalize(Oid mech) throws GSSException
This creates a mechanism name (MN) from an arbitrary internal name โจ. It's equivalent to using the factory methods described in 6.1.9 or 6.1.10 that take the mechanism name as one of their parameters.
Parameters:
mech: The oid for the mechanism for which the canonical form of the name is requested.
6.2.6. export ๐ฆ
public byte[] export() throws GSSException
This returns a canonical, contiguous byte representation of a mechanism name (MN), suitable for direct, byte-by-byte comparison by authorization functions ๐. If the name isn't an MN, implementations may throw a GSSException with the NAME_NOT_MN status code. If an implementation chooses not to throw an exception, it should use some system-specific default mechanism to canonicalize the name and then export it ๐. The format of the header of the output buffer is specified in RFC 2743 ๐.
6.2.7. toString ๐
public String toString()
This returns a textual representation of the GSSName object ๐ฌ. To retrieve the printed name format, which determines the syntax of the returned string, you can use the getStringNameType method ๐.
6.2.8. getStringNameType ๐
public Oid getStringNameType() throws GSSException
This returns the Oid representing the type of name returned through the toString method โจ. Using this Oid, you can work out the syntax of the printable name ๐ง .
6.2.9. isAnonymous ๐
public boolean isAnonymous()
This tests whether this name object represents an anonymous entity ๐ต๏ธโโ๏ธ. It returns "true" if this is an anonymous name ๐ซ .
6.2.10. isMN ๐ฆ
public boolean isMN()
This tests whether this name object contains only one mechanism element, which makes it a mechanism name as defined by RFC 2743 ๐.
6.3. public interface GSSCredential implements Cloneable โจ
okay so this interface is the GSS-API credentials for an entity ๐ A credential holds all the cryptographic info needed to create a context on behalf of the entity it represents. It can contain several mechanism-specific credential elements, each with info for one security mechanism, but they all refer to the same entity. it's giving organized ๐
A credential may be used to perform context initiation, acceptance, or both.
GSS-API implementations must impose a local access-control policy on callers, so unauthorized callers can't grab credentials they aren't entitled to ๐ซ Credential creation is NOT meant to be a "login to the network" function ๐ โโ๏ธ That kind of thing would create new credentials rather than just grabbing a handle to existing ones. If you need that, it should be defined in implementation-specific extensions to the API.
If acquiring a credential is slow for a mechanism, the mechanism may choose to delay the actual acquisition until the credential is needed (for example, by GSSContext) ๐ข Those mechanism-specific decisions should be invisible to the calling app. So the query methods right after you create a credential object must return valid credential data, even if that means paying the cost of the deferred acquisition. no cap, that's the main character move ๐
Applications create a credential object by passing in the parameters they want. Then they can use the query methods to learn about the instantiated credential object (this is the same idea as the gss_inquire routines) ๐ When the credential isn't needed anymore, the app should call dispose (the same as gss_release_cred) to free up any resources the credential object holds and to destroy any cryptographically sensitive info ๐งน
Classes that implement this interface also implement Cloneable ๐งฌ That means the class supports the clone() method, which lets you make duplicate credentials. It's super useful when you call it right before add(), so you keep a copy of the original credential ๐ซถ
6.3.1. Example Code
this example shows creating a GSSCredential for a specific entity, querying its fields, and releasing it when you're done ๐
GSSManager mgr = GSSManager.getInstance();
// start by creating a name object for the entity
GSSName name = mgr.createName("userName", GSSName.NT_USER_NAME);
// now acquire credentials for the entity
GSSCredential cred = mgr.createCredential(name,
GSSCredential.ACCEPT_ONLY);
// display credential information - name, remaining lifetime,
// and the mechanisms it has been acquired over
print(cred.getName().toString());
print(cred.getRemainingLifetime());
Oid [] mechs = cred.getMechs();
if (mechs != null) {
for (int i = 0; i < mechs.length; i++)
print(mechs[i].toString());
}
// release system resources held by the credential
cred.dispose();
6.3.2. Static Constants
public static final int INITIATE_AND_ACCEPT
credential usage flag asking that it be usable for both context initiation and acceptance ๐
public static final int INITIATE_ONLY
credential usage flag asking that it be usable for context initiation only ๐
public static final int ACCEPT_ONLY
credential usage flag asking that it be usable for context acceptance only ๐ช
public static final int DEFAULT_LIFETIME
a lifetime constant for the default credential lifetime โณ
This value must be set to 0.
public static final int INDEFINITE_LIFETIME
a lifetime constant for an indefinite credential lifetime โพ๏ธ This value must be set to the maximum integer value in Java, which is Integer.MAX_VALUE.
6.3.3. dispose
public void dispose() throws GSSException
releases any sensitive info the GSSCredential object might be holding ๐งผ Applications should call this as soon as the credential isn't needed anymore, so the sensitive info sticks around for as little time as possible โฑ๏ธ
6.3.4. getName
public GSSName getName() throws GSSException
retrieves the name of the entity the credential asserts ๐ชช
6.3.5. getName
public GSSName getName(Oid mechOID) throws GSSException
retrieves a mechanism name of the entity the credential asserts. It's equivalent to calling canonicalize() on the name returned by 7.3.3 โจ
Parameters:
mechOID: the mechanism for which information should be returned.
6.3.6. getRemainingLifetime
public int getRemainingLifetime() throws GSSException
returns the remaining lifetime in seconds for a credential โ The remaining lifetime is the minimum lifetime across all the underlying credential mechanisms. A return value of GSSCredential.INDEFINITE_LIFETIME means the credential doesn't expire ๐ A return value of 0 means the credential is already expired ๐
6.3.7. getRemainingInitLifetime
public int getRemainingInitLifetime(Oid mech) throws GSSException
returns the remaining lifetime in seconds for the credential to stay capable of initiating security contexts under the specified mechanism ๐ A return value of GSSCredential.INDEFINITE_LIFETIME means the credential doesn't expire for context initiation. A return value of 0 means it's already expired.
Parameters:
mechOID: the mechanism for which information should be returned.
6.3.8. getRemainingAcceptLifetime
public int getRemainingAcceptLifetime(Oid mech) throws GSSException
returns the remaining lifetime in seconds for the credential to stay capable of accepting security contexts under the specified mechanism ๐ช A return value of GSSCredential.INDEFINITE_LIFETIME means the credential doesn't expire for context acceptance. A return value of 0 means it's already expired.
Parameters:
mechOID: the mechanism for which information should be returned.
6.3.9. getUsage
public int getUsage() throws GSSException
returns the credential usage flag ๐ฏ The return value will be one of GSSCredential.INITIATE_ONLY, GSSCredential.ACCEPT_ONLY, or GSSCredential.INITIATE_AND_ACCEPT.
6.3.10. getUsage
public int getUsage(Oid mechOID) throws GSSException
returns the credential usage flag for the specified credential mechanism ๐ฏ The return value will be one of GSSCredential.INITIATE_ONLY, GSSCredential.ACCEPT_ONLY, or GSSCredential.INITIATE_AND_ACCEPT.
Parameters:
mechOID: the mechanism for which information should be returned.
6.3.11. getMechs
public Oid[] getMechs() throws GSSException
returns an array of the mechanisms this credential supports ๐งฉ
6.3.12. add
public void add(GSSName aName, int initLifetime, int acceptLifetime,
Oid mech, int usage) throws GSSException
adds a mechanism-specific credential element to an existing credential ๐งฑ This lets you build a credential one mechanism at a time.
this one is mainly meant for context acceptors building acceptance credentials that will be used with a bunch of different clients using different security mechanisms ๐ซ
add() adds the new credential element "in-place". If you want the new element in a new credential instead, call clone() first to get a copy of this credential, then call its add() method. ๐ช
Parameters:
aName: the name of the principal this credential is to be acquired for. Use "null" to specify the default principal.initLifetime: the number of seconds the credentials should remain valid for initiating security contexts. UseGSSCredential.INDEFINITE_LIFETIMEto request the maximum permitted lifetime. UseGSSCredential.DEFAULT_LIFETIMEto request the default credential lifetime.acceptLifetime: the number of seconds the credentials should remain valid for accepting security contexts. UseGSSCredential.INDEFINITE_LIFETIMEto request the maximum permitted lifetime. UseGSSCredential.DEFAULT_LIFETIMEto request the default credential lifetime.mech: the mechanisms over which the credential is to be acquired.usage: the intended usage for this credential object. The value of this parameter must be one of:GSSCredential.ACCEPT_AND_INITIATE,GSSCredential.ACCEPT_ONLY,GSSCredential.INITIATE_ONLY
6.3.13. equals
public boolean equals(Object another)
checks whether this GSSCredential refers to the same entity as the object you pass in ๐ค Both credentials must be acquired over the same mechanisms and must refer to the same principal. Returns "true" if they refer to the same entity, "false" otherwise.
(Heads up, bestie: the Java language spec requires that two objects which are equal according to the equals(Object) method must return the same integer result when hashCode() is called on them ๐ง )
Parameters:
another: another GSSCredential object for comparison.
6.4. public interface GSSContext ๐ฆ
okay so this interface is the GSS-API security context, and it's giving everything ๐ It gives you the security services (wrap, unwrap, getMIC, verifyMIC) that live over the context. Contexts get set up between peers using credentials you've already acquired locally. You can have multiple contexts going between the same pair of peers at once, using the same credentials or different ones. Bestie, GSS-API doesn't care what transport you're using, so it's on your calling application to carry its tokens between peers ๐
Before the context establishment phase starts, the context initiator can ask for specific characteristics they want from the context. You set those with the set methods. Once the context is established, you check what the context actually gives you (the characteristics and services it offers) with the query methods. Main character energy ๐
The context establishment phase kicks off with the first call to the init method from the context initiator. During this phase, initSecContext and acceptSecContext produce GSS-API authentication tokens, and your application needs to send those to its peer ๐จ If anything goes wrong at any point, an exception gets thrown and the code jumps into a catch block ๐ If nothing goes wrong, the code keeps flowing normally and you can call isEstablished(). If it returns false, that means you need a token from your peer before you can keep establishing the context. If it returns true, the local end of the context is established. Even then, you might still have to send a token to the peer, if GSS-API produced one ๐
Also, during establishment, you can call isProtReady() to find out if the context is ready for per-message operations. This lets you use per-message operations on contexts that aren't fully established yet. Iconic โจ
Once the context is established, or isProtReady() returns "true", you can call the query routines to see the actual characteristics and services of the context. You can also start using the per-message methods wrap and getMIC to get cryptographic operations on application-supplied data ๐
When you're done with the context, your application should call dispose to release any system resources the context is using ๐งน
6.4.1. Example Code ๐ธ
This example shows how the GSSContext interface gets used by the initiating peer. It walks through the whole lifecycle: creating the object, setting the flags you want, establishing the context, checking the actual context flags, doing per-message operations on application data, and finally deleting the context ๐
GSSManager mgr = GSSManager.getInstance();
// start by creating the name for a service entity
GSSName targetName = mgr.createName("service@host",
GSSName.NT_HOSTBASED_SERVICE);
// create a context using default credentials for the above entity
// and the implementation specific default mechanism
GSSContext context = mgr.createContext(targetName,
null, /* default mechanism */
null, /* default credentials */
GSSContext.INDEFINITE_LIFETIME);
// set desired context options - all others are false by default
context.requestConf(true);
context.requestMutualAuth(true);
context.requestReplayDet(true);
context.requestSequenceDet(true);
// establish a context between peers - using byte arrays
byte []inTok = new byte[0];
try {
do {
byte[] outTok = context.initSecContext(inTok, 0,
inTok.length);
// send the token if present
if (outTok != null)
sendToken(outTok);
// check if we should expect more tokens
if (context.isEstablished())
break;
// another token expected from peer
inTok = readToken();
} while (true);
} catch (GSSException e) {
print("GSSAPI error: " + e.getMessage());
}
// display context information
print("Remaining lifetime in seconds = " + context.getLifetime());
print("Context mechanism = " + context.getMech().toString());
print("Initiator = " + context.getSrcName().toString());
print("Acceptor = " + context.getTargName().toString());
if (context.getConfState())
print("Confidentiality security service available");
if (context.getIntegState())
print("Integrity security service available");
// perform wrap on an application supplied message, appMsg,
// using QOP = 0, and requesting privacy service
byte [] appMsg ...
MessageProp mProp = new MessageProp(0, true);
byte []tok = context.wrap(appMsg, 0, appMsg.length, mProp);
if (mProp.getPrivacy())
print("Message protected with privacy.");
sendToken(tok);
// release the local-end of the context
context.dispose();
6.4.2. Static Constants โจ
public static final int DEFAULT_LIFETIME
This is a lifetime constant for the default context lifetime. This value must be set to 0 ๐
public static final int INDEFINITE_LIFETIME
This is a lifetime constant for indefinite context lifetime. In Java, this value must be set to the maximum integer value, which is Integer.MAX_VALUE ๐
6.4.3. initSecContext ๐ง
public byte[] initSecContext(byte inputBuf[], int offset, int len)
throws GSSException
The context initiator calls this to kick off the context creation process. It's basically the stream-based method, except the token buffers are byte arrays instead of stream objects ๐ซถ This method might return an output token that your application has to send to the peer, so the peer can process it with the accept call. Usually you'd send it by calling flush() on an OutputStream that wraps the connection between the two peers ๐ฟ You can call isEstablished() to check whether context establishment is finished for this peer. If it returns "false", more tokens are expected to be passed into initSecContext(). Heads up, it's possible for initSecContext() to return a token for the peer and for isEstablished() to return "true" too. That means the token still needs to go to the peer, but the local end of the context is now fully established ๐ฆ
After context establishment is complete, you can check the available context options with the get methods ๐
Parameters:
inputBuf: Token generated by the peer. This parameter is ignored on the first call.offset: The offset within the inputBuf where the token begins.len: The length of the token within the inputBuf (starting at the offset).
6.4.3.1. Example Code ๐
// Create a new GSSContext implementation object.
// GSSContext wrapper implements interface GSSContext.
GSSContext context = mgr.createContext(...);
byte []inTok = new byte[0];
try {
do {
byte[] outTok = context.initSecContext(inTok, 0,
inTok.length);
// send the token if present
if (outTok != null)
sendToken(outTok);
// check if we should expect more tokens
if (context.isEstablished())
break;
// another token expected from peer
inTok = readToken();
} while (true);
} catch (GSSException e) {
print("GSSAPI error: " + e.getMessage());
}
6.4.4. initSecContext ๐ธ
public int initSecContext(InputStream inStream,
OutputStream outStream) throws GSSException
The context initiator calls this to kick off the context creation process. It's equivalent to the byte array based method ๐ This method might write an output token to the outStream, which your application has to send to the peer for processing by the accept call. Usually you'd do that by calling flush() on an OutputStream wrapping the connection between the two peers ๐ฟ You can call isEstablished() to see if context establishment is done for this peer. If it returns "false", more tokens are expected to be supplied to the initSecContext method. Same deal as before: initSecContext() can return a token for the peer and isEstablished() can return "true" at the same time. The token still has to go to the peer, but the local end is now fully established โจ
GSS-API authentication tokens have a definitive start and end. Each invocation of this method tries to read one of these tokens, and it may block on the stream if only part of the token is available โณ
Once context establishment is complete, you can check the available context options with the get methods ๐
Parameters:
inStream: Contains the token generated by the peer. This parameter is ignored on the first call.outStream: Output stream where the output token will be written. During the final stage of context establishment, there may be no bytes written.
6.4.4.1. Example Code ๐
This sample code just shows the token exchange during the context establishment phase. Most Java apps are expected to use custom Input and Output stream implementations that wrap their communication routines ๐ง For example, a simple read on the application InputStream, when called by the Context, might cause a token to be read from the peer. And a simple flush() on the application OutputStream might send a previously written token to the peer ๐ค
// Create a new GSSContext implementation object.
// GSSContext wrapper implements interface GSSContext.
GSSContext context = mgr.createContext(...);
// use standard java.io stream objects
ByteArrayOutputStream os = new ByteArrayOutputStream();
ByteArrayInputStream is = null;
try {
do {
context.initSecContext(is, os);
// send token if present
if (os.size() > 0)
sendToken(os);
// check if we should expect more tokens
if (context.isEstablished())
break;
// another token expected from peer
is = recvToken();
} while (true);
} catch (GSSException e) {
print("GSSAPI error: " + e.getMessage());
}
6.4.5. acceptSecContext ๐
public byte[] acceptSecContext(byte inTok[], int offset, int len)
throws GSSException
okay so the context acceptor calls this whenever a token comes in from the peer ๐ฉ It's basically the stream version, except the token buffers are plain byte arrays instead of stream objects ๐
This method may return an output token that the application will need to send to the peer, so the peer can process it with the init call. ๐ฆ
A null return value means no token needs to go to the peer ๐
โโ๏ธ You can call isEstablished() to check whether context establishment is finished for this peer. If isEstablished() returns "false", it's giving "send me more tokens" ๐ฅบ
Heads up, it's possible for acceptSecContext() to return a token for the peer and for isEstablished() to return "true" too. That means the token still has to be sent, but your local end of the context is now fully set up ๐ธ
Once context establishment is done, you can check the available context options with the get methods โจ
Parameters:
inTok: the token the peer generated.offset: where the token starts insideinTok.len: the length of the token insideinTok, counting from theoffset.
6.4.5.1. Example Code ๐
// acquire server credentials
GSSCredential server = mgr.createCredential(...);
// create acceptor GSS-API context from the default provider
GSSContext context = mgr.createContext(server, null);
try {
do {
byte [] inTok = readToken();
byte []outTok = context.acceptSecContext(inTok, 0,
inTok.length);
// possibly send token to peer
if (outTok != null)
sendToken(outTok);
// check if local context establishment is complete
if (context.isEstablished())
break;
} while (true);
} catch (GSSException e) {
print("GSS-API error: " + e.getMessage());
}
6.4.6. acceptSecContext ๐
public void acceptSecContext(InputStream inStream,
OutputStream outStream) throws GSSException
Same energy as the byte array method, just with streams ๐ซถ The context acceptor calls this when a token arrives from the peer. It may write an output token to the outStream, and the application has to send that to the peer so the peer's initSecContext method can process it. Usually the app does this by calling flush() on an OutputStream that wraps the connection between the two peers ๐ฅ
isEstablished() tells you whether context establishment is complete for this peer. A "false" there means more tokens are still expected by this method ๐
Same deal as before: acceptSecContext() can return a token for the peer while isEstablished() also returns "true". The token still needs to go to the peer, but the local end is fully established now โจ
GSS-API authentication tokens have a clear start and end ๐ง Each invocation of this method tries to read one of them, and it may block on the stream if only part of the token is there yet ๐
After context establishment, you can check the context options with the get methods ๐
Parameters:
inStream: contains the token generated by the peer.outStream: the output stream where the output token gets written. During the final stage of context establishment, there may be no bytes written at all ๐
6.4.6.1. Example Code ๐
This sample code just shows the token exchange during context establishment. Most Java apps are expected to use custom Input and Output stream implementations that wrap the communication routines ๐ง For example, a plain read on the app's InputStream, when the Context calls it, might read a token from the peer. A plain flush() on the app's OutputStream might send a token you already wrote out to the peer ๐
// acquire server credentials
GSSCredential server = mgr.createCredential(...);
// create acceptor GSS-API context from the default provider
GSSContext context = mgr.createContext(server, null);
// use standard java.io stream objects
ByteArrayOutputStream os = new ByteArrayOutputStream();
ByteArrayInputStream is = null;
try {
do {
is = recvToken();
context.acceptSecContext(is, os);
// possibly send token to peer
if (os.size() > 0)
sendToken(os);
// check if local context establishment is complete
if (context.isEstablished())
break;
} while (true);
} catch (GSSException e) {
print("GSS-API error: " + e.getMessage());
}
6.4.7. isEstablished ๐ฆ
public boolean isEstablished()
This is the check you use during context establishment to see the state of the context ๐ It returns "true" if this is a fully established context on the caller's side and no more tokens are needed from the peer. Call it after initSecContext() or acceptSecContext() when no GSSException gets thrown โจ
6.4.8. dispose ๐๏ธ
public void dispose() throws GSSException
This releases any system resources and cryptographic information stored in the context object ๐งผ It also invalidates the context, so it's bye bye to that context ๐
6.4.9. getWrapSizeLimit ๐
public int getWrapSizeLimit(int qop, boolean confReq,
int maxTokenSize) throws GSSException
This returns the biggest message size that, if you hand it to the wrap method with the same confReq and qop parameters, will give you an output token no bigger than maxTokenSize bytes ๐ It's built for apps that talk over protocols with a maximum message size, so they can chop messages into pieces before protecting them ๐งฉ
GSS-API implementations are recommended but not required to detect invalid QOP values when getWrapSizeLimit gets called. This routine only guarantees a maximum message size. It doesn't guarantee that specific QOP values will actually be available for message protection ๐
If this call succeeds, that doesn't promise wrap will be able to protect a message of the computed length ๐ค That depends on system resources being available when wrap runs. Still, if the implementation has its own upper limit on message length for wrap, the implementation should not return a value bigger than that limit ๐
Parameters:
qop: the level of protection wrap will be asked to provide.confReq: whether wrap will be asked to provide privacy service.maxTokenSize: the desired maximum size of the token wrap emits.
6.4.10. wrap ๐
public byte[] wrap(byte inBuf[], int offset, int len,
MessageProp msgProp) throws GSSException
This applies per-message security services over the established security context ๐ It returns a token with a cryptographic MIC and can optionally encrypt the inBuf you give it. It does the same job as its stream counterpart, and the returned byte array holds both the MIC and the message ๐
The MessageProp object gets created by the application and is used to set a QOP value, which picks the cryptographic algorithms, plus a privacy service to optionally encrypt the message ๐ง The underlying mechanism might not be able to provide the privacy service. Whatever privacy service it actually provides gets set in this MessageProp object, and the caller should check that after the call returns ๐ If the mechanism can't provide the requested QOP, it throws a GSSException with the BAD_QOP code ๐ญ
Some app-level protocols want to use tokens from wrap for "secure framing", so implementations should support wrapping zero-length messages ๐ธ
The application is responsible for sending the token to the peer ๐ฌ
Parameters:
inBuf: the application data to be protected.offset: where the data starts insideinBuf.len: the length of the data insideinBuf, starting at theoffset.msgProp: an instance of MessageProp the application uses to set the desired QOP and privacy state. Set the desired QOP to 0 to request the default QOP. When this method returns, this object holds the actual privacy state the underlying mechanism applied to the message ๐ง
6.4.11. wrap ๐
public void wrap(InputStream inStream, OutputStream outStream,
MessageProp msgProp) throws GSSException
This lets you apply per-message security services over the established security context โจ It produces a token with a cryptographic MIC and can optionally encrypt the message in inStream. The outStream ends up holding both the MIC and the message ๐
Just like the byte array version, the MessageProp object is created by the application and sets the QOP value and the privacy service ๐
The mechanism might not be able to provide the privacy service, so it sets the actual privacy service it does provide in this MessageProp object. The caller should query that after the call returns ๐ If the mechanism can't provide the requested QOP, it throws a GSSException with the BAD_QOP code ๐
Again, some app-level protocols want "secure framing" with tokens from wrap, so implementations should support wrapping zero-length messages ๐ซถ
The application is responsible for sending the token to the peer ๐ฆ
Parameters:
inStream: the input stream containing the application data to be protected.outStream: the output stream the protected message gets written to. The application is responsible for sending this to the other peer, which processes it in its unwrap method.msgProp: an instance of MessageProp the application uses to set the desired QOP and privacy state. Set the desired QOP to 0 to request the default QOP. When this method returns, this object holds the actual privacy state the underlying mechanism applied to the message ๐
6.4.12. unwrap ๐
public byte [] unwrap(byte[] inBuf, int offset, int len,
MessageProp msgProp) throws GSSException
Okay so this is the peer app's move ๐
It processes tokens that came out of wrap, and it's basically the same vibe as the stream version ๐ซถ It hands back the message the peer gave to wrap and checks the embedded MIC along the way ๐ฅ
The MessageProp object gets created by the application, and the mechanism uses it to report stuff back, like the QOP, whether confidentiality was applied, and other supplementary message state info โจ
Some app-level protocols want to use wrap tokens for "secure framing" ๐ง So implementations should support wrapping and unwrapping zero-length messages too ๐ธ
Parameters:
inBuf: the GSS-API wrap token received from the peer ๐offset: where the token starts insideinBuf๐len: how long the token is insideinBuf, counting from the offset ๐msgProp: when the method returns, this object holds the applied QOP, the privacy state of the message, and the supplementary info from 4.12.3 about whether the token was a duplicate, old, out of sequence or arriving after a gap ๐
6.4.13. unwrap ๐ฆ
public void unwrap(InputStream inStream, OutputStream outStream,
MessageProp msgProp) throws GSSException
Same energy as the byte array one, just with streams ๐ It's the peer's unwrap, it spits out the message you gave to wrap, and it verifies the embedded MIC while it's at it ๐
The MessageProp object is made by the app, and the mechanism fills it in with info like the QOP, whether confidentiality was applied, and other supplementary message state info ๐
Secure framing is a whole thing for some protocols, so implementations should support wrapping and unwrapping zero-length messages as well ๐
Parameters:
inStream: the input stream holding the GSS-API wrap token received from the peer ๐ฅoutStream: the output stream where the application message gets written ๐คmsgProp: when the method returns, this object holds the applied QOP, the privacy state of the message, and the supplementary info from 4.12.3 about whether the token was a duplicate, old, out of sequence or arriving after a gap ๐
6.4.14. getMIC ๐
public byte[] getMIC(byte []inMsg, int offset, int len,
MessageProp msgProp) throws GSSException
Okay so getMIC returns a token with a cryptographic MIC for your message, which you then send to the peer ๐ Here's the tea: unlike wrap, which tucks the whole user message inside the token, getMIC only puts the MIC in the output token ๐ซฃ It does the same job as the stream version โจ
Privacy can only be applied through wrap, so don't go looking for it here ๐
โโ๏ธ
Secure framing again ๐ง Some app protocols want MICs from tokens made by getMIC, so implementations should support deriving MICs from zero-length messages too ๐
Parameters:
inMsg: the message to generate the MIC over ๐offset: where the token starts insideinMsg๐len: how long the token is insideinMsg, starting at the offset ๐msgProp: an instance ofMessagePropthe application uses to pick the QOP it wants. Set the QOP to0inmsgPropto ask for the default QOP. You can also passnullformsgPropto get the default QOP, which is honestly the same thing ๐
6.4.15. getMIC ๐ธ
public void getMIC(InputStream inStream, OutputStream outStream,
MessageProp msgProp) throws GSSException
This one makes a MIC token for your message to send to the peer, just like the byte array version ๐ฆ Same deal as before: getMIC only produces the MIC in the output token, and wrap is the one that actually wraps the message ๐ It's identical in functionality to the byte array counterpart ๐
Reminder bestie: privacy only happens through wrap ๐
Zero-length messages again, because secure framing is iconic ๐ Implementations should support deriving MICs from zero-length messages โจ
Parameters:
inStream: the input stream with the message to generate the MIC over ๐ฅoutStream: the output stream where the GSS-API output token gets written ๐คmsgProp: an instance ofMessagePropthe application uses to set the QOP it wants. Set the QOP to0inmsgPropto request the default QOP. Or passnullformsgPropto request the default QOP ๐ซถ
6.4.16. verifyMIC ๐
public void verifyMIC(byte []inTok, int tokOffset, int tokLen,
byte[] inMsg, int msgOffset, int msgLen,
MessageProp msgProp) throws GSSException
This checks the cryptographic MIC in the token against the message you gave it ๐ต๏ธโโ๏ธ It's the same as the stream version in terms of what it does ๐
The MessageProp object is created by the app, and the mechanism uses it to tell you stuff like the QOP, which shows how strong the protection was, plus other supplementary message state info ๐
For secure framing, implementations should support calculating and verifying MICs over zero-length messages too ๐ง
Parameters:
inTok: the token from the peer'sgetMICmethod ๐tokOffset: where the token starts insideinTok๐tokLen: how long the token is insideinTok, starting at the offset ๐inMsg: the application message to verify the cryptographic MIC over ๐msgOffset: where the message starts insideinMsg๐msgLen: how long the message is insideinMsg, starting at the offset ๐msgProp: when the method returns, this object holds the applied QOP and the supplementary info from 4.12.3 about whether the token was a duplicate, old, out of sequence or arriving after a gap. The confidentiality state gets set to"false"๐
6.4.17. verifyMIC ๐
public void verifyMIC(InputStream tokStream, InputStream msgStream,
MessageProp msgProp) throws GSSException
Stream edition of the verify call ๐ It does the same job as the byte array counterpart: it checks the cryptographic MIC in the token against the message ๐ซถ
The app makes the MessageProp object, and the mechanism uses it to report the QOP (the strength of protection applied to the message) and other supplementary message state info โจ
Secure framing once more ๐ Implementations should support calculating and verifying MICs over zero-length messages โจ
Parameters:
tokStream: the input stream with the token generated by the peer'sgetMICmethod ๐ฅmsgStream: the input stream with the application message to verify the cryptographic MIC over ๐ฅmsgProp: when the method returns, this object holds the applied QOP and the supplementary info from 4.12.3 about whether the token was a duplicate, old, out of sequence or arriving after a gap. The confidentiality state gets set to"false"๐
6.4.18. export ๐งณ
public byte [] export() throws GSSException
This is for sharing the work between multiple processes ๐ฏโโ๏ธ You'll usually see it used by the context-acceptor. Picture an app where one process gets the incoming connection requests and accepts the security contexts, then hands the established context off to one or more other processes for the message exchange ๐ฆ
This method deactivates the security context and makes an interprocess token. When that token goes into the byte array constructor of the GSSContext interface in another process, the context comes back to life over there ๐ Only one instance of a given context can be active at any time. If the context exporter tries to access the exported context afterward, it'll fail ๐
โโ๏ธ
The implementation might limit which processes can import the interprocess token, based on local security policy or on implementation decisions. For example, some implementations only let contexts move between processes running under the same account, or processes in the same process group ๐
The interprocess token can hold security-sensitive stuff, like cryptographic keys ๐ญ Mechanisms are encouraged either to keep that sensitive info out of interprocess tokens, or to encrypt the token before it's returned to the app. But in a typical GSS-API implementation, that might not be possible. So the app has to protect the interprocess token carefully, and make sure any process the token gets passed to is trustworthy ๐
Okay bestie, this one's serious. Protect that interprocess token like it owes you money, and only hand it to processes you actually trust ๐๐
6.4.19. requestMutualAuth ๐ค
public void requestMutualAuth(boolean state) throws GSSException
This sets the request state of the mutual authentication flag for the context ๐ It only works before the context creation process begins, and only for the initiator ๐ฌ
Parameters:
state: a boolean for whether mutual authentication should be requested during context establishment โ
6.4.20. requestReplayDet ๐
public void requestReplayDet(boolean state) throws GSSException
This sets the request state of the replay detection service for the context ๐ก๏ธ Same rules: it's only valid before the context creation process begins, and only for the initiator ๐
Parameters:
state: a boolean for whether replay detection is wanted over the established context ๐
6.4.21. requestSequenceDet ๐ข
public void requestSequenceDet(boolean state) throws GSSException
This sets the request state for the sequence checking service of the context ๐ It's only valid before the context creation process begins, and only for the initiator ๐งญ
Parameters:
state: a boolean for whether sequence detection is wanted over the established context ๐ข
6.4.22. requestCredDeleg ๐๏ธ
public void requestCredDeleg(boolean state) throws GSSException
This sets the request state for the credential delegation flag for the context ๐ซก Once again, it's only valid before the context creation process begins, and only for the initiator ๐
Parameters:
state: a boolean for whether credential delegation is wanted ๐
6.4.23. requestAnonymity โจ
public void requestAnonymity(boolean state) throws GSSException
okay so this one is how you ask for anonymous support over the context ๐ bestie, it is ONLY valid before the context creation process even starts, and only the initiator can call it. no take-backs once the handshake is going ๐
Parameters:
state: a boolean saying whether anonymity support is requested.
6.4.24. requestConf ๐
public void requestConf(boolean state) throws GSSException
this is you asking for confidentiality service over the context ๐ same rules apply: only before context creation begins, and only the initiator gets to ask.
Parameters:
state: a boolean saying whether confidentiality services should be requested for the context.
6.4.25. requestInteg ๐ฆ
public void requestInteg(boolean state) throws GSSException
requesting integrity services over the context, so nobody gets to quietly mess with your data ๐ซถ again, only valid before context creation starts, and only for the initiator.
Parameters:
state: a boolean saying whether integrity services should be requested for the context.
6.4.26. requestLifetime โณ
public void requestLifetime(int lifetime) throws GSSException
this sets how long you want the context to last, measured in seconds โญ only valid before context creation begins, and only for the initiator. if you want forever-ish, use GSSContext.INDEFINITE_LIFETIME, and if you're happy with the usual, use GSSContext.DEFAULT_LIFETIME ๐
Parameters:
lifetime: the desired context lifetime in seconds.
6.4.27. setChannelBinding ๐
public void setChannelBinding(ChannelBinding cb) throws GSSException
this sets the channel bindings you want used during context establishment ๐ธ it's only valid before context creation begins.
Parameters:
cb: the channel bindings to be used.
6.4.28. getCredDelegState ๐
public boolean getCredDelegState()
this one returns the state of the delegated credentials for the context, and it's giving honesty ๐
before establishment is finished, or when isProtReady returns "false", you get the desired state. after that, you get the actual state over the established context.
6.4.29. getMutualAuthState ๐ซถ
public boolean getMutualAuthState()
returns the state of the mutual authentication option for the context โจ same deal as before: before establishment completes, or while isProtReady returns "false", it's the desired state. otherwise it tells you what's actually happening over the established context.
6.4.30. getReplayDetState ๐
public boolean getReplayDetState()
returns the state of the replay detection option for the context ๐ before establishment completes, or while isProtReady returns "false", it gives you the desired state. otherwise it's the real deal over the established context.
6.4.31. getSequenceDetState ๐ง
public boolean getSequenceDetState()
returns the state of the sequence detection option for the context ๐ same pattern: desired state before establishment completes or while isProtReady is "false", actual state after that.
6.4.32. getAnonymityState ๐
public boolean getAnonymityState()
returns "true" if this is an anonymous context ๐ถ๏ธ before establishment completes, or while isProtReady returns "false", it's the desired state. otherwise it shows the actual state over the established context.
6.4.33. isTransferable ๐
public boolean isTransferable() throws GSSException
returns "true" if the context can be handed off to other processes using the export method ๐ฆ this call is only valid on fully established contexts, so wait till it's ready, bestie.
6.4.34. isProtReady ๐ฅ
public boolean isProtReady()
returns "true" if per-message operations can be applied over the context โจ some mechanisms let you do per-message stuff before the context is fully established, and when this says "true", the get methods start returning the actual context state instead of the desired one. main character energy ๐
6.4.35. getConfState ๐
public boolean getConfState()
returns the confidentiality service state over the context ๐ before establishment completes, or while isProtReady returns "false", it's the desired state. otherwise it tells you the actual state over the established context.
6.4.36. getIntegState ๐ก๏ธ
public boolean getIntegState()
returns the integrity service state over the context ๐ before establishment completes, or while isProtReady returns "false", you get the desired state. after that, it reports the actual state over the established context.
6.4.37. getLifetime โณ
public int getLifetime()
returns the context lifetime in seconds ๐ธ before establishment completes, or while isProtReady returns "false", you get the desired lifetime. otherwise it tells you how much lifetime is left for the context.
6.4.38. getSrcName ๐ชช
public GSSName getSrcName() throws GSSException
returns the name of the context initiator ๐ this call is only valid after the context is fully established, or once isProtReady returns "true". it is guaranteed to return an MN, which is so reliable I'm obsessed ๐ญ
6.4.39. getTargName ๐ฏ
public GSSName getTargName() throws GSSException
returns the name of the context target (acceptor) ๐ฆ only valid after the context is fully established, or once isProtReady returns "true". and yes, it's guaranteed to return an MN too ๐
6.4.40. getMech ๐งฉ
public Oid getMech() throws GSSException
returns the mechanism oid for this context โจ you're allowed to call it before the context is fully established, but in the negotiated mechanism case, the mechanism returned might change between calls. plot twist energy ๐
6.4.41. getDelegCred ๐
public GSSCredential getDelegCred() throws GSSException
returns the delegated credential object on the acceptor's side ๐ to check whether delegated credentials are actually available, call getDelegCredState first. this call is only valid on fully established contexts.
6.4.42. isInitiator ๐งญ
public boolean isInitiator() throws GSSException
returns "true" if this is the initiator of the context ๐ this call is only valid after the context creation process has started.
6.5. public class MessageProp ๐
public class MessageProp
okay this is a utility class the per-message GSSContext methods use to pass along per-message properties ๐
When you use it with the GSSContext interface's wrap and getMIC methods, you create an instance to say which QOP you want, and whether you want confidentiality services applied to the data you hand over (that's wrap only). if you want the default QOP, just use 0 for QOP ๐
When you use it with the unwrap and verifyMIC methods of the GSSContext interface, the instance tells you which QOP was applied and what confidentiality services were on the message you gave it. with verifyMIC, the confidentiality state is always "false" ๐ once these methods return, the object also holds any supplementary status values for the token you just processed. those can flag old tokens, out of sequence tokens, gap tokens, or duplicate tokens. so basically it's giving detective vibes ๐
6.5.1. Constructors ๐๏ธ
public MessageProp(boolean privState)
this constructor sets QOP to 0, which means you're asking for the default QOP ๐
Parameters:
privState: the desired privacy state. "true" means privacy, and "false" means integrity only.
public MessageProp(int qop, boolean privState)
this constructor sets the values for the QOP and the privacy state all in one go ๐ธ
Parameters:
qop: the desired QOP. use 0 to request a default QOP.privState: the desired privacy state. "true" means privacy, and "false" means integrity only.
6.5.2. getQOP ๐ข
public int getQOP()
retrieves the QOP value ๐
6.5.3. getPrivacy ๐
public boolean getPrivacy()
retrieves the privacy state ๐
6.5.4. getMinorStatus ๐งพ
public int getMinorStatus()
retrieves the minor status that the underlying mechanism might have set ๐ฆ
6.5.5. getMinorString ๐
public String getMinorString()
returns a string explaining the mechanism-specific error code ๐
null gets returned when no mechanism error code has been set.
6.5.6. setQOP โ๏ธ
public void setQOP(int qopVal)
sets the QOP value ๐
Parameters:
qopVal: the QOP value to be set. use 0 to request a default QOP value.
6.5.7. setPrivacy ๐
public void setPrivacy(boolean privState)
sets the privacy state ๐ซถ
Parameters:
privState: the privacy state to set.
6.5.8. isDuplicateToken ๐ฏ
public boolean isDuplicateToken()
returns "true" if this is a duplicate of an earlier token ๐
6.5.9. isOldToken โ
public boolean isOldToken()
returns "true" if the token's validity period has expired ๐
6.5.10. isUnseqToken ๐
public boolean isUnseqToken()
returns "true" if a later token has already been processed ๐ง
6.5.11. isGapToken ๐ณ๏ธ
public boolean isGapToken()
returns "true" if an expected per-message token was not received ๐ญ
6.5.12. setSupplementaryStates ๐
public void setSupplementaryStates(boolean duplicate,
boolean old, boolean unseq, boolean gap,
int minorStatus, String minorString)
this method sets the state for the supplementary information flags and the minor status in MessageProp โจ the application doesn't use it. the GSS implementation uses it to pass this information back to the caller of a per-message context method ๐
Parameters:
duplicate: "true" if the token was a duplicate of an earlier token, "false" otherwise.old: "true" if the token's validity period has expired, "false" otherwise.unseq: "true" if a later token has already been processed, "false" otherwise.gap: "true" if one or more predecessor tokens have not yet been successfully processed, "false" otherwise.minorStatus: the integer minor status code that the underlying mechanism wants to set.minorString: the textual representation of theminorStatusvalue.
6.6. public class ChannelBinding ๐
public class ChannelBinding
ngl this is the important one ๐ฅบ the GSS-API lets callers provide their own channel binding information ๐ channel bindings make peer entity authentication stronger during context establishment. they let GSS-API callers tie the establishment of the security context to things like addresses, or to application-specific data ๐
The caller who starts the security context has to work out the right channel binding values and set them in the GSSContext object ๐ฏ the acceptor has to provide an identical binding, so it can check that the tokens it receives have the correct channel-related characteristics. it has to match exactly, bestie ๐ซถ
Using channel bindings is optional in GSS-API โจ and since channel-binding information can be sent inside context establishment tokens, applications should not use confidential data as channel-binding components. > don't put secrets in there, babe, they travel in the tokens ๐จ
6.6.1. Constructors โจ
Okay so this first constructor is the main one ๐
It makes a ChannelBinding with actual address info and data in it. Any field the app doesn't want to set can just be null ๐
public ChannelBinding(InetAddress initAddr, InetAddress acceptAddr,
byte[] appData)
Creates a ChannelBinding object with user supplied address information and data. null values can be used for any fields which the application does not want to specify.
Parameters:
initAddr: The address of the context initiator. Anullvalue can be supplied to indicate that the application does not want to set this value.acceptAddr: The address of the context acceptor. Anullvalue can be supplied to indicate that the application does not want to set this value.appData: Application supplied data to be used as part of the channel bindings. Anullvalue can be supplied to indicate that the application does not want to set this value.
The second one is the simpler sibling ๐
public ChannelBinding(byte[] appData)
Creates a ChannelBinding object without any addressing information.
Parameters:
appData: Application supplied data to be used as part of the channel bindings.
6.6.2. getInitiatorAddress ๐ฆ
public InetAddress getInitiatorAddress()
Returns the initiator's address for this channel binding. null is returned if the address has not been set.
6.6.3. getAcceptorAddress ๐ธ
public InetAddress getAcceptorAddress()
Returns the acceptor's address for this channel binding. null is returned if the address has not been set.
6.6.4. getApplicationData ๐
public byte[] getApplicationData()
Returns application data being used as part of the ChannelBinding. null is returned if no application data has been specified for the channel binding.
6.6.5. equals ๐
public boolean equals(Object obj)
Returns true if two channel bindings match. Quick heads up, the Java language specification requires that two objects that are equal according to the equals(Object) method must return the same integer result when the hashCode() method is called on them ๐ง
Parameters:
obj: Another channel binding to compare with.
6.7. public class Oid ๐
This class represents Universal Object Identifiers (Oids) and their associated operations.
Oids are hierarchically globally-interpretable identifiers used within the GSS-API framework to identify mechanisms and name formats. Their structure and encoding is defined in ISOIEC-8824 and ISOIEC-8825. For example, the Oid representation of Kerberos V5 mechanism is "1.2.840.113554.1.2.2" ๐ฅ
The GSSName name class contains public static Oid objects representing the standard name types defined in GSS-API.
6.7.1. Constructors โจ
public Oid(String strOid) throws GSSException
Creates an Oid object from a string representation of its integer components (e.g. "1.2.840.113554.1.2.2").
Parameters:
strOid: The string representation for the oid.
public Oid(InputStream derOid) throws GSSException
Creates an Oid object from its DER encoding. This refers to the full encoding including tag and length. The structure and encoding of Oids is defined in ISOIEC-8824 and ISOIEC-8825. This method is identical in functionality to its byte array counterpart.
Parameters:
derOid: Stream containing the DER encoded oid.
public Oid(byte[] DEROid) throws GSSException
Creates an Oid object from its DER encoding. This refers to the full encoding including tag and length. The structure and encoding of Oids is defined in ISOIEC-8824 and ISOIEC-8825. This method is identical in functionality to its byte array counterpart.
Parameters:
derOid: Byte array storing a DER encoded oid.
6.7.2. toString ๐
public String toString()
Returns a string representation of the oid's integer components in dot separated notation (e.g. "1.2.840.113554.1.2.2").
6.7.3. equals ๐ฆ
public boolean equals(Object Obj)
Returns true if the two Oid objects represent the same oid value. Same deal as before, the Java language specification requires that two objects that are equal according to the equals(Object) method must return the same integer result when the hashCode() method is called on them ๐ง
Parameters:
obj: Another Oid object to compare with.
6.7.4. getDER ๐ธ
public byte[] getDER()
Returns the full ASN.1 DER encoding for this oid object, which includes the tag and length.
6.7.5. containedIn ๐
public boolean containedIn(Oid[] oids)
A utility method to test if an Oid object is contained within the supplied Oid object array.
Parameters:
oids: An array of oids to search.
6.8. public class GSSException extends Exception ๐
This exception is thrown whenever a fatal GSS-API error occurs, including mechanism specific errors. It may contain both the major and minor GSS-API status codes. The mechanism implementers are responsible for setting appropriate minor status codes when throwing this exception. Aside from delivering the numeric error code(s) to the caller, this class also maps those numeric values to textual representations. All Java GSS-API methods are declared throwing this exception.
All implementations are encouraged to use the Java internationalization techniques to provide local translations of the message strings ๐
6.8.1. Static Constants ๐
All valid major GSS-API error code values are declared as constants in this class. Here they all are:
public static final int BAD_BINDINGS: Channel bindings mismatch error.public static final int BAD_MECH: Unsupported mechanism requested error.public static final int BAD_NAME: Invalid name provided error.public static final int BAD_NAMETYPE: Name of unsupported type provided error.public static final int BAD_STATUS: Invalid status code error - this is the default status value.public static final int BAD_MIC: Token had invalid integrity check error.public static final int CONTEXT_EXPIRED: Specified security context expired error.public static final int CREDENTIALS_EXPIRED: Expired credentials detected error.public static final int DEFECTIVE_CREDENTIAL: Defective credential error.public static final int DEFECTIVE_TOKEN: Defective token error.public static final int FAILURE: General failure, unspecified at GSS-API level.public static final int NO_CONTEXT: Invalid security context error.public static final int NO_CRED: Invalid credentials error.public static final int BAD_QOP: Unsupported QOP value error.public static final int UNAUTHORIZED: Operation unauthorized error.public static final int UNAVAILABLE: Operation unavailable error.public static final int DUPLICATE_ELEMENT: Duplicate credential element requested error.public static final int NAME_NOT_MN: Name contains multi-mechanism elements error.public static final int DUPLICATE_TOKEN: The token was a duplicate of an earlier token. This is a fatal error code that may occur during context establishment. It is not used to indicate supplementary status values. The MessageProp object is used for that purpose.public static final int OLD_TOKEN: The token's validity period has expired. This is a fatal error code that may occur during context establishment. It is not used to indicate supplementary status values. The MessageProp object is used for that purpose.public static final int UNSEQ_TOKEN: A later token has already been processed. This is a fatal error code that may occur during context establishment. It is not used to indicate supplementary status values. The MessageProp object is used for that purpose.public static final int GAP_TOKEN: An expected per-message token was not received. This is a fatal error code that may occur during context establishment. It is not used to indicate supplementary status values. The MessageProp object is used for that purpose.
6.8.2. Constructors โจ
public GSSException(int majorCode)
Creates a GSSException object with a specified major code.
Parameters:
majorCode: The GSS error code causing this exception to be thrown.
public GSSException(int majorCode, int minorCode, String minorString)
Creates a GSSException object with the specified major code, minor code, and minor code textual explanation. This constructor is to be used when the exception is originating from the security mechanism. It allows to specify the GSS code and the mechanism code.
Parameters:
majorCode: The GSS error code causing this exception to be thrown.minorCode: The mechanism error code causing this exception to be thrown.minorString: The textual explanation of the mechanism error code.
6.8.3. getMajor ๐
public int getMajor()
Returns the major code representing the GSS error code that caused this exception to be thrown.
6.8.4. getMinor ๐ฆ
public int getMinor()
Returns the mechanism error code that caused this exception. The minor code is set by the underlying mechanism. A value of 0 indicates that the mechanism error code is not set.
6.8.5. getMajorString ๐ธ
public String getMajorString()
Returns a string explaining the GSS major error code causing this exception to be thrown.
6.8.6. getMinorString ๐
public String getMinorString()
Returns a string explaining the mechanism specific error code. null will be returned when no mechanism error code has been set.
6.8.7. setMinor ๐
public void setMinor(int minorCode, String message)
Used internally by the GSS-API implementation and the underlying mechanisms to set the minor code and its textual representation.
Parameters:
minorCode: The mechanism specific error code.message: A textual explanation of the mechanism error code.
6.8.8. toString ๐ง
public String toString()
Returns a textual representation of both the major and minor status codes.
6.8.9. getMessage ๐
public String getMessage()
Returns a detailed message of this exception. It overrides Throwable.getMessage. It is customary in Java to use this method to obtain exception information.
7. Sample Applications ๐โจ
7.1. Simple GSS Context Initiator ๐
Okay so this is the client side, bestie ๐ It's a partial sketch of a simple client that acts as a GSS context initiator, and it shows how to use the Java bindings for the GSS-API from Generic Security Service API Version 2 : Java bindings. It assumes a GSS-API implementation that supports the mechanism it needs is there as a library package (org.ietf.jgss), either in the standard JRE or in the CLASSPATH the app sets up ๐ง
import org.ietf.jgss.*;
/**
* This is a partial sketch for a simple client program that acts
* as a GSS context initiator. It illustrates how to use the Java
* bindings for the GSS-API specified in
* Generic Security Service API Version 2 : Java bindings
*
*
* This code sketch assumes the existence of a GSS-API
* implementation that supports the mechanism that it will need and
* is present as a library package (org.ietf.jgss) either as part of
* the standard JRE or in the CLASSPATH the application specifies.
*/
public class SimpleClient {
private String serviceName; // name of peer (ie. server)
private GSSCredential clientCred = null;
private GSSContext context = null;
private Oid mech; // underlying mechanism to use
private GSSManager mgr = GSSManager.getInstance();
...
...
private void clientActions() {
initializeGSS();
establishContext();
doCommunication();
}
/**
* Acquire credentials for the client.
*/
private void initializeGSS() {
try {
clientCred = mgr.createCredential(null /*default princ*/,
GSSCredential.INDEFINITE_LIFETIME /* max lifetime */,
mech /* mechanism to use */,
GSSCredential.INITIATE_ONLY /* init context */);
print("GSSCredential created for " +
cred.getName().toString());
print("Credential lifetime (sec)=" +
cred.getRemainingLifetime());
} catch (GSSException e) {
print("GSS-API error in credential acquisition: "
+ e.getMessage());
...
...
}
...
...
}
/**
* Does the security context establishment with the
* server.
*/
private void establishContext() {
byte[] inToken = new byte[0];
byte[] outToken = null;
try {
GSSName peer = mgr.createName(serviceName,
GSSName.NT_HOSTBASED_SERVICE);
context = mgr.createContext(peer, mech, gssCred,
GSSContext.INDEFINITE_LIFETIME/*lifetime*/);
// Will need to support confidentiality
context.requestConf(true);
while (!context.isEstablished()) {
outToken = context.initSecContext(inToken, 0,
inToken.length);
if (outToken != null)
writeGSSToken(outToken);
if (!context.isEstablished())
inToken = readGSSToken();
}
GSSName peer = context.getSrcName();
print("Security context established with " + peer +
" using underlying mechanism " + mech.toString());
} catch (GSSException e) {
print("GSS-API error during context establishment: "
+ e.getMessage());
...
...
}
...
...
}
/**
* Sends some data to the server and reads back the
* response.
*/
private void doCommunication() {
byte[] inToken = null;
byte[] outToken = null;
byte[] buffer;
// Container for multiple input-output arguments to and
// from the per-message routines (e.g., wrap/unwrap).
MessageProp messgInfo = new MessageProp();
try {
/*
* Now send some bytes to the server to be
* processed. They will be integrity protected but
* not encrypted for privacy.
*/
buffer = readFromFile();
// Set privacy to false and use the default QOP
messgInfo.setPrivacy(false);
outToken = context.wrap(buffer, 0, buffer.length,
messgInfo);
writeGSSToken(outToken);
/*
* Now read the response from the server.
*/
inToken = readGSSToken();
buffer = context.unwrap(inToken, 0, inToken.length,
messgInfo);
// All ok if no exception was thrown!
GSSName peer = context.getSrcName();
print("Message from " + peer.toString()
+ " arrived.");
print("Was it encrypted? " +
messgInfo.getPrivacy());
print("Duplicate Token? " +
messgInfo.isDuplicateToken());
print("Old Token? " +
messgInfo.isOldToken());
print("Unsequenced Token? " +
messgInfo.isUnseqToken());
print("Gap Token? " +
messgInfo.isGapToken());
...
...
} catch (GSSException e) {
print("GSS-API error in per-message calls: "
+ e.getMessage());
...
...
}
...
...
} // end of doCommunication method
...
...
} // end of class SimpleClient
7.2. Simple GSS Context Acceptor โจ
Now the server side, and this one is giving main character energy ๐ It's a partial sketch of a simple server that acts as a GSS context acceptor, and it uses the same Java bindings from Generic Security Service API Version 2 : Java bindings. Same deal as before: it assumes a GSS-API implementation that supports the mechanisms it needs is present as a library package (org.ietf.jgss), either in the standard JRE or in the CLASSPATH ๐ซถ
import org.ietf.jgss.*;
/**
* This is a partial sketch for a simple server program that acts
* as a GSS context acceptor. It illustrates how to use the Java
* bindings for the GSS-API specified in
* Generic Security Service API Version 2 : Java bindings
*
* This code sketch assumes the existence of a GSS-API
* implementation that supports the mechanisms that it will need and
* is present as a library package (org.ietf.jgss) either as part of
* the standard JRE or in the CLASSPATH the application specifies.
*/
import org.ietf.jgss.*;
public class SimpleServer {
private String serviceName;
private GSSName name;
private GSSCredential cred;
private GSSManager mgr;
...
...
/**
* Wait for client connections, establish security contexts and
* provide service.
*/
private void loop() {
...
...
mgr = GSSManager.getInstance();
name = mgr.createName(serviceName,
GSSName.NT_HOSTBASED_SERVICE);
cred = mgr.createCredential(name,
GSSCredential.INDEFINITE_LIFETIME,
null,
GSSCredential.ACCEPT_ONLY);
// Loop infinitely
while (true) {
Socket s = serverSock.accept();
// Start a new thread to serve this connection
Thread serverThread = new ServerThread(s);
serverThread.start();
}
}
/**
* Inner class ServerThread whose run() method provides the
* secure service to a connection.
*/
private class ServerThread extends Thread {
...
...
/**
* Deals with the connection from one client. It also
* handles all GSSException's thrown while talking to
* this client.
*/
public void run() {
byte[] inToken = null;
byte[] outToken = null;
byte[] buffer;
GSSName peer;
// Container for multiple input-output arguments to and
// from the per-message routines (ie. wrap/unwrap).
MessageProp supplInfo = new MessageProp();
GSSContext secContext = null;
try {
// Now do the context establishment loop
GSSContext context = mgr.createContext(cred);
while (!context.isEstablished()) {
inToken = readGSSToken();
outToken = context.acceptSecContext(inToken, 0,
inToken.length);
if (outToken != null)
writeGSSToken(outToken);
}
// SimpleServer wants confidentiality to be
// available. Check for it.
if (!context.getConfState()){
...
...
}
GSSName peer = context.getSrcName();
Oid mech = context.getMech();
print("Security context established with " +
peer.toString() +
" using underlying mechanism " +
mech.toString() +
" from Provider " +
context.getProvider().getName());
// Now read the bytes sent by the client to be
// processed.
inToken = readGSSToken();
// Unwrap the message
buffer = context.unwrap(inToken, 0, inToken.length,
supplInfo);
// All ok if no exception was thrown!
// Print other supplementary per-message status
// information
print("Message from " +
peer.toString() + " arrived.");
print("Was it encrypted? " +
supplInfo.getPrivacy());
print("Duplicate Token? " +
supplInfo.isDuplicateToken());
print("Old Token? " + supplInfo.isOldToken());
print("Unsequenced Token? " +
supplInfo.isUnseqToken());
print("Gap Token? " + supplInfo.isGapToken());
/*
* Now process the bytes and send back an encrypted
* response.
*/
buffer = serverProcess(buffer);
// Encipher it and send it across
supplInfo.setPrivacy(true); // privacy requested
supplInfo.setQOP(0); // default QOP
outToken = context.wrap(buffer, 0, buffer.length,
supplInfo);
writeGSSToken(outToken);
} catch (GSSException e) {
print("GSS-API Error: " + e.getMessage());
// Alternatively, could call e.getMajorMessage()
// and e.getMinorMessage()
print("Abandoning security context.");
...
...
}
...
...
} // end of run method in ServerThread
} // end of inner class ServerThread
...
...
} // end of class SimpleServer
8. Security Considerations ๐
okay bestie, security time, and this part is lowkey important ๐ญ The Java language security model lets platform providers set policy-based, fine-grained access control over any resource an app wants. When a Java security manager is in play (like with applets running in browsers, but not only that), the application code is sandboxed by default ๐ง
The people who run the platform JRE decide which permissions, if any, get granted to code from different codebases. So the administrator needs to know about any special requirements the GSS provider has for system resources. For example, a Kerberos provider might want to open a network connection to the KDC to get initial credentials. That's not allowed inside the sandbox unless the administrator has granted the permission. And the granting and checking of permissions happens transparently to the application, which is outside the scope of this document ๐ฆ
The Java language lets administrators pre-configure a list of security service providers in the <JRE>/lib/security/java.security file. At runtime, the system checks these providers in order of preference when it's looking for security-related services. Apps can modify this list through methods in the "Security" class in the "java.security" package. BUT these changes would be visible across the entire JVM and affect all code running in it, so this operation isn't available in the sandbox and needs special permissions. So when a GSS application has special needs that a particular security provider meets, it has two choices:
- To install the provider on a JVM-wide basis using the java.security.Security class, and then depend on the system to find the right provider automatically when the need arises. (This would require the application to be granted an "insertProvider SecurityPermission".)
- To pass an instance of the provider to the local instance of GSSManager, so that only factory calls going through that GSSManager use the desired provider. (This would not require any permissions.)
โ ๏ธ Read the security part carefully, babe. Option 1 is a JVM-wide change that needs permissions, and option 2 is scoped to one GSSManager and needs none. Choose wisely ๐
9. Acknowledgments ๐ซถ
this proposed API builds on earlier work from the IETF's CAT WG, as outlined in both RFC 2743 and RFC 2744. A lot of the conceptual definitions, implementation directions, and explanations come straight from those documents ๐
big thanks to Mike Eisler, Lin Ling, Ram Marti, Michael Saltz and other members of Sun's development team for their helpful input, comments and suggestions ๐ We'd also love to thank Joe Salowey and Michael Smith for their insightful ideas and suggestions that helped shape this document โจ
10. Bibliography ๐
here are the references, complete with all their details ๐
[GSSAPIv2] Linn, J., "Generic Security Service Application
Program Interface, Version 2", RFC 2078, January
1997.
[GSSAPIv2-UPDATE] Linn, J., "Generic Security Service Application
Program Interface, Version 2, Update 1", RFC 2743,
January 2000.
[GSSAPI-Cbind] Wray, J., "Generic Security Service API Version 2 :
C-bindings", RFC 2744, January 2000.
[KERBV5] Linn, J., "The Kerberos Version 5 GSS-API
Mechanism", RFC 1964, June 1996.
[SPKM] Adams, C., "The Simple Public-Key GSS-API
Mechanism", RFC 2025, October 1996.
11. Authors' Addresses ๐ธ
if you want to send comments about this memo, address them to:
<cat-ietf@mit.edu>
the authors, in their own words:
Jack Kabat
ValiCert, Inc.
339 N. Bernardo Avenue
Mountain View, CA
94043, USA
Phone: +1-650-567-5496
EMail: jackk@valicert.com
Mayank Upadhyay
Sun Microsystems, Inc.
901 San Antonio Road, MS CUP02-102
Palo Alto, CA 94303
Phone: +1-408-517-5956
EMail: mdu@eng.sun.com
12. Full Copyright Statement โ๏ธ
the legal bit, kept complete and word for word so nothing gets lost in translation ๐งพ
Copyright (C) The Internet Society (2000). All Rights Reserved.
This document and translations of it may be copied and furnished to
others, and derivative works that comment on or otherwise explain it
or assist in its implementation may be prepared, copied, published
and distributed, in whole or in part, without restriction of any
kind, provided that the above copyright notice and this paragraph are
included on all such copies and derivative works. However, this
document itself may not be modified in any way, such as by removing
the copyright notice or references to the Internet Society or other
Internet organizations, except as needed for the purpose of
developing Internet standards in which case the procedures for
copyrights defined in the Internet Standards process must be
followed, or as required to translate it into languages other than
English.
The limited permissions granted above are perpetual and will not be
revoked by the Internet Society or its successors or assigns.
This document and the information contained herein is provided on an
"AS IS" basis and THE INTERNET SOCIETY AND THE INTERNET ENGINEERING
TASK FORCE DISCLAIMS ALL WARRANTIES, EXPRESS OR IMPLIED, INCLUDING
BUT NOT LIMITED TO ANY WARRANTY THAT THE USE OF THE INFORMATION
HEREIN WILL NOT INFRINGE ANY RIGHTS OR ANY IMPLIED WARRANTIES OF
MERCHANTABILITY OR FITNESS FOR A PARTICULAR PURPOSE.
Acknowledgement ๐
funding for the RFC Editor function is currently provided by the Internet Society ๐โจ
๐ฆ where this page comes from
- ietf: rfc2853 2000-06
- ๐ girlified by claude-haiku-5-5 on : a language model's rewrite of the original, under its terms