RFC 8353

Generic Security Service API Version 2: Java Bindings Update

📅 published May 2018, from ietf

Generic Security Service API Version 2: Java Bindings Update 🎀

okay bestie, this is RFC 8353 💖 an IETF Request for Comments, and the title is "Generic Security Service API Version 2: Java Bindings Update". It was written by M. Upadhyay from Google, S. Malkani from ActivIdentity, and W. Wang from Oracle 🫶. It's dated May 2018 🌸, it's on the Standards Track ✨, and its ISSN is 2070-1721. The big tea is that it obsoletes RFC 5653 👀, which is the older Java bindings doc, so this one is the new main character 👑.


Abstract 🦋

okay so the GSS-API (Generic Security Services Application Programming Interface) gives application programmers uniform access to security services, no matter which underlying cryptographic mechanism is doing the work 🔐. This document updates the Java bindings for the GSS-API that were specified in "Generic Security Service API Version 2: Java Bindings Update" (RFC 5653) 💅. It obsoletes RFC 5653 by adding a new output token field to the GSSException class. The point is that when the initSecContext or acceptSecContext methods of the GSSContext class fail, they now get a chance to emit an error token. That token can be sent to the peer for debugging or just for information 🧚. The stream-based GSSContext methods are also removed in this version, so they're gone 😭.

The GSS-API is described at a language-independent, conceptual level in "Generic Security Service Application Program Interface Version 2, Update 1" (RFC 2743) 🌈. It lets a caller application authenticate a principal identity, delegate rights to a peer, and apply security services such as confidentiality and integrity on a per-message basis 🍓. Some examples of security mechanisms defined for the GSS-API are "The Simple Public-Key GSS-API Mechanism (SPKM)" (RFC 2025) and "The Kerberos Version 5 Generic Security Service Application Program Interface (GSS-API) Mechanism: Version 2" (RFC 4121) ⭐.


Status of This Memo 💜

this one is an Internet Standards Track document, which means it's the real deal 🔥.

It's a product of the Internet Engineering Task Force (IETF), and it represents the consensus of the IETF community. It got public review and was approved for publication by the Internet Engineering Steering Group (IESG) 🫶. If you want more on Internet Standards, check out Section 2 of RFC 7841 🧐.

For the current status of this document, any errata, and how to give feedback on it, head to https://www.rfc-editor.org/info/rfc8353 ✨.


Copyright (c) 2018 IETF Trust and the persons identified as the document authors. All rights reserved 💖.

This document is subject to BCP 78 and the IETF Trust's Legal Provisions Relating to IETF Documents, which you can find at https://trustee.ietf.org/license-info as in effect on the date of publication. please actually read these, bestie, because they spell out your rights and restrictions for this document 📜. Code Components extracted from this document must include Simplified BSD License text, as described in Section 4.e of the Trust Legal Provisions. They're also provided without warranty, as described in the Simplified BSD License 🛡️.

This document may contain material from IETF Documents or IETF Contributions that were published or made publicly available before November 10, 2008 🕰️. The people controlling the copyright in some of that older material may not have granted the IETF Trust the right to allow modifications outside the IETF Standards Process 🙈. Without an adequate license from those copyright holders, this document may not be modified outside the IETF Standards Process, and derivative works of it may not be created outside the IETF Standards Process. The only exceptions are formatting it for publication as an RFC or translating it into languages other than English 🌍.


Table of Contents 🧚

Okay bestie, the paste only has the table of contents 🎀, and that part was already covered. So here's the listing again, copied exactly so nothing gets lost ✨


   1.  Introduction  . . . . . . . . . . . . . . . . . . . . . . . .   6
   2.  Notational Conventions  . . . . . . . . . . . . . . . . . . .   7
   3.  GSS-API Operational Paradigm  . . . . . . . . . . . . . . . .   7
   4.  Additional Controls . . . . . . . . . . . . . . . . . . . . .   9
     4.1.  Delegation  . . . . . . . . . . . . . . . . . . . . . . .  10
     4.2.  Mutual Authentication . . . . . . . . . . . . . . . . . .  11
     4.3.  Replay and Out-of-Sequence Detection  . . . . . . . . . .  11
     4.4.  Anonymous Authentication  . . . . . . . . . . . . . . . .  12
     4.5.  Integrity and Confidentiality . . . . . . . . . . . . . .  13
     4.6.  Inter-process Context Transfer  . . . . . . . . . . . . .  13
     4.7.  The Use of Incomplete Contexts  . . . . . . . . . . . . .  14
   5.  Calling Conventions . . . . . . . . . . . . . . . . . . . . .  15
     5.1.  Package Name  . . . . . . . . . . . . . . . . . . . . . .  15
     5.2.  Provider Framework  . . . . . . . . . . . . . . . . . . .  15
     5.3.  Integer Types . . . . . . . . . . . . . . . . . . . . . .  16
     5.4.  Opaque Data Types . . . . . . . . . . . . . . . . . . . .  16
     5.5.  Strings . . . . . . . . . . . . . . . . . . . . . . . . .  16
     5.6.  Object Identifiers  . . . . . . . . . . . . . . . . . . .  16
     5.7.  Object Identifier Sets  . . . . . . . . . . . . . . . . .  17
     5.8.  Credentials . . . . . . . . . . . . . . . . . . . . . . .  17
     5.9.  Contexts  . . . . . . . . . . . . . . . . . . . . . . . .  19
     5.10. Authentication Tokens . . . . . . . . . . . . . . . . . .  20
     5.11. Inter-process Tokens  . . . . . . . . . . . . . . . . . .  20
     5.12. Error Reporting . . . . . . . . . . . . . . . . . . . . .  20
       5.12.1.  GSS Status Codes . . . . . . . . . . . . . . . . . .  21
       5.12.2.  Mechanism-Specific Status Codes  . . . . . . . . . .  23
       5.12.3.  Supplementary Status Codes . . . . . . . . . . . . .  23
     5.13. Names . . . . . . . . . . . . . . . . . . . . . . . . . .  24
     5.14. Channel Bindings  . . . . . . . . . . . . . . . . . . . .  27
     5.15. Optional Parameters . . . . . . . . . . . . . . . . . . .  28
   6.  Introduction to GSS-API Classes and Interfaces  . . . . . . .  28
     6.1.  GSSManager Class  . . . . . . . . . . . . . . . . . . . .  28
     6.2.  GSSName Interface . . . . . . . . . . . . . . . . . . . .  29
     6.3.  GSSCredential Interface . . . . . . . . . . . . . . . . .  30
     6.4.  GSSContext Interface  . . . . . . . . . . . . . . . . . .  31
     6.5.  MessageProp Class . . . . . . . . . . . . . . . . . . . .  32
     6.6.  GSSException Class  . . . . . . . . . . . . . . . . . . .  32
     6.7.  Oid Class . . . . . . . . . . . . . . . . . . . . . . . .  32
     6.8.  ChannelBinding Class  . . . . . . . . . . . . . . . . . .  33
   7.  Detailed GSS-API Class Description  . . . . . . . . . . . . .  33
     7.1.  public abstract class GSSManager  . . . . . . . . . . . .  33
       7.1.1.  getInstance . . . . . . . . . . . . . . . . . . . . .  34
       7.1.2.  getMechs  . . . . . . . . . . . . . . . . . . . . . .  34
       7.1.3.  getNamesForMech . . . . . . . . . . . . . . . . . . .  35
       7.1.4.  getMechsForName . . . . . . . . . . . . . . . . . . .  35
       7.1.5.  createName  . . . . . . . . . . . . . . . . . . . . .  35
       7.1.6.  createName  . . . . . . . . . . . . . . . . . . . . .  36
       7.1.7.  createName  . . . . . . . . . . . . . . . . . . . . .  36
       7.1.8.  createName  . . . . . . . . . . . . . . . . . . . . .  37
       7.1.9.  createCredential  . . . . . . . . . . . . . . . . . .  38
       7.1.10. createCredential  . . . . . . . . . . . . . . . . . .  38
       7.1.11. createCredential  . . . . . . . . . . . . . . . . . .  39
       7.1.12. createContext . . . . . . . . . . . . . . . . . . . .  39
       7.1.13. createContext . . . . . . . . . . . . . . . . . . . .  40
       7.1.14. createContext . . . . . . . . . . . . . . . . . . . .  40
       7.1.15. addProviderAtFront  . . . . . . . . . . . . . . . . .  41
         7.1.15.1.  addProviderAtFront Example Code  . . . . . . . .  42
       7.1.16. addProviderAtEnd  . . . . . . . . . . . . . . . . . .  43
         7.1.16.1.  addProviderAtEnd Example Code  . . . . . . . . .  43
       7.1.17. Example Code  . . . . . . . . . . . . . . . . . . . .  44
     7.2.  public interface GSSName  . . . . . . . . . . . . . . . .  45
       7.2.1.  Static Constants  . . . . . . . . . . . . . . . . . .  45
       7.2.2.  equals  . . . . . . . . . . . . . . . . . . . . . . .  46
       7.2.3.  equals  . . . . . . . . . . . . . . . . . . . . . . .  46
       7.2.4.  canonicalize  . . . . . . . . . . . . . . . . . . . .  47
       7.2.5.  export  . . . . . . . . . . . . . . . . . . . . . . .  47
       7.2.6.  toString  . . . . . . . . . . . . . . . . . . . . . .  47
       7.2.7.  getStringNameType . . . . . . . . . . . . . . . . . .  47
       7.2.8.  isAnonymous . . . . . . . . . . . . . . . . . . . . .  47
       7.2.9.  isMN  . . . . . . . . . . . . . . . . . . . . . . . .  48
       7.2.10. Example Code  . . . . . . . . . . . . . . . . . . . .  48
     7.3.  public interface GSSCredential implements Cloneable . . .  49
       7.3.1.  Static Constants  . . . . . . . . . . . . . . . . . .  50
       7.3.2.  dispose . . . . . . . . . . . . . . . . . . . . . . .  50
       7.3.3.  getName . . . . . . . . . . . . . . . . . . . . . . .  50
       7.3.4.  getName . . . . . . . . . . . . . . . . . . . . . . .  51
       7.3.5.  getRemainingLifetime  . . . . . . . . . . . . . . . .  51
       7.3.6.  getRemainingInitLifetime  . . . . . . . . . . . . . .  51
       7.3.7.  getRemainingAcceptLifetime  . . . . . . . . . . . . .  51
       7.3.8.  getUsage  . . . . . . . . . . . . . . . . . . . . . .  52
       7.3.9.  getUsage  . . . . . . . . . . . . . . . . . . . . . .  52
       7.3.10. getMechs  . . . . . . . . . . . . . . . . . . . . . .  52
       7.3.11. add . . . . . . . . . . . . . . . . . . . . . . . . .  52
       7.3.12. equals  . . . . . . . . . . . . . . . . . . . . . . .  53
       7.3.13. Example Code  . . . . . . . . . . . . . . . . . . . .  54
     7.4.  public interface GSSContext . . . . . . . . . . . . . . .  54
       7.4.1.  Static Constants  . . . . . . . . . . . . . . . . . .  55
       7.4.2.  initSecContext  . . . . . . . . . . . . . . . . . . .  56
       7.4.3.  acceptSecContext  . . . . . . . . . . . . . . . . . .  56
       7.4.4.  isEstablished . . . . . . . . . . . . . . . . . . . .  57
       7.4.5.  dispose . . . . . . . . . . . . . . . . . . . . . . .  57
       7.4.6.  getWrapSizeLimit  . . . . . . . . . . . . . . . . . .  58
       7.4.7.  wrap  . . . . . . . . . . . . . . . . . . . . . . . .  58
       7.4.8.  unwrap  . . . . . . . . . . . . . . . . . . . . . . .  59
       7.4.9.  getMIC  . . . . . . . . . . . . . . . . . . . . . . .  60
       7.4.10. verifyMIC . . . . . . . . . . . . . . . . . . . . . .  61
       7.4.11. export  . . . . . . . . . . . . . . . . . . . . . . .  62
       7.4.12. requestMutualAuth . . . . . . . . . . . . . . . . . .  62
       7.4.13. requestReplayDet  . . . . . . . . . . . . . . . . . .  63
       7.4.14. requestSequenceDet  . . . . . . . . . . . . . . . . .  63
       7.4.15. requestCredDeleg  . . . . . . . . . . . . . . . . . .  63
       7.4.16. requestAnonymity  . . . . . . . . . . . . . . . . . .  64
       7.4.17. requestConf . . . . . . . . . . . . . . . . . . . . .  64
       7.4.18. requestInteg  . . . . . . . . . . . . . . . . . . . .  64
       7.4.19. requestLifetime . . . . . . . . . . . . . . . . . . .  64
       7.4.20. setChannelBinding . . . . . . . . . . . . . . . . . .  65
       7.4.21. getCredDelegState . . . . . . . . . . . . . . . . . .  65
       7.4.22. getMutualAuthState  . . . . . . . . . . . . . . . . .  65
       7.4.23. getReplayDetState . . . . . . . . . . . . . . . . . .  65
       7.4.24. getSequenceDetState . . . . . . . . . . . . . . . . .  66
       7.4.25. getAnonymityState . . . . . . . . . . . . . . . . . .  66
       7.4.26. isTransferable  . . . . . . . . . . . . . . . . . . .  66
       7.4.27. isProtReady . . . . . . . . . . . . . . . . . . . . .  66
       7.4.28. getConfState  . . . . . . . . . . . . . . . . . . . .  66
       7.4.29. getIntegState . . . . . . . . . . . . . . . . . . . .  67
       7.4.30. getLifetime . . . . . . . . . . . . . . . . . . . . .  67
       7.4.31. getSrcName  . . . . . . . . . . . . . . . . . . . . .  67
       7.4.32. getTargName . . . . . . . . . . . . . . . . . . . . .  67
       7.4.33. getMech . . . . . . . . . . . . . . . . . . . . . . .  67
       7.4.34. getDelegCred  . . . . . . . . . . . . . . . . . . . .  68
       7.4.35. isInitiator . . . . . . . . . . . . . . . . . . . . .  68
       7.4.36. Example Code  . . . . . . . . . . . . . . . . . . . .  68
     7.5.  public class MessageProp  . . . . . . . . . . . . . . . .  70
       7.5.1.  Constructors  . . . . . . . . . . . . . . . . . . . .  70
       7.5.2.  getQOP  . . . . . . . . . . . . . . . . . . . . . . .  71
       7.5.3.  getPrivacy  . . . . . . . . . . . . . . . . . . . . .  71
       7.5.4.  getMinorStatus  . . . . . . . . . . . . . . . . . . .  71
       7.5.5.  getMinorString  . . . . . . . . . . . . . . . . . . .  71
       7.5.6.  setQOP  . . . . . . . . . . . . . . . . . . . . . . .  71
       7.5.7.  setPrivacy  . . . . . . . . . . . . . . . . . . . . .  72
       7.5.8.  isDuplicateToken  . . . . . . . . . . . . . . . . . .  72
       7.5.9.  isOldToken  . . . . . . . . . . . . . . . . . . . . .  72
       7.5.10. isUnseqToken  . . . . . . . . . . . . . . . . . . . .  72
       7.5.11. isGapToken  . . . . . . . . . . . . . . . . . . . . .  72
       7.5.12. setSupplementaryStates  . . . . . . . . . . . . . . .  72
     7.6.  public class ChannelBinding . . . . . . . . . . . . . . .  73
       7.6.1.  Constructors  . . . . . . . . . . . . . . . . . . . .  73
       7.6.2.  getInitiatorAddress . . . . . . . . . . . . . . . . .  74
       7.6.3.  getAcceptorAddress  . . . . . . . . . . . . . . . . .  74
       7.6.4.  getApplicationData  . . . . . . . . . . . . . . . . .  74
       7.6.5.  equals  . . . . . . . . . . . . . . . . . . . . . . .  75

