5 PKCS#11 Reference Guide
The Java platform defines a set of programming interfaces for performing cryptographic operations. These interfaces are collectively known as the Java Cryptography Architecture (JCA) and the Java Cryptography Extension (JCE). See Java Cryptography Architecture (JCA) Reference Guide.
The cryptographic interfaces are provider-based. Specifically, applications talk to Application Programming Interfaces (APIs), and the actual cryptographic operations are performed in configured providers which adhere to a set of Service Provider Interfaces (SPIs). This architecture supports different provider implementations. Some providers may perform cryptographic operations in software; others may perform the operations on a hardware token (for example, on a smartcard device or on a hardware cryptographic accelerator).
The Cryptographic Token Interface Standard, PKCS#11, is produced by RSA Security and defines native programming interfaces to cryptographic tokens, such as hardware cryptographic accelerators and smartcards. Existing applications that use the JCA and JCE APIs can access native PKCS#11 tokens with the PKCS#11 provider. No modifications to the application are required. The only requirement is to properly configure the provider.
Although an application can make use of most PKCS#11 features using existing APIs, some applications might need more flexibility and capabilities. For example, an application might want to deal with smartcards being removed and inserted dynamically more easily. Or, a PKCS#11 token might require authentication for some non-key-related operations and therefore, the application must be able to log into the token without using keystore. The JCA gives applications greater flexibility in dealing with different providers.
This document describes how native PKCS#11 tokens can be configured into the Java platform for use by Java applications. It also describes how the JCA makes it easier for applications to deal with different types of providers, including PKCS#11 providers.
SunPKCS11 Provider
The SunPKCS11 provider, in contrast to most other providers, does not implement cryptographic algorithms itself. Instead, it acts as a bridge between the Java JCA and JCE APIs and the native PKCS#11 cryptographic API, translating the calls and conventions between the two.
This means that Java applications calling standard JCA and JCE APIs can, without modification, take advantage of algorithms offered by the underlying PKCS#11 implementations, such as, for example,
- Cryptographic smartcards,
- Hardware cryptographic accelerators, and
- High performance software implementations.
Note:
Java SE only facilitates accessing native PKCS#11 implementations, it does not itself include a native PKCS#11 implementation. However, cryptographic devices such as Smartcards and hardware accelerators often come with software that includes a PKCS#11 implementation, which you need to install and configure according to manufacturer's instructions.SunPKCS11 Requirements
The SunPKCS11 provider requires an implementation of PKCS#11
v2.20 or later to be installed on the system. This implementation
must take the form of a shared-object library (.so
on Solaris and Linux) or dynamic-link library (.dll
on Windows or .dylib
on macOS). Consult your vendor
documentation to find out if your cryptographic device includes such
a PKCS#11 implementation, how to configure it, and what the name of
the library file is.
The SunPKCS11 provider supports a number of algorithms, provided that the underlying PKCS#11 implementation offers them. The algorithms and their corresponding PKCS#11 mechanisms are listed in the table in SunPKCS11 Provider Supported Algorithms.
SunPKCS11 Configuration
The SunPKCS11 provider is in the module jdk.crypto.cryptoki. To use the provider, you must first install it statically or programmatically.
java-home/conf/security/java.security
).
Note:
Properties in thejava.security
file are typically parsed only once. If you
have modified any property in this file, restart your applications to ensure that
the changes are properly reflected.For
example, here's a fragment of the java.security
file that installs the
SunPKCS11 provider with the configuration file
/opt/bar/cfg/pkcs11.cfg
.
# configuration for security providers 1-12 omitted
security.provider.13=SunPKCS11 /opt/bar/cfg/pkcs11.cfg
To install the provider dynamically, create an instance of the provider with the appropriate configuration filename and then install it. Here is an example.
String configName = "/opt/bar/cfg/pkcs11.cfg";
Provider p = Security.getProvider("SunPKCS11");
p = p.configure(configName);
Security.addProvider(p);
Note:
Save the returned Provider object from the configure method, then add that object, as demonstrated in this example:
p = p.configure(configName);
Security.addProvider(p);
Don't add the provider from which you called the configure method:
p.configure(configName);
Security.addProvider(p);
If this provider cannot be configured in-place, then a new provider is created and returned. Therefore, always use the provider returned from the configure method.
To use more than one slot per PKCS#11 implementation, or to use more than one PKCS#11 implementation, simply repeat the installation for each with the appropriate configuration file. This will result in a SunPKCS11 provider instance for each slot of each PKCS#11 implementation.
The configuration file is a text file that contains entries in the following format:
attribute=value
The valid values for attribute and value are described in the table in this section:
The two mandatory attributes arename
and library
.
Here is a sample configuration file:
name = FooAccelerator
library = /opt/foo/lib/libpkcs11.so
Comments are denoted by lines starting with the #
(number)
symbol.
Table 5-1 Attributes in the PKCS#11 Provider Configuration File
Attribute | Value | Description |
---|---|---|
allowSingleThreadedModules |
Boolean value, default:
true |
If true , allows
modules that only support single threaded access. Single threaded modules cannot be
used safely from multiple PKCS#11 consumers in the same process, for example, when
using Network Security Services (NSS) with SunPKCS11.
|
attributes |
See Attributes Configuration | Specifies additional PKCS#11 attributes that should be set when creating PKCS#11 key objects. This makes it possible to accommodate tokens that require particular attributes. |
cleaner.longInterval |
Integer in milliseconds, default
60000 . The value must be greater than 1000 ms.
|
Specifies how often, in milliseconds.
the cleaner thread should check for native references during non-busy periods, that
is, the frequency that the cleaner thread checks the queue for native
references.
Note: The cleaner thread will switch to thecleaner.shortInterval frequency if native
PKCS11 references for cleaning are detected.
|
cleaner.shortInterval |
Integer in milliseconds, default:
2000 . The value must be greater than 1000 ms.
|
Specifies how often, in milliseconds,
native reference clearing should be performed during busy periods, that is, the
frequency that the cleaner thread processes no-longer-needed native references in
the queue to free up native memory.
Note: The cleaner thread will switch to thecleaner.longInterval frequency after 200 failed tries, that
is, when no references are found in the queue.
|
description |
Description of this provider instance | Specifies the string that
the provider instance's Provider.getInfo() method returns. If no
string is specified, then a default description is returned.
|
destroyTokenAfterLogout |
Boolean value, default:
false |
If true , then when
java.security.AuthProvider.logout() is called from
the SunPKCS11 provider instance, the underlying token object will be destroyed and
resources will be freed. This essentially renders the SunPKCS11 provider instance
unusable after logout() calls.
Note: You should not add a SunPKCS11 provider with this attribute set to true to the system provider list because the provider object is not useable after logout() is called. |
disabledMechanisms |
Brace enclosed, whitespace-separated list of PKCS#11 mechanisms to disable | Specifies the list of
PKCS#11 mechanisms that this provider instance should ignore. The provider ignores
any mechanism listed, even if they are supported by the token and the SunPKCS11
provider. Specify the strings SecureRandom and
KeyStore to disable those services.
At most, you
can specify one of |
enabledMechanisms |
Brace enclosed, whitespace-separated list of PKCS#11 mechanisms to enable | Specifies the list
PKCS#11 mechanisms that this provider instance should use, provided that they are
supported by both the SunPKCS11 provider and PKCS#11 token. All other mechanisms are
ignored. Each entry in the list is the name of a PKCS#11 mechanism. Here is an
example that lists two PKCS#11 mechanisms.
At most, you can specify one of enabledMechanisms or
disabledMechanisms . If you specify neither, then the mechanisms
enabled are those that are supported by both the SunPKCS11 provider (see SunPKCS11 Provider Supported Algorithms) and the PKCS#11 token.
|
explicitCancel |
Boolean value, default:
true |
If true , indicates
that you must explicitly cancel operations.
|
functionList |
Name of C function that returns the
PKCS#11 function list, default: C_GetFunctionList
|
This option primarily exists for the deprecated Secmod.Module.getProvider() method. |
handleStartupErrors |
Possible values: ignoreAll ,
ignoreMissingLibrart , or halt ; default:
halt |
Describes how to handle errors during startup. |
insertionCheckInterval |
Integer in milliseconds, default 2000 . The value
must be greater than 100 ms.
|
Specifies how often to test for token insertion, in milliseconds, if no token is present. |
keyStoreCompatibilityMode |
Boolean value, default: true |
If true , indicates
that P11Keystore is more tolerant of input parameters.
|
library |
Pathname of PKCS#11 implementation |
Specifies the the full pathname (including extension) of the PKCS#11
implementation; the format of the pathname is platform dependent. For example,
Note:
|
name |
Name suffix of this provider instance | Specifies the string,
which is concatenated with the prefix SunPKCS11- to produce this
provider instance's name (that is, the string returned by its
Provider.getName() method). For example, if the
name attribute is "FooAccelerator" , then the
provider instance's name will be
"SunPKCS11-FooAccelerator" .
|
nssArgs |
Quoted string | Specifies a special initialization argument string for the NSS soft token. This is used when using the NSS soft token directly without secmod mode. |
nssDbMode |
See Table 5-2 | See Table 5-2 |
nssLibraryDirectory |
See Table 5-2 | See Table 5-2 |
nssModule |
See Table 5-2 | See Table 5-2 |
nssNetscapeDbWorkaround |
See Table 5-2 | See Table 5-2 |
nssOptimizeSpace |
See Table 5-2 | See Table 5-2 |
nssSecmodDirectory |
See Table 5-2 | See Table 5-2 |
nssUseSecmod |
See Table 5-2 | See Table 5-2 |
omitInitialize |
Boolean value, default:
false |
If true , then omit
the call to the C_Initialize() function. Use only if
the PKCS#11 implementation has been initialized earlier with a C_Initialize() call.
|
showInfo |
Boolean value, default:
false |
If true , then
display provider information during start up. Provider information includes the
provider's name and supported PKCS#11 mechanisms.
|
slot |
Slot ID | Specifies the ID of the
slot that this provider instance is to be associated with. For example, you would
use 1 for the slot with the id 1 under PKCS#11. At
most one of slot or slotListIndex may be
specified. If neither is specified, the default is a slotListIndex
of 0 .
|
slotListIndex |
Slot index | Specifies the slot index
that this provider instance is to be associated with. It is the index into the list
of all slots returned by the PKCS#11 function C_GetSlotList . For
example, 0 indicates the first slot in the list. At most one of
slot or slotListIndex may be specified. If
neither is specified, the default is a slotListIndex of
0 .
|
useEcX963Encoding |
Boolean value, default:
false |
Indicates that the X9.63 encoding for
EC points is used (true ) or that the encoding is wrapped in an
ASN.1 OctetString (false ).
|
Attributes Configuration
The attributes option allows you to specify additional PKCS#11 attributes that should be set when creating PKCS#11 key objects. By default, the SunPKCS11 provider only specifies mandatory PKCS#11 attributes when creating objects. For example, for RSA public keys it specifies the key type and algorithm (CKA_CLASS and CKA_KEY_TYPE) and the key values for RSA public keys (CKA_MODULUS and CKA_PUBLIC_EXPONENT). The PKCS#11 library you are using will assign implementation specific default values to the other attributes of an RSA public key, for example that the key can be used to encrypt and verify messages (CKA_ENCRYPT and CKA_VERIFY = true).
The attributes
option can be used if you do not like the
default values your PKCS#11 implementation assigns or if your PKCS#11 implementation does
not support defaults and requires a value to be specified explicitly. Note that specifying
attributes that your PKCS#11 implementation does not support or that are invalid for the
type of key in question may cause the operation to fail at runtime.
The option can be specified zero or more times. The options are processed in
the order specified in the configuration file. The attributes
option has
the format:
attributes(operation, keytype, keyalgorithm) = {
name1 = value1
[...]
}
Valid values for operation
are:
generate
, for keys generated via a KeyPairGenerator or KeyGeneratorimport
, for keys created via a KeyFactory or SecretKeyFactory. This also applies to Java software keys automatically converted to PKCS#11 key objects when they are passed to the initialization method of a cryptographic operation, for exampleSignature.initSign()
.*
, for keys created using either a generate or a create operation.
Valid values for keytype
are CKO_PUBLIC_KEY
,
CKO_PRIVATE_KEY
, and CKO_SECRET_KEY
, for public,
private, and secret keys, respectively, and *
to match any type of key.
Valid values for keyalgorithm
are one of the
CKK_xxx
constants from the PKCS#11 specification, or *
to match keys of any algorithm. See SunPKCS11 Provider Supported Algorithms.
The attribute names and values are specified as a list of one or more
name-value pairs. name
must be a CKA_xxx
constant from the
PKCS#11 specification, for example CKA_SENSITIVE
. value
can be one of the following:
- A boolean value,
true
orfalse
- An integer, in decimal form (default) or in hexadecimal form if it begins
with
0x
. null
, indicating that this attribute should not be specified when creating objects.
If the attributes
option is specified multiple times, the
entries are processed in the order specified with the attributes aggregated and later
attributes overriding earlier ones. For example, consider the following configuration file
excerpt:
attributes(*,CKO_PRIVATE_KEY,*) = {
CKA_SIGN = true
}
attributes(*,CKO_PRIVATE_KEY,CKK_DH) = {
CKA_SIGN = null
}
attributes(*,CKO_PRIVATE_KEY,CKK_RSA) = {
CKA_DECRYPT = true
}
The first entry says to specify CKA_SIGN = true
for all
private keys. The second option overrides that with null
for Diffie-Hellman
private keys, so the CKA_SIGN
attribute will not specified for them at all.
Finally, the third option says to also specify CKA_DECRYPT = true
for RSA
private keys. That means RSA private keys will have both CKA_SIGN = true
and CKA_DECRYPT = true
set.
There is also a special form of the attributes
option. You
can write attributes = compatibility
in the configuration file. That is a
shortcut for a whole set of attribute statements. They are designed to provider maximum
compatibility with existing Java applications, which may expect, for example, all key
components to be accessible and secret keys to be usable for both encryption and decryption.
The compatibility
attributes line can be used together with other
attributes
lines, in which case the same aggregation and overriding rules
apply as described earlier.
Accessing Network Security Services (NSS)
Network Security Services (NSS) is a set of open source security libraries whose crypto APIs are based on PKCS#11 but it includes special features that are outside of the PKCS#11 standard. The SunPKCS11 provider includes code to interact with these NSS specific features, including several NSS specific configuration directives.
For best results, we recommend that you use the latest version of NSS available. It should be at least version 3.12.
The SunPKCS11 provider uses NSS specific code when any of the nss
configuration directives described in Table 5-2 are used. In that case, the regular configuration commands
library
, slot
, and
slotListIndex
cannot be used.
Table 5-2 NSS Attributes and Values
Attribute | Value | Description |
---|---|---|
nssDbMode |
One of
readWrite , readOnly , and
noDb , default:
readWrite |
This
directives determines how the NSS database is accessed. In
read-write mode, full access is possible but only one process at a
time should be accessing the databases. Read-only mode disallows
modifications to the files.
The noDb mode allows NSS to be used without database files purely as a cryptographic provider. It is not possible to create persistent keys using the PKCS11 KeyStore. |
nssLibraryDirectory |
Directory containing the NSS and Netscape Portable Runtime (NSPR)
libraries (which includes libnss3.so )
|
This is the full path name of the directory containing the NSS and NSPR libraries. It must be specified unless NSS has already been loaded and initialized by another component running in the same process as the Java VM. If this value is set, then Note: Depending on your platform, you may have
to set the environment variable |
nssModule |
One
of keystore , crypto ,
fips , and trustanchors |
NSS makes its functionality available using several different libraries and slots. This directive determines which of these modules is accessed by this instance of SunPKCS11. The The The The
If this value is set, then |
nssNetscapeDbWorkaround |
Boolean value, default:
true |
If true, then the P11KeyStore specifies the
|
nssOptimizeSpace |
Boolean value, default:
false |
Indicates that NSS favors performance (if |
nssSecmodDirectory |
The full path name of
the directory containing the NSS configuration and key information
(secmod.db , key3.db , and
cert8.db )
|
This directive must be specified unless NSS has already been
initialized by another component (see
If this value is set, then |
nssUseSecmod |
Boolean value | If true, then NSS secmod
mode is used. It's implicitly set to true if
nssLibraryDirectory ,
nssSecmodDirectory , or
nssModule is specified.
|
Example 5-1 SunPKCS11 Configuration Files for NSS
NSS as a pure cryptography provider
name = NSScrypto
nssLibraryDirectory = /opt/tests/nss/lib
nssDbMode = noDb
attributes = compatibility
NSS as a FIPS 140 compliant crypto token
name = NSSfips
nssLibraryDirectory = /opt/tests/nss/lib
nssSecmodDirectory = /opt/tests/nss/fipsdb
nssModule = fips
Troubleshooting PKCS#11
There could be issues with PKCS#11 which requires debugging. To show debug info about Library, Slots, Token, and Mechanism, add showInfo=true
in the SunPKCS11 provider configuration file, which is <java-home>/conf/security/sunpkcs11-solaris.cfg
or the configuration file that you specified statically or dynamically as described in SunPKCS11 Configuration.
For additional debugging info, users can start or restart the Java processes with one of the following options:
-
For general SunPKCS11 provider debugging info:
-Djava.security.debug=sunpkcs11
-
For PKCS#11 keystore specific debugging info:
-Djava.security.debug=pkcs11keystore
Disabling PKCS#11 Providers and/or Individual PKCS#11 Mechanisms
As part of the troubleshooting process, it could be helpful to temporarily disable a PKCS#11 provider or the specific mechanism of a given provider.
Disabling PKCS#11 Providers
A PKCS#11 provider can be disabled by using one of the following methods:
-
Disable PKCS#11 for a single Java process. Start or restart the Java process with the following Java command line flag:
-Dsun.security.pkcs11.enable-solaris=false
Note:
This step is only applicable to the SunPKCS11 provider when backed by the default Solaris PKCS#11 provider file (<java_home>/conf/security/sunpkcs11-solaris.cfg
). -
Disable PKCS#11 for all Java processes run with a particular Java installation: This can be done dynamically by using the API (not shown in this section) or statically by editing the
<java_home>/conf/security/java.security
file and commenting out the SunPKCS11 security provider (do not forget to re-number the order of providers, if necessary) as follows:# # List of providers and their preference orders: # security.provider.1=SUN security.provider.2=SunRsaSign security.provider.3=SunEC security.provider.4=SunJSSE security.provider.5=SunJCE security.provider.6=SunJGSS security.provider.7=SunSASL security.provider.8=XMLDSig security.provider.9=SunPCSC security.provider.10=JdkLDAP security.provider.11=JdkSASL security.provider.12=SunMSCAPI #security.provider.13=SunPKCS11
Start or restart the Java processes being run on this installation of Java.
Disabling Specific Mechanisms
When an issue occurs in one of the mechanisms of PKCS#11, it can be resolved by disabling only that particular mechanism, rather than the entire PKCS#11 provider (do not forget to re-enable the PKCS#11 provider if it was disabled earlier).
Note:
To disable the PKCS#11 SecureRandom implementation only, you can add SecureRandom to the list of disabled mechanisms in the <java-home>/conf/security/sunpkcs11-solaris.cfg
file:
name = Solaris
description = SunPKCS11 accessing Solaris Cryptographic Framework
library = /usr/lib/$ISA/libpkcs11.so
handleStartupErrors = ignoreAll
# Use the X9.63 encoding for EC points (do not wrap in an ASN.1 OctetString).
useEcX963Encoding = true
attributes = compatibility
disabledMechanisms = {
CKM_DSA_KEY_PAIR_GEN
SecureRandom
}
Application Developers
Java applications can use the existing JCA and JCE APIs to access PKCS#11 tokens through the SunPKCS11 provider.
Token Login
You can login to the keystore using a Personal Identification Number and perform PKCS#11 operations.
Certain PKCS#11 operations, such as accessing private keys, require a login using a Personal Identification Number, or PIN, before the operations can proceed. The most common type of operations that require login are those that deal with keys on the token. In a Java application, such operations often involve first loading the keystore. When accessing the PKCS#11 token as a keystore via the java.security.KeyStore
class, you can supply the PIN in the password input parameter to the load
method. The PIN will then be used by the SunPKCS11 provider for logging into the token. Here is an example.
char[] pin = ...;
KeyStore ks = KeyStore.getInstance("PKCS11");
ks.load(null, pin);
This is fine for an application that treats PKCS#11 tokens as static keystores. For an application that wants to accommodate PKCS#11 tokens more dynamically, such as smartcards being inserted and removed, you can use the new KeyStore.Builder
class. Here is an example of how to initialize the builder for a PKCS#11 keystore with a callback handler.
KeyStore.CallbackHandlerProtection chp =
new KeyStore.CallbackHandlerProtection(new MyGuiCallbackHandler());
KeyStore.Builder builder =
KeyStore.Builder.newInstance("PKCS11", null, chp);
For the SunPKCS11 provider, the callback handler must be able to satisfy a PasswordCallback
, which is used to prompt the user for the PIN. Whenever the application needs access to the keystore, it uses the builder as follows.
KeyStore ks = builder.getKeyStore();
Key key = ks.getKey(alias, null);
The builder will prompt for a password as needed using the previously configured callback handler. The builder will prompt for a password only for the initial access. If the user of the application continues using the same Smartcard, the user will not be prompted again. If the user removes and inserts a different smartcard, the builder will prompt for a password for the new card.
Depending on the PKCS#11 token, there may be non-key-related operations that also require token login. Applications that use such operations can use the java.security.AuthProvider class. The AuthProvider
class extends from java.security.Provider
and defines methods to perform login and logout operations on a provider, as well as to set a callback handler for the provider to use.
For the SunPKCS11 provider, the callback handler must be able to satisfy a PasswordCallback
, which is used to prompt the user for the PIN.
Here is an example of how an application might use an AuthProvider
to log into the token. (Note that you must configure the SunPKCS11 provider before using it.)
Provider p = Security.getProvider("SunPKCS11");
AuthProvider aprov = (AuthProvider)p.configure(<provider configuration file>);
aprov.login(subject, new MyGuiCallbackHandler());
Token Keys
Java Key
objects may or may not contain actual key material.
- A software Key object does contain the actual key material and allows access to that material.
- An unextractable key on a secure token (such as a smartcard) is represented by a Java Key object that does not contain the actual key material. The Key object only contains a reference to the actual key.
Applications and providers must use the correct interfaces to represent these different types of Key objects. Software Key objects (or any Key object that has access to the actual key material) should implement the interfaces in the java.security.interfaces and javax.crypto.interfaces packages (such as DSAPrivateKey
). Key objects representing unextractable token keys should only implement the relevant generic interfaces in the java.security and javax.crypto packages (PrivateKey
, PublicKey
, or SecretKey
). Identification of the algorithm of a key should be performed using the Key.getAlgorithm()
method.
Note that a Key object for an unextractable token key can only be used by the provider associated with that token.
Delayed Provider Selection
Java cryptography getInstance()
methods, such as Cipher.getInstance("AES")
, return the implementation from the first provider that implemented the requested algorithm. However, the JDK delays the selection of the provider until the relevant initialization method is called. The initialization method accepts a Key
object and can determine at that point which provider can accept the specified Key
object. This ensures that the selected provider can use the specified Key
object. (If an application attempts to use a Key
object for an unextractable token key with a provider that only accepts software key objects, then the provider throws an InvalidKeyException
. This is an issue for the Cipher
, KeyAgreement
, Mac
, and Signature
classes.) The following represents the affected initialization methods.
Cipher
.init(..., Key key, ...)KeyAgreement
.init(Key key, ...)Mac
.init(Key key, ...)Signature
.initSign(PrivateKey privateKey)
Note:
Once the provider is selected, for example, after the first initialization call, the
JDK won't switch to a different provider for subsequent initialization calls. To
reselect a provider based on a specific Key
object, call
getInstance() to get a new instance, and then call this
instance's initialization method with the Key
object instead of
reusing the older, already-initialized instance.
Although this delayed provider selection is hidden from the application, it does
affect the behavior of the getProvider()
method for
Cipher
, KeyAgreement
, Mac
, and
Signature
. If getProvider()
is called
before the initialization operation has occurred (and therefore before
provider selection has occurred), then the first provider that supports the requested
algorithm is returned. This may not be the same provider as the one selected
after the initialization method is called. If getProvider()
is called after the initialization operation has occurred, then the actual
selected provider is returned. It is recommended that applications only call
getProvider()
after they have called the relevant initialization
method.
In addition to getProvider()
, the following additional methods are similarly affected.
Cipher.getBlockSize
Cipher.getExcemptionMechanism
Cipher.getIV
Cipher.getOutputSize
Cipher.getParameters
Mac.getMacLength
Signature.getParameters
Signature.setParameter
JAAS KeyStoreLoginModule
The JDK comes with a JAAS keystore login module, KeyStoreLoginModule, that allows an application to authenticate using its identity in a specified keystore. After authentication, the application would acquire its principal and credentials information (certificate and private key) from the keystore. By using this login module and configuring it to use a PKCS#11 token as a keystore, the application can acquire this information from a PKCS#11 token.
Use the following options to configure the KeyStoreLoginModule
to use a PKCS#11 token as the keystore.
keyStoreURL="NONE"
keyStoreType="PKCS11"
keyStorePasswordURL=some_pin_url
where
- some_pin_url
- The location of the PIN. If the
keyStorePasswordURL
option is omitted, then the login module will get the PIN via the application's callback handler, supplying it with aPasswordCallback
. Here is an example of a configuration file that uses a PKCS#11 token as a keystore.other { com.sun.security.auth.module.KeyStoreLoginModule required keyStoreURL="NONE" keyStoreType="PKCS11" keyStorePasswordURL="file:/home/joe/scpin"; };
If more than one SunPKCS11 provider has been configured dynamically or in the
java.security
security properties file, you can use the
keyStoreProvider
option to target a specific provider instance. The
argument to this option is the name of the provider. For the SunPKCS11 provider, the
provider name is of the form SunPKCS11-TokenName
, where TokenName
is the name suffix that the provider instance has
been configured with, as detailed in Table 5-1. For example, the following configuration file names the PKCS#11 provider instance
with name suffix SmartCard
.
other {
com.sun.security.auth.module.KeyStoreLoginModule required
keyStoreURL="NONE"
keyStoreType="PKCS11"
keyStorePasswordURL="file:/home/joe/scpin"
keyStoreProvider="SunPKCS11-SmartCard";
};
Some PKCS#11 tokens support login via a protected authentication path. For example, a smartcard may have a dedicated PIN-pad to enter the pin. Biometric devices will also have their own means to obtain authentication information. If the PKCS#11 token has a protected authentication path, then use the protected=true
option and omit the keyStorePasswordURL
option. Here is an example of a configuration file for such a token.
other {
com.sun.security.auth.module.KeyStoreLoginModule required
keyStoreURL="NONE"
keyStoreType="PKCS11"
protected=true;
};
Tokens as JSSE Keystore and Trust Stores
To use PKCS#11 tokens as JSSE keystores or trust stores, the JSSE application can use the APIs described in Token Login to instantiate a KeyStore that is backed by a PKCS#11 token and pass it to its key manager and trust manager. The JSSE application will then have access to the keys on the token.
JSSE also supports configuring the use of keystores and trust stores via system properties, as described in the Java Secure Socket Extension (JSSE) Reference Guide. To use a PKCS#11 token as a keystore or trust store, set the javax.net.ssl.keyStoreType
and javax.net.ssl.trustStoreType
system properties, respectively, to "PKCS11", and set the javax.net.ssl.keyStore
and javax.net.ssl.trustStore
system properties, respectively, to NONE
. To specify the use of a specific provider instance, use the javax.net.ssl.keyStoreProvider
and javax.net.ssl.trustStoreProvider
system properties (for example, "SunPKCS11-SmartCard").
Using keytool and jarsigner with PKCS#11 Tokens
If the SunPKCS11 provider has been configured in the java.security
security properties file (located in the $JAVA_HOME/conf/security
directory of the Java runtime), then keytool and jarsigner can be used to operate on the PKCS#11 token by specifying the following options.
-keystore NONE
-storetype PKCS11
Here an example of a command to list the contents of the configured PKCS#11 token.
keytool -keystore NONE -storetype PKCS11 -list
The PIN can be specified using the -storepass
option. If none has
been specified, then keytool
and jarsigner
will prompt for the token PIN. If the token has a protected authentication
path (such as a dedicated PIN-pad or a biometric reader), then the
-protected
option must be specified, and no
password options can be specified.
If more than one SunPKCS11 provider has been configured in the java.security
security properties file, you can use the -providerName
option to target a specific provider instance. The argument to this option is the name of the provider.
-providerName providerName
For the SunPKCS11 provider, providerName
is of the form
SunPKCS11-TokenName
where:
- TokenName
- The name suffix that the provider instance has been configured with, as detailed in
Table 5-1. For example, the following command lists the contents of
the PKCS#11 keystore provider instance with name suffix
SmartCard
.keytool -keystore NONE -storetype PKCS11 \ -providerName SunPKCS11-SmartCard \ -list
If the SunPKCS11 provider has not been configured in the
java.security
security properties file, you can use the following options to instructkeytool
andjarsigner
to install the provider dynamically.-providerClass sun.security.pkcs11.SunPKCS11
-providerArg ConfigFilePath
- ConfigFilePath
-
The path to the token configuration file. Here is an example of a command to list a PKCS#11 keystore when the SunPKCS11 provider has not been configured in the
java.security
file.keytool -keystore NONE -storetype PKCS11 \ -providerClass sun.security.pkcs11.SunPKCS11 \ -providerArg /foo/bar/token.config \ -list
Note:
Sometimes the hardware token is too small to store the certificates. You
can use the jarsigner
tool's
-certchain
option to load them from an
external file.
Signing JAR File with jdk.security.jarsigner API and PKCS#11 Token
To sign a JAR file with the jdk.security.jarsigner API and a PKCS#11 token as a keystore, follow these steps:
-
Access the PKCS#11 token's keystore as described in Token Login:
char[] pin = ...; KeyStore ks = KeyStore.getInstance("PKCS11"); ks.load(null, pin); KeyStore ks = builder.getKeyStore(); Key key = ks.getKey(alias, null);
-
Create a JarSigner object with the JarSigner.Builder(Keystore.PrivateKeyEntry) constructor:
JarSigner mySigner = JarSigner.Builder(key).build();
-
Sign the JAR file:
try (ZipFile in = new ZipFile(inputFile); FileOutputStream out = new FileOutputStream(outputFile)) { mySigner.sign(in, out); }
Keystore Entry Syntax in Policy File
The keystore
entry in the default policy
implementation has the following syntax, which accommodates a PIN and
multiple PKCS#11 provider instances:
keystore "some_keystore_url", "keystore_type", "keystore_provider";
keystorePasswordURL "some_password_url";
Where
- keystore_provider
- The keystore provider name (for example,
"SunPKCS11-SmartCard"
). - some_password_url
- A URL pointing to the location of the token PIN.
Both keystore_provider and
the keystorePasswordURL line
are optional. If keystore_provider has not been specified,
then the first configured provider that supports the
specified keystore type is used. If the
keystorePasswordURL
line has not been specified, then no password is used.
See Default Policy Implementation and Policy File Syntax.
Example 5-2 Keystore Policy Entry for a PKCS#11 Token
The following is an example keystore policy entry for a PKCS#11 token:
keystore "NONE", "PKCS11", "SunPKCS11-SmartCard";
keystorePasswordURL "file:/foo/bar/passwordFile";
Provider Developers
The java.security.Provider
class enables provider developers to more easily support PKCS#11 tokens and cryptographic services through provider services and parameter support.
See Example Provider for an example of a simple provider designed to demonstrate provider services and parameter support.
Provider Services
For each service implemented by the provider, there must be a property whose name is the type of service (Cipher
, Signature
, etc), followed by a period and the name of the algorithm to which the service applies. The property value must specify the fully qualified name of the class implementing the service. Here is an example of a provider setting KeyAgreement.DiffieHellman
property to have the value com.sun.crypto.provider.DHKeyAgreement
.
put("KeyAgreement.DiffieHellman", "com.sun.crypto.provider.DHKeyAgreement")
The public static nested class Provider.Service encapsulates the properties of a provider service (including its type, attributes, algorithm name, and algorithm aliases). Providers can instantiate Provider.Service
objects and register them by calling the Provider.putService()
method. This is equivalent to creating a Property
entry and calling the Provider.put()
method. Note that legacy Property
entries registered via Provider.put
are still supported.
Here is an example of a provider creating a Service
object with the KeyAgreement
type, for the DiffieHellman
algorithm, implemented by the class com.sun.crypto.provider.DHKeyAgreement
.
Service s = new Service(this, "KeyAgreement", "DiffieHellman",
"com.sun.crypto.provider.DHKeyAgreement", null, null);
putService(s);
Using Provider.Service
objects instead of legacy Property
entries has a couple of major benefits. One benefit is that it allows the provider to have greater flexibility when Instantiating Engine Classes. Another benefit is that it allows the provider to test Parameter Support. These features are discussed in detail next.
Instantiating Engine Classes
By default, the Java Cryptography framework looks up the provider property for a particular service and directly instantiates the engine class registered for that property. A provider can to override this behavior and instantiate the engine class for the requested service itself.
To override the default behavior, the provider overrides the Provider.Service.newInstance()
method to add its custom behavior. For example, the provider might call a custom constructor, or might perform initialization using information not accessible outside the provider (or that are only known by the provider).
Parameter Support
The Java Cryptography framework may attempt a fast check to determine whether a provider's service implementation can use an application-specified parameter. To perform this fast check, the framework calls Provider.Service.supportsParameter()
.
The framework relies on this fast test during delayed provider selection (see Delayed Provider Selection). When an application invokes an initialization method and passes it a Key
object, the framework asks an underlying provider whether it supports the object by calling its Service.supportsParameter()
method. If supportsParameter()
returns false
, the framework can immediately remove that provider from consideration. If supportsParameter()
returns true
, the framework passes the Key
object to that provider's initialization engine class implementation. A provider that requires software Key
objects should override this method to return false
when it is passed non-software keys. Likewise, a provider for a PKCS#11 token that contains unextractable keys should only return true
for Key
objects that it created, and which therefore correspond to the keys on its respective token.
Note:
The default implementation ofsupportsParameter()
returns true
. This allows existing providers to work without modification. However, because of this lenient default implementation, the framework must be prepared to catch exceptions thrown by providers that reject the Key
object inside their initialization engine class implementations. The framework treats these cases the same as when supportsParameter()
returns false
.
Parameter Support
The Java Cryptography framework may attempt a fast check to determine whether a provider's service implementation can use an application-specified parameter. To perform this fast check, the framework calls Provider.Service.supportsParameter()
.
The framework relies on this fast test during delayed provider selection (see Delayed Provider Selection). When an application invokes an initialization method and passes it a Key
object, the framework asks an underlying provider whether it supports the object by calling its Service.supportsParameter()
method. If supportsParameter()
returns false
, the framework can immediately remove that provider from consideration. If supportsParameter()
returns true
, the framework passes the Key
object to that provider's initialization engine class implementation. A provider that requires software Key
objects should override this method to return false
when it is passed non-software keys. Likewise, a provider for a PKCS#11 token that contains unextractable keys should only return true
for Key
objects that it created, and which therefore correspond to the keys on its respective token.
Note:
The default implementation ofsupportsParameter()
returns true
. This allows existing providers to work without modification. However, because of this lenient default implementation, the framework must be prepared to catch exceptions thrown by providers that reject the Key
object inside their initialization engine class implementations. The framework treats these cases the same as when supportsParameter()
returns false
.
SunPKCS11 Provider Supported Algorithms
Note:
SunPKCS11 can be instructed to ignore mechanisms by using thedisabledMechanisms
and
enabledMechanisms
configuration directives (see SunPKCS11 Configuration).
For Elliptic Curve
mechanisms, the SunPKCS11 provider will only use keys that use the
namedCurve
choice as encoding for the parameters and only allow
the uncompressed point format. The SunPKCS11 provider assumes that a token supports
all standard named domain parameters.
Note:
For Elliptic Curve (EC) names, the SunPKCS11 provider supports any EC name that the SunEC provider supports as long as the token supports it; see Supported Elliptic Curve Names in The SunEC Provider.Table 5-3 Java Algorithms Supported by the SunPKCS11 Provider
Java Algorithm | PKCS#11 Mechanisms |
---|---|
Cipher.AES_128/CBC/NoPadding | CKM_AES_CBC |
Cipher.AES_128/ECB/NoPadding | CKM_AES_ECB |
Cipher.AES_128/GCM/NoPadding | CKM_AES_GCM |
Cipher.AES_192/CBC/NoPadding | CKM_AES_CBC |
Cipher.AES_192/ECB/NoPadding | CKM_AES_ECB |
Cipher.AES_192/GCM/NoPadding | CKM_AES_GCM |
Cipher.AES_256/CBC/NoPadding | CKM_AES_CBC |
Cipher.AES_256/ECB/NoPadding | CKM_AES_ECB |
Cipher.AES_256/GCM/NoPadding | CKM_AES_GCM |
Cipher.AES/CBC/NoPadding | CKM_AES_CBC |
Cipher.AES/CBC/PKCS5Padding | CKM_AES_CBC_PAD, CKM_AES_CBC |
Cipher.AES/CTR/NoPadding | CKM_AES_CTR |
Cipher.AES/ECB/NoPadding | CKM_AES_ECB |
Cipher.AES/ECB/PKCS5Padding | CKM_AES_ECB |
Cipher.AES/GCM/NoPadding | CKM_AES_GCM |
Cipher.ARCFOUR | CKM_RC4 |
Cipher.Blowfish/CBC/NoPadding | CKM_BLOWFISH_CBC |
Cipher.Blowfish/CBC/PKCS5Padding | CKM_BLOWFISH_CBC |
Cipher.DES/CBC/NoPadding | CKM_DES_CBC |
Cipher.DES/CBC/PKCS5Padding | CKM_DES_CBC_PAD, CKM_DES_CBC |
Cipher.DES/ECB/NoPadding | CKM_DES_ECB |
Cipher.DES/ECB/PKCS5Padding | CKM_DES_ECB |
Cipher.DESede/CBC/NoPadding | CKM_DES3_CBC |
Cipher.DESede/CBC/PKCS5Padding | CKM_DES3_CBC_PAD, CKM_DES3_CBC |
Cipher.DESede/ECB/NoPadding | CKM_DES3_ECB |
Cipher.DESede/ECB/PKCS5Padding | CKM_DES3_ECB |
Cipher.RSA/ECB/NoPadding | CKM_RSA_X_509 |
Cipher.RSA/ECB/PKCS1Padding | CKM_RSA_PKCS |
KeyAgreement.DiffieHellman | CKM_DH_PKCS_DERIVE |
KeyAgreement.ECDH | CKM_ECDH1_DERIVE |
KeyFactory.DiffieHellman | Any supported Diffie-Hellman mechanism |
KeyFactory.DSA | Any supported DSA mechanism |
KeyFactory.EC | Any supported EC mechanism |
KeyFactory.RSA | Any supported RSA mechanism |
KeyGenerator.AES | CKM_AES_KEY_GEN |
KeyGenerator.ARCFOUR | CKM_RC4_KEY_GEN |
KeyGenerator.Blowfish | CKM_BLOWFISH_KEY_GEN |
KeyGenerator.DES | CKM_DES_KEY_GEN |
KeyGenerator.DESede | CKM_DES3_KEY_GEN |
KeyPairGenerator.DiffieHellman | CKM_DH_PKCS_KEY_PAIR_GEN |
KeyPairGenerator.DSA | CKM_DSA_KEY_PAIR_GEN |
KeyPairGenerator.EC | CKM_EC_KEY_PAIR_GEN |
KeyPairGenerator.RSA | CKM_RSA_PKCS_KEY_PAIR_GEN |
KeyStore.PKCS11 | Always available |
Mac.HmacMD5 | CKM_MD5_HMAC |
Mac.HmacSHA1 | CKM_SHA_1_HMAC |
Mac.HmacSHA224 | CKM_SHA224_HMAC |
Mac.HmacSHA256 | CKM_SHA256_HMAC |
Mac.HmacSHA384 | CKM_SHA384_HMAC |
Mac.HmacSHA512 | CKM_SHA512_HMAC |
MAC.HmacSHA512/224 | CKM_SHA512_224_HMAC |
MAC.HmacSHA512/256 | CKM_SHA512_256_HMAC |
MessageDigest.MD2 | CKM_MD2 |
MessageDigest.MD5 | CKM_MD5 |
MessageDigest.SHA1 | CKM_SHA_1 |
MessageDigest.SHA-224 | CKM_SHA224 |
MessageDigest.SHA-256 | CKM_SHA256 |
MessageDigest.SHA-384 | CKM_SHA384 |
MessageDigest.SHA-512 | CKM_SHA512 |
MessageDigest.SHA-512/224 | CKM_SHA512_224 |
MessageDigest.SHA-512/256 | CKM_SHA512_256 |
SecretKeyFactory.AES | CKM_AES_CBC |
SecretKeyFactory.ARCFOUR | CKM_RC4 |
SecretKeyFactory.Blowfish | CKM_BLOWFISH_CBC |
SecretKeyFactory.DES | CKM_DES_CBC |
SecretKeyFactory.DESede | CKM_DES3_CBC |
SecureRandom.PKCS11 | CK_TOKEN_INFO has the CKF_RNG bit set |
Signature.MD2withRSA | CKM_MD2_RSA_PKCS, CKM_RSA_PKCS, CKM_RSA_X_509 |
Signature.MD5withRSA | CKM_MD5_RSA_PKCS, CKM_RSA_PKCS, CKM_RSA_X_509 |
Signature.NONEwithDSA | CKM_DSA |
Signature.NONEwithECDSA | CKM_ECDSA |
Signature.RSASSA-PSS | CKM_RSA_PKCS_PSS |
Signature.SHA1withDSA | CKM_DSA_SHA1, CKM_DSA |
Signature.SHA1withECDSA | CKM_ECDSA_SHA1, CKM_ECDSA |
Signature.SHA1withRSA | CKM_SHA1_RSA_PKCS, CKM_RSA_PKCS, CKM_RSA_X_509 |
Signature.SHA1withRSASSA-PSS | CKM_SHA1_RSA_PKCS_PSS |
Signature.SHA224withDSA | CKM_DSA_SHA224 |
Signature.SHA224withECDSA | CKM_ECDSA |
Signature.SHA224withRSA | CKM_SHA224_RSA_PKCS, CKM_RSA_PKCS, CKM_RSA_X_509 |
Signature.SHA224withRSASSA-PSS | CKM_SHA224_RSA_PKCS_PSS |
Signature.SHA256withDSA | CKM_DSA_SHA256 |
Signature.SHA256withECDSA | CKM_ECDSA |
Signature.SHA256withRSA | CKM_SHA256_RSA_PKCS, CKM_RSA_PKCS, CKM_RSA_X_509 |
Signature.SHA256withRSASSA-PSS | CKM_SHA256_RSA_PKCS_PSS |
Signature.SHA384withDSA | CKM_DSA_SHA384 |
Signature.SHA384withECDSA | CKM_ECDSA |
Signature.SHA384withRSA | CKM_SHA384_RSA_PKCS, CKM_RSA_PKCS, CKM_RSA_X_509 |
Signature.SHA384withRSASSA-PSS | CKM_SHA384_RSA_PKCS_PSS |
Signature.SHA512withDSA | CKM_DSA_SHA512 |
Signature.SHA512withECDSA | CKM_ECDSA |
Signature.SHA512withRSA | CKM_SHA512_RSA_PKCS, CKM_RSA_PKCS, CKM_RSA_X_509 |
Signature.SHA512withRSASSA-PSS | CKM_SHA512_RSA_PKCS_PSS |
SunPKCS11 Provider KeyStore Requirements
The following describes the requirements placed by the SunPKCS11 provider's KeyStore implementation on the underlying native PKCS#11 library.
Note:
Changes may be made in future releases to maximize interoperability with as many existing PKCS#11 libraries as possible.Read-Only Access
To map existing objects stored on a PKCS#11 token to KeyStore entries, the SunPKCS11 provider's KeyStore implementation performs the following operations.
- A search for all private key objects on the token is performed by calling
C_FindObjects[Init|Final]
. The search template includes the following attributes:- CKA_TOKEN = true
- CKA_CLASS = CKO_PRIVATE_KEY
- A search for all certificate objects on the token is performed by calling
C_FindObjects[Init|Final]
. The search template includes the following attributes:- CKA_TOKEN = true
- CKA_CLASS = CKO_CERTIFICATE
- Each private key object is matched with its corresponding certificate by retrieving their respective CKA_ID attributes. A matching pair must share the same unique CKA_ID.
For each matching pair, the certificate chain is built by following the issuer->subject path. From the end entity certificate, a call for
C_FindObjects[Init|Final]
is made with a search template that includes the following attributes:- CKA_TOKEN = true
- CKA_CLASS = CKO_CERTIFICATE
- CKA_SUBJECT = [DN of certificate issuer]
This search is continued until either no certificate for the issuer is found, or until a self-signed certificate is found. If more than one certificate is found the first one is used.
Once a private key and certificate have been matched (and its certificate chain built), the information is stored in a private key entry with the CKA_LABEL value from end entity certificate as the KeyStore alias.
If the end entity certificate has no CKA_LABEL, then the alias is derived from the CKA_ID. If the CKA_ID can be determined to consist exclusively of printable characters, then a String alias is created by decoding the CKA_ID bytes using the UTF-8 charset. Otherwise, a hex String alias is created from the CKA_ID bytes ("0xFFFF...", for example).
If multiple certificates share the same CKA_LABEL, then the alias is derived from the CKA_LABEL plus the end entity certificate issuer and serial number (
"MyCert/CN=foobar/1234"
, for example). - Each certificate not part of a private key entry (as the end entity certificate) is
checked whether it is trusted. If the CKA_TRUSTED attribute is true, then a
KeyStore trusted certificate entry is created with the CKA_LABEL value as the
KeyStore alias. If the certificate has no CKA_LABEL, or if multiple certificates
share the same CKA_LABEL, then the alias is derived as described previously.
If the CKA_TRUSTED attribute is not supported then no trusted certificate entries are created.
- Any private key or certificate object not part of a private key entry or trusted certificate entry is ignored.
- A search for all secret key objects on the token is performed by calling
C_FindObjects[Init|Final]
. The search template includes the following attributes:- CKA_TOKEN = true
- CKA_CLASS = CKO_SECRET_KEY
A KeyStore secret key entry is created for each secret key object, with the CKA_LABEL value as the KeyStore alias. Each secret key object must have a unique CKA_LABEL.
Write Access
To create new KeyStore entries on a PKCS#11 token to KeyStore entries, the SunPKCS11 provider's KeyStore implementation performs the following operations.
- When creating a KeyStore entry (during KeyStore.setEntry, for example), C_CreateObject is called with
CKA_TOKEN=true
to create token objects for the respective entry contents.Private key objects are stored with
CKA_PRIVATE=true
. The KeyStore alias (UTF8-encoded) is set as the CKA_ID for both the private key and the corresponding end entity certificate. The KeyStore alias is also set as the CKA_LABEL for the end entity certificate object.Each certificate in a private key entry's chain is also stored. The CKA_LABEL is not set for CA certificates. If a CA certificate is already in the token, a duplicate is not stored.
Secret key objects are stored with
CKA_PRIVATE=true
. The KeyStore alias is set as the CKA_LABEL. - If an attempt is made to convert a session object to a token object (for example, if KeyStore.setEntry is called and the private key object in the specified entry is a session object), then C_CopyObject is called with
CKA_TOKEN=true
. - If multiple certificates in the token are found to share the same CKA_LABEL, then the write capabilities to the token are disabled.
- Since the PKCS#11 specification does not allow regular applications to set
CKA_TRUSTED=true
(only token initialization applications may do so), trusted certificate entries can not be created.
Miscellaneous
In addition to the searches listed previously, the following searches may be used by
the SunPKCS11 provider's KeyStore implementation to perform internal functions.
Specifically, C_FindObjects[Init|Final]
may be called with any of
the following attribute templates:
-
CKA_TOKEN true CKA_CLASS CKO_CERTIFICATE CKA_SUBJECT [subject DN]
-
CKA_TOKEN true CKA_CLASS CKO_SECRET_KEY CKA_LABEL [label]
-
CKA_TOKEN true CKA_CLASS CKO_CERTIFICATE or CKO_PRIVATE_KEY CKA_ID [cka_id]
Example Provider
The following is an example of a simple provider that demonstrates features of the Provider class.
package com.foo;
import java.io.*;
import java.lang.reflect.*;
import java.security.*;
import javax.crypto.*;
/**
* Example provider that demonstrates some Provider class features.
*
* . Implement multiple different algorithms in a single class.
* Previously each algorithm needed to be implemented in a separate class
* (e.g. one for SHA-256, one for SHA-384, etc.)
*
* . Multiple concurrent instances of the provider frontend class each
* associated with a different backend.
*
* . It uses "unextractable" keys and lets the framework know which key
* objects it can and cannot support
*
* Note that this is only a simple example provider designed to demonstrate
* several of the new features. It is not explicitly designed for efficiency.
*/
public final class ExampleProvider extends Provider {
// Reference to the crypto backend that implements all the algorithms.
final CryptoBackend cryptoBackend;
public ExampleProvider(String name, CryptoBackend cryptoBackend) {
super(name, 1.0, "JCA/JCE provider for " + name);
this.cryptoBackend = cryptoBackend;
// register the algorithms we support (SHA-256, SHA-384, DESede, and AES)
putService(new MyService
(this, "MessageDigest", "SHA-256", "com.foo.ExampleProvider$MyMessageDigest"));
putService(new MyService
(this, "MessageDigest", "SHA-384", "com.foo.ExampleProvider$MyMessageDigest"));
putService(new MyCipherService
(this, "Cipher", "DES", "com.foo.ExampleProvider$MyCipher"));
putService(new MyCipherService
(this, "Cipher", "AES", "com.foo.ExampleProvider$MyCipher"));
}
// The API of our fictitious crypto backend.
static abstract class CryptoBackend {
abstract byte[] digest(String algorithm, byte[] data);
abstract byte[] encrypt(String algorithm, KeyHandle key, byte[] data);
abstract byte[] decrypt(String algorithm, KeyHandle key, byte[] data);
abstract KeyHandle createKey(String algorithm, byte[] keyData);
}
// The shell of the representation the crypto backend uses for keys.
private static final class KeyHandle {
// fill in code
}
// We have our own ServiceDescription implementation that overrides newInstance()
// that calls the (Provider, String) constructor instead of the no-args constructor.
private static class MyService extends Service {
private static final Class[] paramTypes = {Provider.class, String.class};
MyService(Provider provider, String type, String algorithm,
String className) {
super(provider, type, algorithm, className, null, null);
}
public Object newInstance(Object param) throws NoSuchAlgorithmException {
try {
// Get the Class object for the implementation class.
Class clazz;
Provider provider = getProvider();
ClassLoader loader = provider.getClass().getClassLoader();
if (loader == null) {
clazz = Class.forName(getClassName());
} else {
clazz = loader.loadClass(getClassName());
}
// Fetch the (Provider, String) constructor.
Constructor cons = clazz.getConstructor(paramTypes);
// Invoke constructor and return the SPI object.
Object obj = cons.newInstance(new Object[] {provider, getAlgorithm()});
return obj;
} catch (Exception e) {
throw new NoSuchAlgorithmException("Could not instantiate service", e);
}
}
}
// Custom ServiceDescription class for Cipher objects. See supportsParameter().
private static class MyCipherService extends MyService {
MyCipherService(Provider provider, String type, String algorithm,
String className) {
super(provider, type, algorithm, className);
}
// We override supportsParameter() to let the framework know which
// keys we can support. We support instances of MySecretKey, if they
// are stored in our provider backend, plus SecretKeys with a RAW encoding.
public boolean supportsParameter(Object obj) {
if (obj instanceof SecretKey == false) {
return false;
}
SecretKey key = (SecretKey)obj;
if (key.getAlgorithm().equals(getAlgorithm()) == false) {
return false;
}
if (key instanceof MySecretKey) {
MySecretKey myKey = (MySecretKey)key;
return myKey.provider == getProvider();
} else {
return "RAW".equals(key.getFormat());
}
}
}
// Our generic MessageDigest implementation. It implements all digest
// algorithms in a single class. We only implement the bare minimum
// of MessageDigestSpi methods.
private static final class MyMessageDigest extends MessageDigestSpi {
private final ExampleProvider provider;
private final String algorithm;
private ByteArrayOutputStream buffer;
MyMessageDigest(Provider provider, String algorithm) {
super();
this.provider = (ExampleProvider)provider;
this.algorithm = algorithm;
engineReset();
}
protected void engineReset() {
buffer = new ByteArrayOutputStream();
}
protected void engineUpdate(byte b) {
buffer.write(b);
}
protected void engineUpdate(byte[] b, int ofs, int len) {
buffer.write(b, ofs, len);
}
protected byte[] engineDigest() {
byte[] data = buffer.toByteArray();
byte[] digest = provider.cryptoBackend.digest(algorithm, data);
engineReset();
return digest;
}
}
// our generic Cipher implementation, only partially complete. It implements
// all cipher algorithms in a single class. We implement only as many of the
// CipherSpi methods as required to show how it could work
private static abstract class MyCipher extends CipherSpi {
private final ExampleProvider provider;
private final String algorithm;
private int opmode;
private MySecretKey myKey;
private ByteArrayOutputStream buffer;
MyCipher(Provider provider, String algorithm) {
super();
this.provider = (ExampleProvider)provider;
this.algorithm = algorithm;
}
protected void engineInit(int opmode, Key key, SecureRandom random)
throws InvalidKeyException {
this.opmode = opmode;
myKey = MySecretKey.getKey(provider, algorithm, key);
if (myKey == null) {
throw new InvalidKeyException();
}
buffer = new ByteArrayOutputStream();
}
protected byte[] engineUpdate(byte[] b, int ofs, int len) {
buffer.write(b, ofs, len);
return new byte[0];
}
protected int engineUpdate(byte[] b, int ofs, int len, byte[] out, int outOfs) {
buffer.write(b, ofs, len);
return 0;
}
protected byte[] engineDoFinal(byte[] b, int ofs, int len) {
buffer.write(b, ofs, len);
byte[] in = buffer.toByteArray();
byte[] out;
if (opmode == Cipher.ENCRYPT_MODE) {
out = provider.cryptoBackend.encrypt(algorithm, myKey.handle, in);
} else {
out = provider.cryptoBackend.decrypt(algorithm, myKey.handle, in);
}
buffer = new ByteArrayOutputStream();
return out;
}
// code for remaining CipherSpi methods goes here
}
// our SecretKey implementation. All our keys are stored in our crypto
// backend, we only have an opaque handle available. There is no
// encoded form of these keys.
private static final class MySecretKey implements SecretKey {
final String algorithm;
final Provider provider;
final KeyHandle handle;
MySecretKey(Provider provider, String algorithm, KeyHandle handle) {
super();
this.provider = provider;
this.algorithm = algorithm;
this.handle = handle;
}
public String getAlgorithm() {
return algorithm;
}
public String getFormat() {
return null; // this key has no encoded form
}
public byte[] getEncoded() {
return null; // this key has no encoded form
}
// Convert the given key to a key of the specified provider, if possible
static MySecretKey getKey(ExampleProvider provider, String algorithm, Key key) {
if (key instanceof SecretKey == false) {
return null;
}
// algorithm name must match
if (!key.getAlgorithm().equals(algorithm)) {
return null;
}
// if key is already an instance of MySecretKey and is stored
// on this provider, return it right away
if (key instanceof MySecretKey) {
MySecretKey myKey = (MySecretKey)key;
if (myKey.provider == provider) {
return myKey;
}
}
// otherwise, if the input key has a RAW encoding, convert it
if (!"RAW".equals(key.getFormat())) {
return null;
}
byte[] encoded = key.getEncoded();
KeyHandle handle = provider.cryptoBackend.createKey(algorithm, encoded);
return new MySecretKey(provider, algorithm, handle);
}
}
}