RFC 7512

The PKCS #11 URI Scheme

πŸ“… published April 2015, from ietf

The PKCS #11 URI Scheme πŸŽ€

okay bestie, this is RFC 7512 πŸ’–. It's a Standards Track document, so it's the serious, implementers-must-follow kind πŸ”₯. It was written by J. Pechanec and D. Moffat from Oracle Corporation, it's dated April 2015, and it's ISSN 2070-1721 ✨. The title block doesn't list any RFCs it obsoletes or updates, so it's not replacing or patching anything 🧚.

Abstract 🌸

so this memo defines a PKCS #11 Uniform Resource Identifier (URI) Scheme πŸ¦‹. You use it to identify PKCS #11 objects that live inside PKCS #11 tokens, and also to identify PKCS #11 tokens, slots, or libraries πŸ’œ. The whole scheme is built on how PKCS #11 objects, tokens, slots, and libraries already get identified in "PKCS #11 v2.20: Cryptographic Token Interface Standard" πŸ“š.

Status of This Memo πŸŽ€

this is an Internet Standards Track document πŸ’….

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) πŸ‘‘. for more on Internet Standards, check Section 2 of RFC 5741 ✨.

if you want the current status, any errata, or a way to send feedback, go to http://www.rfc-editor.org/info/rfc7512 πŸ”—.