Heads up, bestie 🥺 The paste stopped at the table of contents, so the body of the RFC (Section 1, Introduction onward) isn't here yet. Send the next chunk and I'll keep going, same voice, same exact details 💖
     7.7.  public class Oid  . . . . . . . . . . . . . . . . . . . .  75
       7.7.1.  Constructors  . . . . . . . . . . . . . . . . . . . .  75
       7.7.2.  toString  . . . . . . . . . . . . . . . . . . . . . .  76
       7.7.3.  equals  . . . . . . . . . . . . . . . . . . . . . . .  76
       7.7.4.  getDER  . . . . . . . . . . . . . . . . . . . . . . .  76
       7.7.5.  containedIn . . . . . . . . . . . . . . . . . . . . .  77
     7.8.  public class GSSException extends Exception . . . . . . .  77
       7.8.1.  Static Constants  . . . . . . . . . . . . . . . . . .  77
       7.8.2.  Constructors  . . . . . . . . . . . . . . . . . . . .  80
       7.8.3.  getMajor  . . . . . . . . . . . . . . . . . . . . . .  81
       7.8.4.  getMinor  . . . . . . . . . . . . . . . . . . . . . .  81
       7.8.5.  getMajorString  . . . . . . . . . . . . . . . . . . .  81
       7.8.6.  getMinorString  . . . . . . . . . . . . . . . . . . .  81
       7.8.7.  getOutputToken  . . . . . . . . . . . . . . . . . . .  82
       7.8.8.  setMinor  . . . . . . . . . . . . . . . . . . . . . .  82
       7.8.9.  toString  . . . . . . . . . . . . . . . . . . . . . .  82
       7.8.10. getMessage  . . . . . . . . . . . . . . . . . . . . .  82
   8.  Sample Applications . . . . . . . . . . . . . . . . . . . . .  83
     8.1.  Simple GSS Context Initiator  . . . . . . . . . . . . . .  83
     8.2.  Simple GSS Context Acceptor . . . . . . . . . . . . . . .  87
   9.  Security Considerations . . . . . . . . . . . . . . . . . . .  90
   10. IANA Considerations . . . . . . . . . . . . . . . . . . . . .  91
   11. Changes since RFC 5653  . . . . . . . . . . . . . . . . . . .  91
   12. Changes since RFC 2853  . . . . . . . . . . . . . . . . . . .  93
   13. References  . . . . . . . . . . . . . . . . . . . . . . . . .  94
     13.1.  Normative References . . . . . . . . . . . . . . . . . .  94
     13.2.  Informative References . . . . . . . . . . . . . . . . .  95
   Acknowledgments . . . . . . . . . . . . . . . . . . . . . . . . .  96
   Authors' Addresses  . . . . . . . . . . . . . . . . . . . . . . .  96

1. Introduction ✨

okay bestie, so this doc is all about Java language bindings for the Generic Security Services Application Programming Interface (GSS-API) version 2 💖 GSS-API version 2 is described in a language-independent format in RFC 2743 [RFC2743]. The GSS-API lets a caller application authenticate a principal identity, delegate rights to a peer, and apply security services like confidentiality and integrity on a per-message basis 🔐

this document and its predecessors, RFC 2853 [RFC2853] and RFC 5653 [RFC5653], build on the work the working group (WG) did in the area of RFC 2743 [RFC2743] and the C-bindings of RFC 2744 [RFC2744] 🧚 whenever it made sense, text from the C-bindings document (RFC 2744) gets reused to explain generic concepts and give direction to the implementors 📚

the design goals here are to cover all the functionality defined in RFC 2743 [RFC2743] and to deliver those services in an object-oriented way 💅 it also wants to satisfy both kinds of Java app developers: the ones who want access to a "system-wide" GSS-API implementation, and the ones who want to provide their own "custom" implementation 🎀

a system-wide implementation is one that every application can reach 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 accessible to any application via the CLASSPATH.

a custom implementation, on the other hand, is one that usually gets bundled with the app when it's distributed 🫶 it's expected to exist for some particular need of the app, like support for some specific mechanism 🦋

the design also aims to give you a flexible framework for adding and managing GSS-API mechanisms 👑 GSS-API leans on the Java Cryptography Architecture (JCA) provider model to support the plugability of mechanisms ⭐ mechanisms can be added system-wide, so all users of the framework have them available, and the spec also allows adding mechanisms per instance of the GSS-API 🍓

lastly, this spec presents an API that will naturally fit within the operating environment of the Java platform 🌸 readers are assumed to already be familiar with both the GSS-API and the Java platform, so no spoon-feeding here 🙈

2. Notational Conventions

the key words "MUST", "MUST NOT", "REQUIRED", "SHALL", "SHALL NOT", "SHOULD", "SHOULD NOT", "RECOMMENDED", "NOT RECOMMENDED", "MAY", and "OPTIONAL" in this document are to be interpreted as described in BCP 14 [RFC2119] [RFC8174] when, and only when, they appear in all capitals, as shown here 🧠✨

3. GSS-API Operational Paradigm

"Generic Security Service Application Programming Interface, Version 2" [RFC2743] defines a generic security API for calling applications 💜 it lets a communicating application authenticate the user associated with another application, delegate rights to another application, and apply security services like confidentiality and integrity on a per-message basis 🔐

there are four stages to using GSS-API 🎀

  1. The application acquires a set of credentials it can use to prove its identity to other processes 🪪 the application's credentials vouch for its global identity, which may or may not be related to any local username it might be running under.
  2. A pair of communicating applications establishes a joint security context using their credentials 🤝 the security context holds shared state information, which is required so per-message security services can be provided. Examples of state that might be shared between applications as part of a security context are cryptographic keys and message sequence numbers 🔑 as part of setting up a security context, the context initiator is authenticated to the responder, and the responder may be required to be authenticated back to the initiator 🔄 the initiator may optionally give the responder the right to initiate further security contexts, acting as an agent or delegate of the initiator. This transfer of rights is termed "delegation" and is achieved by creating a set of credentials, similar to those the initiating application 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 applications treat as cryptographically protected, opaque data. The caller of such a GSSContext method is responsible for transferring the token to the peer application, encapsulated if necessary in an application-to-application protocol. On receipt of such a token, the peer application should pass it to a corresponding GSSContext method, which decodes the token and extracts the information, updating the security context state information accordingly 📦

  1. 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 application transmitting a message it wants to protect will call the appropriate GSSContext method (getMIC or wrap) to apply protection before sending the resulting token to the receiving application. The receiver passes the received token (and, for data protected by getMIC, the accompanying message data) to the corresponding decoding method of the GSSContext interface (verifyMIC or unwrap) to remove the protection and validate the data 🛡️

  1. At the completion of a communications session (which may extend across several transport connections), each application uses a GSSContext method to invalidate the security context and release any system or cryptographic resources held 🧹 multiple contexts may also be used, either successively or simultaneously, within a single communications association, at the discretion of the applications 🌈

4. Additional Controls

this section talks about the OPTIONAL services a context initiator can request from the GSS-API before context establishment 🦋 each of these services is requested by calling the appropriate mutator method in the GSSContext object before the first call to init is performed. only the context initiator can request context flags 🚩

the OPTIONAL services defined are:

  • Delegation: the (usually temporary) transfer of rights from initiator to acceptor, enabling the acceptor to authenticate itself as an agent of the initiator 🎭
  • Mutual Authentication: in addition to the initiator authenticating its identity to the context acceptor, the context acceptor SHOULD also authenticate itself to the initiator 🤜🤛
  • Replay Detection: in addition to providing message integrity services, GSSContext per-message operations of getMIC and wrap SHOULD include message numbering information to enable verifyMIC and unwrap to detect if a message has been duplicated 👯
  • Out-of-Sequence Detection: in addition to providing message integrity services, GSSContext per-message operations (getMIC and wrap) SHOULD include message sequencing information to enable verifyMIC and unwrap to detect if a message has been received out of sequence 🔢
  • Anonymous Authentication: the establishment of the security context SHOULD NOT reveal the initiator's identity to the context acceptor 🕶️

some mechanisms may not support all OPTIONAL services, and some mechanisms may only support some services in conjunction with others 🧩 the GSSContext interface offers query methods so the calling application can verify which services will be available from the context once the establishment phase is complete 🔍 in general, if the security mechanism is capable of providing a requested service, it SHOULD do so, even if additional services must be enabled in order to provide the requested service 💖 if the mechanism is incapable of providing a requested service, it SHOULD proceed without the service, leaving the application to abort the context establishment process if it considers the requested service to be mandatory 😤

some mechanisms MAY specify that support for some services is optional and that implementors of the mechanism need not provide it 🤷‍♀️ this is most commonly true of the confidentiality service, often because of legal restrictions on the use of data encryption, but it may apply to any of the services 🔒 such mechanisms are required to send at least one token from acceptor to initiator during context establishment when the initiator indicates a desire to use such a service, so that the initiating GSS-API can correctly indicate whether the service is supported by the acceptor's GSS-API 🫧

4.1. Delegation 💌

okay so the GSS-API lets the initiating app decide whether delegation happens, using requestCredDeleg, and it has to be called before the first init call 🎀 Some mechanisms don't do delegation at all, so for those, an app trying to turn it on just gets ignored 🙈

On the acceptor side, if the initiator turned delegation on, the acceptor can check that with getCredDelegState on the GSSContext interface ✨ If it's enabled, grab the delegated credential with getDelegCred. That GSSCredential object can then be used to start later security contexts as an agent or delegate of the initiator 🦋 Now, if the original initiator is "A" and the delegate is "B", the identity inside the delegated credential could be either "A" or "B acting for A". It depends on the mechanism, so it's giving choose your own adventure 💅

