RFC 5653
Generic Security Service API Version 2: Java Bindings Update
Generic Security Service API Version 2: Java Bindings Update 💖
Okay bestie, let's start with the basics 🎀. This RFC is RFC 5653, published in August 2009 by the IETF's Network Working Group. The authors are M. Upadhyay from Google and S. Malkani from ActivIdentity 🫶. It's on the Standards Track, which means it's an official Internet standard and not just an opinion piece 💅. It obsoletes RFC 2853, so 2853 is officially retired and this one replaces it 💀. It doesn't update anything else.
Abstract ✨
Okay so the GSS-API (Generic Security Services Application Program Interface) gives app developers one consistent way to use security features, no matter which crypto mechanism is running underneath 🔐. This doc is a glow-up for the Java bindings of that API, which were originally written up in [RFC 2853] 💜. It fixes a bunch of transcription typos and tidies up some stuff people noticed while actually building with it. Since it's doing that in specific, incremental ways, it obsoletes RFC 2853 🦋.
The language-independent version of the GSS-API lives in [RFC 2743] 🌸. With it, a caller app can prove who a principal is, hand off (delegate) rights to a peer, and turn on security services like confidentiality and integrity for each message, one message at a time 🔥. Some example mechanisms built for GSS-API are [RFC 2025] (The Simple Public-Key GSS-API Mechanism) and [RFC 4121] (the Kerberos Version 5 GSS-API Mechanism, version 2) 👑.
Status of This Memo ⭐
This is an Internet standards track protocol document for the Internet community, and the authors are asking for discussion and suggestions to make it better 🍓. For the current standardization state and status of this protocol, check the latest edition of "Internet Official Protocol Standards" ([STD 1]) 🧚. Distribution of this memo is unlimited, so share it with everyone 🌈.
Copyright Notice 💅
Copyright (c) 2009 IETF Trust and the people listed as the document's authors. All rights reserved 🎀.
This document is covered by [BCP 78] and the IETF Trust's Legal Provisions Relating to IETF Documents as they stood on the publication date. You can read those at http://trustee.ietf.org/license-info 📜. Please actually read them, because they spell out your rights and restrictions for this document 🙈.
Heads up: some of the material in this document may come from IETF Documents or IETF Contributions that were published or made public before November 10, 2008. The people who hold copyright on some of that older material might not have given the IETF Trust permission to allow changes to it outside the IETF Standards Process. So unless you get a proper license from those copyright holders, this document can't be modified outside the IETF Standards Process, and derivative works can't be made outside it either. The one exceptions are formatting it for publication as an RFC and translating it into languages other than English 🫠.
Table of Contents 🦋
The table of contents and the rest of the document come in the next parts, so stay tuned, bestie 💖
Hmm, bestie, I can't do part 2 yet 🥺 The text you pasted is just the table of contents, and that was already covered in part 1. There's no body text after it, like Section 1's Introduction, to rewrite.
Could you paste the next chunk of RFC 5653 starting at Section 1 (Introduction)? I'll keep going from there with every requirement, number, code block, and reference exactly as written. 💅✨
1. Introduction ✨
Okay bestie, this doc is all about Java language bindings for GSS-API version 2 💖. GSS-API v2 is described in a language-independent format in RFC 2743 [GSSAPIv2-UPDATE], and GSS-API is the thing that lets a caller app authenticate a principal identity, delegate rights to a peer, and apply security services like confidentiality and integrity on a per-message basis 🔐.
This document and its predecessor, RFC 2853 [RFC2853], build on the work the working group (WG) did for RFC 2743 [GSSAPIv2-UPDATE] and the C-bindings in RFC 2744 [GSSAPI-Cbind]. Whenever it made sense, the text was borrowed from the C-bindings document (RFC 2744) to explain generic concepts and guide implementors 📚.
The goals here were to cover all the functionality in RFC 2743 [GSSAPIv2-UPDATE] and to deliver it in an object-oriented way 🦋. The spec also wants to serve both kinds of Java devs: the ones who want a "system-wide" GSS-API implementation, and the ones who want to bring their own "custom" implementation 🎀.
A system-wide implementation is available to every app as a library package. It might be the standard package in the Java runtime environment (JRE) being used, or it might be installed separately and reachable by any app via the CLASSPATH 🌸.
A custom implementation, on the other hand, is usually bundled with the app when it gets distributed. It's expected to exist for some particular need of the app, like support for one specific mechanism 💅.
The design also wants to give you a flexible framework for adding and managing GSS-API mechanisms ✨. GSS-API leans on the Java Cryptography Architecture (JCA) provider model so mechanisms can be plugged in 🧚. Mechanisms can be added system-wide, so every user of the framework has them, or they can be added per-instance of the GSS-API 🍓.
Lastly, this spec presents an API that fits naturally into the Java platform's operating environment 👑. Readers are assumed to already know both GSS-API and the Java platform, so no hand-holding here 😌.
2. Conventions and Licenses 📜
The key words "MUST", "MUST NOT", "REQUIRED", "SHALL", "SHALL NOT", "SHOULD", "SHOULD NOT", "RECOMMENDED", "MAY", and "OPTIONAL" in this document are to be interpreted as described in [RFC2119] 🫶.
The following license applies to all code segments in this specification. If you extract code from this spec, please include the following text in the code 💜:
/*
-- Copyright (c) 2009 IETF Trust and the persons identified as
-- authors of the code. All rights reserved.
--
-- Redistribution and use in source and binary forms, with or without
-- modification, are permitted provided that the following conditions
-- are met:
--
-- - Redistributions of source code must retain the above copyright
-- notice, this list of conditions and the following disclaimer.
--
-- - Redistributions in binary form must reproduce the above copyright
-- notice, this list of conditions and the following disclaimer in
-- the documentation and/or other materials provided with the
-- distribution.
--
-- - Neither the name of Internet Society, IETF or IETF Trust, nor the
-- names of specific contributors, may be used to endorse or promote
-- products derived from this software without specific prior
-- written permission.
--
-- THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND
-- CONTRIBUTORS 'AS IS' AND ANY EXPRESS OR IMPLIED WARRANTIES,
-- INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
-- MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
-- DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS
-- BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
-- EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED
-- TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
-- DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON
-- ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
-- OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
-- OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
-- POSSIBILITY OF SUCH DAMAGE.
--
-- This code is part of RFC 5653; see the RFC itself for full legal
-- notices.
*/
3. GSS-API Operational Paradigm 🌈
"Generic Security Service Application Programming Interface, Version 2" [GSSAPIv2-UPDATE] defines a generic security API for calling apps 🔥. It lets a communicating app authenticate the user tied to another app, delegate rights to another app, and apply security services like confidentiality and integrity on a per-message basis 🫢.
There are four stages to using GSS-API, and I'm obsessed with how clean the flow is 😭:
- The app acquires a set of credentials it can use to prove its identity to other processes. These credentials vouch for its global identity, which may or may not be related to any local username it runs under 🪪.
- Two communicating apps establish a joint security context using their credentials ✨. The security context holds shared state that's needed so per-message security services can work, like cryptographic keys and message sequence numbers 🔑. As part of setting it up, the context initiator gets authenticated to the responder, and the initiator may require that the responder gets authenticated back to it. The initiator may optionally give the responder the right to start further security contexts, acting as its agent or delegate. This transfer of rights is called "delegation", and it's done by creating a set of credentials, similar to the ones the initiating app uses, but which the responder can use 🤝.
A GSSContext object is used to establish and maintain the shared information that makes up the security context 💖. Certain GSSContext methods generate a token, which apps treat as cryptographically protected, opaque data 🔒. The caller of such a method is responsible for transferring the token to the peer app, encapsulated if necessary in an app-to-app protocol 📨. When the peer gets such a token, it should pass it to a matching GSSContext method, which decodes the token, extracts the information, and updates the security context state accordingly 🧠.
- Per-message services are invoked on a GSSContext object to apply either:
integrity and data origin authentication, or
confidentiality, integrity and data origin authentication
to application data, which GSS-API treats as arbitrary octet-strings 🦋. An app sending a message it wants to protect calls the right GSSContext method (getMIC or wrap) to apply protection, then sends the resulting token to the receiving app 📬. The receiver passes the received token (and, for data protected by getMIC, the accompanying message-data) to the matching decoding method of the GSSContext interface (verifyMIC or unwrap) to remove the protection and validate the data 🛡️.
- At the end of a communications session (which may span several transport connections), each app uses a GSSContext method to invalidate the security context and release any system or cryptographic resources it holds 🧹. Multiple contexts may also be used, either one after another or at the same time, within a single communications association, at the discretion of the apps 🎀.
4. Additional Controls ✨
okay so this section is all about the optional extras a context initiator can ask GSS-API for before the context gets established 💅 each one gets requested by calling the matching mutator method on the GSSContext object before the first call to init happens. and only the context initiator can request context flags 🎀
The optional services defined are:
- Delegation: the (usually temporary) transfer of rights from initiator to acceptor, so the acceptor can authenticate itself as an agent of the initiator 🦋
- Mutual Authentication: on top of the initiator authenticating its identity to the context acceptor, the context acceptor should also authenticate itself to the initiator 🫶
- Replay Detection: on top of providing message integrity, the per-message operations getMIC and wrap of GSSContext should include message numbering information, so verifyMIC and unwrap can detect if a message has been duplicated 👑
- Out-of-Sequence Detection: on top of providing message integrity, the per-message operations (getMIC and wrap) of GSSContext should include message sequencing information, so verifyMIC and unwrap can detect if a message has been received out of sequence ⭐
- Anonymous Authentication: establishing the security context should not reveal the initiator's identity to the context acceptor 🙈
not every mechanism supports every optional service, and some mechanisms only support certain services when others are turned on too 🌸 the GSSContext interface has query methods so the calling application can check which services will actually be available once the establishment phase is done 🔍 generally, if the security mechanism can provide a requested service, it should do so even if other services have to be enabled to make that happen ✨ if the mechanism can't provide a requested service, it should carry on without that service rather than leaving the application to abort context establishment, unless the application considers the requested service to be mandatory 🫠
some mechanisms say that support for certain services is optional, so implementors don't have to provide it 💜 this is most common for confidentiality, often because of legal restrictions on data-encryption, but it can apply to any of the services. these mechanisms are required to send at least one token from acceptor to initiator during context establishment whenever the initiator says it wants to use such a service, so the initiating GSS-API can correctly tell whether the acceptor's GSS-API supports it 🎀
4.1. Delegation
okay delegation is giving "I'll let you act on my behalf" 🧚 the GSS-API lets the initiating application control delegation through the requestCredDeleg method, and it has to be called before the first call to init is issued. some mechanisms don't support delegation at all, and for those, any attempt by an application to turn it on just gets ignored 🥲
the acceptor of a security context, where the initiator enabled delegation, can check whether delegation was turned on by using the getCredDelegState method of the GSSContext interface 👀 if it was, the delegated credential object can be grabbed by calling the getDelegCred method 💖 the GSSCredential object you get back can then be used to start later GSS-API security contexts as an agent or delegate of the initiator 🌈 and here's the twist: if the original initiator's identity is "A" and the delegate's identity is "B", then depending on the underlying mechanism, the identity carried by the delegated credential may be either "A" or "B acting for A" 🤯
for lots of mechanisms that support delegation, a simple yes-or-no boolean just isn't enough control 🙄 some extra things a mechanism might let an application control are the duration of the delegation, the network addresses the delegation is valid from, and the constraints on what tasks a delegate is allowed to do 📋 those extra controls are currently outside the scope of the GSS-API 💅 implementations that support mechanisms with extra controls should provide extension routines so those controls can be used (maybe by changing the initiator's GSS-API credential object before it gets used to establish a context) ✨ still, the simple delegation control GSS-API gives should always be able to override the other mechanism-specific delegation controls 👑 if the application tells 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 turn on services even if they weren't requested, because delegation may only be provided when the application explicitly asks for it 🔒
4.2. Mutual Authentication
so usually the context acceptor is going to want the context initiator to authenticate itself, so it can make an access-control decision before doing anything for the initiator 🔐 sometimes though, the initiator also wants the acceptor to prove who it is, and that's where mutual authentication comes in 🫶 GSS-API lets the initiating application ask for this by calling the requestMutualAuth method of the GSSContext interface with a "true" parameter, and it has to happen before the first call to init 💌 the initiating application gets told whether or not the context acceptor has authenticated itself 📬
ngl, not every mechanism supports mutual authentication, and some mechanisms always do it whether or not the initiating application asks 🤷♀️ in particular, some mechanisms might need mutual authentication in order to support replay or out-of-sequence message detection, and for those mechanisms, asking for either of those services will automatically turn on mutual authentication too 🔁
4.3. Replay and Out-of-Sequence Detection
okay so once a security context has been established, the GSS-API may be able to detect messages that arrive in the wrong order 👯 protection can be applied to messages by either application, by calling either the getMIC or the wrap method of the GSSContext interface, and then the peer application checks it by calling verifyMIC or unwrap on the peer's GSSContext object 🎀
the getMIC method calculates a cryptographic checksum of an application message and returns that checksum inside a token 🧾 the application passes both the token and the message to the peer application, which hands them to the verifyMIC method of the peer's GSSContext object 📨
the wrap method calculates a cryptographic checksum of an application message and puts both the checksum and the message inside one single token 🎁 the application passes that token to the peer application, which gives it to the unwrap method of the peer's GSSContext object to pull out the message and verify the checksum 🔓
either pair of routines might be able to detect out-of-sequence message delivery or duplicated messages 👀 the details of those mis-ordered messages are shown through supplementary query methods of the MessageProp object that gets filled in by each of these routines 💅
and no, a mechanism doesn't need to keep a list of every token it has ever processed in order to support these status codes 🙅♀️ a typical mechanism might just remember info about the most recent "N" tokens it processed, which lets it tell duplicates and missing tokens apart within those most recent "N" messages 🧠 if it gets a token older than the most recent "N", the isOldToken method of the MessageProp instance will return "true" 🕰️
4.4. Anonymous Authentication
okay this one is so iconic 🕵️♀️ sometimes an application wants to start the authentication process to authenticate a peer, WITHOUT revealing its own identity 🙊 picture an application that gives access to a database full of medical information and offers unrestricted access to the service 🏥 a client of that service might want to authenticate the service (so it can trust the info it gets back), but might not want the service to be able to learn the client's identity 😳 maybe because of privacy concerns about the specific inquiries, or maybe just to avoid getting put on mailing lists 📧
normally with GSS-API, the initiator's identity is made available to the acceptor as a result of context establishment 🪪 but context initiators can request that their identity not be revealed to the context acceptor 🫥 lots of mechanisms don't support anonymous authentication, and for those, the request won't be honored 😭 an authentication token will still be generated anyway, but the application is always told when a requested service isn't available, and it gets to decide whether to abort context establishment if anonymity matters more than the other security services that would need a context to be established 🧐
on top of telling the application that a context was established anonymously (through the isAnonymous method of the GSSContext class), the getSrcName method of the acceptor's GSSContext object will, for those contexts, return a reserved internal-form name that the implementation defines 🏷️
the toString method for a GSSName object representing an anonymous entity will return a printable name 🖨️ that returned value will be syntactically distinguishable from any valid principal name the implementation supports 💎 the name-type object identifier will be an oid representing the value of NT_ANONYMOUS 🆔 this name-type oid will be defined as a public, static Oid object of the GSSName class ✨ the printable form of an anonymous name should be chosen so it implies anonymity, since this name might show up in things like audit logs 📜 for example, the string "<anonymous>" might be a good choice, as long as no valid printable names the implementation supports can begin with "<" and end with ">" 💅
when you use the equal method of the GSSName interface and one of the operands is a GSSName instance representing an anonymous entity, the method must return "false". no exceptions, bestie 🚫
4.5. Confidentiality
if a GSSContext supports the confidentiality service, the wrap method can be used to encrypt application messages 🔐 the messages get encrypted selectively, under the control of the setPrivacy method of the MessageProp object used in the wrap method 🎀 it's giving secret-keeper energy 🤫💜
4.6. Inter-process Context Transfer ✨
okay so GSS-APIv2 lets you move a security context between processes on the same machine 🦋 It's done with the export method of GSSContext and a byte array constructor of that same class. The most common use is a client-server setup where the server is one process that accepts incoming security contexts, then launches child processes to handle the data on those contexts 💅 The children need to reach the security context object made in the parent so they can use per-message protection services and delete the context when the session ends 🫶
Here's the catch: the security context holds sequencing information, so sharing one between processes is usually impractical 😭 So instead, the GSSContext interface gives you an export method. The process that owns the context calls it to say "I'm done with this, I won't use it anymore," and it creates an inter-process token holding everything the adopting process needs to recreate the context 🌸 Once 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 builds a new GSSContext object with the byte array constructor. The new context has the same properties as the original 👑
The inter-process token may contain sensitive data from the original security context, including cryptographic keys. Apps that use inter-process tokens to move contexts around have to take appropriate steps to protect those tokens in transit 🔥
Implementations aren't required to support inter-process transfer of security contexts 🙈 To check whether a context object can be transferred, call the isTransferable method of the GSSContext interface and it'll tell you 🧚
4.7. The Use of Incomplete Contexts 🌈
Some mechanisms let you use the per-message services before context establishment is finished ⭐ For example, a mechanism might put enough info in its initial context-level tokens for the context acceptor to decode messages protected with wrap or getMIC right away. With a mechanism like that, the initiating application doesn't have to wait for later context-level tokens to be sent and received before it starts using the per-message protection services 🍓
To find out whether per-message services are available before the context is fully established, an application can call the isProtReady method of the GSSContext class 💜 If you want to use per-message protection on a partially established context, check this method first before you try to call wrap or getMIC 🎀
5. Calling Conventions 💖
Java gives implementors more than just a language syntax. It also gives them an operating environment 🌸 For example, memory is managed automatically, so the application doesn't have to step in and handle it 🧚 These language features make the API simpler and got rid of certain GSS-API functions entirely ✨
On top of that, the JCA defines a provider model that gives you implementation-independent access to security services 🦋 With this model, applications can switch between different implementations seamlessly and add new services on the fly 💅 The GSS-API spec builds on these ideas by using providers for the mechanism implementations 👑
5.1. Package Name 🎀
The classes and interfaces in this document live in the package called "org.ietf.jgss" 💜 Apps that want to use this API should import that package name, as shown in section 8 🫶
5.2. Provider Framework 🌈
The Java security APIs use a provider architecture. This lets applications 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 properties that represent the different security services available to the names of the actual vendor classes that implement those services ⭐ When an application requests a service, it just names the provider it wants, and the API passes the request along to the service classes that provider offers 💅
Using the Java security provider model shields applications from the implementation details of the services they use 🌸 Apps can switch providers easily, and new providers can be added whenever 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 [RFC4121], and another might have components that support the Simple Public-Key GSS-API Mechanism (SPKM) [RFC2025] 💖 By handing mechanism-specific work off 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 out of scope for this document. That job is being deferred to a Service Provider Interface (SPI) specification 🙈 An SPI specification doesn't have to exist for adopting this API specification, and using providers in a GSS-API framework implementation isn't mandatory either 🧚 Still, pair the provider framework with an SPI specification and you can build an extensible, implementation-independent GSS-API framework 🎀
5.3. Integer Types 🔢
All numeric values are declared as the "int" primitive Java type 🍓 The Java specification guarantees that this is a 32-bit two's complement signed number 💅
Throughout the API, the "boolean" primitive Java type is used wherever a boolean value is needed or returned ✨
5.4. Opaque Data Types 🫧
Java byte arrays represent the opaque data types that the GSS-API consumes and produces as tokens 🌸 Java arrays carry a length field, so users can easily find out how big they are 🦋 The language has automatic garbage collection, so developers don't have to release memory themselves, and buffer ownership gets a lot simpler 👑
5.5. Strings 💬
The String object represents all textual data 💜 Java's String transparently treats every character as a two-byte Unicode character, which supports many locales 🌈 Every routine that returns or accepts textual data uses the String object ⭐
5.6. Object Identifiers 🪪
An Oid object represents Universal Object Identifiers (Oids) 🦋 Oids are ISO-defined, hierarchically globally interpretable identifiers, and the GSS-API framework uses them to identify security mechanisms and name formats 💖 You can create an Oid object from a string of its dot notation, like "1.3.6.1.5.6.2", or from its ASN.1 DER encoding 🫶 The class also has methods to test equality and to give you the DER representation of the object 🍓
Oid instances are immutable, meaning there are no methods that let you change an Oid's contents 🔒 That's what lets you treat these objects as "statics" without making copies 💅
Certain routines let you use a default oid, and for those you can pass a "null" value 🎀
5.7. Object Identifier Sets 🌸
The Java bindings represent object identifier sets as arrays of Oid objects 🧚 Every Java array has a length field, which makes them easy to manipulate and reference ✨
To fully support the functionality of RFC 2743 [GSSAPIv2-UPDATE], the Oid class includes a method that checks whether an Oid object exists in a specified array 🦋 This does the same job as gss_test_oid_set_member 💅 Java arrays and automatic garbage collection mean these routines are no longer needed: gss_create_empty_oid_set, gss_release_oid_set, and gss_add_oid_set_member 👑 Java GSS-API implementations won't contain them 🙈 Automatic garbage collection plus the immutable Oid property clears up all the memory management issues the C counterpart has 🌈
Whenever an Object Identifier Set needs a default value, you can use a "null" value. Check the detailed method description for the specifics 💖
5.8. Credentials ✨
Okay bestie, credentials time 🔐 GSS-API credentials are represented by the GSSCredential interface, and it's giving full toolkit 🧰 It has several constructs so you can create most of the common credential objects for both the initiator and the acceptor. To compare two of them, you use the interface's equals method 💅 Here's the general vibe, pulled straight from the C-bindings spec:
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 the holder the power to act as any of them 👑 All the principal identities asserted by a single GSS-API credential should belong to the same entity, though enforcing that is up to the implementation. One GSSCredential object represents all the credential elements that have been acquired.
Passing null as the GSSCredential input when you create a GSSContext object is a way of saying "I want to act as the default principal" 🙈 Individual GSS-API implementations are free to decide what that default looks like for their mechanism, but for portability, the following default behavior is recommended.
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.
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 the default behavior whenever possible 🫶 Applications that request default behavior are likely to be more portable across mechanisms and implementations than ones that instantiate a GSSCredential object for a specific identity. Very smart, very intentional.
5.9. Contexts ✨
Now we're talking about contexts 🦋 The GSSContext interface represents one end of a GSS-API security context. It stores the state information for that end of the peer communication, including the cryptographic state information. The initiator and the acceptor create the context object in different ways 🍓 Once the context exists, the initiator can set various context options that determine the characteristics of the security context it wants. When every application-desired characteristic is set, the initiator calls the initSecContext method, which produces a token for the peer's acceptSecContext method to consume.
It's the application's job to deliver the authentication token(s) between the peer applications so they can be processed 📬 After the context-establishment phase is done, both the initiator and the acceptor can retrieve context attributes using the accessor methods. These reflect the actual attributes of the established context. At that point, the application can use the context to apply cryptographic services to its data 🔐
5.10. Authentication Tokens ✨
A token is a caller-opaque type that GSS-API uses to keep each end of the GSS-API security context in sync 🔄 It's a cryptographically protected octet-string, generated by the underlying mechanism at one end of a GSS-API security context for use by the peer mechanism at the other end. Encapsulating the token within the application protocol (if required) and transferring it are the responsibility of the peer applications 💌
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.
5.11. Inter-Process Tokens ✨
Some GSS-API routines exist to move data between processes in multi-process programs 🧚 These routines use a caller-opaque octet-string, which the GSS-API generates in one process for use by the GSS-API in another process. The calling application is responsible for transferring these tokens between processes.
Now, the important tea ☕ GSS-API implementors are encouraged to avoid putting sensitive information inside inter-process tokens, or to cryptographically protect them. Even so, many implementations won't be able to avoid placing key material or other sensitive data inside them 😭 So the application has to make sure inter-process tokens are protected in transit and only sent to processes that are trustworthy 🔥
Inter-process tokens are a big deal: they can carry key material, so protect them in transit and only hand them to trustworthy processes.
An inter-process token is represented as a byte array emitted from the export method of the GSSContext interface. The receiver of that token would initialize a GSSContext object with it to create a new context. Once a context has been exported, the GSSContext object is invalidated and is no longer available 👋
5.12. Error Reporting ✨
Okay, error reporting 💀 [GSSAPIv2-UPDATE] (that's RFC 2743 [GSSAPIv2-UPDATE]) 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, also called the 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 🎀 Mechanism-specific errors and GSS-API level errors both get signaled through instances of this class. Because exceptions take over that job, the major and minor codes don't need to be passed around within the API calls, which is honestly so clean ✨ 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 🌈
5.12.1. GSS Status Codes ✨
okay so GSS status codes are for errors that don't depend on which underlying mechanism is doing the security work 🔐 bestie, these are the generic API routine errors, the ones defined in the GSS-API spec itself. The Java bindings use the exceptions mechanism for these, so there's no need for calling errors. literally so clean 💅
A GSS status code means one single fatal generic API error came out of the routine that threw the GSSException. Using exceptions is how the method announces that something fatal happened during its run 😭 But the GSS-API operational model also lets per-message calls signal supplementary status info, and those can't be exceptions, because exceptions are way too dramatic for informatory or warning-ish stuff 🙈 So those come back as return values instead. The two per-message methods that can produce that extra info are GSSContext.verifyMIC() and GSSContext.unwrap(). They fill the supplementary status codes into the MessageProp object that got passed in, iykyk 🎀
A GSSException object does a few jobs: it sets the various error codes, translates them into text, and holds the definitions of all the numeric error values 🧚 Here's the table of error codes, no cap:
Table: GSS Status Codes
Name Value Meaning
BAD_BINDINGS 1 Incorrect channel bindings were
supplied.
BAD_MECH 2 An unsupported mechanism
was requested.
BAD_NAME 3 An invalid name was supplied.
BAD_NAMETYPE 4 A supplied name was of an
unsupported type.
BAD_STATUS 5 An invalid status code was
supplied.
BAD_MIC 6 A token had an invalid MIC.
CONTEXT_EXPIRED 7 The context has expired.
CREDENTIALS_EXPIRED 8 The referenced credentials
have expired.
DEFECTIVE_CREDENTIAL 9 A supplied credential was
invalid.
DEFECTIVE_TOKEN 10 A supplied token was invalid.
FAILURE 11 Miscellaneous failure,
unspecified at the GSS-API
level.
NO_CONTEXT 12 Invalid context has been
supplied.
NO_CRED 13 No credentials were supplied, or
the credentials were unavailable
or inaccessible.
BAD_QOP 14 The quality-of-protection (QOP)
requested could not be provided.
UNAUTHORIZED 15 The operation is forbidden by
the 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.
The following four status codes (DUPLICATE_TOKEN, OLD_TOKEN,
UNSEQ_TOKEN, and GAP_TOKEN) are contained in a GSSException
only if detected during context establishment, in which case it
is a fatal error. (During per-message calls, these values are
indicated as supplementary information contained in the
MessageProp object.) They are:
DUPLICATE_TOKEN 19 The token was a duplicate of an
earlier version.
OLD_TOKEN 20 The token's validity period has
expired.
UNSEQ_TOKEN 21 A later token has already been
processed.
GAP_TOKEN 22 The expected token was not
received.
🌸 The GSS major status code FAILURE is what you get when the underlying mechanism hit an error that doesn't have a specific GSS status code. The mechanism-specific status code can give you more details about it, so go check there 🔥
The different major status codes that can live inside the GSSException object thrown by the methods in this specification are the same major status codes returned by the matching calls in RFC 2743 [GSSAPIv2-UPDATE]. Same vibes, same family 👑
5.12.2. Mechanism-Specific Status Codes 🦋
Mechanism-specific status codes get communicated in two ways. Either they're part of a GSSException thrown from the mechanism-specific layer to signal a fatal error, or they're part of the MessageProp object that the per-message calls use to signal non-fatal errors. Two routes, one destination 🫶
A default value of 0 in either the GSSException object or the MessageProp object stands for "no mechanism-specific status code here" 🍓
5.12.3. Supplementary Status Codes ⭐
Supplementary status codes are confined to the per-message methods of the GSSContext interface 💜 Since they're just informative, it isn't appropriate to use exceptions for them. Instead, the per-message operations of the GSSContext interface hand these values back inside a MessageProp object.
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 had that specific property. The application has to decide what to do about these supplementary values 💀 The GSS-API doesn't treat them as errors, so don't panic when you see them 💖
5.13. Names ✨
okay so a name is basically how we identify a person or entity 🫶 and GSS-API's whole job here is to check that the name actually belongs to the entity claiming it. no cap, that's the vibe 💅
different authentication mechanisms might use different namespaces for their principals, which is why naming in GSS-API gets complex 😭 especially when you've got multiple mechanisms in one environment, or even one mechanism that supports more than one namespace.
there are two distinct conceptual representations for names, and I'm obsessed with how they fit together 🎀
- A GSS-API form represented by implementations of the
GSSNameinterface. A singleGSSNameobject can hold multiple names from different namespaces, but all of those names should refer to the same entity 🦋 For example, you could get an internal name from calling thegetNamemethod of theGSSCredentialinterface on a credential that has credential elements for several authentication mechanisms using different namespaces. ThatGSSNameobject will hold a separate name for the entity for each mechanism.
For GSS-API implementations that support multiple namespaces, GSSName implementations must contain enough information to figure out which namespace each primitive name belongs to ✨
- Mechanism-specific contiguous byte array and string forms. There are different
GSSNameinitialization methods for byte array and string formats, so apps can pick whatever fits their calling style and name type 🌸 These formats 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 mechanism that uses that name. String name forms are assumed to be printable, so GSS-API apps can use them to talk to their users 💬 Byte array name formats are assumed to be non-printable, like the byte array you get back from theexportmethod of theGSSNameinterface 🤖
A GSSName object can be turned into a contiguous representation with its toString method, and that guarantees the name comes out in a printable format 🍓 Different initialization methods in the GSSName interface support multiple syntaxes for each supported namespace, so users get to choose the name representation they like best 👑 The toString method should use an implementation-chosen printable syntax for each supported name type. To find out the printable name type, you can use the getStringNameType method 🧚
There's no guarantee that calling toString on the GSSName interface gives back the same string you originally imported, ngl 🙈 It's also possible the name was never built from a string at all! The same goes for namespace identifiers, which might not survive unchanged after a trip through the internal name form. For example, picture a mechanism that authenticates X.500 names but also does an algorithmic mapping of Internet DNS names into X.500. That mechanism's GSSName implementation might, when given a DNS name, make 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, the toString method would most likely produce 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 🫶 Since that name was authenticated by a single mechanism, it only holds a single name, even if the internal name the context initiator gave to the GSSContext object had multiple components. These are called internal-mechanism names (or MNs), and the names the GSSContext interface emits from getSrcName and getTargName are always this type ✨ Some apps need MNs without wanting to pay the overhead of an authentication operation, so there are creation methods that take the name buffer, the name type, and the mechanism Oid the name should be created for 👑 When you already have a GSSName object, you can call the canonicalize method to convert 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, way better than comparing the printable names an implementation might support. Since GSS-API assumes all primitive names inside a given internal name refer to the same entity, equal can return "true" if the two names share at least one primitive name in common 🔥 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 at all 🧠
When you're working with big access control lists, creating a GSSName object for every name and calling equal on each name from the Access Control List (ACL) can be super expensive 😭 So as an alternative, GSS-API defines a special form of the contiguous byte array name that can be compared directly, byte by byte 💅 Contiguous names suitable for comparison are generated by the export method. Exported names can be re-imported with the byte array constructor, as long as you specify 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 for the standard name types 🎀 Structurally, an exported name object has a header containing an OID that identifies the mechanism that authenticated the name, and a trailer containing the name itself. The syntax of the trailer is defined by the individual mechanism specification. The detailed format is described in the language-independent GSS-API specification [GSSAPIv2-UPDATE] 📖
Heads up, bestie: the results from theequalsmethod will generally be different from what you get by callingcanonicalizeandexportand then comparing the byte array output 🚨 The first approach figures out whether two (unauthenticated) names identify the same principal. The second figures out whether a particular mechanism would authenticate them as the same principal. These two operations will generally only give the same results for MNs.
It's really important to know that all of the above are guidelines for how GSSName implementations should behave 🌈 They aren't meant to be 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 behavior follows those guidelines 🦋
5.14. Channel Bindings 🎀
GSS-API lets you use user-specified tags to identify a given context to the peer application 🧚 These tags are meant to identify the particular 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 the 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, as long as that happens before the first call to init or accept 🫶 Unless you've used setChannelBinding to set the ChannelBinding for a GSSContext object, a "null" ChannelBinding is assumed 🙈 InetAddress is currently the only address type defined within the Java platform, so it's the only one supported in the ChannelBinding class 👑 Apps that use other address types can include them as part of the application-specific data 🌸
Conceptually, GSS-API concatenates the initiator and acceptor address information with the application-supplied byte array to form an octet-string 🤯 The mechanism then calculates a Message Integrity Code (MIC) over that octet-string and binds the MIC to the context establishment token emitted by the init method of the GSSContext interface 🔐 The context acceptor sets the same bindings on its own GSSContext object, and while the accept method is processed, a MIC is calculated the same way. The calculated MIC is compared with the one found in the token. If the MICs differ, accept throws a GSSException with the major code set to BAD_BINDINGS, and the context will not be established 🚫
Careful, bestie: some mechanisms may include the actual channel binding data in the token (instead of just a MIC) 😬 So applications should therefore not use confidential data as channel-binding components. Keep those secrets secret! 🤫
Individual mechanisms can impose extra constraints on the addresses that appear in channel bindings 🧐 For example, a mechanism might verify that the initiator address field of the channel binding contains the correct network address of the host system. So portable applications should therefore make sure they either provide correct information for the address fields, or leave the addressing information unset 💅
5.15. Stream Objects 🌊
The context object has overloaded methods that use input and output streams to convey authentication and per-message GSS-API tokens 🦋 It's really important to know that the streams are expected to contain the usual GSS-API tokens, which would otherwise be handled through byte arrays. The tokens are expected to have a definite start and a definite end 🎀 Callers are responsible for making sure the supplied streams won't block, or don't expect to block until a full token is processed by the GSS-API method. Only a single GSS-API token gets processed per invocation of a stream-based method ✨
Streams let callers control and manage the buffers they supply 💖 Because streams are non-primitive objects, callers can make them as complicated or as simple as they want, just by using the streams defined in the java.io package or by creating their own through inheritance. That gives apps the greatest flexibility 🧚
5.16. Optional Parameters 🍓
Whenever the application wants to omit 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 👑
6. Introduction to GSS-API Classes and Interfaces ✨
okay so this section is like a little tour of the GSS-API classes and interfaces 🎀 the implementations get pulled in from the CLASSPATH that the application sets up. and if Java GSS ever becomes part of the standard Java APIs, these classes will just be there by default on every system as part of the JRE's system classes 💖
this section also shows which RFC 2743 [GSSAPIv2-UPDATE] functionality each class covers ✨ the full details of the classes and their methods are in section 7 🦋
6.1. GSSManager Class
this one is an abstract class that acts as a factory 🏭 it makes implementations of the GSS-API interfaces and also has methods for asking questions about the underlying security mechanisms 🔥
you can grab a default implementation with the static method getInstance() 💅 and if an app wants its own version of GSSManager, it can just extend the abstract class itself 🫶
this class has the equivalents of these RFC 2743 [GSSAPIv2-UPDATE] routines:
RFC 2743 Routine Function Section(s)
gss_import_name Create an internal name from 7.1.6-
the supplied information. 7.1.9
gss_acquire_cred Acquire credential 7.1.10-
for use. 7.1.12
gss_import_sec_context Create a previously exported 7.1.15
context.
gss_indicate_mechs List the mechanisms 7.1.3
supported by this GSS-API
implementation.
gss_inquire_mechs_for_name List the mechanisms 7.1.5
supporting the
specified name type.
gss_inquire_names_for_mech List the name types 7.1.4
supported by the
specified mechanism.
6.2. GSSName Interface
GSS-API names show up in the Java bindings through the GSSName interface 🌸 different name formats and what they mean are identified with Universal Object Identifiers (oids) 🧚 you can figure out a name's format from the unique oid of its name type ✨ here are the GSS-API routines the GSSName interface gives you:
RFC 2743 Routine Function Section(s)
gss_display_name Covert internal name 7.2.7
representation to text format.
gss_compare_name Compare two internal names. 7.2.3,
7.2.4
gss_release_name Release resources associated N/A
with the internal name.
gss_canonicalize_name Convert an internal name to a 7.2.5
mechanism name.
gss_export_name Convert a mechanism name to 7.2.6
export format.
gss_duplicate_name Create a copy of the internal N/A
name.
no need for gss_release_name here 🙈 Java does its own garbage collection, so it's handled for you. gss_duplicate_name is redundant too 💅 the GSSName interface has no mutator methods that can change the state of the object, so it's totally safe to share across threads 🧵💜
6.3. GSSCredential Interface
the GSSCredential interface is in charge of wrapping 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 multiple mechanism-specific credentials, and each of those is called a credential element ✨ this interface gives you the functionality of these GSS-API routines:
RFC 2743 Routine Function Section(s)
gss_add_cred Constructs credentials 7.3.12
incrementally.
gss_inquire_cred Obtain information about 7.3.4-
credential. 7.3.11
gss_inquire_cred_by_mech Obtain per-mechanism 7.3.5-
information about 7.3.10
a credential.
gss_release_cred Dispose of credentials 7.3.3
after use.
6.4. GSSContext Interface
this one is the big boss for context stuff 👑 it covers the context-level calls needed to set up and manage a security context between peers, plus the per-message services apps use 💖 a context is established between a pair of peers and lets you use security services on a per-message basis for application data 🦋 and it's created over a single security mechanism 🔥 the GSSContext interface gives you the functionality of these GSS-API routines:
RFC 2743 Routine Function Section(s)
gss_init_sec_context Initiate the creation of a 7.4.3-
security context with a peer. 7.4.6
gss_accept_sec_context Accept a security context 7.4.7-
initiated by a peer. 7.4.10
gss_delete_sec_context Destroy a security context. 7.4.12
gss_context_time Obtain remaining context 7.4.41
time.
gss_inquire_context Obtain context 7.4.32-
characteristics. 7.4.46
gss_wrap_size_limit Determine token-size limit 7.4.13
for gss_wrap.
gss_export_sec_context Transfer security context 7.4.22
to another process.
gss_get_mic Calculate a cryptographic 7.4.18,
Message Integrity Code (MIC) 7.4.19
for a message.
gss_verify_mic Verify integrity on a received 7.4.20,
message. 7.4.21
gss_wrap Attach a MIC to a message and 7.4.14,
optionally encrypt the message 7.4.15
content.
gss_unwrap Obtain a previously wrapped 7.4.16,
application message verifying 7.4.17
its integrity and optionally
decrypting it.
so the gss_process_context_token routine did not make it into the Java bindings spec 🙅♀️ and the matching functionality of gss_delete_sec_context was tweaked so it doesn't return any peer tokens 🔄 this was proposed to follow the recommendations stated in RFC 2743 [GSSAPIv2-UPDATE] ✨ GSSContext still lets you destroy the locally stored context info though 🫶
6.5. MessageProp Class
this is a helper class for the per-message operations on the context 💅 the app creates an instance of it and passes it into the per-message calls ✨ sometimes the app tells the GSS-API implementation something through this object, and other times the GSS-API hands information back to the app by setting it here 🎁 for the details on the per-message operations wrap, unwrap, getMIC, and verifyMIC, check the GSSContext interfaces 🧚
6.6. GSSException Class
exceptions are how the Java bindings signal fatal errors to the calling apps 💀 this replaces the major and minor codes that the C-bindings spec used for signaling failures 🙈 the GSSException class handles both minor and major codes, and also turns them into textual representation 📝 every GSS-API method is declared as throwing this exception 🌈
RFC 2743 Routine Function Section
gss_display_status Retrieve textual 7.8.5, 7.8.6,
representation of error 7.8.8, 7.8.9
codes.
6.7. Oid Class
this utility class represents Universal Object Identifiers and handles the operations that go with them 🌸 GSS-API uses object identifiers to tell security mechanisms and name types apart 🔍 aside from being used anytime an object identifier is needed, this class also implements this GSS-API functionality:
RFC 2743 Routine Function Section
gss_test_oid_set_member Determine if the specified oid 7.7.5
is part of a set of oids.
6.8. ChannelBinding Class
an instance of this class is used to hand channel binding information to the GSSContext object before a security context establishment starts 🎀 the app can use a byte array for application data that goes into the channel binding, and it can also use instances of the InetAddress 🌐 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 kinds of addresses can just include them as part of the application data 🫶
7. Detailed GSS-API Class Description 💅
okay this section is the detailed breakdown 📖 it lists the public methods that each of the GSS-API classes and interfaces must provide 👑
7.1. public abstract class GSSManager ✨
okay so the GSSManager class is an abstract class that acts like a factory 🏭 for three GSS interfaces: GSSName, GSSCredential, and GSSContext. it also lets apps check what mechanisms the GSS implementation has available and what name types each mechanism supports 🔍. you can grab the default GSSManager subclass with the static method getInstance(), but apps are totally free to make their own subclasses of GSSManager 💅
all but one method in this class are declared abstract, which means subclasses have to provide the full implementation for those methods 🫶 the only exception is the static method getInstance(). that one has platform-specific code that returns 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 ever calls the default constructor the compiler gives the base class. it's giving consistency 🎀
a subclass extending the GSSManager abstract class may be built as a modular provider-based layer that uses some well-known service provider specification 🧩 the GSSManager API gives the app methods to set provider preferences on that kind of 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 that make these modes work: addProviderAtFront() and addProviderAtEnd() ✨ they basically build an ordered list of <provider, oid> pairs, where each pair says which provider is preferred for a given oid.
using these methods doesn't need any knowledge of whatever service provider specification the GSSManager subclass follows 🌸 it's hoped these methods will cover the needs of most apps. extra methods might get added to an extended GSSManager later, and those could become part of a service provider specification that gets standardized down the road 🌈
7.1.1. Example Code
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);
7.1.2. getInstance
public static GSSManager getInstance()
returns the default GSSManager implementation 💖
7.1.3. getMechs
public abstract Oid[] getMechs()
returns an array of Oid objects showing the mechanisms available to GSS-API callers. it returns a "null" value when no mechanisms are available 🙈 (an example: mechanisms are dynamically configured and none are installed right now).
7.1.4. getNamesForMech
public abstract Oid[] getNamesForMech(Oid mech)
throws GSSException
returns the name type Oid's supported by the specified mechanism 🍓
Parameters:
mech: the Oid object for the mechanism to query.
7.1.5. getMechsForName
public abstract Oid[] getMechsForName(Oid nameType)
returns an array of Oid objects for the mechanisms that support the specific name type 🧚 it returns "null" when no mechanisms are found to support the specified name type.
Parameters:
nameType: the Oid object for the name type.
7.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 specified namespace into a GSSName object ✨ usually the GSSName it creates won't be an MN. the two exceptions are when the namespace type parameter says NT_EXPORT_NAME, or when the GSS-API implementation isn't multi-mechanism 💅
Parameters:
nameStr: the string representing a printable form of the name to create.nameType: the Oid specifying the namespace of the printable name. nameType is there to describe and qualify how the input nameStr gets interpreted. it doesn't necessarily imply a type for the output GSSName implementation 🤓 the "null" value can be used to say each mechanism that looks at nameStr should assume its own default printable syntax.
7.1.7. createName
public abstract GSSName createName(byte[] name, Oid nameType)
throws GSSException
this is a factory method that turns a contiguous byte array holding a name from the specified namespace into a GSSName object 🫧 usually the GSSName created won't be an MN. the two exceptions are when the namespace type parameter says NT_EXPORT_NAME, or when the GSS-API implementation isn't multi-mechanism.
Parameters:
name: the byte array containing the name to create.nameType: the Oid specifying the namespace of the name in the byte array. nameType describes and qualifies how the input name byte array gets interpreted, and doesn't necessarily imply a type for the output GSSName implementation. the "null" value can be used to say each mechanism that examines the byte array should assume a mechanism-specific default syntax 🌷
7.1.8. createName
public abstract GSSName createName(String nameStr, Oid nameType,
Oid mech) throws GSSException
this is a factory method that turns a contiguous string name from the specified namespace into a GSSName object that is a mechanism name (MN) 🦋 basically it's a utility that does the work of two steps: the createName described in section 7.1.6, and then the GSSName.canonicalize() described in section 7.2.5 💫
Parameters:
nameStr: the string representing a printable form of the name to create.nameType: the Oid specifying the namespace of the printable name supplied. nameType describes and qualifies how the input nameStr gets interpreted, and doesn't necessarily imply a type for the output GSSName implementation. the "null" value can be used to say a mechanism-specific default printable syntax should be assumed when the mechanism examines nameStr.mech: the Oid specifying the mechanism this name should be created for.
7.1.9. createName
public abstract GSSName createName(byte[] name, Oid nameType,
Oid mech) throws GSSException
this is a factory method that turns a contiguous byte array holding a name from the specified namespace into a GSSName object that is an MN 🌈 same deal as above: it's a utility that does the equivalent of two steps, the createName described in section 7.1.7, and then the GSSName.canonicalize() described in section 7.2.5.
Parameters:
name: the byte array representing the name to create.nameType: the Oid specifying the namespace of the name in the byte array. nameType describes and qualifies how the input name byte array gets interpreted, and doesn't necessarily imply a type for the output GSSName implementation. the "null" value can be used to say each mechanism that examines the byte array should assume a mechanism-specific default syntax.mech: the Oid specifying the mechanism this name should be created for.
7.1.10. createCredential
public abstract GSSCredential createCredential(int usage)
throws GSSException
this is a factory method for acquiring default credentials 🔑 it makes the GSS-API use system-specific defaults for the set of mechanisms, the name, and a DEFAULT lifetime.
Parameters:
usage: the intended usage for this credential object. the value of this parameter must be one of:GSSCredential.INITIATE_AND_ACCEPT(0)GSSCredential.INITIATE_ONLY(1), orGSSCredential.ACCEPT_ONLY(2)
7.1.11. createCredential
public abstract GSSCredential createCredential(GSSName aName,
int lifetime, Oid mech, int usage)
throws GSSException
this is a factory method for acquiring a single mechanism credential 🧁
Parameters:
aName: the name of the principal this credential is being acquired for. use "null" to specify the default principal.lifetime: the number of seconds credentials should stay valid. useGSSCredential.INDEFINITE_LIFETIMEto request that the credentials have the maximum permitted lifetime. useGSSCredential.DEFAULT_LIFETIMEto request the default credential lifetime ⏳mech: the oid of the desired mechanism. use "(Oid) null" to request the default mechanism(s).usage: the intended usage for this credential object. the value of this parameter must be one of:GSSCredential.INITIATE_AND_ACCEPT(0)GSSCredential.INITIATE_ONLY(1), orGSSCredential.ACCEPT_ONLY(2)
7.1.12. createCredential ✨
public abstract GSSCredential createCredential(GSSName aName,
int lifetime, Oid[] mechs, int usage)
throws GSSException
Okay bestie, this is the factory method for getting credentials across a whole set of mechanisms 🔑✨ It acquires credentials for every mechanism listed in the mechs array. If you want to know which mechanisms actually worked, call GSSCredential.getMechs() afterwards, because not every one is guaranteed to succeed 🦋
Parameters:
aName: The name of the principal this credential is being acquired for. Passnullto mean the default principal 💖lifetime: How many seconds the credentials should stay valid ⏳ UseGSSCredential.INDEFINITE_LIFETIMEto ask for the maximum lifetime the system allows, orGSSCredential.DEFAULT_LIFETIMEto ask for the default lifetime.mechs: The array of mechanisms the credential should be acquired over 🌸 Pass(Oid[]) nullto ask for a system-specific default set of mechanisms.usage: How you plan to use this credential object 🎀 The value must be one of these:
GSSCredential.INITIATE_AND_ACCEPT(0),
GSSCredential.INITIATE_ONLY(1), or
GSSCredential.ACCEPT_ONLY(2)
7.1.13. createContext
public abstract GSSContext createContext(GSSName peer, Oid mech,
GSSCredential myCred, int lifetime)
throws GSSException
This is the factory method for making a context on the initiator's side 🚀 You can still tweak the context flags with the mutator methods before you call GSSContext.initSecContext(), so don't panic if you need to change things first 💅
Parameters:
peer: The name of the target peer 🎯mech: The Oid of the mechanism you want. Pass(Oid) nullto ask for the default mechanism ✨myCred: The initiator's credentials. Passnullto act as a default initiator principal 🧚lifetime: How long, in seconds, you're requesting the context to last ⏰ UseGSSContext.INDEFINITE_LIFETIMEorGSSContext.DEFAULT_LIFETIMEto ask for indefinite or default context lifetime.
7.1.14. createContext
public abstract GSSContext createContext(GSSCredential myCred)
throws GSSException
This is the factory method for making a context on the acceptor's side 🫶 The context's properties come from the input token that gets handed to the accept method, so the acceptor doesn't have to set them up by hand.
Parameters:
myCred: The acceptor's credentials. Passnullto act as a default acceptor principal 💜
7.1.15. createContext
public abstract GSSContext createContext(byte[] interProcessToken)
throws GSSException
This one is for creating a context that was exported earlier 📦 The context properties are read straight out of the input token, and you can't change them later through the set methods. No edits after the fact, periodt 🙅♀️
Parameters:
interProcessToken: The token that was previously emitted from the export method 🎁
7.1.16. addProviderAtFront
public abstract void addProviderAtFront(Provider p, Oid mech)
throws GSSException
This method lets the application tell the GSSManager that a particular provider should be tried ahead of every other one whenever support is needed for a given mechanism 👑 If you pass null instead of an Oid for the mechanism, the GSSManager must use that provider ahead of all others no matter which mechanism is involved. The GSSManager should only move on to a different provider when the chosen one doesn't support the mechanism it needs 🦋
Calling this method over and over keeps your older settings, but it pushes them down in preference. You end up with an ordered list of provider and Oid pairs that keeps growing at the top ⬆️
Calling addProviderAtFront with a null Oid removes all the previous preferences that were set for that provider in this GSSManager instance 🧹 Calling it with a non-null Oid removes only the previous preference that used that mechanism and that 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 isn't available 😭
Parameters:
p: The provider instance that should be used whenever support is needed formech💖mech: The mechanism the provider is being set for 🌈
7.1.17. Example Code
Say an application wants provider A to always get checked first when 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 with Oid m1 to come from provider B before A gets checked, it would call:
mgr.addProviderAtFront(B, m1);
So the GSSManager would check with B first when it needs m1. If B doesn't support m1, the GSSManager keeps going and checks with A. If the application needs some other mechanism m2, which is different from m1, the GSSManager skips B entirely and checks with A directly 🎀
Now suppose that later, the same GSSManager instance gets this call:
mgr.addProviderAtFront(B, null)
The earlier (B, m1) setting gets subsumed by this one, so it should be removed. In effect, the preference list is now {(B, null), (A, null), ... //followed by the pre-configured list.
But this call:
mgr.addProviderAtFront(A, m3)
doesn't subsume the earlier (A, null) setting. The list ends up as {(A, m3), (B, null), (A, null), ...} 💅
7.1.18. addProviderAtEnd
public abstract void addProviderAtEnd(Provider p, Oid mech)
throws GSSException
This method tells the GSSManager that the application wants a particular 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 at all.
Calling this method repeatedly keeps the older settings, but it moves them above the newer ones in preference. That makes an ordered list of providers and Oid pairs that grows at the bottom ⬇️ So the older provider settings always get used first.
If there's any existing preference that conflicts 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 say the operation isn't available 💀
Parameters:
p: The provider instance that should be used whenever support is needed formech🍓mech: The mechanism the provider is being set for 🦋
7.1.19. Example Code
Suppose an application wants this: when a mechanism with Oid m1 is needed, the system's default providers get checked first, and provider A only gets checked if they don't support m1. It would make this call:
GSSManager mgr = GSSManager.getInstance();
mgr.addProviderAtEnd(A, m1);
Now, if it also wants provider B checked for all mechanisms after every configured provider has been checked, it would call:
mgr.addProviderAtEnd(B, null);
In effect, the preference list is now {..., (A, m1), (B, null)}.
Suppose that later, the same GSSManager instance gets this call:
mgr.addProviderAtEnd(B, m2)
The existing (B, null) setting already covers this, so the request should be ignored. The same thing happens if someone asks for the pairs that already exist, (A, m1) or (B, null) 🙃
But this call:
mgr.addProviderAtEnd(A, null)
isn't subsumed by the earlier (A, m1) setting, so the list ends up as {..., (A, m1), (B, null), (A, null)} ✨
7.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), and you can tell what format a name is by the unique Oid of its namespace type 🌈
7.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), compares it, gets a printable version of it, 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);
7.2.2. Static Constants 💖
public static final Oid NT_HOSTBASED_SERVICE
Okay bestie, this is the name type for a host-based service 🖥️✨. It's for services tied to host computers, and the name is built from two bits, "service" and "hostname", like this:
service@hostname
Values for the "service" part get registered with the IANA 🦋. This one stands for: { iso(1) member-body(2) Unites States(840) mit(113554) infosys(1) gssapi(2) generic(1) service_name(4) }
public static final Oid NT_USER_NAME
This name type says "this is a named user on a local system" 🎀. It stands for: { 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 one is for a numeric user identifier that matches a user on a local system (like a Uid) 🔢. It stands for: { 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
Here you've got a string of digits that represents the numeric user identifier of a user on a local system 💜. It stands for: { 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
Name type for an anonymous entity 🧚. It stands for: { iso(1), org(3), dod(6), internet(1), security(5), nametypes(6), gss-anonymous-name(3) }
public static final Oid NT_EXPORT_NAME
This name type marks an exported name, the kind the export method produces 🌸. It stands for: { iso(1), org(3), dod(6), internet(1), security(5), nametypes(6), gss-api-exported-name(4) }
7.2.3. equals 🍓
public boolean equals(GSSName another) throws GSSException
This compares two GSSName objects to figure out if they point to the same entity 👀. It may throw a GSSException when the names can't be compared. If either name is an anonymous entity, the method returns "false", so no anonymous name is ever equal to anything 🙈.
Parameters:
another: the GSSName object you're comparing against.
7.2.4. equals 💅
public boolean equals(Object another)
This is the variation of equals from section 7.2.3, and it exists to override the Object.equals() method that the class inherits 🫶. It acts exactly like section 7.2.3, with one difference: it never throws a GSSException. If an error happens, it returns "false" instead. (Heads up: the Java language spec requires that two objects which are equal under equals(Object) must return the same integer when hashCode() is called on them 🔥.)
Parameters:
another: the GSSName object you're comparing against.
7.2.5. canonicalize ✨
public GSSName canonicalize(Oid mech) throws GSSException
This creates a mechanism name (MN) from any internal name 🦋. It's the same as using the factory methods from sections 7.1.8 or 7.1.9 that take the mechanism name as a parameter.
Parameters:
mech: the oid for the mechanism you want the canonical form of the name for.
7.2.6. export 📦
public byte[] export() throws GSSException
This gives back a canonical, contiguous byte representation of a mechanism name (MN), made to be compared byte-by-byte 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 decides not to throw, 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 [GSSAPIv2-UPDATE] 📜.
7.2.7. toString 🎀
public String toString()
This returns a textual representation of the GSSName object 💬. If you want to know which printed name format the string uses, you can call the getStringNameType method 👑.
7.2.8. getStringNameType 🌈
public Oid getStringNameType() throws GSSException
This returns the oid for the type of name that toString gives back 🧾. With that oid, you can figure out the syntax of the printable name ✨.
7.2.9. isAnonymous 🙈
public boolean isAnonymous()
This checks whether the name object represents an anonymous entity. It returns "true" if the name is anonymous 🫥.
7.2.10. isMN 🧚
public boolean isMN()
This checks whether the name object has only one mechanism element, which means it's a mechanism name as defined by RFC 2743 [GSSAPIv2-UPDATE] 💖.
7.3. public interface GSSCredential implements Cloneable 🔥
public interface GSSCredential implements Cloneable
This interface holds the GSS-API credentials for an entity 🔐. A credential has all the cryptographic info needed to create a context on behalf of the entity it represents 🗝️. It can hold several distinct, mechanism-specific credential elements, each carrying info for one security mechanism, but they all refer to the same entity 👯♀️.
A credential can be used for context initiation, context acceptance, or both 🦋.
GSS-API implementations must impose a local access-control policy on callers, so unauthorized callers can't acquire credentials they aren't entitled to 🚫. Credential creation in GSS-API isn't meant to be a "login to the network" function 🙅♀️. A login would mean creating new credentials, not just grabbing a handle to ones that already exist. If functions like that are needed, they should be defined in implementation-specific extensions to the API 🛠️.
If acquiring a credential is slow for a mechanism, the mechanism may choose to hold off on the actual acquisition until the credential is needed (for example, by GSSContext) ⏳. Those mechanism-specific choices should be invisible to the calling app 👀. So the query methods right after creating a credential object must return valid credential data, even if that means taking on the cost of the deferred acquisition 💅.
Applications create a credential object by passing in the parameters they want 📝. Then they can use the query methods to learn about the credential they got (this is like the gss_inquire routines) 🔎. When the credential isn't needed anymore, the application should call dispose (the same as gss_release_cred) 🧹. That frees the resources the credential object holds and destroys any cryptographically sensitive info 💥.
Classes that implement this interface also implement Cloneable 👯. That means they support the clone() method, which lets you make duplicate credentials 🪞. This is handy to call right before the add() call, so you keep a copy of the original credential 🫶.
7.3.1. Example Code 🍓
Here's how it looks: we make a GSSCredential for a specific entity, query its fields, and then release it when we'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();
7.3.2. Static Constants 💜
public static final int INITIATE_AND_ACCEPT
This is the credential usage flag asking that the credential can be used for both context initiation and acceptance 🦋. Its value is 0.
public static final int INITIATE_ONLY
This flag asks that the credential can be used for context initiation only 🚀. Its value is 1.
public static final int ACCEPT_ONLY
This flag asks that the credential can be used for context acceptance only 🛡️. Its value is 2.
public static final int DEFAULT_LIFETIME
This lifetime constant stands for the default credential lifetime ⏰.
Its value is 0.
public static final int INDEFINITE_LIFETIME
This lifetime constant stands for an indefinite credential lifetime ♾️. Its value is the maximum integer value in Java, which is Integer.MAX_VALUE 🌟.
7.3.3. dispose 🧹
public void dispose() throws GSSException
This releases any sensitive info the GSSCredential object may be holding 🔒. Applications should call this as soon as the credential isn't needed anymore, so the sensitive info isn't kept around any longer than necessary 💨.
7.3.4. getName 🫶
public GSSName getName() throws GSSException
This retrieves the name of the entity the credential asserts 📛.
7.3.5. getName 🌸
public GSSName getName(Oid mechOID) throws GSSException
This retrieves a mechanism name of the entity the credential asserts 🪪. It's the same as calling canonicalize() on the name that section 7.3.4 returns 🔁.
Parameters:
mechOID: the mechanism you want the information returned for.
7.3.6. getRemainingLifetime ⏳
public int getRemainingLifetime() throws GSSException
This returns the remaining lifetime of a credential, in seconds ⌛. The remaining lifetime is the minimum lifetime across all the underlying credential mechanisms 📉. If it returns GSSCredential.INDEFINITE_LIFETIME, the credential never expires ♾️. If it returns 0, the credential has already expired 💀.
7.3.7. getRemainingInitLifetime 🦋
public int getRemainingInitLifetime(Oid mech) throws GSSException
This returns how many seconds are left before the credential stops being able to initiate security contexts under the mechanism you specify 🚪. If it returns GSSCredential.INDEFINITE_LIFETIME, the credential doesn't expire for context initiation 🌈. If it returns 0, the credential has already expired 💀.
Parameters:
mechOID: the mechanism you want the information returned for.
7.3.8. getRemainingAcceptLifetime 🧚
public int getRemainingAcceptLifetime(Oid mech) throws GSSException
This returns how many seconds are left before the credential stops being able to accept security contexts under the mechanism you specify 🎟️. If it returns GSSCredential.INDEFINITE_LIFETIME, the credential doesn't expire for context acceptance 🌟. If it returns 0, the credential has already expired 💀.
Parameters:
mechOID: the mechanism you want the information returned for.
7.3.9. getUsage 👑
public int getUsage() throws GSSException
This returns the credential usage flag, taken as a union over all mechanisms 🧩. The return value is one of GSSCredential.INITIATE_AND_ACCEPT (0), GSSCredential.INITIATE_ONLY (1), or GSSCredential.ACCEPT_ONLY (2) 💖.
7.3.10. getUsage 🦋
public int getUsage(Oid mechOID) throws GSSException
okay so this one returns the credential usage flag, but only for the mechanism you pass in 🔥 The return value will be one of GSSCredential.INITIATE_AND_ACCEPT(0), GSSCredential.INITIATE_ONLY(1), or GSSCredential.ACCEPT_ONLY(2) ✨
Parameters:
mechOID: the mechanism you want the info for 💜
7.3.11. getMechs 🌸
public Oid[] getMechs() throws GSSException
this one hands you back an array of the mechanisms this credential supports 🧚
7.3.12. add 💅
public void add(GSSName aName, int initLifetime, int acceptLifetime,
Oid mech, int usage) throws GSSException
this adds a mechanism-specific credential-element to a credential that already exists 🎀 It's how you build a credential one mechanism at a time, which is so satisfying ngl 😭
This routine is basically built for context acceptors when they're making acceptance credentials. Those get used with a whole bunch of different clients, each using different security mechanisms, so it makes sense 🫶
Heads up: this routine adds the new credential element "in-place". So if you want the element in a new credential instead, first call clone() to get a copy of this credential, then call add() on that copy 🙈
Parameters:
aName: the name of the principal this credential is being acquired for. Usenullto specify the default principal 💖initLifetime: how many seconds the credentials should stay valid for initiating security contexts. UseGSSCredential.INDEFINITE_LIFETIMEif you want the maximum permitted lifetime, orGSSCredential.DEFAULT_LIFETIMEif you want the default credential lifetime 🦋acceptLifetime: how many seconds the credentials should stay valid for accepting security contexts. Same deal as above:GSSCredential.INDEFINITE_LIFETIMEasks for the maximum permitted lifetime, andGSSCredential.DEFAULT_LIFETIMEasks for the default credential lifetime ✨mech: the mechanisms the credential is going to be acquired over 🌈usage: the intended usage for this credential object. It has to be one ofGSSCredential.INITIATE_AND_ACCEPT(0),GSSCredential.INITIATE_ONLY(1), orGSSCredential.ACCEPT_ONLY(2)👑
7.3.13. equals 🍓
public boolean equals(Object another)
this checks whether this GSSCredential refers to the same entity as the object you hand it. For two credentials to match, they have to be acquired over the same mechanisms AND refer to the same principal 🔥 It returns "true" if they refer to the same entity, and "false" otherwise 🙈
(Side note, but it matters: the Java language specification [JLS] says that two objects that are equal according to the equals(Object) method must return the same integer result when hashCode() gets called on them. So keep that in mind 💀)
Parameters:
another: another GSSCredential object to compare against 💜
7.4. public interface GSSContext 🧚
okay this interface wraps up the GSS-API security context and gives you the security services (wrap, unwrap, getMIC, verifyMIC) that are available over the context 🎀 Security contexts get established between peers, using credentials you got locally. You can have multiple contexts between the same pair of peers at once, using the same credentials or different ones 🦋 GSS-API works no matter what transport protocol you're on, and it's up to your calling application to carry its tokens between the peers 🫶
Before context establishment starts, the context initiator can ask for specific characteristics it wants from the finished context. You set those with the set methods. Once the context is established, you can check what characteristics and services the context actually offers using the query methods ✨
The establishment phase kicks off with the very first call to the init method by the context initiator 💅 During this phase, initSecContext and acceptSecContext produce GSS-API authentication tokens, and your app 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 normal flow keeps going and your app can call isEstablished() 🌸
- If isEstablished() returns "false", it means a token is still needed from the peer before establishment can continue 💜
- If it returns "true", that means the local end of the context is established. Heads up though: a token might still need to go to the peer, if GSS-API produced one 🙈
During establishment, you can call isProtReady() to find out whether the context can be used for per-message operations yet. This means apps can do per-message operations on contexts that aren't fully established 🌈
Once the context is established, or isProtReady() returns "true", you can call the query routines to figure out the actual characteristics and services of the context 👑 Your app can also start using the per-message methods wrap and getMIC to get cryptographic operations on application-supplied data 💖
And when you don't need the context anymore, the application should call dispose to release any system resources the context might be using 🧹✨
7.4.1. Example Code 🎀
The example below shows how to use the GSSContext interface from the initiating peer 🦋 It covers a bunch of stuff: making the object, setting the flags you want, establishing the context, checking the actual context flags, doing per-message operations on app 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();
7.4.2. Static Constants ⭐
public static final int DEFAULT_LIFETIME
this is a lifetime constant for the default context lifetime. Its value is 0 🍓
public static final int INDEFINITE_LIFETIME
this is a lifetime constant for an indefinite context lifetime. Its value is the maximum integer value in Java, which is Integer.MAX_VALUE 🌈
7.4.3. initSecContext 🧚
public byte[] initSecContext(byte[] inputBuf, int offset, int len)
throws GSSException
the context initiator calls this to kick off context creation. It does the same job as the stream-based method, except the token buffers are handled as byte arrays instead of stream objects 🦋 This method might return an output token that the app needs to send to the peer so the peer can process it with the accept call. Usually the app does that by calling flush() on an OutputStream wrapping the connection between the two peers 🫶
Call isEstablished() to check whether context establishment is done for this peer ✨ A "false" from isEstablished() means more tokens are expected to be passed into initSecContext(). Also, it's possible for initSecContext() to return a token for the peer AND for isEstablished() to return "true" 🔥 That means the token still needs to be sent to the peer, but the local end of the context is now fully established 💅
Once context establishment finishes, you can check the available context options using the get methods 👑
Parameters:
inputBuf: the token generated by the peer. This parameter is ignored on the first call 🌸offset: where the token starts withininputBuf💜len: the length of the token withininputBuf, starting at the offset 🙈
7.4.4. 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());
}
7.4.5. initSecContext ✨
public int initSecContext(InputStream inStream,
OutputStream outStream) throws GSSException
okay bestie, this is the initiator's big moment 🚀 it's how the context initiator kicks off context creation! it's the stream version of the byte-array method, so same energy, just with streams 🌊 it may write an output token to the outStream, and the app needs to send that to the peer so the peer can process it in the accept call 💌 usually you'd do that by calling flush() on an OutputStream wrapped around the connection between the two peers 🔌
you can call isEstablished() to check if context establishment is done for this peer ✅ if it says false, more tokens are still expected to be supplied to initSecContext. but here's the lowkey plot twist 🙈: it's possible for initSecContext() to hand back a token for the peer and isEstablished() to say true at the same time. that means the token still needs to go to the peer, but your local end is already fully established 💅
the GSS-API authentication tokens have a definitive start and end 🎀 this method reads one of those tokens per invocation, and it may block on the stream if only part of the token is available 😭 once context establishment wraps up, you can query the available context options through the get methods ✨
Parameters:
inStream: the token generated by the peer. this one gets ignored on the first call 🍓outStream: the output stream where the output token gets written. during the final stage of context establishment, there might be no bytes written at all 🌸
7.4.6. Example Code
this sample is just a quick demo of the token exchange during context establishment 🧚 most Java apps will probably bring their own 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 pull a token in from the peer, and a simple flush() on the app's OutputStream might send a previously written token out 📬
// 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());
}
7.4.7. acceptSecContext 🫶
public byte[] acceptSecContext(byte[] inTok, int offset, int len)
throws GSSException
this one is the context acceptor's turn 🎯 it gets called whenever a token comes in from the peer. it's the same deal as the stream-based method, except the token buffers are handled as byte arrays instead of stream objects 🧃
this method may hand back an output token that the app needs to send to the peer so the init call can process it further 💌 a null return means no token needs to go to the peer 🙅♀️ you can call isEstablished() to see if context establishment is complete for this peer, and a false there means more tokens are expected to be supplied to this method 👀
same plot twist as before 🙈: acceptSecContext() can return a token for the peer and isEstablished() can say true. the token still has to go to the peer, but your local end is fully established 💖 and once everything's done, the available context options can be queried through the get methods 🦋
Parameters:
inTok: the token generated by the peer 🍓offset: where in theinTokthe token begins 📍len: how long the token is within theinTok(counting from the offset) 📏
7.4.8. 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());
}
7.4.9. acceptSecContext 💜
public void acceptSecContext(InputStream inStream,
OutputStream outStream) throws GSSException
the stream version of the acceptor call 🌊 it's called by the context acceptor when a token arrives from the peer, and it's equivalent to the byte array method. it may write an output token to the outStream, which the app then sends to the peer so the peer's initSecContext method can process it 📬 usually that happens by calling flush() on an OutputStream wrapped around the connection between the two peers 🔌
check isEstablished() to see if context establishment is complete for this peer ✅ a false means more tokens are expected to be supplied to this method. and yup, the same twist applies 🙈: acceptSecContext() can return a token for the peer and isEstablished() can say true. the token goes out, but your local end is already fully established 💅
the GSS-API tokens have a definitive start and end, and this method reads one of them per invocation. it may block on the stream if only part of the token is available 😭 when you're all done, the available context options can be queried through the get methods ✨
Parameters:
inStream: the token generated by the peer 🍓outStream: the output stream where the output token gets written. during the final stage of context establishment, there might be no bytes written 🌸
7.4.10. Example Code
this sample is a demo of the token exchange during context establishment 🧚 the same vibes as before: most Java apps will bring their own custom Input and Output stream implementations that wrap the communication routines. a plain read on the app's InputStream (when the Context calls it) might grab a token from the peer, and a simple flush() on the app's OutputStream might push a previously written token out 📬
// 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());
}
7.4.11. isEstablished 🌈
public boolean isEstablished()
this is your "are we there yet?" check 🚗💨 it's used during context establishment to figure out 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() returns with no GSSException thrown 💖
7.4.12. dispose 🗑️
public void dispose() throws GSSException
time to clean up 🧹 dispose() releases any system resources and cryptographic information stored in the context object. and heads up 🚨: this invalidates the context, so it's officially done. no take-backs 💀
7.4.13. getWrapSizeLimit 🦋
public int getWrapSizeLimit(int qop, boolean confReq,
int maxTokenSize) throws GSSException
this one's all about size limits 📏 it returns the maximum message size that, if you hand it to the wrap method with the same confReq and qop parameters, will give you an output token of no more than maxTokenSize bytes. so basically it tells you how big a message can be before the token gets too chunky 🍰
it's meant for apps that talk over protocols with a maximum message size, since it lets the app fragment messages before applying protection 🧩
GSS-API implementations are recommended but not required to detect invalid QOP values when getWrapSizeLimit gets called. ngl this call only guarantees a maximum message size, not that specific QOP values will actually be available for message protection 🙃
finishing the call doesn't guarantee that wrap will be able to protect a message of the computed length, because that can depend on the system resources available when wrap runs 😬 BUT if the implementation itself puts an upper limit on the length of messages that wrap can process, the implementation should not return a value greater than that length 🫶
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 🎀
7.4.14. wrap ✨
public byte[] wrap(byte[] inBuf, int offset, int len,
MessageProp msgProp) throws GSSException
okay bestie, this one is the main event 💖 It applies per-message security services over the context you already set up. It hands back a token with a cryptographic MIC and can optionally encrypt inBuf 🔐 It does the same job as its stream counterpart, and the returned byte array holds both the MIC and the message 🎀
The MessageProp object is created by the app. You use it to pick a QOP value (that's what picks the crypto algorithms) and a privacy service (encryption, yes or no) 💅 The mechanism underneath might not be able to give you the privacy service you asked for, so it sets the privacy service it actually provides back into this MessageProp object. Check that after the call returns, no cap 👀 If the mechanism can't do the QOP you requested, it throws a GSSException with the BAD_QOP code 💀
Some app protocols want to use wrap tokens for "secure framing," so implementations should support wrapping zero-length messages 🧚
The app is the one that sends the token to the peer 📬
Parameters:
inBuf: the application data you want protected 🍓offset: where the data starts insideinBuflen: how long the data is, counting fromoffsetmsgProp: theMessagePropthe app uses to set the QOP and privacy state you want. Set the QOP to0to ask for the default QOP. When the method returns, this object contains the privacy state the mechanism really applied to the message 🫶
7.4.15. wrap ✨
public void wrap(InputStream inStream, OutputStream outStream,
MessageProp msgProp) throws GSSException
this is the stream version, same energy 🌊 It lets you apply per-message security services over the established context. It produces a token with a cryptographic MIC and can optionally encrypt the message coming in on inStream. The outStream ends up holding both the MIC and the message 🦋
Same MessageProp deal as before: the app makes it, picks a QOP that selects the crypto algorithms and a privacy service for optional encryption ✨ The mechanism might not be able to give you that privacy service, so it writes what it actually provides into this object, and you should query it after the call returns 👑 If the QOP can't be provided, it throws a GSSException with the BAD_QOP code 💀
For secure framing, implementations should support wrapping zero-length messages too 🌸
The app is responsible for sending the token to the peer 💌
Parameters:
inStream: the input stream holding the application data to protectoutStream: the output stream where the protected message gets written. The app has to send this to the other peer so it can be processed in that peer'sunwrapmethod 🚀msgProp: theMessagePropfor setting the desired QOP and privacy state. Set the QOP to0to request the default QOP. When this method returns, the object holds the actual privacy state the mechanism applied 🎀
7.4.16. unwrap ✨
public byte[] unwrap(byte[] inBuf, int offset, int len,
MessageProp msgProp) throws GSSException
this is how the peer app opens up tokens made by the wrap call 🔓 It does the same thing as the stream counterpart. It returns the message the peer gave to wrap, and it checks the MIC inside while doing it 🫶
The app makes the MessageProp object, and the mechanism uses it to hand info back to you, like the QOP, whether confidentiality got applied, and other supplementary message state 💅
For secure framing, implementations should support wrapping and unwrapping zero-length messages ✨
Parameters:
inBuf: the GSS-API wrap token that came in from the peer 📥offset: where the token starts insideinBuflen: how long the token is, counting fromoffsetmsgProp: when the method returns, this object holds the applied QOP, the privacy state of the message, and supplementary info (described in section 5.12.3). That info says whether the token was a duplicate, old, out of sequence, or arrived after a gap 👀
7.4.17. unwrap ✨
public void unwrap(InputStream inStream, OutputStream outStream,
MessageProp msgProp) throws GSSException
stream version of unwrap, same vibe 🌈 It's the same in functionality as the byte array one. It writes out the message the peer gave to wrap, and it verifies the MIC along the way 🦋
The app makes the MessageProp, and the mechanism uses it to return the QOP, confidentiality info, and other supplementary state 💜
Secure framing needs zero-length support here too, so implementations should support wrapping and unwrapping zero-length messages 🧚
Parameters:
inStream: the input stream holding the GSS-API wrap token from the peeroutStream: the output stream where the application message gets written 💌msgProp: when the method returns, this object holds the applied QOP, the privacy state, and supplementary info (described in section 5.12.3), saying whether the token was a duplicate, old, out of sequence, or arrived after a gap ✨
7.4.18. getMIC ✨
public byte[] getMIC(byte[] inMsg, int offset, int len,
MessageProp msgProp) throws GSSException
getMIC gives you a token with a cryptographic MIC for your message, so you can send it to the peer app 📮 The difference from wrap is that wrap puts the whole user message inside the returned token, but getMIC only returns the MIC in the output token. It's identical in functionality to the stream counterpart 🎀
⚠️ privacy can only be applied through the wrap call. getMIC can't do encryption, bestie, so don't go looking for it here 🙈
For secure framing, implementations should support deriving MICs from zero-length messages 🧚
Parameters:
inMsg: the message you want to generate the MIC over 💖offset: where the token begins insideinMsglen: how long the token is insideinMsg, counting fromoffsetmsgProp: aMessagePropthe app uses to set the desired QOP. Set the QOP to0inmsgPropto request the default QOP. You can also pass"null"formsgPropto request the default QOP 🍓
7.4.19. getMIC ✨
public void getMIC(InputStream inStream, OutputStream outStream,
MessageProp msgProp) throws GSSException
stream version of getMIC 🌸 It produces a token with a cryptographic MIC for the message, to transfer to the peer app. Same deal as before: wrap packs the whole message in, but this one only produces the MIC in the output token. It's identical in functionality to the byte array counterpart 🦋
⚠️ privacy can only be applied through the wrap call. Same reminder, periodt 💅
Secure framing again, so implementations should support deriving MICs from zero-length messages ✨
Parameters:
inStream: the input stream with the message you want to generate the MIC over 📥outStream: the output stream where the GSS-API output token gets written 📤msgProp: theMessagePropfor setting the desired QOP. Set the QOP to0inmsgPropto request the default QOP. Or pass"null"formsgPropto request the default QOP 👑
7.4.20. verifyMIC ✨
public void verifyMIC(byte[] inTok, int tokOffset, int tokLen,
byte[] inMsg, int msgOffset, int msgLen,
MessageProp msgProp) throws GSSException
this one checks the cryptographic MIC inside the token against the message you supply 🔍 It's equivalent in functionality to its stream counterpart 💜
The app creates the MessageProp object, and the mechanism uses it to hand back info like the QOP (which shows how strong the protection was) and other supplementary message state 🫶
For secure framing, implementations should support calculating and verifying MICs over zero-length messages 🧚
Parameters:
inTok: the token the peer'sgetMICmethod generated 📬tokOffset: where the token begins insideinToktokLen: how long the token is insideinTok, counting fromtokOffsetinMsg: the application message you're verifying the cryptographic MIC over 💖msgOffset: where the message begins insideinMsgmsgLen: how long the message is insideinMsg, counting frommsgOffsetmsgProp: when the method returns, this object holds the applied QOP and supplementary info (described in section 5.12.3), saying whether the token was a duplicate, old, out of sequence, or arrived after a gap. The confidentiality state gets set to"false"🌈
7.4.21. verifyMIC 🔐
public void verifyMIC(InputStream tokStream, InputStream msgStream,
MessageProp msgProp) throws GSSException
okay so this one checks the cryptographic MIC 🫶. The MIC lives inside the token parameter, and it gets verified against the message you hand over. It does the same job as its byte array counterpart, so if you already know that one, you're basically set ✨.
The MessageProp object is created by the application, and the underlying mechanism uses it to send info back to the caller 💅. That includes the QOP, which tells you how strong the protection was, plus other supplementary message state.
Some application-level protocols might want to use tokens from getMIC for "secure framing" 🧚, so implementations should support calculating and verifying MICs over zero-length messages. Lowkey genius, no cap 💜.
Parameters:
- tokStream: Input stream containing the token generated by the peer's getMIC method.
- msgStream: Input stream containing the application message over which to verify the cryptographic MIC.
- msgProp: Upon return from the method, this object will contain the applied QOP and supplementary information, described in section 5.12.3, stating whether the token was a duplicate, old, out of sequence, or arriving after a gap. The confidentiality state will be set to "false". 🌸
7.4.22. export 📦
public byte[] export() throws GSSException
this one exists to share the work between multiple processes 👑. The usual setup is that the context acceptor is a single process taking in connection requests and accepting security contexts, then it passes the established context on to one or more other processes that do the message exchange. It's giving teamwork 🦋.
When you call this, the security context gets deactivated, and it creates an inter-process token 🎀. If you pass that token to the byte array constructor of the GSSContext interface in another process, the context wakes back up over there ✨. Only one instance of a given context can be active at any one time, so if the exporter tries to touch the exported context afterward, it's going to fail 💀.
The implementation can limit which processes are allowed to import the token. That might come from local security policy, or from implementation decisions 🍓. For example, some implementations might only let contexts move between processes running under the same account, or processes in the same process group 🌈.
The inter-process token can hold security-sensitive stuff, like cryptographic keys 🙈. Mechanisms are encouraged to either keep that sensitive info out of the token, or encrypt the token before handing it back to the application. But in a typical GSS-API implementation, that might not be possible 😭. So the application has to really take care of protecting the inter-process token, and make sure that any process it gets transferred to is trustworthy. Be careful, bestie 🚨.
7.4.23. requestMutualAuth 🤝
public void requestMutualAuth(boolean state) throws GSSException
this sets the request state of the mutual authentication flag for the context 💖. It's only valid before the context creation process begins, and only for the initiator.
Parameters:
- state: Boolean representing if mutual authentication should be requested during context establishment.
7.4.24. requestReplayDet 🔁
public void requestReplayDet(boolean state) throws GSSException
this sets the request state of the replay detection service for the context ✨. Same rules apply: only valid before context creation begins, and only for the initiator.
Parameters:
- state: Boolean representing if replay detection is desired over the established context.
7.4.25. requestSequenceDet 🔢
public void requestSequenceDet(boolean state) throws GSSException
this one sets the request state for the sequence checking service of the context 🦢. Again, only valid before the context creation process begins, and only for the initiator.
Parameters:
- state: Boolean representing if sequence detection is desired over the established context.
7.4.26. requestCredDeleg 🗝️
public void requestCredDeleg(boolean state) throws GSSException
this sets the request state for the credential delegation flag for the context 🎀. It's only valid before the context creation process begins, and only for the initiator.
Parameters:
- state: Boolean representing if credential delegation is desired.
7.4.27. requestAnonymity 🕶️
public void requestAnonymity(boolean state) throws GSSException
this one requests anonymous support over the context 🫥. It's only valid before the context creation process begins, and only for the initiator.
Parameters:
- state: Boolean representing if anonymity support is requested.
7.4.28. requestConf 🔒
public void requestConf(boolean state) throws GSSException
this requests that the confidentiality service be available over the context 💜. It's only valid before the context creation process begins, and only for the initiator.
Parameters:
- state: Boolean indicating if confidentiality services are to be requested for the context.
7.4.29. requestInteg 🛡️
public void requestInteg(boolean state) throws GSSException
this requests that integrity services be available over the context 🌷. Only valid before context creation begins, and only for the initiator.
Parameters:
- state: Boolean indicating if integrity services are to be requested for the context.
7.4.30. requestLifetime ⏳
public void requestLifetime(int lifetime) throws GSSException
this sets the desired lifetime for the context, in seconds ⭐. It's only valid before the context creation process begins, and only for the initiator. If you want indefinite or default context lifetime, use GSSContext.INDEFINITE_LIFETIME and GSSContext.DEFAULT_LIFETIME 🧚.
Parameters:
- lifetime: The desired context lifetime in seconds.
7.4.31. setChannelBinding 🧷
public void setChannelBinding(ChannelBinding cb) throws GSSException
this sets the channel bindings to be used during context establishment 🔗. It's only valid before the context creation process begins.
Parameters:
- cb: Channel bindings to be used.
7.4.32. getCredDelegState 👀
public boolean getCredDelegState()
this returns the state of the delegated credentials for the context 🦋. If you call it before context establishment is completed, or when the isProtReady method returns "false", you get the desired state. Otherwise, it tells you the actual state over the established context 💅.
7.4.33. getMutualAuthState 🤝
public boolean getMutualAuthState()
this returns the state of the mutual authentication option for the context ✨. Before context establishment completes, or when isProtReady returns "false", it gives you the desired state. After that, it shows the actual state over the established context 💖.
7.4.34. getReplayDetState 🔁
public boolean getReplayDetState()
this returns the state of the replay detection option for the context 🌸. Same deal: desired state before establishment completes or when isProtReady returns "false", and the actual state over the established context otherwise 🫶.
7.4.35. getSequenceDetState 🔢
public boolean getSequenceDetState()
this returns the state of the sequence detection option for the context 🎀. Desired state before establishment completes or when isProtReady returns "false", actual state over the established context after that 👑.
7.4.36. getAnonymityState 🕶️
public boolean getAnonymityState()
this returns "true" if this is an anonymous context 🫥. Before context establishment completes, or when isProtReady returns "false", it returns the desired state. Otherwise, it shows the actual state over the established context 🌈.
7.4.37. isTransferable 🚚
public boolean isTransferable() throws GSSException
this returns "true" if the context can be transferred to other processes through the use of the export method 🧳. It's only valid on fully established contexts.
7.4.38. isProtReady 🟢
public boolean isProtReady()
this returns "true" if the per-message operations can be applied over the context ✅. Some mechanisms let you use per-message operations before the context is fully established 🍓. When this returns "true", the get methods give you the actual context state characteristics instead of the desired ones 💜.
7.4.39. getConfState 🔐
public boolean getConfState()
this returns the confidentiality service state over the context 🌷. Before context establishment completes, or when isProtReady returns "false", you get the desired state. Otherwise, it shows the actual state over the established context ✨.
7.4.40. getIntegState 🛡️
public boolean getIntegState()
this returns the integrity service state over the context 💖. When it's called before context establishment completes, or when isProtReady returns "false", it gives the desired state. After that, it shows the actual state over the established context 🫶.
7.4.41. getLifetime ⏳
public int getLifetime()
this returns the context lifetime in seconds ⭐. Before context establishment completes, or when isProtReady returns "false", it gives the desired lifetime. Otherwise, it shows the remaining lifetime for the context 🦢.
7.4.42. getSrcName 🪪
public GSSName getSrcName() throws GSSException
this returns the name of the context initiator 🧑🎤. It's only valid after the context is fully established, or after isProtReady returns "true". It is guaranteed to return an MN 💅.
7.4.43. getTargName 🎯
public GSSName getTargName() throws GSSException
this returns the name of the context target (acceptor) 🌸. It's only valid after the context is fully established, or after isProtReady returns "true". It is guaranteed to return an MN 🫶.
7.4.44. getMech 🧩
public Oid getMech() throws GSSException
this returns the mechanism oid for this context 🦋. It can be called before the context is fully established, but in the negotiated mechanism case, the mechanism returned might change on successive calls 🌈.
7.4.45. getDelegCred 🎁
public GSSCredential getDelegCred() throws GSSException
this returns the delegated credential object on the acceptor's side 👑. To check whether delegated credentials are available, call getDelegCredState. This call is only valid on fully established contexts 💖.
7.4.46. isInitiator 🚀
public boolean isInitiator() throws GSSException
this returns "true" if this is the initiator of the context ✨. It's only valid after the context creation process has started 🧚.
7.5. public class MessageProp ✨
okay so this one is a utility class that lives inside the per-message GSSContext methods, and its whole job is to carry per-message properties back and forth 💌
When you're using the wrap and getMIC methods of the GSSContext interface, you make an instance of this class to say which QOP you want and whether you want confidentiality applied to your caller-supplied data (wrap only). If you want the default QOP, use the value 0 for QOP 🎀
When you're on the receiving end with the unwrap and verifyMIC methods, this object tells you which QOP was applied and what confidentiality services were used on the message you were handed. For verifyMIC, the confidentiality state will always be "false" (no privacy, just integrity, bestie 🔒). When those methods return, this object also holds any supplementary status values for the token you just processed. Those values can flag old tokens, out of sequence tokens, gap tokens, or duplicate tokens 👀
7.5.1. Constructors
There are two constructors, and they're both super chill 🦋
public MessageProp(boolean privState)
This one sets QOP to 0, which means you're asking for the default QOP.
Parameters:
privState: The desired privacy state. "true" for privacy and "false" for integrity only.
public MessageProp(int qop, boolean privState)
This one sets the values for both the qop and the privacy state.
Parameters:
qop: The desired QOP. Use 0 to request a default QOP.privState: The desired privacy state. "true" for privacy and "false" for integrity only.
7.5.2. getQOP
public int getQOP()
Gets you the QOP value. Simple, iconic 💅
7.5.3. getPrivacy
public boolean getPrivacy()
Retrieves the privacy state ✨
7.5.4. getMinorStatus
public int getMinorStatus()
Retrieves the minor status that the underlying mechanism might have set 🧚
7.5.5. getMinorString
public String getMinorString()
Returns a string explaining the mechanism-specific error code. If no mechanism error code has been set, you'll get "null" back, so don't panic when you see it 🙈
7.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.
7.5.7. setPrivacy
public void setPrivacy(boolean privState)
Sets the privacy state.
Parameters:
privState: The privacy state to set.
7.5.8. isDuplicateToken
public boolean isDuplicateToken()
Returns "true" if this token is a duplicate of an earlier one. Caught in the act 😭
7.5.9. isOldToken
public boolean isOldToken()
Returns "true" if the token's validity period has expired. Time's up, babe ⏰
7.5.10. isUnseqToken
public boolean isUnseqToken()
Returns "true" if a later token has already been processed. Out of order and it's giving chaos 🌀
7.5.11. isGapToken
public boolean isGapToken()
Returns "true" if an expected per-message token was not received. Somebody's missing from the group chat 🕳️
7.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 inside MessageProp. The application doesn't call this one. It's the GSS implementation that uses it to hand this info back to whoever called a per-message context method 🫶
Parameters:
duplicate: "true" if the token was a duplicate of an earlier token; otherwise, "false".old: "true" if the token's validity period has expired; otherwise, "false".unseq: "true" if a later token has already been processed; otherwise, "false".gap: "true" if one or more predecessor tokens have not yet been successfully processed; otherwise, "false".minorStatus: The integer minor status code that the underlying mechanism wants to set.minorString: The textual representation of the minorStatus value.
7.6. public class ChannelBinding 🌸
okay this one is so important for security, so pay attention 👑 GSS-API has this idea of caller-provided channel binding information. Channel bindings make peer entity authentication stronger during context establishment. They let GSS-API callers tie the security context to relevant stuff like addresses or to application-specific data 🔐
The caller who starts the security context has to figure 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 gets have the correct channel-related characteristics. The same values on both sides, no exceptions 🤝
Using channel bindings is optional in GSS-API. Here's the spicy part: channel-binding info can get sent inside context establishment tokens, so applications should therefore not use confidential data as channel-binding components. Keep your secrets out of there, periodt 🤫
7.6.1. Constructors
public ChannelBinding(InetAddress initAddr, InetAddress acceptAddr,
byte[] appData)
This creates a ChannelBinding object with user-supplied address information and data. You can use "null" for any field your application doesn't want to set.
Parameters:
initAddr: The address of the context initiator. A "null" value can be supplied to indicate that the application does not want to set this value.acceptAddr: The address of the context acceptor. A "null" value 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. A "null" value can be supplied to indicate that the application does not want to set this value.
public ChannelBinding(byte[] appData)
This creates a ChannelBinding object without any addressing information. Just the vibes and the data 🦋
Parameters:
appData: Application supplied data to be used as part of the channel bindings.
7.6.2. getInitiatorAddress
public InetAddress getInitiatorAddress()
Returns the initiator's address for this channel binding. If the address hasn't been set, you get "null" back.
7.6.3. getAcceptorAddress
public InetAddress getAcceptorAddress()
Returns the acceptor's address for this channel binding. If the address hasn't been set, you get "null" back.
7.6.4. getApplicationData
public byte[] getApplicationData()
Returns the application data being used as part of the ChannelBinding. If no application data has been specified for the channel binding, you get "null" 🙃
7.6.5. equals
public boolean equals(Object obj)
Returns "true" if two channel bindings match. Quick side note from the Java spec (it's a whole thing, lol): two objects that count as equal under the equals(Object) method have to return the same integer result when hashCode() is called on them. Gotta respect the rules 📜
Parameters:
obj: Another channel binding with which to compare.
7.7. public class Oid 🧚
this class represents Universal Object Identifiers (Oids) and all the operations you can do with them ✨
Oids are hierarchically globally interpretable identifiers. GSS-API uses them to identify mechanisms and name formats. Think of them like a family tree that the whole world agrees on 🌳
The structure and encoding of Oids is defined in ISOIEC-8824 and ISOIEC-8825. For example, the Oid for the Kerberos v5 mechanism is "1.2.840.113554.1.2.2" 💜
The GSSName name class holds public static Oid objects for the standard name types defined in GSS-API, so you don't have to build those yourself 🎀
7.7.1. Constructors
There are three ways to make an Oid, and each one comes with its own little twist 💅
public Oid(String strOid) throws GSSException
This creates an Oid object from a string of its integer components (for example, "1.2.840.113554.1.2.2").
Parameters:
strOid: The string representation for the oid.
public Oid(InputStream derOid) throws GSSException
This creates an Oid object from its DER encoding. The DER encoding means the full encoding, tag and length included. The structure and encoding of Oids is defined in ISOIEC-8824 and ISOIEC-8825. This method does exactly the same thing as its byte array counterpart, so pick whichever feels cuter 🌈
Parameters:
derOid: Stream containing the DER-encoded oid.
public Oid(byte[] DEROid) throws GSSException
This creates an Oid object from its DER encoding. Again, that means the full encoding, tag and length included. 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... wait, it IS the byte array counterpart, so you're all set 🙈
Parameters:
derOid: Byte array storing a DER-encoded oid.
7.7.2. toString
public String toString()
Returns a string of the oid's integer components in dot separated notation (for example, "1.2.840.113554.1.2.2") 🍓
7.7.3. equals
public boolean equals(Object Obj)
Returns "true" if the two Oid objects represent the same oid value. Same little note as before: the Java language specification [JLS] says two objects that are equal under the equals(Object) method must return the same integer result when hashCode() is called on them. Consistency is everything 💖
Parameters:
obj: Another Oid object with which to compare.
7.7.4. getDER
public byte[] getDER()
Returns the full ASN.1 DER encoding for this oid object, tag and length included 📦
7.7.5. containedIn
public boolean containedIn(Oid[] oids)
A utility method that checks whether an Oid object is inside the Oid object array you pass in. It's like a quick "is this one in the group?" check 👀
Parameters:
oids: An array of oids to search.
7.8. public class GSSException extends Exception 💀
this exception gets thrown whenever a fatal GSS-API error happens, and that includes mechanism-specific errors. It can carry both the major and the minor GSS-API status codes. The mechanism implementors are responsible for setting the right minor status codes when they throw it. Aside from handing the numeric error code(s) to the caller, this class also maps those numbers to readable text. And every Java GSS-API method is declared as throwing this exception, so you'll be seeing it a lot 😭
All implementations are encouraged to use the Java internationalization techniques so the message strings can get local translations. Speak every language, bestie 🌍✨
7.8.1. Static Constants ✨
Okay bestie, every valid major GSS-API error code value is declared as a constant in this class 💅
public static final int BAD_BINDINGS🔗 Channel bindings mismatch error. The value is 1.public static final int BAD_MECH🙅 Unsupported mechanism requested error. The value is 2.public static final int BAD_NAME🏷️ Invalid name provided error. The value is 3.public static final int BAD_NAMETYPE🪪 Name of unsupported type provided error. The value is 4.public static final int BAD_STATUS🫥 Invalid status code error, and this is the default status value. The value is 5.public static final int BAD_MIC🧾 Token had invalid integrity check error. The value is 6.public static final int CONTEXT_EXPIRED⏰ Specified security context expired error. The value is 7.public static final int CREDENTIALS_EXPIRED🪫 Expired credentials detected error. The value is 8.public static final int DEFECTIVE_CREDENTIAL🩹 Defective credential error. The value is 9.public static final int DEFECTIVE_TOKEN🫠 Defective token error. The value is 10.public static final int FAILURE💀 General failure, unspecified at GSS-API level. The value is 11.public static final int NO_CONTEXT🌫️ Invalid security context error. The value is 12.public static final int NO_CRED🙈 Invalid credentials error. The value is 13.public static final int BAD_QOP🎚️ Unsupported QOP value error. The value is 14.public static final int UNAUTHORIZED🚫 Operation unauthorized error. The value is 15.public static final int UNAVAILABLE😴 Operation unavailable error. The value is 16.public static final int DUPLICATE_ELEMENT👯 Duplicate credential element requested error. The value is 17.public static final int NAME_NOT_MN🧩 Name contains multi-mechanism elements error. The value is 18.public static final int DUPLICATE_TOKEN🔁 The token was a duplicate of an earlier token. This only shows up in an exception when it's detected during context establishment, and in that case it counts as a fatal error. Non-fatal supplementary codes are indicated via the MessageProp object instead. The value is 19.public static final int OLD_TOKEN🕰️ The token's validity period has expired. This only shows up in an exception when it's detected during context establishment, and in that case it counts as a fatal error. Non-fatal supplementary codes are indicated via the MessageProp object instead. The value is 20.public static final int UNSEQ_TOKEN🔀 A later token has already been processed. This only shows up in an exception when it's detected during context establishment, and in that case it counts as a fatal error. Non-fatal supplementary codes are indicated via the MessageProp object instead. The value is 21.public static final int GAP_TOKEN🕳️ An expected per-message token was not received. This only shows up in an exception when it's detected during context establishment, and in that case it counts as a fatal error. Non-fatal supplementary codes are indicated via the MessageProp object instead. The value is 22.
7.8.2. Constructors
Two ways to make a GSSException, depending on where the error came from 🧁
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 one is meant for when the exception is coming from the security mechanism, so you can specify both 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.
7.8.3. getMajor
public int getMajor() returns the major code representing the GSS error code that caused this exception to be thrown 🎯
7.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 means the mechanism error code isn't set 🌱
7.8.5. getMajorString
public String getMajorString() returns a string explaining the GSS major error code that caused this exception to be thrown 📖
7.8.6. getMinorString
public String getMinorString() returns a string explaining the mechanism-specific error code. It returns "null" when no mechanism error code has been set 🫧
7.8.7. setMinor
public void setMinor(int minorCode, String message) is 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.
7.8.8. toString
public String toString() returns a textual representation of both the major and minor status codes 🧸
7.8.9. getMessage
public String getMessage() returns a detailed message for this exception. It overrides Throwable.getMessage, and in Java this is the method people customarily use to get exception information 💜
8. Sample Applications ✨
8.1. Simple GSS Context Initiator 🎀
okay so this one is the client side of the story 🧚 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 the Generic Security Service API Version 2 : Java bindings spec. the sketch assumes there's a GSS-API implementation that supports the mechanism it needs, sitting around as the org.ietf.jgss package, either in the standard JRE or on the CLASSPATH the app sets. bestie, the whole flow is: set up, establish the context, then talk 💅
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 (i.e., 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
8.2. Simple GSS Context Acceptor ✨
okay bestie, this is the server side 🧚 It's the mirror of the initiator from 8.1, and it's so satisfying to see both halves fit together. Here's the sketch 💅
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
// (i.e., 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
9. Security Considerations 🔒
okay this section is SERIOUS, so read it with your whole chest 😭 The Java language security model lets platform providers set policy-based, fine-grained access control over any resource an application wants. When a Java security manager is in play (like, but not limited to, applets running in browsers), the application code is sandboxed by default. 🫶
The administrators of the platform JRE decide what permissions, if any, get granted to code from different codebases. So the admin has to know about any special requirements the GSS provider might have for system resources. For instance, a Kerberos provider might want to make a network connection to the Key Distribution Center (KDC) to get initial credentials. That would NOT be allowed inside the sandbox unless the admin had granted permissions for it. Also, this granting and checking of permissions happens transparently to the application, and it's 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 goes through these providers in order of preference when it's looking for security-related services. Applications can modify this list through methods in the "Security" class in the "java.security" package. BUT, these modifications would be visible across the entire Java Virtual Machine (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 a "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.)
10. Acknowledgments 🌸
this proposed API builds on earlier work from the IETF's CAT WG, as laid out in both RFC 2743 [GSSAPIv2-UPDATE] and RFC 2744 [GSSAPI-Cbind]. A lot of the conceptual definitions, implementation directions, and explanations come straight from those documents. 📚
Huge thanks to Mike Eisler, Lin Ling, Ram Marti, Michael Saltz, and the 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. 👑
11. Changes since RFC 2853 🦋
this document has the following changes:
- Major GSS Status Code Constant Values
RFC 2853 listed all the GSS status code values in two different sections: section 4.12.1 defined numeric values for them, and section 6.8.1 defined them as static constants in the GSSException class without assigning any values. Due to an inconsistent ordering between these two sections, all of the GSS major status codes resulted in misalignment, and a subsequent disagreement between deployed implementations. 😭
This document defines the numeric values of the GSS status codes in both sections, while maintaining the original ordering from section 6.8.1 of RFC 2853 [RFC2853], and obsoletes the GSS status code values defined in section 4.12.1. The relevant sections in this document are sections 5.12.1 and 7.8.1.
- GSS Credential Usage Constant Values
RFC 2853 section 6.3.2 defines static constants for the GSSCredential usage flags. However, the values of these constants were not defined anywhere in RFC 2853 [RFC2853].
This document defines the credential usage values in section 7.3.2. The original ordering of these values from section 6.3.2 of RFC 2853 [RFC2853] is maintained.
- GSS Host-Based Service Name
RFC 2853 [RFC2853], section 6.2.2, defines the static constant for the GSS host-based service OID NT_HOSTBASED_SERVICE, using a deprecated OID value.
This document updates the NT_HOSTBASED_SERVICE OID value in section 7.2.2 to be consistent with the C-bindings in RFC 2744 [GSSAPI-Cbind]. ✨
12. References 📖
12.1. Normative References
[GSSAPI-Cbind]Wray, J., "Generic Security Service API Version 2 : C-bindings", RFC 2744, January 2000.[GSSAPIv2-UPDATE]Linn, J., "Generic Security Service Application Program Interface Version 2, Update 1", RFC 2743, January 2000.[RFC2025]Adams, C., "The Simple Public-Key GSS-API Mechanism (SPKM)", RFC 2025, October 1996.[RFC2119]Bradner, S., "Key words for use in RFCs to Indicate Requirement Levels", BCP 14, RFC 2119, March 1997.[RFC2853]Kabat, J. and M. Upadhyay, "Generic Security Service API Version 2 : Java Bindings", RFC 2853, June 2000.[RFC4121]Zhu, L., Jaganathan, K., and S. Hartman, "The Kerberos Version 5 Generic Security Service Application Program Interface (GSS-API) Mechanism: Version 2", RFC 4121, July 2005.
12.2. Informative References
[JLS]Gosling, J., Joy, B., Steele, G., and G. Bracha "The Java Language Specification", Third Edition, http://java.sun.com/docs/books/jls/.
Authors' Addresses ✨
okay bestie, meet the two legends who wrote this doc 💖 first up is Mayank D. Upadhyay, repping Google Inc. 🦋
Mayank D. Upadhyay
Google Inc.
1600 Amphitheatre Parkway
Mountain View, CA 94043
USA
EMail: m.d.upadhyay+ietf@gmail.com
and then we've got Seema Malkani from ActivIdentity Corp. 🌸 she's literally part of the reason this spec exists, so slay her forever 👑
Seema Malkani
ActivIdentity Corp.
6623 Dumbarton Circle
Fremont, California 94555
USA
EMail: Seema.Malkani@gmail.com
that's the whole document, babe 🌈✨ thank you for reading all of it 🫶
📦 where this page comes from
- ietf: rfc5653 2009-08
- 💖 girlified by claude-haiku-5-5 on : a language model's rewrite of the original, under its terms