Copyright (c) 2015 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 (http://trustee.ietf.org/license-info) as they stood on the publication date. please actually read those, they spell out your rights and restrictions πŸ™ˆ. and if you pull Code Components out of this document, they have to include Simplified BSD License text, as described in Section 4.e of the Trust Legal Provisions, and they come with no warranty, as described in the Simplified BSD License πŸ’€.

Table of Contents 🧚

1. Introduction ....................................................2
2. PKCS #11 URI Scheme Definition ..................................4
   2.1. PKCS #11 URI Scheme Name ...................................4
   2.2. PKCS #11 URI Scheme Status .................................4
   2.3. PKCS #11 URI Scheme Syntax .................................4
   2.4. PKCS #11 URI Scheme Query Attribute Semantics ..............9
   2.5. PKCS #11 URI Matching Guidelines ..........................11
   2.6. PKCS #11 URI Comparison ...................................12
   2.7. Generating PKCS #11 URIs ..................................14
3. Examples of PKCS #11 URIs ......................................14
4. IANA Considerations ............................................17
   4.1. URI Scheme Registration ...................................17
5. Internationalization Considerations ............................18
6. Security Considerations ........................................18
7. References .....................................................19
   7.1. Normative References ......................................19
   7.2. Informative References ....................................19
Contributors ......................................................20
Authors' Addresses ................................................20

1. Introduction ✨

okay so "PKCS #11 v2.20: Cryptographic Token Interface Standard" [PKCS11] describes an API called Cryptoki, for devices that hold cryptographic information and do cryptographic functions πŸ”. Cryptoki (pronounced "crypto-key" and short for "cryptographic token interface") is object-based and simple πŸ’–. it's all about technology independence (any kind of device can be used) and resource sharing (multiple applications can access multiple devices), and it gives apps one common, logical view of the device: a cryptographic token ✨.

it's really useful when apps or libraries that work with PKCS #11 tokens can accept one common identifier 🫢. with it, consumers can point at an existing PKCS #11 storage object in a token, an existing token itself, a slot, or an existing Cryptoki library (also called a producer, module, or provider) 🧚. storage objects can be a certificate, a data object, or a public, private, or secret key πŸ”‘. this document's URI scheme is how you uniquely identify those objects πŸ¦‹.

the attributes that describe a storage object can include an object label, its type, and its ID 🏷️. the attributes that identify a PKCS #11 token can include a token label, manufacturer name, serial number, and token model πŸ’œ. slots can be identified with a slot ID, description, and manufacturer 🌸. Cryptoki libraries can be identified with a library manufacturer, description, and version 🌈. library attributes may be necessary if more than one Cryptoki library provides a token and/or PKCS #11 objects with the same name πŸ‘€. there's a set of query attributes too, so you're covered ✨.

a PKCS #11 URI can't identify other objects defined in the specification [PKCS11] besides storage objects πŸ™…β€β™€οΈ. for example, a hardware feature and a mechanism can't be identified by a PKCS #11 URI πŸ’€. also, a Cryptoki library doesn't have to provide storage objects at all, and in that case the URI can still be used to identify a specific PKCS #11 token, slot, or an API producer 🫢.

a subset of existing PKCS #11 structure members and object attributes was picked to uniquely identify a storage object, token, slot, or library πŸŽ€. you can use it in a configuration file, on a command line, or in a configuration property of something else πŸ’…. if you ever need a more complex information exchange about PKCS #11 entities, a different means of data marshalling should be chosen for that 🧠.

a PKCS #11 URI is NOT meant for creating new PKCS #11 objects in tokens or for creating PKCS #11 tokens. it's only for identifying and working with existing storage objects, tokens, and slots through the PKCS #11 API, or for identifying Cryptoki libraries themselves πŸ™ˆ.

this URI scheme is designed specifically with a mapping to the PKCS #11 API in mind πŸ’–. it uses the scheme, path, and query components from the "Uniform Resource Identifier (URI): Generic Syntax" [RFC3986] document 🌸. it doesn't use the hierarchical element for a naming authority in the path, because the authority part couldn't be mapped to PKCS #11 API elements πŸ™…β€β™€οΈ. and it doesn't use the fragment component 🚫.

if an application has no access to a producer (or producers) of the PKCS #11 API, the query component module attributes can be used ✨. the PKCS #11 URI consumer can always decide to provide its own user interface to locate and load PKCS #11 API producers πŸ‘‘.

The key words "MUST", "MUST NOT", "REQUIRED", "SHALL", "SHALL NOT", "SHOULD", "SHOULD NOT", "RECOMMENDED", "MAY", and "OPTIONAL" in this document are to be interpreted as described in [RFC2119]. πŸ“Œ

2. PKCS #11 URI Scheme Definition πŸ’…

in accordance with [RFC4395], this section gives you all the info you need to register the PKCS #11 URI scheme πŸ“.

2.1. PKCS #11 URI Scheme Name 🏷️

pkcs11

2.2. PKCS #11 URI Scheme Status βœ…

Permanent

2.3. PKCS #11 URI Scheme Syntax πŸ¦‹

a PKCS #11 URI is a sequence of attribute value pairs separated by a semicolon. they form a one-level path component, and you can optionally add a query πŸ’œ. except for the value of the "id" attribute (more on that later in this section), these attribute value pairs and query components are made entirely of textual data. so they SHOULD all first be encoded as octets using the UTF-8 character encoding [RFC3629], in accordance with Section 2.5 of [RFC3986] 🌈. then, only the octets that don't correspond to characters in the unreserved set, or to permitted characters from the reserved set, SHOULD be percent-encoded 🫢.

the value of the "id" attribute SHOULD NOT be encoded as UTF-8, because it can contain non-textual data. instead, it SHOULD be entirely percent-encoded πŸ™ˆ. see the important caveats in Sections 2.5 and 5 about working with UTF-8 strings that have characters outside the US-ASCII character set ⚠️.

the grammar rules "unreserved" and "pct-encoded" in the PKCS #11 URI scheme definition below are imported from [RFC3986] πŸ“¦. quick special case: according to Appendix A of [RFC3986], a space MUST be percent-encoded 🫧. (heads up, that one's a MUST!)

the PKCS #11 specification puts various limits on attribute values. there's a more restrictive character set for the "serial" attribute, and fixed-size buffers for almost all the others, including the "token", "manufacturer", and "model" attributes πŸ“. the PKCS #11 URI scheme syntax doesn't impose those limits itself πŸ™…β€β™€οΈ. but if a PKCS #11 URI consumer runs into values that the PKCS #11 specification wouldn't accept, it MUST refuse the URI as invalid πŸš«πŸ’€.

a PKCS #11 URI takes the following form (for an explanation of Augmented BNF, see [RFC5234]):

 pk11-URI             = "pkcs11:" pk11-path [ "?" pk11-query ]
 ; Path component and its attributes.  Path may be empty.
 pk11-path            = [ pk11-pattr *(";" pk11-pattr) ]
 pk11-pattr           = pk11-token / pk11-manuf / pk11-serial /
                        pk11-model / pk11-lib-manuf /
                        pk11-lib-ver / pk11-lib-desc /
                        pk11-object / pk11-type / pk11-id /
                        pk11-slot-desc / pk11-slot-manuf /
                        pk11-slot-id / pk11-v-pattr
 ; Query component and its attributes.  Query may be empty.
 pk11-qattr           = pk11-pin-source / pk11-pin-value /
                        pk11-module-name / pk11-module-path /
                        pk11-v-qattr
 pk11-query           = [ pk11-qattr *("&" pk11-qattr) ]
 ; Section 2.2 of [RFC3986] mandates all potentially reserved characters
 ; that do not conflict with actual delimiters of the URI do not have
 ; to be percent-encoded.
 pk11-res-avail       = ":" / "[" / "]" / "@" / "!" / "$" /
                        "'" / "(" / ")" / "*" / "+" / "," / "="
 pk11-path-res-avail  = pk11-res-avail / "&"
 ; "/" and "?" in the query component MAY be unencoded but "&" MUST
 ; be encoded since it functions as a delimiter within the component.
 pk11-query-res-avail = pk11-res-avail / "/" / "?" / "|"
 pk11-pchar           = unreserved / pk11-path-res-avail / pct-encoded
 pk11-qchar           = unreserved / pk11-query-res-avail / pct-encoded
 pk11-token           = "token" "=" *pk11-pchar
 pk11-manuf           = "manufacturer" "=" *pk11-pchar
 pk11-serial          = "serial" "=" *pk11-pchar
 pk11-model           = "model" "=" *pk11-pchar
 pk11-lib-manuf       = "library-manufacturer" "=" *pk11-pchar
 pk11-lib-desc        = "library-description" "=" *pk11-pchar
 pk11-lib-ver         = "library-version" "=" 1*DIGIT [ "." 1*DIGIT ]
 pk11-object          = "object" "=" *pk11-pchar
 pk11-type            = "type" "=" ( "public" / "private" / "cert" /
                        "secret-key" / "data" )
 pk11-id              = "id" "=" *pk11-pchar
 pk11-slot-manuf      = "slot-manufacturer" "=" *pk11-pchar
 pk11-slot-desc       = "slot-description" "=" *pk11-pchar
 pk11-slot-id         = "slot-id" "=" 1*DIGIT
 pk11-pin-source      = "pin-source" "=" *pk11-qchar
 pk11-pin-value       = "pin-value" "=" *pk11-qchar
 pk11-module-name     = "module-name" "=" *pk11-qchar
 pk11-module-path     = "module-path" "=" *pk11-qchar
 pk11-v-attr-nm-char  = ALPHA / DIGIT / "-" / "_"
 ; The permitted value of a vendor-specific attribute is based on
 ; whether the attribute is used in the path or in the query.
 pk11-v-pattr         = 1*pk11-v-attr-nm-char "=" *pk11-pchar
 pk11-v-qattr         = 1*pk11-v-attr-nm-char "=" *pk11-qchar

Okay so the path component is basically where you find the resource itself πŸ—‚οΈ It holds attributes that identify a resource in a one-level hierarchy provided by Cryptoki producers. The query component is the helper squad 🫢 It can hold a few attributes that might be needed to actually retrieve the thing the path points to. Path attributes are separated by the ; character, and query attributes use & as their delimiter. Main character energy for each one, honestly ✨

Both the path and the query components MAY contain vendor-specific attributes πŸ’… Those attribute names MUST NOT clash with existing attribute names, so no copying someone else's name, periodt. Also, per [BCP178], the older habit of starting vendor attributes with an "x-" prefix is now deprecated πŸ™ˆ So if you see it in old URIs, it's giving outdated.

Now the delimiters, which are very picky πŸŽ€ The general / delimiter MUST be percent-encoded in the path component, so that generic URI parsers never chop the path into multiple segments. It MAY be left unencoded in the query component. The ? delimiter MUST be percent-encoded in the path component, because the PKCS #11 URI scheme uses a query component. The # delimiter MUST always be percent-encoded, so generic URI parsers don't think a hash is the start of a fragment identifier 🧚 All the other generic delimiters MAY be used unencoded in a PKCS #11 URI: :, [, ], and @ ✨

Next up is the mapping table between the path attributes and the PKCS #11 API structure members and object attributes 🌸 PKCS #11 URI users might be pretty new to the PKCS #11 spec, so this mapping is a bit of a compromise between matching the spec names exactly and keeping the URI easy to use and understand πŸ¦‹

   +----------------------+---------------------+----------------------+
   | URI component path   | Attribute           | PKCS #11             |
   | attribute name       | represents          | specification        |
   |                      |                     | counterpart          |
   +----------------------+---------------------+----------------------+
   | id                   | key identifier for  | "CKA_ID" object      |
   |                      | object              | attribute            |
   +----------------------+---------------------+----------------------+
   | library-description  | character-string    | "libraryDescription" |
   |                      | description of the  | member of CK_INFO    |
   |                      | library             | structure.  It is a  |
   |                      |                     | UTF-8 string.        |
   +----------------------+---------------------+----------------------+
   | library-manufacturer | ID of the Cryptoki  | "manufacturerID"     |
   |                      | library             | member of the        |
   |                      | manufacturer        | CK_INFO structure.   |
   |                      |                     | It is a UTF-8        |
   |                      |                     | string.              |
   +----------------------+---------------------+----------------------+
   | library-version      | Cryptoki library    | "libraryVersion"     |
   |                      | version number      | member of the        |
   |                      |                     | CK_INFO structure.   |
   +----------------------+---------------------+----------------------+
   | manufacturer         | ID of the token     | "manufacturerID"     |
   |                      | manufacturer        | member of            |
   |                      |                     | CK_TOKEN_INFO        |
   |                      |                     | structure.  It is a  |
   |                      |                     | UTF-8 string.        |
   +----------------------+---------------------+----------------------+
   | model                | token model         | "model" member of    |
   |                      |                     | CK_TOKEN_INFO        |
   |                      |                     | structure.  It is a  |
   |                      |                     | UTF-8 string.        |
   +----------------------+---------------------+----------------------+
   | object               | description (name)  | "CKA_LABEL" object   |
   |                      | of the object       | attribute.  It is a  |
   |                      |                     | UTF-8 string.        |
   +----------------------+---------------------+----------------------+
   | serial               | character-string    | "serialNumber"       |
   |                      | serial number of    | member of            |
   |                      | the token           | CK_TOKEN_INFO        |
   |                      |                     | structure.           |
   +----------------------+---------------------+----------------------+
   | slot-description     | slot description    | "slotDescription"    |
   |                      |                     | member of            |
   |                      |                     | CK_SLOT_INFO         |
   |                      |                     | structure.  It is a  |
   |                      |                     | UTF-8 string.        |
   +----------------------+---------------------+----------------------+
   | slot-id              | Cryptoki-assigned   | decimal number of    |
   |                      | value that          | "CK_SLOT_ID" type.   |
   |                      | identifies a slot   |                      |
   +----------------------+---------------------+----------------------+
   | slot-manufacturer    | ID of the slot      | "manufacturerID"     |
   |                      | manufacturer        | member of            |
   |                      |                     | CK_SLOT_INFO         |
   |                      |                     | structure.  It is a  |
   |                      |                     | UTF-8 string.        |
   +----------------------+---------------------+----------------------+
   | token                | application-defined | "label" member of    |
   |                      | label, assigned     | the CK_TOKEN_INFO    |
   |                      | during token        | structure.  It is a  |
   |                      | initialization      | UTF-8 string.        |
   +----------------------+---------------------+----------------------+
   | type                 | object class (type) | "CKA_CLASS" object   |
   |                      |                     | attribute.           |
   +----------------------+---------------------+----------------------+

          Table 1: Mapping between URI Path Component Attributes
                     and PKCS #11 Specification Names

Next is the mapping between the "type" attribute values and the matching PKCS #11 object classes πŸ“

                +-----------------+-----------------------+
                | Attribute value | PKCS #11 object class |
                +-----------------+-----------------------+
                | cert            | CKO_CERTIFICATE       |
                | data            | CKO_DATA              |
                | private         | CKO_PRIVATE_KEY       |
                | public          | CKO_PUBLIC_KEY        |
                | secret-key      | CKO_SECRET_KEY        |
                +-----------------+-----------------------+

               Table 2: Mapping between the "type" Attribute
                        and PKCS #11 Object Classes

The query attributes are the sidekicks πŸ‘‘ The "pin-source" attribute says where the application or library should look for the normal user's token PIN. The "pin-value" attribute gives the normal user's PIN value directly, if that's needed. And the "module-name" and "module-path" attributes tweak the default settings for reaching PKCS #11 providers. For the definition of a "normal user", check out [PKCS11] πŸ’–

Heads up, bestie: the ABNF rules above are a best-effort definition, and this paragraph adds some extra constraints πŸ™ˆ A PKCS #11 URI MUST NOT contain duplicate attributes of the same name in the URI path component. Basically, each attribute can show up at most once in the path component 😭 Outside of the query attributes defined in this document, duplicate (vendor) attributes MAY be present in the URI query component, and it's up to the URI consumer to decide how to handle those duplicates 🫢

Last but not least, the id value can hold non-textual data πŸ¦‹ Earlier in this section it was mentioned that the corresponding PKCS #11 "CKA_ID" object attribute can contain arbitrary binary data, so yeah, text isn't guaranteed here. That's why the whole value of the id attribute SHOULD be percent-encoded πŸ’œ

The library-version attribute is giving major AND minor version vibes πŸ¦‹ Its format is "M.N". Both numbers are one byte in size (check the libraryVersion member of the CK_INFO structure in [PKCS11] for the deets ✨). The value "M" for the attribute MUST be interpreted as "M" for the major and "0" for the minor version of the library. If the attribute is present, the major version number is REQUIRED. Both "M" and "N" MUST be decimal numbers πŸ”’

Okay so Slot ID is a number that Cryptoki assigns itself, and it's not guaranteed to stay the same across PKCS #11 module initializations 😭 Some libraries and modules do give you stable slot identifiers though 🧚 So when the slot description and manufacturer ID aren't enough to uniquely identify a specific reader, the slot ID MAY be used to make the token identification more precise. In other scenarios, using slot IDs is likely to cause usability issues πŸ’€

An empty PKCS #11 URI path component attribute that does allow for an empty value matches a corresponding structure member or an object attribute with an empty value. Also, according to the PKCS #11 specification [PKCS11], empty character values in a PKCS #11 API producer must be padded with spaces and should not be NULL terminated 🌸


2.4. PKCS #11 URI Scheme Query Attribute Semantics ✨

An application can always ask for a PIN by any means it decides to πŸ’… Plus, so PKCS #11 URIs stay portable, the format and interpretation of the pin-source attribute value is left to be implementation specific. Still, the following rules SHOULD be followed in descending order for the value of the pin-source attribute:

  • If the value represents a URI, it SHOULD be treated as an object containing the PIN. That URI may be file:, https:, another PKCS #11 URI, or something else πŸ”—
  • If the value contains |<absolute-command-path>, the implementation SHOULD read the PIN from the output of an application specified with the absolute path <absolute-command-path>. The | pipe character doesn't have to be percent-encoded in the query component of a PKCS #11 URI πŸ™ˆ
  • Interpret the value as needed in an implementation-dependent way.

If a URI contains both pin-source and pin-value query attributes, the URI SHOULD be refused as invalid 🚫

Use of the pin-value attribute may have security-related consequences. Section 6 should be consulted before this attribute is ever used. Standard percent-encoding rules SHOULD be followed for the attribute value πŸ”

A consumer of PKCS #11 URIs MAY accept the query component attributes module-name and module-path to modify the default settings for accessing a PKCS #11 provider or providers πŸŽ€

Processing the URI query module attributes SHOULD follow these rules:

  • The attribute module-name SHOULD contain a case-insensitive PKCS #11 module name (not a path nor a filename) without system-specific affixes. An affix could be a .so or .DLL suffix, or a lib prefix, for example. Not using system-specific affixes is expected to increase portability of PKCS #11 URIs among different systems 🌈 A URI consumer searching for PKCS #11 modules SHOULD use system or application-specific locations to find modules based on the name provided in the attribute.
  • The attribute module-path SHOULD contain a system-specific absolute path to the PKCS #11 module, or a system-specific absolute path to the directory where PKCS #11 modules are located. For security reasons, a URI with a relative path in this attribute SHOULD be rejected πŸ›‘οΈ
  • The URI consumer MAY refuse to accept either of the attributes, or both. If the attribute present in the URI string isn't accepted, a warning message SHOULD be presented to the provider of the URI, and system-specific module locations SHOULD be used ⚠️
  • If either of the module attributes is present, only those modules found matching these query attributes SHOULD be used to search for an entity represented by the URI πŸ”
  • Using the module attributes does not suppress matching of any other URI path component attributes present in a URI.
  • The semantics of using both attributes in the same URI string is implementation specific, but such use SHOULD be avoided. module-name is preferred over module-path because it's system-independent, but module-path may be more suitable for development and debugging πŸ§‘β€πŸ’»
  • A URI MUST NOT contain multiple module attributes of the same name πŸ™…β€β™€οΈ

Use of the module attributes may have security-related consequences. Section 6 should be consulted before these attributes are ever used πŸ₯Ί

The word "module" was chosen over "library" in these query attribute names to avoid confusion with the semantically different library attributes used in the URI path component πŸ’œ


2.5. PKCS #11 URI Matching Guidelines ✨

A PKCS #11 URI can identify PKCS #11 storage objects, tokens, slots, or Cryptoki libraries. Since one URI may identify four different types of entities, the context it's used in may be needed to figure out the type πŸ‘€ For example, a URI with only library attributes could represent all objects in all tokens in all Cryptoki libraries it identifies, all tokens in those libraries, or just the libraries themselves.

The following guidelines can help a PKCS #11 URI consumer (for example, an application accepting PKCS #11 URIs) match the URI with the resource it wants 🎯

  • The consumer needs to know whether the URI is meant to identify PKCS #11 storage object(s), token(s), slot(s), or Cryptoki producer(s).
  • If the consumer is willing to accept query component module attributes, only those PKCS #11 providers matching these attributes SHOULD be worked with. See Section 2.4 for more information.
  • An unrecognized attribute in the URI path component, including a vendor-specific attribute, SHOULD result in an empty set of matched resources. The consumer can consider whether an error message shown to the user is appropriate in that case πŸ’€
  • An unrecognized attribute in the URI query SHOULD be ignored. The consumer can consider whether a warning message shown to the user is appropriate in that case πŸ™ƒ
  • An attribute that isn't present in the URI path component but is known to a consumer matches everything. Each additional attribute present in the URI path component further restricts the selection βœ‚οΈ
  • Logically, a URI with an empty path component matches everything. For example, if it's used to identify storage objects, it matches all accessible objects in all tokens provided by all relevant PKCS #11 API producers 🌍
  • Use of PIN attributes may change the set of storage objects visible to the consumer πŸ”‘
  • Besides the query component attributes defined in this document, vendor-specific query attributes may contain further information about how to perform the selection, or other related information 🧩

As noted in Section 5, the PKCS #11 specification isn't clear about how to normalize UTF-8-encoded Unicode characters [RFC3629]. That's why it is RECOMMENDED not to use characters outside the US-ASCII character set for labels and names. Those who find they need such characters should be cautious, conservative, and put in extra effort to be sure they know what they're doing, because failing to do so may create both operational and security risks 🚨 When matching UTF-8 string-based attributes (see Table 1) with characters outside the US-ASCII repertoire, normalizing all UTF-8 strings before string comparison may be the only safe approach. For example, for objects (keys), a PKCS #11 attribute search template would only contain attributes that are not UTF-8 strings, and then another pass through the returned objects is needed for UTF-8 string comparison after normalization is applied 🧐


2.6. PKCS #11 URI Comparison ✨

Comparing two URIs is a way of figuring out whether they're equivalent without looking at the actual resource they point to πŸ‘― The goal is to minimize false negatives while strictly avoiding false positives. When working with UTF-8 strings containing characters outside the US-ASCII character set, see the important caveats in Sections 2.5 and 5 ⚠️

Two PKCS #11 URIs are said to be equal if their URIs, as character strings, are identical as specified in Section 6.2.1 of [RFC3986], or if both of the following rules are fulfilled:

  • The set of attributes present in the URI is equal. The ordering of attributes in the URI string isn't significant for the comparison mechanism πŸ”€
  • The values of the respective attributes are equal, based on the rules specified below.

The rules for comparing values of the respective attributes are:

  • The values of the path component attributes library-description, library-manufacturer, manufacturer, model, object, serial, slot-description, slot-manufacturer, token, and type, plus the query component attribute module-name, MUST be compared using a simple string comparison, as specified in Section 6.2.1 of [RFC3986], after both case and percent-encoding normalization have been applied as specified in Section 6.2.2 of [RFC3986] πŸ’–
  • The value of the attribute id MUST be compared using simple string comparison after all bytes are percent-encoded using uppercase letters for digits A-F πŸ”‘
  • The value of the attribute library-version MUST be processed as a specific scheme-based normalization permitted by Section 6.2.3 of [RFC3986]. The value MUST be split into a major and minor version, with the character . (dot) serving as the delimiter. A library-version of "M" MUST be treated as "M" for the major version and "0" for the minor version. Then the resulting minor and major version numbers MUST be compared separately and numerically πŸ”’
  • The value of the attribute slot-id MUST be processed as a specific scheme-based normalization permitted by Section 6.2.3 of [RFC3986] and compared numerically πŸ“
  • If the value of pin-source contains a file: URI or |<absolute-command-path>, it MUST be compared using simple string comparison after the full syntax-based normalization, as specified in Section 6.2.2 of [RFC3986], has been applied. If the value of the pin-source attribute is believed to be overloaded, case and percent-encoding normalization SHOULD be applied before the values are compared, but the exact mechanism of comparison is left to the application 🧩
  • The value of the attribute module-path MUST be compared using simple string comparison after the full syntax-based normalization, as specified in Section 6.2.2 of [RFC3986], has been applied πŸ—‚οΈ
  • When comparing vendor-specific attributes, case and percent-encoding normalization, as specified in Section 6.2.2 of [RFC3986], SHOULD be applied before the values are compared, but the exact mechanism of such a comparison is left to the application 🌸

2.7. Generating PKCS #11 URIs ✨

okay so when you're making PKCS #11 URIs for resources, the exact set of attributes you put in is totally context specific 🫢 like, it depends on what you're doing, bestie. A URI-generating app MAY provide a PKCS #11 URI template [RFC6570] support so it can list URIs that get you back to the same resource(s) again, as long as the template actually captured the context it needed 🧚

3. Examples of PKCS #11 URIs πŸŽ€

this section is the fun part!! πŸ’– It shows how PKCS #11 token objects, tokens, slots, and libraries can all be identified with the PKCS #11 URI scheme. heads up that some of these examples have line breaks and spaces added so they're easier to read πŸ‘€ As specified in Appendix C of [RFC3986], whitespace SHOULD be ignored when extracting the URI. Also, every space that's actually part of a URI is percent-encoded, per Appendix A of [RFC3986] πŸ’…

An empty PKCS #11 URI might be useful to PKCS #11 consumers. Check out Section 2.5 for what that one means 🌸

  pkcs11:

One of the simplest and most useful forms is a PKCS #11 URI that only has an object label and its type πŸ“ The default token gets used, so the URI doesn't mention it. Just a heads up, when you're specifying public objects, a token PIN may not be required πŸ¦‹

  pkcs11:object=my-pubkey;type=public

When you're specifying a private key, you'd usually reach for the "pin-source" attribute, "pin-value", or some app-specific method πŸ” Note that '/' is NOT percent-encoded in the "pin-source" attribute value, since this attribute lives in the query component and not the path component. That means a '?' separates it from the rest of the URI πŸ‘‘

  pkcs11:object=my-key;type=private?pin-source=file:/etc/token

This one identifies a certificate in the software token πŸ’œ Check out the empty "serial" value, which only matches an empty "serialNumber" member of the "CK_TOKEN_INFO" structure. The "id" value is fully percent-encoded, as recommended πŸ™ˆ And yes, ',' is in the reserved set, but it doesn't need to be percent-encoded because it doesn't clash with any sub-delimiters being used. The '#' in "The Software PKCS #11 Softtoken" MUST be percent-encoded, no exceptions 🚫

  pkcs11:token=The%20Software%20PKCS%2311%20Softtoken;
         manufacturer=Snake%20Oil,%20Inc.;
         model=1.0;
         object=my-certificate;
         type=cert;
         id=%69%95%3E%5C%F4%BD%EC%91;
         serial=
         ?pin-source=file:/etc/token_pin

Next up is how to use the "module-name" query attribute 🧚 So the module lives at /usr/lib/libmypkcs11.so.1. The attribute value is "mypkcs11", which is just the module name, without the full path, the platform-specific "lib" prefix, or the ".so.1" suffix ✨

  pkcs11:object=my-sign-key;
         type=private
         ?module-name=mypkcs11

The "module-path" query attribute is up next πŸ¦‹ It's handy if a user needs to provide the key through a PKCS #11 module on removable media, for example. How the PIN gets obtained to access the private key is left to the application πŸ’…

  pkcs11:object=my-sign-key;
         type=private
         ?module-path=/mnt/libmypkcs11.so.1

When a token is what's expected, you can identify it without naming any PKCS #11 objects 🌈 A PIN might still be needed if you're, say, listing every object on the token. Section 6 should be consulted before the "pin-value" attribute is ever used, so read that first, bestie 😭

  pkcs11:token=Software%20PKCS%2311%20softtoken;
         manufacturer=Snake%20Oil,%20Inc.
         ?pin-value=the-pin

When a slot is what's expected, you can identify it without naming any PKCS #11 objects on whatever token might be inserted into it 🌸

  pkcs11:slot-description=Sun%20Metaslot

The Cryptoki library on its own can also be identified, with no PKCS #11 token or object involved πŸ“

  pkcs11:library-manufacturer=Snake%20Oil,%20Inc.;
         library-description=Soft%20Token%20Library;
         library-version=1.23

This example has a semicolon inside an attribute value, and that means it MUST be percent-encoded πŸ™…β€β™€οΈ The token value MUST be read as "My token; created by Joe". Lowercase letters MAY be used in the percent-encoding, like in the "id" value below, BUT Sections 2.1 and 6.2.2.1 of [RFC3986] say all percent-encoded characters SHOULD use uppercase hexadecimal digits. Also, if you were comparing the URI string, the algorithm in Section 2.6 explicitly requires uppercase A-F digits in "id" attribute values, so it's not optional, babe πŸ’… And like Section 2.3 explained, library version "3" MUST be interpreted as "3" for the major version and "0" for the minor version of the library πŸŽ€

  pkcs11:token=My%20token%25%20created%20by%20Joe;
         library-version=3;
         id=%01%02%03%Ba%dd%Ca%fe%04%05%06

If you need to include a literal "%;" substring, both characters MUST be escaped 🫢 The token value MUST be read as "A name with a substring %;".

  pkcs11:token=A%20name%20with%20a%20substring%20%25%3B;
         object=my-certificate;
         type=cert

The next one has a small A with acute in the token name ✨ It MUST be encoded into octets using UTF-8 and then percent-encoded. A small A with acute is U+225 in Unicode, and its UTF-8 encoding is 195 161 in decimal, which is "%C3%A1" in percent-encoding 🧚 Also see Section 5 for using characters outside the US-ASCII set in labels πŸ’–

  pkcs11:token=Name%20with%20a%20small%20A%20with%20acute:%20%C3%A1;
         object=my-certificate;
         type=cert

Both the path and query components MAY contain vendor-specific attributes 🌈 Attributes in the query component MUST be delimited by '&'.

  pkcs11:token=my-token;
         object=my-certificate;
         type=cert;
         vendor-aaa=value-a
         ?pin-source=file:/etc/token_pin
         &vendor-bbb=value-b

4. IANA Considerations 🌸

4.1. URI Scheme Registration πŸ¦‹

okay so this document moves the "pkcs11" URI scheme from the "Provisional URI Schemes" registry over to the "Permanent URI Schemes" registry πŸŽ‰ The registration request follows [RFC4395], and here's the whole thing:

   URI scheme name: pkcs11

   URI scheme status: permanent

   URI scheme syntax: Defined in Section 2.3 of [RFC7512].

   URI scheme semantics: Defined in Section 1 of [RFC7512].

   Encoding considerations: See Sections 2.3 and 5 of [RFC7512].

   Applications/protocols that use this URI scheme name: For general
   information, see Section 1 of [RFC7512].  A list of known
   consumers of the PKCS #11 URI include GnuTLS, Gnome, p11-kit,
   Oracle Solaris 11 and higher, OpenSC, OpenConnect, and FreeIPA.

   Interoperability considerations: See Section 5 of [RFC7512].

   Security considerations: See Section 6 of [RFC7512].

   Contact: Jan Pechanec <Jan.Pechanec@Oracle.com>, Darren Moffat
   <Darren.Moffat@Oracle.com>

   Author/Change Controller: IESG <iesg@ietf.org>

   References: [RFC7512]

5. Internationalization Considerations 🌈

Quick vibe check: the PKCS #11 spec doesn't define a canonical form for strings of CK_UTF8CHAR type πŸ€” That means the usual false negative and false positive (aliasing) problems pop up with unnormalized strings. Since all PKCS #11 items are local and local security is assumed, these issues are mostly about usability and interoperability πŸ’…

To make the user experience better, it is RECOMMENDED that applications creating PKCS #11 objects or labeling tokens don't use characters outside the US-ASCII set for labels πŸŽ€ If that's not possible, labels SHOULD be normalized to Normalization Form C (NFC) [UAX15]. For the same reason, PKCS #11 libraries, slots (token readers), and tokens SHOULD use only US-ASCII characters for their names, and if they can't, they SHOULD normalize their names to NFC 🌸 When an application lists PKCS #11 libraries, slots, tokens, and/or objects, it SHOULD normalize their names to NFC if characters outside the US-ASCII set are expected 🧚 When matching PKCS #11 URIs to libraries, slots, tokens, and/or objects, applications MAY convert names to a chosen normalization form before the string comparison, since some of those names might predate these recommendations 🫢 See also Section 2.5.


6. Security Considerations πŸ”

this section is SERIOUS, bestie 😀 There are general security considerations for URI schemes in Section 7 of [RFC3986], and you need to know them.

From those, Section 7.1 of [RFC3986] applies, because there's no guarantee that the same PKCS #11 URI will always identify the same object, token, slot, or library in the future 😭

Section 7.2 of [RFC3986] applies too πŸ’€ By accepting the "module-name" or "module-path" query attributes, the consumer could end up loading arbitrary code into a process. Yikes!!

Section 7.5 of [RFC3986] applies as well, since a PKCS #11 URI might show up in world-readable command-line arguments, get stored in public configuration files, or otherwise get used in clear text πŸ™ˆ For that reason, the "pin-value" attribute should only be used if the URI string itself is protected with the same level of security as the token PIN it goes with πŸ”’

The PKCS #11 spec doesn't let you authenticate devices to users; it only authenticates users to tokens πŸ™…β€β™€οΈ So local and physical security are what's really required here. The user must be in possession of their tokens, and the system the tokens are inserted into must be secure πŸ›‘οΈ Because of that, the usual normalization security concerns don't come up. For the same reason, confusable script issues don't come up either πŸ’… Still, if you need characters outside the US-ASCII set, the best move is to normalize all strings in PKCS #11 API elements to NFC. See also Section 5 🫢


7. References πŸ“š

7.1. Normative References πŸ’œ

   [RFC2119]  Bradner, S., "Key words for use in RFCs to Indicate
              Requirement Levels", BCP 14, RFC 2119, March 1997,
              <http://www.rfc-editor.org/info/rfc2119>.

   [RFC3629]  Yergeau, F., "UTF-8, a transformation format of ISO
              10646", STD 63, RFC 3629, November 2003,
              <http://www.rfc-editor.org/info/rfc3629>.

   [RFC3986]  Berners-Lee, T., Fielding, R., and L. Masinter, "Uniform
              Resource Identifier (URI): Generic Syntax", STD 66, RFC
              3986, January 2005,
              <http://www.rfc-editor.org/info/rfc3986>.

   [RFC5234]  Crocker, D., Ed. and P. Overell, "Augmented BNF for Syntax
              Specifications: ABNF", STD 68, RFC 5234, January 2008,
              <http://www.rfc-editor.org/info/rfc5234>.

7.2. Informative References ✨

okay bestie, these are the extra-credit reads πŸ“šπŸ§š the stuff that's helpful background but isn't required to actually build the thing:


Contributors 🫢

Stef Walter, Nikos Mavrogiannopoulos, Nico Williams, Dan Winship, Jaroslav Imrich, and Mark Phalan contributed to the development of this document. Shawn Emery shepherded the document. πŸ’–


Authors' Addresses πŸ’Œ

These are the lovely humans who wrote it, so here's where you can find them πŸ¦‹

Jan Pechanec
Oracle Corporation
4180 Network Circle
Santa Clara, CA 95054
United States

EMail: Jan.Pechanec@Oracle.com
URI:   http://www.oracle.com


Darren J. Moffat
Oracle Corporation
Oracle Parkway
Thames Valley Park
Reading  RG6 1RA
United Kingdom

EMail: Darren.Moffat@Oracle.com
URI:   http://www.oracle.com
πŸ“¦ where this page comes from