For lots of delegation-capable mechanisms, a simple yes or no just isn't enough 😭 Other things a mechanism might let an app control are how long the delegation lasts, which network addresses it's valid from, and what tasks the delegate is allowed to do. Those extra controls are currently outside the scope of GSS-API 🧚 Implementations that support mechanisms with extra controls SHOULD provide extension routines so those controls can actually be used (maybe by changing the initiator's GSS-API credential object before it gets used to establish a context) 🌸 But the simple GSS-API delegation control SHOULD always be able to override the other mechanism-specific delegation controls 👑 And if the application tells the GSSContext object that delegation isn't wanted, the implementation MUST NOT let delegation happen. This is the exception to the general rule that a mechanism may turn on services even when they weren't requested. Delegation only happens when the app explicitly asks for it. No surprises, bestie 🫶

4.2. Mutual Authentication 🤝

usually the context acceptor wants the context initiator to authenticate itself, so the acceptor can make an access-control decision before doing anything for the initiator 💖 Sometimes, though, the initiator wants the acceptor to authenticate too 👀 GSS-API lets the initiating app ask for this mutual authentication by calling requestMutualAuth on the GSSContext interface with "true", and it has to do that before making the first call to init 🦋 The initiating app is told whether or not the context acceptor actually authenticated itself ✨

Here's the tea: some mechanisms may not support mutual authentication, and other mechanisms may always do it, whether or not the app asks 💅 Also, some mechanisms might need mutual authentication to support replay or out-of-sequence message detection. For those, asking for either of those services automatically turns on mutual authentication too 🔁 It's giving built-in bundle energy 🎁

4.3. Replay and Out-of-Sequence Detection 🔁

The GSS-API MAY provide detection of mis-ordered messages once a security context has been established 🌈 Protection MAY be applied to messages by either application. It can be done by calling either the getMIC or wrap methods of the GSSContext interface, and the peer application verifies it by calling verifyMIC or unwrap on the peer's GSSContext object 💜

So, getMIC works out a cryptographic checksum (an authentication tag) of an application message and hands that checksum back in a token 🍓 The application SHOULD pass both the token and the message to the peer application, which presents them to the verifyMIC method of the peer's GSSContext object 🫶

The wrap method is a bit different. It calculates a cryptographic checksum of an application message, then puts both the checksum and the message inside a single token 🎀 The application SHOULD pass that token to the peer application, which presents it to the unwrap method of the peer's GSSContext object. That's how it pulls out the message and verifies the checksum ✨

Either pair of routines may be capable of detecting out-of-sequence message delivery or duplicated messages 👑 The details of mis-ordered messages come through supplementary query methods of the MessageProp object, which each of these routines fills in 💅

A mechanism doesn't have to keep a list of every token it has ever processed in order to support these status codes 🌸 A typical mechanism might only remember the most recent "N" tokens. That lets it spot duplicates and missing tokens within the most recent "N" messages. If a token older than the most recent "N" shows up, the isOldToken method of the MessageProp instance returns "true" 😭

4.4. Anonymous Authentication 🕶️

okay this one is so cute 🥹 Sometimes an application wants to start the authentication process to authenticate a peer without revealing its own identity. Picture an app giving access to a database full of medical info that offers unrestricted access to the service 🧚 A client of that service might want to authenticate the service, so it can trust any info it gets back. But it might not want the service to learn who the client is. Maybe it's about privacy concerns over the specific inquiries, or maybe it just doesn't want to end up on mailing lists 🙈

Normally in GSS-API, the initiator's identity gets made available to the acceptor as part of the context establishment process ✨ 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 still gets generated, though 🦋 The application is always informed if a requested service is unavailable, 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 💅

Beyond telling the app 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 such contexts, return a reserved internal-form name. The implementation defines that name 🌈

The toString method for a GSSName object representing an anonymous entity returns a printable name 💖 That returned value is syntactically distinguishable from any valid principal name the implementation supports. The associated name-type Object Identifier (OID) is an OID representing the value of NT_ANONYMOUS 🍓 That name-type OID is 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, because this name may show up in things like audit logs 👀 For example, the string "<anonymous>" might be a good choice, as long as no valid printable names supported by the implementation can begin with "<" and end with ">" ✨

One more thing, bestie 🚨 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" 🙅‍♀️

4.5. Integrity and Confidentiality 🔐

If a GSSContext supports the integrity service, the getMic method can be used to create message integrity check tokens on application messages 💜

If a GSSContext supports the confidentiality service, the wrap method can be used to encrypt application messages 🌸 The encryption is selective, controlled by the setPrivacy method of the MessageProp object used in the wrap method. Confidentiality gets applied if the privacy state is set to true 🎀

4.6. Inter-process Context Transfer 🧚

GSS-APIv2 has functionality that lets a security context get transferred between processes on a single machine ✨ It's done using the export method of GSSContext and a byte array constructor of the same class 💅 The most common use for this is a client-server design where the server is a single process that accepts incoming security contexts, then launches child processes to deal with the data on those contexts 👑 In a design like that, the child processes need access to the security context object created in the parent, so they can use per-message protection services and delete the security context when the communication session ends 🫶

Here's the vibe check: the security context data structure is expected to contain sequencing information, so sharing a context between processes is generally impractical 😭 That's why the GSSContext interface has an export method. The process that currently owns the context can call it to declare that it doesn't plan to use the context again, and to create an inter-process token containing the info the adopting process needs to successfully recreate the context 🦋 After a successful export, GSS-API makes the original security context inaccessible to the calling process, and any further use of that object results in failures 💀 The originating process hands the inter-process token over to the adopting process, which creates a new GSSContext object using the byte array constructor. The properties of the context are equivalent to those of the original context 🌈

The inter-process token MAY contain sensitive data from the original security context, including cryptographic keys. Applications that use inter-process tokens to transfer security contexts MUST take appropriate steps to protect these tokens in transit 🔐

Implementations are not required to support inter-process transfer of security contexts 🌸 Calling the isTransferable method of the GSSContext interface will tell you whether the context object is transferable 🎀

4.7. The Use of Incomplete Contexts ⏳

Some mechanisms may allow the per-message services to be used before the context establishment process is complete 🦋 For example, a mechanism may include enough information in its initial context-level tokens for the context acceptor to immediately decode messages protected with wrap or getMIC. For a mechanism like that, the initiating application need not wait until subsequent context-level tokens have been sent and received before invoking the per-message protection services ✨

An application can invoke the isProtReady method of the GSSContext class to check whether the per-message services are available ahead of complete context establishment 💖 Applications that want to use per-message protection services on partially established contexts SHOULD query this method before trying to invoke wrap or getMIC 🫶

5. Calling Conventions 💅✨

Okay so Java doesn't just give you a syntax for the language, it gives you a whole operating environment 🌸 Memory gets managed automatically, so the app never has to clean up after itself 🧹 Because of that, the API ends up simpler, and some GSS-API functions just got cut entirely 💀

Java's JCA also has a provider model, which is so iconic 👑 It means you get implementation-independent access to security services. Apps can switch between implementations and even add new services on the fly. GSS-API uses those same ideas by relying on providers for the mechanism implementations 🦋

5.1. Package Name

The classes and interfaces in this document live in the package called "org.ietf.jgss" 📦 If an app wants to use this API, it should import this package name, as shown in Section 8 🎀

5.2. Provider Framework

Java's security APIs run on a provider architecture, and it's giving modular and extensible 🧩 Apps stay implementation independent, and security API implementations stay modular too. java.security.Provider is an abstract class that a vendor extends. It maps the properties for each security service to the names of the vendor classes that actually implement those services. When an app requests a service, it just names the provider it wants, and the API passes the request along to that provider's service classes 💖

Using the Java security provider model keeps apps shielded from the implementation details of the services they use 🫶 Apps can swap providers easily, and new providers can be added as needed, even at runtime 🔥

GSS-API can use providers to find the components for specific underlying security mechanisms. For example, one provider might have components that let GSS-API support the Kerberos v5 mechanism [RFC4121], and another might have components for the Simple Public-Key GSS-API Mechanism (SPKM) [RFC2025] 🧚 Mechanism-specific stuff gets delegated to components pulled from providers, so GSS-API can be extended to support an arbitrary list of mechanisms ✨

How GSS-API finds and queries these providers is out of scope for this document. That job is being saved for a Service Provider Interface (SPI) specification 🙈 An SPI specification isn't required in order to adopt this API spec, and you don't have to use providers to implement a GSS-API framework either. Still, when you pair the provider framework with an SPI specification, you can build an extensible and implementation-independent GSS-API framework, which is literally goals 🌈

5.3. Integer Types

All numeric values are declared as the "int" primitive Java type. The Java spec guarantees this will be a 32-bit two's complement signed number 🔢

Throughout this API, the "boolean" primitive Java type is used anywhere a boolean value is needed or returned ✅

5.4. Opaque Data Types

Java byte arrays represent the opaque data types that GSS-API consumes and produces in the form of tokens 🍓 Java arrays come with a length field, so users can easily check their size. The language has automatic garbage collection, which means developers don't have to release memory themselves, and buffer ownership issues get a lot simpler 💜

5.5. Strings

The String object is used to represent all textual data 📝 Java's String object treats every character as a two-byte Unicode character, which supports lots of locals 🌍 Every routine that returns or accepts textual data uses the String object, no exceptions 💅

5.6. Object Identifiers

An Oid object represents Universal Object Identifiers (OIDs) 🆔 OIDs are ISO-defined, hierarchically globally interpretable identifiers. GSS-API uses them to identify security mechanisms and name formats 🧠 You can create an Oid object from a string of its dot notation (for example, "1.3.6.1.5.6.2"), or from its ASN.1 DER encoding. There are also methods for testing equality and for getting the DER representation of the object 🎀

The Oid class is immutable, which is so important ✨ No methods let you change the contents of an Oid object. That means you can treat these objects like statics without ever copying them 🦋

Some routines let you use a default OID. In those cases you can pass a "null" value 🫧

5.7. Object Identifier Sets

The Java bindings represent Object Identifier sets as arrays of Oid objects. Java arrays come with a length field, which makes them easy to manipulate and reference 💖

To fully support the functionality of RFC 2743 [RFC2743], the Oid class has a method that checks whether an Oid object exists within a given array. In terms of functionality, this is the same as gss_test_oid_set_member 🧐 Java arrays plus 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 at all 🗑️ Automatic garbage collection and the Oid object's immutability wipe out the memory management problems that the C version has 🙌

Whenever a default value for an Object Identifier set is needed, you can use a "null" value. Check the detailed method description for the specifics 📖

5.8. Credentials

GSS-API credentials are represented by the GSSCredential interface 🔐 It has several constructs for creating most common credential objects for the initiator and the acceptor. Comparisons use the interface's "equals" method 💜 Here's the general description of GSS-API credentials, pulled from the C-bindings specification [RFC2744]:

   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 let their holder act as any of those principals 🎭 All principal identities asserted by a single GSS-API credential SHOULD belong to the same entity, although enforcing that is an implementation-specific matter 🫣 A single GSSCredential object represents all the credential elements that have been acquired 🧺

Creating a GSSContext object lets you pass "null" as the GSSCredential input parameter. That signals the app wants to act as a default principal 🙋‍♀️ Individual GSS-API implementations are free to decide how that default behaves for their mechanism. Still, for portability, the following default behavior by these routines is RECOMMENDED 🌸

For the initiator side of the context:

  1. 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,
  2. 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,
  3. 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,
  4. A user-configurable default identity should be used.

For the acceptor side of the context:

  1. 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,
  2. 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,
  3. 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,
  4. A user-configurable default identity shall be used.

The point of these rules is to let initiator and acceptor both establish security contexts using the default behavior whenever possible 🎀 Apps that request default behavior are likely to be more portable across mechanisms and implementations than apps that instantiate a GSSCredential object for a specific identity 🦋

5.9. Contexts

The GSSContext interface represents one end of a GSS-API security context 🔒 It stores state information for that end of the peer communication, including cryptographic state. The initiator and the acceptor instantiate the context object in different ways 💫 Once the context exists, the initiator MAY choose to set various context options that determine the characteristics of the desired security context ⚙️ When every app-desired characteristic is set, the initiator calls the initSecContext method, which produces a token for the peer's acceptSecContext method to consume 📨 Delivering the authentication token(s) between the peer applications is the app's job 🚚

After context establishment finishes, both the initiator and the acceptor can read the context attributes using the accessor methods 👀 These reflect the actual attributes of the established context, and they might not match what the initiator asked for. If a retrieved attribute doesn't match the desired value but the application protocol needs it, the application SHOULD destroy the security context and not use it for application traffic 🚫 Otherwise, at this point, the app can use the context to apply cryptographic services to its data 🔐✨

5.10. Authentication Tokens 🔐

okay so a token is a caller-opaque type that GSS-API uses to keep both ends of a GSS-API security context in sync 🫶 It's a cryptographically protected octet string, generated by the underlying mechanism at one end so the peer mechanism at the other end can use it. Wrapping it up inside the application protocol (if that's needed) and actually moving it over is the job of the peer applications 📦

Java GSS-API uses byte arrays to represent authentication tokens 🍓

5.11. Inter-process Tokens 🧚

certain GSS-API routines are built to move data between processes in multi-process programs 💅 They use a caller-opaque octet string that the GSS-API makes in one process for the GSS-API in another process to use. The calling application is the one responsible for passing these tokens between processes, so it's giving logistics 🚚

Also, GSS-API implementors are encouraged to keep sensitive info out of inter-process tokens or to protect them cryptographically. But ngl, lots of implementations won't be able to avoid putting key material or other sensitive data in them 😭 So it's the application's responsibility to make sure inter-process tokens are protected in transit and only sent to processes that are trustworthy 🔒

An inter-process token is a byte array that comes out of the export method of the GSSContext interface. Whoever receives it initializes a GSSContext object with that token to make a new context ✨ Once a context has been exported, the GSSContext object is invalidated and no longer available. Gone, bestie 💀

5.12. Error Reporting 🚨

RFC 2743 [RFC2743] defined how major and minor status values get used to signal GSS-API errors. The major code (also called the GSS status code) signals errors at the GSS-API level, no matter which underlying mechanism(s) are in play 🙌 The minor status value (or Mechanism status code) is a mechanism-defined error value that shows 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 come through instances of this class. Since exceptions do the job, you don't need major and minor codes in the API calls at all 🎀 The GSSException class also has methods to get textual representations for both the major and minor values, which is the same functionality as gss_display_status 📖 A GSSException object MAY also include an output token that SHOULD be sent to the peer.

If an exception is thrown during context establishment, the context negotiation has failed and the GSSContext object MUST be abandoned 💔 If it's thrown in a per-message call, though, the context MAY remain useful 💖

5.12.1. GSS Status Codes 🌸

GSS status codes are for errors that are independent of the underlying mechanism(s) providing the security service 🧠 The errors that can be indicated this way are generic API routine errors (errors defined in the GSS-API specification). These bindings use the Java exceptions mechanism, so calling errors aren't needed anymore 💅

A GSS status code indicates a single fatal generic API error from the routine that threw the GSSException. Using exceptions announces that a fatal error happened during the method. The GSS-API operational model also allows supplementary status info to be signaled from the per-message calls. That info has to be handled as return values, because exceptions aren't right for informatory or warning-like stuff 🙈 The only methods that can produce supplementary information are the two per-message methods GSSContext.verifyMIC() and GSSContext.unwrap(). They fill in the supplementary status codes in the MessageProp object that was passed in.

A GSSException object does more than set error codes and translate them into text. It also contains the definitions of all the numeric error values. The following table lists the definitions of error codes:

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 they're detected during context establishment, in which case it's a fatal error 🚫 (During per-message calls, these values are indicated as supplementary information contained in the MessageProp object.) They are:

+-----------------+-------+-----------------------------------------+
| Name            | Value | Meaning                                 |
+-----------------+-------+-----------------------------------------+
| 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 of FAILURE is used to show that the underlying mechanism detected an error with no specific GSS status code defined for it 🤷 The mechanism-specific status code can give more details about the error.

The different major status codes that can be contained in the GSSException object thrown by the methods in this specification are the same as the major status codes returned by the corresponding calls in RFC 2743 [RFC2743]. ✨

5.12.2. Mechanism-Specific Status Codes 🔍

Mechanism-specific status codes get communicated in two ways. They can be part of any GSSException thrown from the mechanism-specific layer to signal a fatal error, or they can be part of the MessageProp object that the per-message calls use to signal non-fatal errors 🧩

A default value of 0 in either the GSSException object or the MessageProp object is used to represent the absence of any mechanism-specific status code 0️⃣

5.12.3. Supplementary Status Codes 🦋

Supplementary status codes are confined to the per-message methods of the GSSContext interface. Because this info is informative, using exceptions to signal it isn't appropriate 🙅‍♀️ Instead, the per-message operations of the GSSContext interface return these values in a MessageProp object.

The MessageProp class defines query methods that return boolean values for the following 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 showed the specified property 👀 The application MUST determine the appropriate course of action for these supplementary values 🧐 They aren't treated as errors by the GSS-API, so no panic moment, periodt 🫶

5.13. Names ✨

okay so a name is how we identify a person or entity 🎀, and GSS-API checks that the name is actually tied to the entity claiming it 🫶 like, is this really them?? 😭

Different authentication mechanisms can use different namespaces for their principals, so GSS-API's naming support is necessarily complex in multi-mechanism environments 💀 (or even in some single-mechanism environments where the underlying mechanism supports multiple namespaces). it's a lot, bestie 🌈

There are two distinct conceptual representations for names 🦋:

  1. A GSS-API form represented by implementations of the GSSName interface: A single GSSName object MAY contain multiple names from different namespaces, but all names SHOULD refer to the same entity. An example of such an internal name would be the name returned from a call to the getName method of the GSSCredential interface, when applied to a credential containing credential elements for multiple authentication mechanisms employing different namespaces. This GSSName object will contain a distinct name for the entity for each authentication mechanism.

For GSS-API implementations supporting multiple namespaces, GSSName implementations MUST contain sufficient information to determine the namespace to which each primitive name belongs.

  1. Mechanism-specific contiguous byte array and string forms: Different GSSName initialization methods are provided to handle both byte array and string formats and to accommodate various calling applications and name types. These formats are capable of containing only a single name (from a single namespace). Contiguous string names are always accompanied by an Object Identifier specifying the namespace to which the name belongs, and their format is dependent on the authentication mechanism that employs that name. The string name forms are assumed to be printable and may therefore be used by GSS-API applications for communication with their users. The byte array name formats are assumed to be in non-printable formats (e.g., the byte array returned from the export method of the GSSName interface).

A GSSName object can be converted to a contiguous representation by using the toString method. This will guarantee that the name will be converted to a printable format. Different initialization methods in the GSSName interface are defined to allow support for multiple syntaxes for each supported namespace and to allow users the freedom to choose a preferred name representation. The toString method SHOULD use an implementation-chosen printable syntax for each supported name type. To obtain the printable name type, the getStringNameType method can be used. 🍓

There is no guarantee that calling the toString method on the GSSName interface will produce the same string form as the original imported string name. Furthermore, it is possible that the name was not even constructed from a string representation. The same applies to namespace identifiers, which may not necessarily survive unchanged after a journey through the internal name form. An example of this might be a mechanism that authenticates X.500 names but provides an algorithmic mapping of Internet DNS names into X.500. That mechanism's implementation of GSSName might, when presented with a DNS name, generate an internal name that contained both the original DNS name and the equivalent X.500 name. Alternatively, it might only store the X.500 name. In the latter case, the toString method of GSSName would most likely generate a printable X.500 name, rather than the original DNS name. 🧚

The context acceptor can obtain a GSSName object representing the entity performing the context initiation (through the usage of the getSrcName method). Since this name has been authenticated by a single mechanism, it contains only a single name (even if the internal name presented by the context initiator to the GSSContext object had multiple components). Such names are termed internal-mechanism names (or MNs), and the names emitted by the GSSContext interface's getSrcName and getTargName methods are always of this type. Since some applications may require MNs without wanting to incur the overhead of an authentication operation, creation methods are provided that take not only the name buffer and name type but also the mechanism OID for which this name should be created. When dealing with an existing GSSName object, the canonicalize method may be invoked to convert a general internal name into an MN. 💜

GSSName objects can be compared using their equal method, which returns "true" if the two names being compared refer to the same entity. This is the preferred way to perform name comparisons instead of using the printable names that a given GSS-API implementation may support. Since GSS-API assumes that all primitive names contained within a given internal name refer to the same entity, equal can return "true" if the two names have at least one primitive name in common. If the implementation embodies knowledge of equivalence relationships between names taken from different namespaces, this knowledge may also allow successful comparisons of internal names containing no overlapping primitive elements. However, applications SHOULD note that to avoid surprising behavior, it is best to ensure that the names being compared are either both mechanism names for the same mechanism or both internal names that are not mechanism names. This holds whether the equals method is used directly or the export method is used to generate byte strings that are then compared byte-by-byte. 👀

When used in large access control lists, the overhead of creating a GSSName object on each name and invoking the equal method on each name from the Access Control List (ACL) may be prohibitive. As an alternative way of supporting this case, GSS-API defines a special form of the contiguous byte array name, which MAY be compared directly (byte by byte). Contiguous names suitable for comparison are generated by the export method. Exported names MAY be re-imported by using the byte array constructor and specifying the NT_EXPORT_NAME as the name type Object Identifier. The resulting GSSName name will also be an MN. 🔥

The GSSName interface defines public static Oid objects representing the standard name types. Structurally, an exported name object consists of a header containing an OID identifying the mechanism that authenticated the name, and a trailer containing the name itself, where the syntax of the trailer is defined by the individual mechanism specification. Detailed description of the format is specified in the language-independent GSS-API specification [RFC2743].

Note that the results obtained by using the equals method will in general be different from those obtained by invoking canonicalize and export and then comparing the byte array output. The first series of operation determines whether two (unauthenticated) names identify the same principal; the second determines whether a particular mechanism would authenticate them as the same principal. These two operations will in general give the same results only for MNs. 🙈

It is important to note that the above are guidelines as to how GSSName implementations SHOULD behave and are not intended to be specific requirements of how name objects must be implemented. The mechanism designers are free to decide on the details of their implementations of the GSSName interface as long as the behavior satisfies the above guidelines. 🌸


5.14. Channel Bindings 🦋

GSS-API supports the use of user-specified tags to identify a given context to the peer application. These tags are intended to be used to identify the particular communications channel that carries the context. Channel bindings are communicated to the GSS-API using the ChannelBinding object. The application MAY use byte arrays as well as instances of InetAddress to specify the application data to be used in the channel binding. The InetAddress for the initiator and/or acceptor can be used within an instance of a ChannelBinding. ChannelBinding can be set for the GSSContext object using the setChannelBinding method before the first call to init or accept has been performed. Unless the setChannelBinding method has been used to set the ChannelBinding for a GSSContext object, "null" ChannelBinding will be assumed. InetAddress is currently the only address type defined within the Java platform and as such, it is the only one supported within the ChannelBinding class. Applications that use other types of addresses can include them as part of the application-specific data. 💅

Conceptually, the GSS-API concatenates the initiator and acceptor address information and the application-supplied byte array to form an octet string. The mechanism calculates a Message Integrity Code (MIC) over this octet string and binds the MIC to the context establishment token emitted by the init method of the GSSContext interface. The same bindings are set by the context acceptor for its GSSContext object, and during processing of the accept method, a MIC is calculated in the same way. The calculated MIC is compared with that found in the token, and if the MICs differ, accept will throw a GSSException with the major code set to BAD_BINDINGS, and the context will not be established. Some mechanisms may include the actual channel-binding data in the token (rather than just a MIC); applications SHOULD therefore not use confidential data as channel-binding components. 🔐

Individual mechanisms may impose additional constraints on addresses that may appear in channel bindings. For example, a mechanism may verify that the initiator address field of the channel binding contains the correct network address of the host system. Portable applications SHOULD therefore ensure that they either provide correct information for the address fields or omit the setting of the addressing information. 🚨

5.15. Optional Parameters 🌷

Whenever the application wishes to omit an optional parameter, the "null" value SHALL be used. The detailed method descriptions indicate which parameters are optional. Method overloading has also been used as a technique to indicate default parameters. ✨


6. Introduction to GSS-API Classes and Interfaces 🎀

This section presents a brief description of the classes and interfaces that constitute the GSS-API. The implementations of these are obtained from the CLASSPATH defined by the application. If Java GSS becomes part of the standard Java APIs, then these classes will be available by default on all systems as part of the JRE's system classes. 🫧

This section also shows the corresponding RFC 2743 [RFC2743] functionality implemented by each of the classes. Detailed description of these classes and their methods is presented in Section 7. 👑

6.1. GSSManager Class 🏭

okay so the GSSManager class is basically the factory bestie 🧚. It makes the GSS-API interface implementations for you, and it also answers questions about the security mechanisms that sit underneath. You can grab the default one with the static method getInstance(). If you're feeling extra and want your own implementation, you can just extend the abstract class yourself 💅

This class has equivalents of the following [RFC2743] routines:

+----------------------------+-------------------------+------------+
| RFC 2743 Routine           | Function                | Section(s) |
+----------------------------+-------------------------+------------+
| gss_import_name            | Create an internal name | 7.1.5 -    |
|                            | from the supplied       | 7.1.8      |
|                            | information.            |            |
| gss_acquire_cred           | Acquire credential for  | 7.1.9 -    |
|                            | use.                    | 7.1.11     |
| gss_import_sec_context     | Create a previously     | 7.1.14     |
|                            | exported context.       |            |
| gss_indicate_mechs         | List the mechanisms     | 7.1.2      |
|                            | supported by this GSS-  |            |
|                            | API implementation.     |            |
| gss_inquire_mechs_for_name | List the mechanisms     | 7.1.4      |
|                            | supporting the          |            |
|                            | specified name type.    |            |
| gss_inquire_names_for_mech | List the name types     | 7.1.3      |
|                            | supported by the        |            |
|                            | specified mechanism.    |            |
+----------------------------+-------------------------+------------+

6.2. GSSName Interface 🏷️

ok so GSS-API names show up in the Java bindings through the GSSName interface. Different name formats and their definitions are identified with Universal OIDs, and you can totally figure out a name's format from the unique OID of its name type 🔍 The GSSName interface gives you these GSS-API routines:

+-----------------------+------------------------------+------------+
| RFC 2743 Routine      | Function                     | Section(s) |
+-----------------------+------------------------------+------------+
| gss_display_name      | Convert internal name        | 7.2.6      |
|                       | representation to text       |            |
|                       | format.                      |            |
| gss_compare_name      | Compare two internal names.  | 7.2.2,     |
|                       |                              | 7.2.3      |
| gss_release_name      | Release resources associated | N/A        |
|                       | with the internal name.      |            |
| gss_canonicalize_name | Convert an internal name to  | 7.2.4      |
|                       | a mechanism name.            |            |
| gss_export_name       | Convert a mechanism name to  | 7.2.5      |
|                       | export format.               |            |
| gss_duplicate_name    | Create a copy of the         | N/A        |
|                       | internal name.               |            |
+-----------------------+------------------------------+------------+

the gss_release_name call isn't provided because Java handles its own garbage collection, so no need 🗑️✨ And gss_duplicate_name is also redundant, because GSSName has no mutator methods that can change the state of the object. That makes it totally safe to share across threads 🧵💜

6.3. GSSCredential Interface 🪪

the GSSCredential interface is in charge of wrapping up GSS-API credentials. Credentials identify a single entity and carry the cryptographic information 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. The GSSCredential interface provides the functionality of these GSS-API routines:

+--------------------------+---------------------------+------------+
| RFC 2743 Routine         | Function                  | Section(s) |
+--------------------------+---------------------------+------------+
| gss_add_cred             | Constructs credentials    | 7.3.11     |
|                          | incrementally.            |            |
| gss_inquire_cred         | Obtain information about  | 7.3.3 -    |
|                          | credential.               | 7.3.10     |
| gss_inquire_cred_by_mech | Obtain per-mechanism      | 7.3.4 -    |
|                          | information about a       | 7.3.9      |
|                          | credential.               |            |
| gss_release_cred         | Dispose of credentials    | 7.3.2      |
|                          | after use.                |            |
+--------------------------+---------------------------+------------+

6.4. GSSContext Interface 🤝

this interface wraps up the context-level calls that peers need for establishing and managing a security context, plus the per-message services that applications use 💬 A context is set up between a pair of peers and lets you use security services on a per-message basis for application data. It's created over a single security mechanism. The GSSContext interface provides the functionality of these GSS-API routines:

+------------------------+-----------------------------+------------+
| RFC 2743 Routine       | Function                    | Section(s) |
+------------------------+-----------------------------+------------+
| gss_init_sec_context   | Initiate the creation of a  | 7.4.2      |
|                        | security context with a     |            |
|                        | peer.                       |            |
| gss_accept_sec_context | Accept a security context   | 7.4.3      |
|                        | initiated by a peer.        |            |
| gss_delete_sec_context | Destroy a security context. | 7.4.5      |
| gss_context_time       | Obtain remaining context    | 7.4.30     |
|                        | time.                       |            |
| gss_inquire_context    | Obtain context              | 7.4.21 -   |
|                        | characteristics.            | 7.4.35     |
| gss_wrap_size_limit    | Determine token-size limit  | 7.4.6      |
|                        | for gss_wrap.               |            |
| gss_export_sec_context | Transfer security context   | 7.4.11     |
|                        | to another process.         |            |
| gss_get_mic            | Calculate a cryptographic   | 7.4.9      |
|                        | Message Integrity Code      |            |
|                        | (MIC) for a message.        |            |
| gss_verify_mic         | Verify integrity on a       | 7.4.10     |
|                        | received message.           |            |
| gss_wrap               | Attach a MIC to a message   | 7.4.7      |
|                        | and optionally encrypt the  |            |
|                        | message content.            |            |
| gss_unwrap             | Obtain a previously wrapped | 7.4.8      |
|                        | application message         |            |
|                        | verifying its integrity and |            |
|                        | optionally decrypting it.   |            |
+------------------------+-----------------------------+------------+

so the gss_process_context_token routine? It's not in the Java bindings spec 🙅‍♀️ The gss_delete_sec_context functionality was also changed so it doesn't return any peer tokens. This was proposed following the recommendations stated in [RFC2743]. What GSSContext does still give you is destroying the locally stored context information 🗑️💅

6.5. MessageProp Class 📨

this helper class is used in the per-message operations on the context. The application creates an instance and then passes it into the per-message calls. Sometimes the application tells the GSS-API implementation things through this object, and other times the GSS-API sends info back to the application by setting it here 🔁 For the details on the per-message operations (wrap, unwrap, getMIC, and verifyMIC), check out the GSSContext interface descriptions.

6.6. GSSException Class 🚨

exceptions are how the Java bindings signal fatal errors to the calling application. 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 it also translates them into text. 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.9, 7.8.10   |
|                    | codes.                     |                 |
+--------------------+----------------------------+-----------------+

6.7. Oid Class 🔢

this utility class represents Universal Object Identifiers and handles the operations on them. GSS-API uses Object Identifiers to tell security mechanisms and name types apart 🏷️ Besides being used whenever an Object Identifier is needed, this class implements the following GSS-API functionality:

+-------------------------+-------------------------------+---------+
| RFC 2743 Routine        | Function                      | Section |
+-------------------------+-------------------------------+---------+
| gss_test_oid_set_member | Determine if the specified    | 7.7.5   |
|                         | OID 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 application can use a byte array for the 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. If your app uses other address types, you can include them as part of the application data 🌸


7. Detailed GSS-API Class Description 📚✨

this section lists a detailed description of all the public methods that each of the GSS-API classes and interfaces MUST provide. The MUST is doing a lot of work here, bestie 😤🫶

7.1. public abstract class GSSManager ✨

okay so GSSManager is an abstract class, and it's basically the factory 🏭 for three big GSS interfaces: GSSName, GSSCredential, and GSSContext 💅. It also lets apps ask which mechanisms the GSS implementation has and which name types each mechanism supports 🦋. An instance of the default GSSManager subclass MAY be obtained through the static method getInstance(), but apps are totally free to make their own subclasses 🌸.

All but one method in this class are abstract, which means subclasses have to bring the complete implementation for those methods 🧚. The only exception is the static getInstance(), which has platform-specific code that hands back 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 🔒. That way every subclass only ever calls the default constructor that the compiler gives the base class.

A subclass extending the GSSManager abstract class MAY be implemented as a modular provider-based layer that uses some well-known service provider specification ✨. The GSSManager API gives apps methods to set provider preferences on that kind of implementation. These methods also let the implementation throw a well-defined exception if provider-based configuration isn't supported. Apps that expect to be portable SHOULD be aware of this and recover cleanly by catching the exception 🫶.

There are three ways providers are expected to get used, and they're honestly iconic 👑:

  1. The application doesn't care which provider is used (the default case).
  2. The application wants a particular provider used preferentially, either for a particular mechanism or all the time, no matter the mechanism.
  3. 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 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 require any knowledge of whatever service provider specification the GSSManager subclass follows, which is the whole vibe 💖. It's hoped that these methods will cover the needs of most applications. Additional methods MAY be added to an extended GSSManager that could be part of a service provider specification that gets standardized later 🧚‍♀️.

When neither of the methods is called, the implementation SHOULD choose a default provider for each mechanism it supports 🍓.

7.1.1. getInstance 🎀

public static GSSManager getInstance()

Returns the default GSSManager implementation.

7.1.2. getMechs 🌸

public abstract Oid[] getMechs()

Returns an array of Oid objects indicating the mechanisms available to GSS-API callers. A "null" value is returned when no mechanisms are available. An example of this would be when mechanisms are dynamically configured and none are currently installed 🙈.

7.1.3. getNamesForMech 🦋

public abstract Oid[] getNamesForMech(Oid mech)
                      throws GSSException

Returns the name type OIDs supported by the specified mechanism.

Parameters:

  • mech: The Oid object for the mechanism to query.

7.1.4. getMechsForName 💅

public abstract Oid[] getMechsForName(Oid nameType)

Returns an array of Oid objects corresponding to the mechanisms that support the specific name type. "null" is returned when no mechanisms are found to support the specified name type.

Parameters:

  • nameType: The Oid object for the name type.

7.1.5. createName 🧚

public abstract GSSName createName(String nameStr, Oid nameType)
                throws GSSException

Factory method to convert a contiguous string name from the specified namespace into a GSSName object. Usually the GSSName object created will not be an MN. Two exceptions are when the namespace type parameter indicates NT_EXPORT_NAME, or when the GSS-API implementation doesn't support multiple mechanisms 🔥.

Parameters:

  • nameStr: The string representing a printable form of the name to create.
  • nameType: The OID specifying the namespace of the printable name is supplied. Note that nameType serves to describe and qualify the interpretation of the input nameStr. It does not necessarily imply a type for the output GSSName implementation. The "null" value can be used to specify that a mechanism-specific default printable syntax SHOULD be assumed by each mechanism that examines nameStr.

7.1.6. createName 💖

public abstract GSSName createName(byte[] name, Oid nameType)
                throws GSSException

Factory method to convert a contiguous byte array containing a name from the specified namespace into a GSSName object. Usually the GSSName object created will not be an MN. Two exceptions are when the namespace type parameter indicates NT_EXPORT_NAME, or when the GSS-API implementation is not a multi-mechanism one 💀.

Parameters:

  • name: The byte array containing the name to create.
  • nameType: The OID specifying the namespace of the name supplied in the byte array. Note that nameType serves to describe and qualify the interpretation of the input name byte array. It does not necessarily imply a type for the output GSSName implementation. The "null" value can be used to specify that a mechanism-specific default syntax SHOULD be assumed by each mechanism that examines the byte array.

7.1.7. createName 🌈

public abstract GSSName createName(String nameStr, Oid nameType,
                Oid mech) throws GSSException

Factory method to convert a contiguous string name from the specified namespace into a GSSName object that is a mechanism name (MN). Put another way, this method is a utility that does the equivalent of two steps: the createName described in Section 7.1.5, plus the GSSName.canonicalize() described in Section 7.2.4 ✨.

Parameters:

  • nameStr: The string representing a printable form of the name to create.
  • nameType: The OID specifying the namespace of the printable name supplied. Note that nameType serves to describe and qualify the interpretation of the input nameStr. It does not necessarily imply a type for the output GSSName implementation. The "null" value can be used to specify that a mechanism-specific default printable syntax SHOULD be assumed when the mechanism examines nameStr.
  • mech: OID specifying the mechanism for which this name should be created.

7.1.8. createName 👑

public abstract GSSName createName(byte[] name, Oid nameType,
                Oid mech) throws GSSException

Factory method to convert a contiguous byte array containing a name from the specified namespace into a GSSName object that is an MN. Again, this is a utility doing the equivalent of two steps: the createName described in Section 7.1.6, plus the GSSName.canonicalize() described in Section 7.2.4 🧁.

Parameters:

  • name: The byte array representing the name to create.
  • nameType: The OID specifying the namespace of the name supplied in the byte array. Note that nameType serves to describe and qualify the interpretation of the input name byte array. It does not necessarily imply a type for the output GSSName implementation. The "null" value can be used to specify that a mechanism-specific default syntax SHOULD be assumed by each mechanism that examines the byte array.
  • mech: OID specifying the mechanism for which this name should be created.

7.1.9. createCredential 🎀

public abstract GSSCredential createCredential(int usage)
                throws GSSException

Factory method for acquiring default credentials. This 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), or
GSSCredential.ACCEPT_ONLY(2)

7.1.10. createCredential 🦋

public abstract GSSCredential createCredential(GSSName aName,
                int lifetime, Oid mech, int usage)
                throws GSSException

Factory method for acquiring a single-mechanism credential 💅.

Parameters:

  • aName: Name of the principal for whom this credential is to be acquired. Use "null" to specify the default principal.
  • lifetime: The number of seconds that credentials should remain valid. Use GSSCredential.INDEFINITE_LIFETIME to request that the credentials have the maximum permitted lifetime. Use GSSCredential.DEFAULT_LIFETIME to request the default credential lifetime.
  • mech: The OID of the desired mechanism. Use "(Oid) null" to request the default mechanism.
  • 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), or
GSSCredential.ACCEPT_ONLY(2)

7.1.11. createCredential 🎀

public abstract GSSCredential createCredential(GSSName aName,
                int lifetime, Oid[] mechs, int usage)
                throws GSSException

okay so this is the factory method for getting credentials over a whole set of mechanisms at once 💅 You hand it an array of mechs and it goes and acquires creds for each one. Not sure which ones actually worked? Call GSSCredential.getMechs() afterwards and it'll tell you 👀

Parameters:

  • aName: the principal this credential is being acquired for. Pass null if you want the default principal.
  • lifetime: how many seconds the credentials should stay valid. GSSCredential.INDEFINITE_LIFETIME asks for the maximum permitted lifetime, and GSSCredential.DEFAULT_LIFETIME asks for the default lifetime.
  • mechs: the array of mechanisms to acquire the credential over. Pass (Oid[]) null if you want a system-specific default set of mechanisms.
  • usage: the intended usage for this credential object. The value of this parameter MUST be one of the following:
    • GSSCredential.INITIATE_AND_ACCEPT (0)
    • GSSCredential.INITIATE_ONLY (1)
    • GSSCredential.ACCEPT_ONLY (2)

7.1.12. createContext 🌸

public abstract GSSContext createContext(GSSName peer, Oid mech,
                GSSCredential myCred, int lifetime)
                throws GSSException

this one is the factory method for making a context on the initiator's side 🦋 You can tweak the context flags through the mutator methods before you call GSSContext.initSecContext(), so you're not locked in yet.

Parameters:

  • peer: the name of the target peer.
  • mech: the OID of the mechanism you want. Pass (Oid) null to ask for the default mechanism.
  • myCred: the initiator's credentials. Pass null to act as a default initiator principal.
  • lifetime: the requested lifetime of the context, in seconds. Use GSSContext.INDEFINITE_LIFETIME or GSSContext.DEFAULT_LIFETIME to ask for indefinite or default context lifetime.

7.1.13. createContext 🧚

public abstract GSSContext createContext(GSSCredential myCred)
                throws GSSException

this is the acceptor's side version 🫶 The context's properties get figured out from the input token that gets handed to the accept method, so you don't set them here.

Parameters:

  • myCred: the acceptor's credentials. Pass null to act as a default acceptor principal.

7.1.14. createContext 💜

public abstract GSSContext createContext(byte[] interProcessToken)
                throws GSSException

this one imports a context that was exported earlier ✨ The context properties come from the input token, and you can't change them with the set methods afterwards. no take-backs, bestie 😤

Parameters:

  • interProcessToken: the token that was previously emitted by the export method.

7.1.15. addProviderAtFront 👑

public abstract void addProviderAtFront(Provider p, Oid mech)
                throws GSSException

this is how you tell the GSSManager that you want a particular provider used ahead of all the others 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 what the mechanism is. It only moves on to a different provider when the one you picked doesn't support the mechanism it needs.

Calling this repeatedly keeps the older settings but pushes them lower in preference. So you end up with an ordered list of provider and OID pairs that grows at the top 📈

Calling addProviderAtFront with a null Oid removes all previous preferences that were set for this provider in the GSSManager instance. Calling it with a non-null Oid removes any previous preference that used this mechanism and this provider together 🧹

If the GSSManager implementation doesn't support an SPI with a pluggable provider architecture, it SHOULD throw a GSSException with the status code GSSException.UNAVAILABLE to say the operation is unavailable 🙈

Parameters:

  • p: the provider instance that should be used whenever support is needed for mech.
  • mech: the mechanism the provider is being set for.

7.1.15.1. addProviderAtFront Example Code 🍓

Say an application wants provider A checked first whenever any mechanism is needed. It would call:

GSSManager mgr = GSSManager.getInstance();
// mgr may at this point have its own pre-configured list
// of provider preferences.  The following will prepend to
// any such list:

mgr.addProviderAtFront(A, null);

Now if it also wants the mechanism with OID m1 to always come from provider B before provider A gets checked, it would call:

mgr.addProviderAtFront(B, m1);

So the GSSManager would check with B first if m1 is needed. If B doesn't support m1, the GSSManager keeps going and checks with A. If any mechanism m2 is needed, and m2 is different from m1, the GSSManager skips B and checks with A directly 🏃‍♀️

Now say, at a later time, this call is made to the same GSSManager instance:

mgr.addProviderAtFront(B, null)

Then the earlier (B, m1) setting gets subsumed by this one and SHOULD be removed. Effectively, the list of preferences now becomes {(B, null), (A, null), ... //followed by the pre-configured list} 💅

But heads up, this call:

mgr.addProviderAtFront(A, m3)

does not subsume the previous (A, null) setting. So the list effectively becomes {(A, m3), (B, null), (A, null), ...} 🤓

7.1.16. addProviderAtEnd 🦋

public abstract void addProviderAtEnd(Provider p, Oid mech)
                throws GSSException

this is the opposite energy 🥹 It tells the GSSManager that you'd like 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 for the mechanism, the GSSManager MUST use the indicated provider for any mechanism.

Calling this repeatedly keeps the older settings but raises them above the newer ones in preference. So you get an ordered list of providers and OID pairs that grows at the bottom, and the older settings get used first 🌈

If there are any existing preferences that conflict with the one being set here, the GSSManager SHOULD ignore this request 🙅‍♀️

If the GSSManager implementation doesn't support an SPI with a pluggable provider architecture, it SHOULD throw a GSSException with the status code GSSException.UNAVAILABLE to say the operation is unavailable 😭

Parameters:

  • p: the provider instance that should be used whenever support is needed for mech.
  • mech: the mechanism the provider is being set for.

7.1.16.1. addProviderAtEnd Example Code 🌸

Say an application wants the system default providers checked first whenever a mechanism with OID m1 is needed. Provider A should only get checked if those defaults 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 all the configured providers have been checked, it would call:

mgr.addProviderAtEnd(B, null);

Effectively, the list of preferences now becomes {..., (A, m1), (B, null)} 🧁

Now say, at a later time, this call is made to the same GSSManager instance:

mgr.addProviderAtEnd(B, m2)

The earlier (B, null) pair subsumes this one, so this request SHOULD be ignored. The same thing happens if someone asks for the pairs (A, m1) or (B, null) that already exist 🙃

But this call:

mgr.addProviderAtEnd(A, null)

is not subsumed by the earlier (A, m1) setting. So the list effectively becomes {..., (A, m1), (B, null), (A, null)} 🎀

7.1.17. 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.2. public interface GSSName 🦄

this interface wraps up a single GSS-API principal entity 🫧 Different name formats and their definitions are identified with Universal OIDs, and you can work out the format of a name from the unique OID of its namespace type ✨

7.2.1. Static Constants ✨

okay so these are the name type OIDs bestie 🧚 they're basically the labels that tell you what kind of name you're looking at, and they're constants so you just grab them off the class 💅

NT_HOSTBASED_SERVICE 🎀 is for services that live on host computers. the name is built from two bits, "service" and "hostname", like this:

service@hostname

the "service" part has values registered with the IANA, and this one stands for:

{ iso(1) member-body(2) United States(840) mit(113554)
  infosys(1) gssapi(2) generic(1) service_name(4) }

NT_USER_NAME 💜 means a named user on a local system, and it's:

{ iso(1) member-body(2) United States(840) mit(113554)
  infosys(1) gssapi(2) generic(1) user_name(1) }

NT_MACHINE_UID_NAME 🦋 is a numeric user ID for someone on a local system (think Uid), and its value is:

{ iso(1) member-body(2) United States(840) mit(113554)
  infosys(1) gssapi(2) generic(1) machine_uid_name(2) }

NT_STRING_UID_NAME 🌸 is a string of digits standing in for that numeric user ID, and it's:

{ iso(1) member-body(2) United States(840) mit(113554)
  infosys(1) gssapi(2) generic(1) string_uid_name(3) }

NT_ANONYMOUS 🙈 is for an anonymous entity, and its value is:

{ iso(1), org(3), dod(6), internet(1), security(5),
  nametypes(6), gss-anonymous-name(3) }

NT_EXPORT_NAME 🍓 marks a name that came out of the export method, and it's:

{ iso(1), org(3), dod(6), internet(1), security(5),
  nametypes(6), gss-api-exported-name(4) }

7.2.2. equals ✨

public boolean equals(GSSName another) throws GSSException

okay this one 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, and if either name is anonymous it just returns "false" 💀

Parameters:

  • another: the GSSName object you're comparing against 🫶

7.2.3. equals ✨

public boolean equals(Object another)

this is the version that overrides Object.equals() 🔥 it works exactly like Section 7.2.2, except it never throws a GSSException. if something goes wrong, you get "false" back instead 🙌

ngl this is the important bit: Java requires that two objects which are equal according to equals(Object) MUST return the same integer result when hashCode() is called on them 🤓

Parameters:

  • another: the GSSName object you're comparing against 🫶

7.2.4. canonicalize ✨

public GSSName canonicalize(Oid mech) throws GSSException

this makes an MN from any internal name 💖 it's basically the same as using the factory methods from Sections 7.1.7 or 7.1.8 that take the mechanism name as a parameter 🎀

Parameters:

  • mech: the OID for the mechanism you want the canonical form for 🦋

7.2.5. export ✨

public byte[] export() throws GSSException

this gives you a canonical contiguous byte representation of an MN 🧚 it's built for direct, byte-by-byte comparison by authorization functions 🔐 if the name isn't an MN, implementations MAY throw a GSSException with the NAME_NOT_MN status code 😭 and if they don't throw, they SHOULD use some system-specific default mechanism to canonicalize the name and then export it 🌈 the header format of the output buffer is specified in RFC 2743 [RFC2743] 📚

7.2.6. toString ✨

public String toString()

this returns a textual representation of the GSSName object 💅 if you want to know what printed name format it's using (that decides the syntax of the string), call the getStringNameType method 🫶

7.2.7. getStringNameType ✨

public Oid getStringNameType() throws GSSException

this returns the OID for the type of name that toString gives back 💜 use that OID and you can figure out the syntax of the printable name ✨

7.2.8. isAnonymous ✨

public boolean isAnonymous()

this tests whether the name object is an anonymous entity 🙈 it returns "true" if it is ✅

7.2.9. isMN ✨

public boolean isMN()

this tests whether the name object has only one mechanism element, which makes it a mechanism name as defined by RFC 2743 [RFC2743] 🦋

7.2.10. Example Code ✨

okay bestie, this example is giving full tutorial energy 🎀 it creates a GSSName, converts it to an MN, compares, gets a printable version, 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 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.3. public interface GSSCredential implements Cloneable ✨

okay this one is the credential, the GSS-API bundle of cryptographic info for an entity 🔐 it's what lets you create a context on behalf of whoever it represents 💖 it MAY hold multiple distinct, mechanism-specific credential elements, but they all have to refer to the same entity 🫶

A credential MAY be used for context initiation, acceptance, or both 🦋

GSS-API implementations MUST impose a local access-control policy on callers 🛡️ so unauthorized callers can't grab credentials they aren't entitled to 🚫 credential creation is NOT meant to be a "login to the network" function, because that would mean making new credentials instead of just getting a handle to existing ones 🙅‍♀️ if you do need a function like that, it SHOULD be defined in implementation-specific extensions to the API 🧚

If credential acquisition is slow for a mechanism, the mechanism MAY wait to acquire it until the credential is actually needed (for example, by GSSContext) 🌸 those implementation choices SHOULD be invisible to the calling application 👀 so the query methods right after creating a credential object MUST return valid credential data, even if that means paying the cost of the deferred acquisition 💅

Applications create a credential object by passing in the desired parameters 💜 then they can use the query methods to learn about the instantiated object (this is the equivalent of the gss_inquire routines) ✨ when the credential isn't needed anymore, the application SHOULD call dispose (the equivalent of gss_release_cred) 🧹 to free up resources and destroy any cryptographically sensitive info 🔒

Classes that implement this interface also implement Cloneable 🦋 which means they support the clone() method for making duplicate credentials 👯 this comes in handy right before the add() call, so you can keep a copy of the original credential 🎀

7.3.1. Static Constants ✨

public static final int INITIATE_AND_ACCEPT

INITIATE_AND_ACCEPT 🌈 is a 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

INITIATE_ONLY 🎀 is a usage flag asking that the credential can be used for context initiation only 🦋 its value is 1 ✨

public static final int ACCEPT_ONLY

ACCEPT_ONLY 💜 is a usage flag asking that the credential can be used for context acceptance only 🫶 its value is 2 ✨

public static final int DEFAULT_LIFETIME

DEFAULT_LIFETIME 🌸 is a lifetime constant for the default credential lifetime 🧚 its value is 0 ✨

public static final int INDEFINITE_LIFETIME

INDEFINITE_LIFETIME 🦋 is a lifetime constant for an indefinite credential lifetime 🔥 its value is the maximum integer value in Java, which is Integer.MAX_VALUE 👑

7.3.2. dispose ✨

public void dispose() throws GSSException

this releases any sensitive info the GSSCredential object might be holding 🔒 applications SHOULD call this as soon as the credential isn't needed anymore, so that sensitive info is kept around for as little time as possible ⏱️💅

7.3.3. getName ✨

public GSSName getName() throws GSSException

this retrieves the name of the entity that the credential asserts 🫶

7.3.4. getName ✨

public GSSName getName(Oid mechOID) throws GSSException

this retrieves a mechanism name of the entity that the credential asserts 🦋 it's equivalent to calling canonicalize() on the name returned by Section 7.3.3 🌸

Parameters:

  • mechOID: the mechanism you want the information returned for 💜

7.3.5. getRemainingLifetime ✨

public int getRemainingLifetime() throws GSSException

this returns the remaining lifetime in seconds for a credential ⏳ it's the minimum lifetime across all the underlying credential mechanisms 🧚 a return value of GSSCredential.INDEFINITE_LIFETIME means the credential never expires 👑 a return value of 0 means it's already expired 💀

7.3.6. getRemainingInitLifetime ✨

public int getRemainingInitLifetime(Oid mech) throws GSSException

this returns the remaining lifetime in seconds for the credential to keep being able to initiate security contexts under the specified mechanism 🚀 a return value of GSSCredential.INDEFINITE_LIFETIME means it doesn't expire for context initiation 🌈 a return value of 0 means it's already expired 😭

Parameters:

  • mechOID: the mechanism you want the information returned for 💖

7.3.7. getRemainingAcceptLifetime ✨

public int getRemainingAcceptLifetime(Oid mech) throws GSSException

this returns the remaining lifetime in seconds for the credential to keep being able to accept security contexts under the specified mechanism 🎀 a return value of GSSCredential.INDEFINITE_LIFETIME means it doesn't expire for context acceptance 🦋 a return value of 0 means it's already expired 💀

Parameters:

  • mechOID: the mechanism you want the information returned for 🫶

7.3.8. getUsage ✨

public int getUsage() throws GSSException

This one returns the credential usage flag as a union over all mechanisms 💅 The return value will be one of GSSCredential.INITIATE_AND_ACCEPT(0), GSSCredential.INITIATE_ONLY(1), or GSSCredential.ACCEPT_ONLY(2).

Specifically, GSSCredential.INITIATE_AND_ACCEPT(0) SHOULD come back as long as there's one credential element that allows context initiation AND one that allows context acceptance 🦋. Those two elements don't have to be the same one, and they don't have to use the same mechanism(s) either. Iconic behavior, honestly.


7.3.9. getUsage 🌸

public int getUsage(Oid mechOID) throws GSSException

Same vibe as the one above, except this one only checks the usage flag for a single mechanism 🎯 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 for which information should be returned.

7.3.10. getMechs 💜

public Oid[] getMechs() throws GSSException

This returns an array of the mechanisms supported by this credential. Simple, but iconic 👑

7.3.11. add 🧚

public void add(GSSName aName, int initLifetime, int acceptLifetime,
                Oid mech, int usage) throws GSSException

Adds a mechanism-specific credential element to an existing credential, so you can build credentials one mechanism at a time 🍓 This is basically a way to layer things up, bestie.

It's mainly meant to be used by context acceptors while they're creating acceptance credentials, since those get used with a bunch of different clients running different security mechanisms. Very main character of them 🌈

This routine adds the new credential element "in-place" 🫶 If you want the element in a brand new credential instead, first call clone() to get a copy of this credential, and then call its add() method. Don't mix those up, periodt.

Parameters:

  • aName: The name of the principal for whom this credential is to be acquired. Use "null" to specify the default principal.
  • initLifetime: The number of seconds that credentials should remain valid for initiating security contexts. Use GSSCredential.INDEFINITE_LIFETIME to request that the credentials have the maximum permitted lifetime. Use GSSCredential.DEFAULT_LIFETIME to request default credential lifetime.
  • acceptLifetime: The number of seconds that credentials should remain valid for accepting security contexts. Use GSSCredential.INDEFINITE_LIFETIME to request that the credentials have the maximum permitted lifetime. Use GSSCredential.DEFAULT_LIFETIME to request default credential lifetime.
  • mech: The mechanisms over which the credential is to be acquired.
  • usage: The intended usage for this credential object. The value of this parameter MUST be one of:
    • GSSCredential.INITIATE_AND_ACCEPT(0)
    • GSSCredential.INITIATE_ONLY(1)
    • GSSCredential.ACCEPT_ONLY(2)

7.3.12. equals ⭐

public boolean equals(Object another)

This tests whether this GSSCredential refers to the same entity as the object you pass in 🙈 Both credentials MUST be acquired over the same mechanisms AND MUST refer to the same principal, so no shortcuts. It returns "true" if the two GSSCredentials refer to the same entity, or "false" otherwise.

ngl this is a super important one: (Note that the Java language specification [JLS] requires that two objects that are equal according to the equals(Object) method MUST return the same integer result when the hashCode() method is called on them.) Yes, the hashCode() result has to match too, no exceptions 💀

Parameters:

  • another: Another GSSCredential object for comparison.

7.3.13. Example Code 🎀

This example shows how to create a GSSCredential for a specific entity, query its fields, and release it when you're done with it. Let's see it in action 💖

<CODE BEGINS>
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.INDEFINITE_LIFETIME,
           (Oid[])null,
           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();
<CODE ENDS>

7.4. public interface GSSContext ✨

This interface wraps up the GSS-API security context and gives you the security services (wrap, unwrap, getMIC, and verifyMIC) that are available over the context 🦋 Security contexts get established between peers using credentials you acquired locally. Multiple contexts can exist at the same time between a pair of peers, using the same or different sets of credentials, so you can be super flexible about it.

GSS-API works independently of the underlying transport protocol, which means it depends on the calling application to transport its tokens between peers 🚀

Before the context establishment phase starts, the context initiator can ask for specific characteristics it wants from the established context. You set those using the set methods 🍓 Once the context is established, you check what characteristics and services the context actually offers by using the query methods.

The context establishment phase kicks off with the first call to the init method by the context initiator 🧚 During this phase, the initSecContext and acceptSecContext methods produce GSS-API authentication tokens, and the calling application needs to send those to its peer. If an error happens at any point, an exception gets thrown and the code jumps into a catch block, where the exception may contain an output token that should be sent to the peer for debugging or informational purposes. If there's no error, the normal flow keeps going, and the application can call the isEstablished() method. If that method returns "false", it means a token is needed from the peer before the context establishment phase can continue. A return value of "true" signals that the local end of the context is established. That might still mean a token needs to go to the peer, if GSS-API produced one 💌 Also, during the context establishment phase, you can call isProtReady() to find out whether the context can be used for the per-message operations. This lets applications use per-message operations on contexts that aren't fully established yet, which is honestly so clever 😭

Once the context has been established, or isProtReady() returns "true", you can call the query routines to find out the actual characteristics and services of the established context ✨ The application can also start using the per-message methods of wrap and getMIC to get cryptographic operations on application-supplied data.

When the context is no longer needed, the application SHOULD call dispose to release any system resources the context may be using 🧹

7.4.1. Static Constants 💜

public static final int DEFAULT_LIFETIME

This is a lifetime constant that represents the default context lifetime. The value of this constant is 0.

public static final int INDEFINITE_LIFETIME

This is a lifetime constant that represents indefinite context lifetime. Its value is the maximum integer value in Java -- Integer.MAX_VALUE 👑

7.4.2. initSecContext 🌈

public byte[] initSecContext(byte[] inputBuf, int offset, int len)
              throws GSSException

The context initiator calls this to kick off the context creation process 🎀 This method MAY return an output token that the application will need to send to the peer for processing by the accept call. The application can call isEstablished() to find out whether the context establishment phase is complete for this peer. A return value of "false" from isEstablished() means more tokens are expected to be supplied to the initSecContext() method. It's possible that initSecContext() returns a token for the peer and isEstablished() returns "true" as well. That means the token needs to be sent to the peer, but the local end of the context is now fully established 🦋

Once context establishment is done, you can check the available context options through the get methods.

A GSSException gets thrown if the call fails. Users SHOULD call its getOutputToken() method to find out if there's a token that can be sent to the acceptor to communicate the reason for the error 🙈

Parameters:

  • inputBuf: Token generated by the peer. This parameter is ignored on the first call.
  • offset: The offset within the inputBuf where the token begins.
  • len: The length of the token within the inputBuf (starting at the offset).

7.4.3. acceptSecContext 🫶

public byte[] acceptSecContext(byte[] inTok, int offset, int len)
           throws GSSException

The context acceptor calls this when it receives a token from the peer 💖

This method MAY return an output token that the application will need to send to the peer for further processing by the init call.

A "null" return value means no token needs to be sent to the peer. The application can call isEstablished() to find out whether the context establishment phase is complete for this peer. A return value of "false" from isEstablished() means more tokens are expected to be supplied to this method.

It's possible that acceptSecContext() returns a token for the peer and isEstablished() returns "true" as well. That means the token needs to be sent to the peer, but the local end of the context is now fully established 🌸

Once context establishment is complete, you can check the available context options through the get methods.

A GSSException gets thrown if the call fails. Users SHOULD call its getOutputToken() method to find out if there's a token that can be sent to the initiator to communicate the reason for the error 💀

Parameters:

  • inTok: Token generated by the peer.
  • offset: The offset within the inTok where the token begins.
  • len: The length of the token within the inTok (starting at the offset).

7.4.4. isEstablished 🍓

public boolean isEstablished()

This is 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. It's meant to be called after a call to initSecContext() or acceptSecContext() when no GSSException is thrown.

7.4.5. dispose 🧚

public void dispose() throws GSSException

This releases any system resources and cryptographic information stored in the context object 🧼 It also invalidates the context, so once you call it, that context is done, no cap.

7.4.6. getWrapSizeLimit 🎀

public int getWrapSizeLimit(int qop, boolean confReq,
              int maxTokenSize) throws GSSException

okay so this one hands you the biggest message you can feed into wrap, using the same confReq and qop, while keeping the output token at or under maxTokenSize bytes 💖 It's giving precision!

This is made for apps that talk over protocols with a max message size 🧚 It lets you chop your message into pieces before you protect it, which is so smart ngl.

Implementations are RECOMMENDED but not required to detect invalid QOP values when getWrapSizeLimit gets called 🙈 This routine only promises a maximum message size. It does not promise that any specific QOP value will be available for protecting messages 💅

Finishing this call successfully doesn't guarantee that wrap will actually protect a message of the computed length 😭 That depends on system resources at the moment wrap runs. BUT if the implementation has its own upper limit on message length for wrap, the implementation SHOULD NOT return a number bigger than that limit 🔥

Parameters:

qop                 Indicates the level of protection wrap will be
                    asked to provide.

confReq             Indicates if wrap will be asked to provide
                    privacy service.

maxTokenSize        The desired maximum size of the token emitted by
                    wrap.

7.4.7. wrap 🦋

public byte[] wrap(byte[] inBuf, int offset, int len,
                   MessageProp msgProp) throws GSSException

wrap is where per-message security services happen over your established context ✨ It hands back a token with a cryptographic MIC, and it MAY optionally encrypt the inBuf you gave it. The returned byte array holds both the MIC and the message, so it's all in one bag 👜

You create the MessageProp object yourself. You use it to pick a QOP value, which chooses the crypto algorithms, and to ask for a privacy service that may encrypt the message 🌸 The mechanism might not be able to give you privacy, though. It writes the privacy service it actually provides back into this MessageProp object, and the caller SHOULD check that object after wrap returns 👀 If the mechanism can't give the requested QOP, 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 application is in charge of sending the token to the peer 📮

Parameters:

inBuf               Application data to be protected.

offset              The offset within the inBuf where the data
                    begins.

len                 The length of the data within the inBuf (starting
                    at the offset).

msgProp             Instance of MessageProp that is used by the
                    application to set the desired QOP and privacy
                    state.  Set the desired QOP to 0 to request the
                    default QOP.  Upon return from this method, this
                    object will contain the actual privacy state that
                    was applied to the message by the underlying
                    mechanism.

7.4.8. unwrap 🌈

public byte[] unwrap(byte[] inBuf, int offset, int len,
                     MessageProp msgProp) throws GSSException

This is the peer's side 🫶 The peer app uses it to process tokens made by wrap. It gives back the message the other side passed to wrap, and it checks the embedded MIC while doing it 🔍

You make the MessageProp object yourself, and the mechanism uses it to tell you stuff like the QOP, whether confidentiality got applied, and other extra message state info 💜

Since some app protocols want wrap tokens for "secure framing," implementations SHOULD support wrapping and unwrapping zero-length messages ✨

Parameters:

inBuf               GSS-API wrap token received from peer.

offset              The offset within the inBuf where the token
                    begins.

len                 The length of the token within the inBuf
                    (starting at the offset).

msgProp             Upon return from the method, this object will
                    contain the applied QOP, the privacy state of the
                    message, 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.

7.4.9. 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 packs the user message inside the token, but getMIC only returns the MIC in the output token. So it's lighter, but nothing's hidden 🫣

Heads up: privacy can ONLY be applied through the wrap call 💀 (Fine, I'll say it in lowercase: privacy can only be applied through the wrap call.)

Since some app protocols want getMIC tokens for "secure framing," implementations SHOULD support deriving MICs from zero-length messages 🧚

Parameters:

inMsg               Message over which to generate MIC.

offset              The offset within the inMsg where the token
                    begins.

len                 The length of the token within the inMsg
                    (starting at the offset).

msgProp             Instance of MessageProp that is used by the
                    application to set the desired QOP.  Set the
                    desired QOP to 0 in msgProp to request the
                    default QOP.  Alternatively, pass in "null" for
                    msgProp to request default QOP.

7.4.10. verifyMIC 💅

public void verifyMIC(byte[] inTok, int tokOffset, int tokLen,
                      byte[] inMsg, int msgOffset, int msgLen,
                      MessageProp msgProp) throws GSSException

verifyMIC checks the cryptographic MIC, which is in the token parameter, against the message you gave it 🔐 Main character energy, honestly.

The MessageProp object is made by the app, and the mechanism uses it to send info back to you, like the QOP showing how strong the protection was, plus other extra message state info 🌸

Since some app protocols want getMIC tokens for "secure framing," implementations SHOULD support calculating and verifying MICs over zero-length messages 💖

Parameters:

inTok               Token generated by peer's getMIC method.

tokOffset           The offset within the inTok where the token
                    begins.

tokLen              The length of the token within the inTok
                    (starting at the offset).

inMsg               Application message over which to verify the
                    cryptographic MIC.

msgOffset           The offset within the inMsg where the message
                    begins.

msgLen              The length of the message within the inMsg
                    (starting at the offset).

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.11. export 🧚‍♀️

public byte[] export() throws GSSException

export is for sharing work between processes 👯‍♀️ The usual setup is a context acceptor: one process takes incoming connection requests and accepts security contexts, then passes the established context to one or more other processes that do the message exchanging. Sisters sharing the bag 💼

This method deactivates the security context and makes an inter-process token. When that token goes into the byte array constructor of the GSSContext interface in another process, it re-activates the context there ✨ Only one instance of a given context can be active at once. If the exporter tries to use the exported context afterwards, it fails 🙅‍♀️

The implementation MAY restrict which processes can import the inter-process token, either because of local security policy or because of implementation choices. For example, some implementations only let contexts move between processes running under the same account, or processes in the same process group 🔒

The inter-process token MAY contain security-sensitive stuff, like cryptographic keys 🔑 Mechanisms are encouraged to either keep that stuff out of inter-process tokens or encrypt the token before handing it back to the app. In a typical GSS-API implementation, though, that might not be possible 😬 So the application MUST take care to protect the inter-process token, and make sure any process the token gets transferred to is trustworthy. This part is SERIOUS, bestie 🚨


7.4.12. requestMutualAuth 🎀

public void requestMutualAuth(boolean state) throws GSSException

This sets the request state of the mutual authentication flag for the context 🤝 It only works before the context creation process begins, and only for the initiator 🏁

Parameters:

state               Boolean representing if mutual authentication
                    should be requested during context establishment.

7.4.13. requestReplayDet 🌸

public void requestReplayDet(boolean state) throws GSSException

This sets the request state of the replay detection service for the context 🔁 Same rules as before: 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.14. requestSequenceDet 🧚

public void requestSequenceDet(boolean state) throws GSSException

This sets the request state for the sequence-checking service of the context 📋 Again, it's only valid before context creation begins, and only for the initiator 👑

Parameters:

state               Boolean representing if sequence detection is
                    desired over the established context.

7.4.15. requestCredDeleg 💜

public void requestCredDeleg(boolean state) throws GSSException

This sets the request state for the credential delegation flag for the context 🪪 Only valid before context creation begins, and only for the initiator. Iconic behavior, tbh ✨

Parameters:

state               Boolean representing if credential delegation is
                    desired.

7.4.16. requestAnonymity 🫥

public void requestAnonymity(boolean state) throws GSSException

This requests anonymous support over the context 🕶️ It's only valid before the context creation process begins, and only for the initiator. Lowkey the most mysterious setter in the whole list 🌙

Parameters:

state               Boolean representing if anonymity support is
                    requested.

7.4.17. requestConf ✨

public void requestConf(boolean state) throws GSSException

okay so this is how you ask for confidentiality service over the context 🔒💖 it's only valid before the context creation process starts, and only the initiator can call it bestie

Parameters:

  • state: a boolean saying whether confidentiality services should be requested for the context

7.4.18. requestInteg ✨

public void requestInteg(boolean state) throws GSSException

this one is the integrity version of the same vibe 🛡️ asking for integrity services over the context. same rules: only before context creation begins, and only for the initiator 🫶

Parameters:

  • state: a boolean saying whether integrity services should be requested for the context

7.4.19. requestLifetime ✨

public void requestLifetime(int lifetime) throws GSSException

this sets how long you want the context to last, in seconds ⏳ again it's only valid before context creation starts, and only for the initiator. if you want forever-ish, use GSSContext.INDEFINITE_LIFETIME, or GSSContext.DEFAULT_LIFETIME if you just want the default 🌸

Parameters:

  • lifetime: the desired context lifetime, in seconds

7.4.20. setChannelBinding ✨

public void setChannelBinding(ChannelBinding cb) throws GSSException

this is where you set the channel bindings that get used during context establishment 🧚 and yep, it's only valid before the context creation process begins

Parameters:

  • cb: the channel bindings to be used

7.4.21. getCredDelegState ✨

public boolean getCredDelegState()

this one tells you the state of the delegated credentials for the context 👀 if you ask before establishment is done, or while isProtReady is "false", you get the desired state back. otherwise you get the actual state over the established context. main character energy of honesty 💅

7.4.22. getMutualAuthState ✨

public boolean getMutualAuthState()

returns the state of the mutual authentication option for the context 🤝 same deal as before: before establishment completes, or when isProtReady returns "false", it gives you the desired state. after that, it's the actual state over the established context ✨

7.4.23. getReplayDetState ✨

public boolean getReplayDetState()

this gives you the state of the replay detection option for the context 🔁 before establishment completes, or when isProtReady returns "false", you get the desired state. otherwise it's the actual state over the established context 💜

7.4.24. getSequenceDetState ✨

public boolean getSequenceDetState()

returns the state of the sequence detection option for the context 🧾 the timing rules are the same as the other get methods: desired state before establishment completes (or while isProtReady is "false"), actual state over the established context after that 🌈

7.4.25. getAnonymityState ✨

public boolean getAnonymityState()

returns "true" if this is an anonymous context 🎭 before establishment completes, or when isProtReady returns "false", it returns the desired state. otherwise, it tells you the actual state over the established context 🙈

7.4.26. isTransferable ✨

public boolean isTransferable() throws GSSException

returns "true" if the context can be transferred to other processes using the export method 🦋 this call is only valid on fully established contexts, so no peeking early bestie

7.4.27. isProtReady ✨

public boolean isProtReady()

returns "true" if per-message operations can be applied over the context 💖 some mechanisms let you do per-message operations even before the context is fully established, which is lowkey iconic. this also tells you that the get methods will now return the actual context state characteristics instead of the desired ones 🧚

7.4.28. getConfState ✨

public boolean getConfState()

returns the confidentiality service state over the context 🔒 before establishment completes, or while isProtReady returns "false", you get the desired state. otherwise you get the actual state over the established context 🫶

7.4.29. getIntegState ✨

public boolean getIntegState()

returns the integrity service state over the context 🛡️ before establishment completes, or while isProtReady returns "false", it gives the desired state. after that, it's the actual state over the established context 🌸

7.4.30. getLifetime ✨

public int getLifetime()

returns the context lifetime in seconds ⏳ before establishment completes, or while isProtReady returns "false", you get the desired lifetime. otherwise it tells you the remaining lifetime for the context. time keeps ticking, periodt 🕰️

7.4.31. getSrcName ✨

public GSSName getSrcName() throws GSSException

returns the name of the context initiator 👑 this call is only valid after the context is fully established, or once isProtReady returns "true". and the best part: it is guaranteed to return an MN 💅

7.4.32. getTargName ✨

public GSSName getTargName() throws GSSException

returns the name of the context target (acceptor) 🎯 same rules as the initiator name: only valid after the context is fully established, or once isProtReady returns "true". and yes, it is guaranteed to return an MN ✨

7.4.33. getMech ✨

public Oid getMech() throws GSSException

returns the mechanism OID for this context 🧩 this method MAY be called before the context is fully established, but the mechanism returned MAY change on successive calls in a negotiated mechanism case. so it's giving "check again later" energy 🦋

7.4.34. getDelegCred ✨

public GSSCredential getDelegCred() throws GSSException

returns the delegated credential object on the acceptor's side 🎀 if you want to check whether delegated credentials are available, call getDelegCredState first. and this call is only valid on fully established contexts 💜

7.4.35. isInitiator ✨

public boolean isInitiator() throws GSSException

returns "true" if this is the initiator of the context 🚀 it's only valid after the context creation process has started, so don't come for it too early 🙈

7.4.36. Example Code ✨

okay here's the example code for the initiating peer, and it's so good 🔥 it shows off using the GSSContext interface: creating the object, setting the desired flags, 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());

    // If the exception contains an output token,
    // it should be sent to the acceptor.
    byte[] outTok = e.getOutputToken();
    if (outTok != null) {
        sendToken(outTok);
    }

    return;
}

// 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.5. public class MessageProp ✨

this is a utility class that the per-message GSSContext methods use to pass along per-message properties 📦

when you use it with the GSSContext interface's wrap and getMIC methods, an instance of this class says which QOP you want and whether you want confidentiality services applied to the caller-supplied data (wrap only). to ask for the default QOP, use 0 for the QOP value. a QOP is an integer value defined by a mechanism 🔢

when you use it with the unwrap and verifyMIC methods, the instance tells you the applied QOP and confidentiality services over the supplied message. for verifyMIC, the confidentiality state is always "false" 🙃 and once these methods return, this object also holds any supplementary status values for the processed token. those values can flag old tokens, out of sequence tokens, gap tokens, or duplicate tokens 🚨

7.5.1. Constructors ✨

public MessageProp(boolean privState)

public MessageProp(int qop, boolean privState)

the first constructor sets the QOP to 0, which means the default QOP is requested 🌷

Parameters for the first constructor:

  • privState: the desired privacy state. "true" means privacy, and "false" means integrity only

the second constructor sets both the QOP and the privacy state 💅

Parameters for the second constructor:

  • qop: the desired QOP. use 0 to request a default QOP
  • privState: the desired privacy state. "true" means privacy, and "false" means integrity only

7.5.2. getQOP ✨

public int getQOP()

retrieves the QOP value 🎁

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, "null" gets returned 🙈

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 is a duplicate of an earlier token 👯

7.5.9. isOldToken ✨

public boolean isOldToken()

returns "true" if the token's validity period has expired ⌛💜

7.5.10. isUnseqToken 🦋

Okay bestie, this one is a quick check. The method is:

public boolean isUnseqToken()

It returns "true" if a later token has already been processed. 🧚

7.5.11. isGapToken 🌸

Next up is the gap check:

public boolean isGapToken()

It returns "true" if an expected per-message token was not received. Missing a piece? This is how you spot it. 🕵️‍♀️✨

7.5.12. setSupplementaryStates 💅

This one is the setter for the supplementary information flags and the minor status in MessageProp, and it's giving "behind the scenes." The application doesn't call it. The GSS implementation does, to pass this info back to whoever called a per-message context method. 🎀

public void setSupplementaryStates(boolean duplicate,
               boolean old, boolean unseq, boolean gap,
               int minorStatus, String minorString)

Here's what each parameter does:

  • 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, channel bindings are giving main character energy. GSS-API lets the caller pass in channel-binding information, which strengthens the peer entity authentication that happens during context establishment. Basically, the caller can tie the security context to stuff like addresses or app-specific data. Lowkey iconic. 👑

The caller who starts the security context MUST work out the right channel-binding values to set in the GSSContext object. The acceptor MUST provide an identical binding so it can check that the tokens it gets have the correct channel-related details. 🤝

Using channel bindings is OPTIONAL in GSS-API. Also, channel-binding info can get sent inside context establishment tokens, so applications SHOULD therefore not use confidential data as channel-binding components. Don't put your secrets in there, bestie! 🙈

7.6.1. Constructors 🧩

Here are the two ways to make a ChannelBinding object:

public ChannelBinding(InetAddress initAddr, InetAddress acceptAddr,
                      byte[] appData)

This one creates a ChannelBinding object with address info and data that you supply. You can pass "null" for any field you don't want to set. 🫶

  • initAddr: the address of the context initiator. Pass "null" if you don't want to set this value. 📍
  • acceptAddr: the address of the context acceptor. Pass "null" if you don't want to set this value. 📍
  • appData: application-supplied data to use as part of the channel bindings. Pass "null" if you don't want to set this value. 💾
public ChannelBinding(byte[] appData)

This one creates a ChannelBinding object with no addressing info, just your app data.

  • appData: application-supplied data to use as part of the channel bindings. 💜

7.6.2. getInitiatorAddress 🌷

public InetAddress getInitiatorAddress()

This returns the initiator's address for this channel binding. If the address was never set, you get "null". 🫥

7.6.3. getAcceptorAddress 🌷

public InetAddress getAcceptorAddress()

This returns the acceptor's address for this channel binding. If the address was never set, you get "null". 🫥

7.6.4. getApplicationData 🍓

public byte[] getApplicationData()

This returns the app data being used as part of the ChannelBinding. If no app data was ever specified for the channel binding, you get "null". 📦

7.6.5. equals ⚖️

public boolean equals(Object obj)

This returns "true" if two channel bindings match. Quick side note from the Java spec, which is very serious about this: two objects that are equal according to the equals(Object) method MUST return the same integer result when the hashCode() method is called on them. Respect the rules, babe. 🤓

  • obj: another channel binding to compare with. 🔍

7.7. public class Oid 🧬

An Oid is a Universal OID, and this class holds it along with the operations you can do on it. OIDs are hierarchical, globally interpretable identifiers, and GSS-API uses them to name mechanisms and name formats. 🌈

The structure and encoding of OIDs are defined in ISOIEC-8824 [ISOIEC-8824] and ISOIEC-8825 [ISOIEC-8825]. For example, the OID for the Kerberos v5 mechanism looks like "1.2.840.113554.1.2.2". Iconic, right? 👑

The GSSName name class has public static Oid objects for the standard name types defined in GSS-API. Those are already baked in for you. 🎁

7.7.1. Constructors 🧱

There are a few ways to build an Oid:

public Oid(String strOid) throws GSSException

This creates an Oid from a string of its integer components, like "1.2.840.113554.1.2.2".

  • strOid: the string representation of the OID. 🔤
public Oid(InputStream derOid) throws GSSException

This creates an Oid from its DER encoding. That means the full encoding, tag and length included. The structure and encoding of OIDs are defined in ISOIEC-8824 [ISOIEC-8824] and ISOIEC-8825 [ISOIEC-8825]. This method does the exact same thing as its byte array counterpart. 🔁

  • derOid: a stream containing the DER-encoded OID. 🌊
public Oid(byte[] derOid) throws GSSException

This creates an Oid from its DER encoding too, again meaning the full encoding with tag and length. The structure and encoding of OIDs are defined in ISOIEC-8824 [ISOIEC-8824] and ISOIEC-8825 [ISOIEC-8825]. This is functionally identical to the InputStream version. 💖

  • derOid: a byte array storing a DER-encoded OID. 📦

7.7.2. toString 💬

public String toString()

This returns a string of the OID's integer components in dot-separated notation, like "1.2.840.113554.1.2.2". ✨

7.7.3. equals 🪞

public boolean equals(Object Obj)

This returns "true" if the two Oid objects represent the same OID value. Heads up from the Java language spec [JLS]: two objects that are equal according to the equals(Object) method MUST return the same integer result when the hashCode() method is called on them. Same rule, same vibe. 🫶

  • obj: another Oid object to compare with. 🔍

7.7.4. getDER 📜

public byte[] getDER()

This returns the full ASN.1 DER encoding for this Oid object, tag and length included. 🧾

7.7.5. containedIn 🔎

public boolean containedIn(Oid[] oids)

This is a utility method that checks whether an Oid object is inside the Oid array you hand it. 🕵️‍♀️

  • 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, including the mechanism-specific ones. It MAY contain both the major and minor GSS-API status codes. The mechanism implementors are responsible for setting the right minor status codes when they throw it. Besides handing the numeric error code(s) to the caller, this class also maps those numbers to readable text. 📖

It MAY also include an output token that SHOULD be sent to the peer. For example, if an initSecContext call fails because of a fatal error, the mechanism MAY define an error token that SHOULD be sent to the peer for debugging or informational purposes. Every Java GSS-API method is declared as throwing this exception, so you'll see it everywhere. 🫠

All implementations are encouraged to use Java internationalization techniques to give local translations of the message strings. Let's make it global, babes! 🌍🌈

7.8.1. Static Constants 🏷️

All valid major GSS-API error code values are declared as constants in this class. Here's the full lineup, with each value listed right after its name. Yes, every single one is here. 💅

  • public static final int BAD_BINDINGS: Channel-bindings mismatch error. The value of this constant is 1. 🔗
  • public static final int BAD_MECH: Unsupported mechanism requested error. The value of this constant is 2. 🚫
  • public static final int BAD_NAME: Invalid name provided error. The value of this constant is 3. 🪪
  • public static final int BAD_NAMETYPE: Name of unsupported type provided error. The value of this constant is 4. 🏷️
  • public static final int BAD_STATUS: Invalid status code error. This is the default status value. The value of this constant is 5. 🧃
  • public static final int BAD_MIC: Token had invalid integrity check error. The value of this constant is 6. 🛡️
  • public static final int CONTEXT_EXPIRED: Specified security context expired error. The value of this constant is 7. ⏰
  • public static final int CREDENTIALS_EXPIRED: Expired credentials detected error. The value of this constant is 8. 🪪⏳
  • public static final int DEFECTIVE_CREDENTIAL: Defective credential error. The value of this constant is 9. 💔
  • public static final int DEFECTIVE_TOKEN: Defective token error. The value of this constant is 10. 🫤
  • public static final int FAILURE: General failure, unspecified at GSS-API level. The value of this constant is 11. 💥
  • public static final int NO_CONTEXT: Invalid security context error. The value of this constant is 12. 👻
  • public static final int NO_CRED: Invalid credentials error. The value of this constant is 13. 🙅‍♀️
  • public static final int BAD_QOP: Unsupported QOP value error. The value of this constant is 14. 🔐
  • public static final int UNAUTHORIZED: Operation unauthorized error. The value of this constant is 15. 🚪
  • public static final int UNAVAILABLE: Operation unavailable error. The value of this constant is 16. 📵
  • public static final int DUPLICATE_ELEMENT: Duplicate credential element requested error. The value of this constant is 17. 👯‍♀️
  • public static final int NAME_NOT_MN: Name contains multi-mechanism elements error. The value of this constant 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 then it's a fatal error. Non-fatal supplementary codes are indicated via the MessageProp object. The value of this constant 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 then it's a fatal error. Non-fatal supplementary codes are indicated via the MessageProp object. The value of this constant 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 then it's a fatal error. Non-fatal supplementary codes are indicated via the MessageProp object. The value of this constant 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 then it's a fatal error. Non-fatal supplementary codes are indicated via the MessageProp object. The value of this constant is 22. 🕳️

7.8.2. Constructors ✨

okay so GSSException has three constructors 🧚 each one is a different flavour, and they all go through the same general-purpose one under the hood 💅

public GSSException(int majorCode)

Creates a GSSException object with a specified major code. 💖

Calling this constructor is equivalent to calling GSSException(majorCode, null, 0, null, null). 🌸

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 is the one you use when the exception comes from the security mechanism, so you can pass in both the GSS code and the mechanism code. 🦋

Calling this constructor is equivalent to calling GSSException(majorCode, null, minorCode, minorString, null). ✨

public GSSException(int majorCode, String majorString,
                    int minorCode, String minorString,
                    byte[] outputToken)

Creates a GSSException object with the specified major code, major code textual explanation, minor code, minor code textual explanation, and an output token. This is the general-purpose constructor, so it can make any type of GSSException at all 👑

Parameters:

  • majorCode: The GSS error code causing this exception to be thrown.
  • majorString: The textual explanation of the GSS error code. If null is provided, a default explanation that matches the majorCode will be set. 🎀
  • minorCode: The mechanism error code causing this exception to be thrown. Can be 0 if no mechanism error code is available.
  • minorString: The textual explanation of the mechanism error code. Can be null if no textual explanation is available.
  • outputToken: The output token that SHOULD be sent to the peer. Can be null if no such token is available. It MUST NOT be an empty array. When provided, the array will be cloned to protect against subsequent modifications. 🫶

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. The value of 0 indicates that the mechanism error code is not set. 🧚

7.8.5. getMajorString

public String getMajorString()

Returns a string explaining the GSS major error code causing this exception to be thrown. 💜

7.8.6. getMinorString

public String getMinorString()

Returns a string explaining the mechanism-specific error code. "null" will be returned when no string explaining the mechanism error code has been set. 🙈

7.8.7. getOutputToken

public byte[] getOutputToken

Returns the output token in a new byte array.

If the method (for example, GSSContext#initSecContext) that throws this GSSException needs to generate an output token that SHOULD be sent to the peer, that token will be stored in this GSSException and can be retrieved with this method. 📬

The return value MUST be null if no such token is generated. It MUST NOT be an empty byte array. 💀

7.8.8. setMinor

public void setMinor(int minorCode, String message)

Used internally by the GSS-API implementation and the underlying mechanisms to set the minor code and its textual representation. 🔧

Parameters:

  • minorCode: The mechanism-specific error code.
  • message: A textual explanation of the mechanism error code.

7.8.9. toString

public String toString()

Returns a textual representation of both the major and minor status codes. 🌈

7.8.10. getMessage

public String getMessage()

Returns a detailed message of this exception. Overrides Throwable.getMessage. It is customary in Java to use this method to obtain exception information. this is literally the method everyone reaches for first 🥹


8. Sample Applications

8.1. Simple GSS Context Initiator

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 RFC 8353.
 *
 *
 * 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 " +
                 clientCred.getName().toString());
           print("Credential lifetime (sec)=" +
                 clientCred.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, clientCred,
                  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();
           }

           peer = context.getTargName();
           print("Security context established with " + peer +
                 " using underlying mechanism " + mech.toString());
       } catch (GSSException e) {
            print("GSS-API error during context establishment: "
                  + e.getMessage());

            // If the exception contains an output token,
            // it should be sent to the acceptor.
            byte[] outTok = e.getOutputToken();
            if (outTok != null) {
                writeGSSToken(outTok);
            }
            ...
            ...
       }
       ...
       ...
   }

   /**
    * 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(true);

       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.getTargName();

           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 so this one is the server side 🧚. The acceptor is the one who receives the handshake and decides whether to trust it. Here's the partial sketch, copied exactly:

<CODE BEGINS>
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() throws Exception {
        ...
        ...
        mgr = GSSManager.getInstance();

        name = mgr.createName(serviceName,
                  GSSName.NT_HOSTBASED_SERVICE);

        cred = mgr.createCredential(name,
                  GSSCredential.INDEFINITE_LIFETIME,
                  (Oid[])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;


            // Container for multiple input-output arguments to
            // and from the per-message routines
            // (i.e., wrap/unwrap).
            MessageProp supplInfo = new MessageProp(true);

            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());

                // 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()

                // If the exception contains an output token,
                // it should be sent to the initiator.
                byte[] outTok = e.getOutputToken();
                if (outTok != null) {
                    writeGSSToken(outTok);
                }
                print("Abandoning security context.");
                ...
                ...
            }
            ...
            ...
        } // end of run method in ServerThread

    } // end of inner class ServerThread

    ...
    ...

} // end of class SimpleServer
<CODE ENDS>

9. Security Considerations 🔥

okay bestie, this part is SERIOUS, so read it carefully 💀. The Java language security model lets platform providers set up policy-based, fine-grained access control over any resource an application wants. When a Java security manager is in play (think applets running in browsers, but it's not limited to that), the application code lives in a sandbox by default 🔒.

Administrators of the platform JRE decide what permissions, if any, get handed out to code from different codebases. So the admin has to know about any special requirements the GSS provider might have for system resources. For example, a Kerberos provider might want to open a network connection to the Key Distribution Center (KDC) to grab initial credentials. That wouldn't be allowed inside the sandbox unless the administrator had granted the permissions for it. And note that all this granting and checking of permissions happens transparently to the application and is outside the scope of this document 🫶.

The Java language also 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 changes would be visible across the entire Java Virtual Machine (JVM), which means they'd affect all code running in it. So this operation isn't available in the sandbox and needs special permissions 🚫. That means that when a GSS application has special needs met by a particular security provider, it has two choices:

  1. 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".)
  2. 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. IANA Considerations 🌈

No IANA actions for this one, bestie. There's nothing to register or update here 🧚.


11. Changes since RFC 5653 💜

Here's everything that's different from RFC 5653 🦋. There are two big changes:

  1. New error token embedded in GSSException

There's a design flaw in the initSecContext and acceptSecContext methods of the GSSContext class, which were defined in "Generic Security Service API Version 2: Java Bindings Update" [RFC5653].

Those methods could either return a token (possibly null, if no more tokens are needed) when the call succeeds, or throw a GSSException if there's a failure, but not both. The C-bindings of GSS-API [RFC2744], on the other hand, CAN do both. A call to the GSS_Init_sec_context() function can return a major status code and, at the same time, fill in the output_token argument if there is one 😭.

Without the ability to emit an error token when there's a failure, a Java application has no way to tell the other side what went wrong. For example, a "reject" NegTokenResp token can never be transmitted for the SPNEGO mechanism [RFC4178] 💀.

A Java method can never return a value and throw an exception at the same time, so the fix is to embed the error token inside the exception so the caller has a chance to retrieve it ✨. This update adds a new GSSException constructor that includes this token inside a GSSException object, plus a getOutputToken() method to retrieve it. The specifications for the initSecContext and acceptSecContext methods are updated to describe the new behaviour, and various examples are updated too.

New JGSS programs SHOULD make use of this new feature, but it is not mandatory 🎀. A program that intends to run with both old and new GSS Java bindings can use reflection to check whether this new method is available and call it accordingly.

  1. Removing Stream-Based GSSContext Methods

The overloaded GSSContext methods that use input and output streams to convey authentication and per-message GSS-API tokens, as described in Section 5.15 of RFC 5653 [RFC5653], are removed in this update 🗑️. The reason is that the wire protocol should be defined by an application and not by a library.

It's also impossible to implement these methods correctly when the token has no self-framing (where the end can't be determined), or when the library has no knowledge of the token format (for example, as a bridge talking to another GSS library) 🙈.

These methods include initSecContext (Section 7.4.5 of RFC 5653 [RFC5653]), acceptSecContext (Section 7.4.9 of RFC 5653 [RFC5653]), wrap (Section 7.4.15 of RFC 5653 [RFC5653]), unwrap (Section 7.4.17 of RFC 5653 [RFC5653]), getMIC (Section 7.4.19 of RFC 5653 [RFC5653]), and verifyMIC (Section 7.4.21 of RFC 5653 [RFC5653]) 💅.

12. Changes since RFC 2853 💅

okay so, bestie, here's everything that's different from RFC 2853 🎀 the document lists three changes:

  1. 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 it 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. ✨
  2. 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.1. The original ordering of these values from Section 6.3.2 of RFC 2853 [RFC2853] is maintained. 🧚
  3. 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.1 to be consistent with the C-bindings in RFC 2744 [RFC2744]. 👑

13. References 📚

13.1. Normative References 🦋

13.2. Informative References 🌸

  • [ISOIEC-8824] International Organization for Standardization, "Information technology -- Abstract Syntax Notation One (ASN.1): Specification of basic notation", ISO/IEC 8824-1:2014, November 2015, https://www.iso.org/standard/68350.html.
  • [ISOIEC-8825] International Organization for Standardization, "Information technology -- ASN.1 encoding rules: Specification of Basic Encoding Rules (BER), Canonical Encoding Rules (CER) and Distinguished Encoding Rules (DER)", ISO/IEC 8825-1:2015, November 2015, https://www.iso.org/standard/68345.html.
  • [JLS] Gosling, J., Joy, B., Steele, G., Bracha, G., Buckley, A., and D. Smith, "The Java Language Specification", Java SE 10 Edition, February 2018, https://docs.oracle.com/javase/specs/jls/se10/html/index.html.

Acknowledgments 🫶

we owe a huge thank-you to Mike Eisler, Lin Ling, Ram Marti, Michael Saltz, and the rest of Sun's development team 💖 they gave helpful input, comments, and suggestions that made this doc so much better ✨

and a big hug to Greg Hudson, Benjamin Kaduk, Joe Salowey, and Michael Smith 🌈 they shared so many insightful ideas and suggestions that really shaped this document 👑

Authors' Addresses 🎀

Mayank D. Upadhyay
Google Inc.
1600 Amphitheatre Parkway
Mountain View, CA  94043
United States of America

Email: m.d.upadhyay+ietf@gmail.com


Seema Malkani
ActivIdentity Corp.
6623 Dumbarton Circle
Fremont, California  94555
United States of America

Email: Seema.Malkani@gmail.com


Weijun Wang
Oracle
Building No. 24, Zhongguancun Software Park
Beijing  100193
China

Email: weijun.wang@oracle.com
📦 where this page comes from