How to Implement a Provider for the JavaTM Cryptography Extension 1.2.1


Introduction
Who Should Read This Document
Related Documentation
A Note on Terminology
What's New in JCE 1.2.1

 
Engine Classes and Corresponding SPI Classes

 
Steps to Implement and Integrate a Provider
Step 1: Write your Service Implementation Code
Step 2: Give your Provider a Name
Step 3: Write your "Master Class," a subclass of Provider
Step 4: Compile your Code
Step 5: Prepare for Testing
Step 6: Write and Compile Test Programs
Step 7: Run your Test Programs
Step 8: Document your Provider and its Supported Services
Step 9: Prepare for Production
Step 10: Run your Test Programs Again
Step 11: Make Your Provider Software and Documentation Available to Clients
How a Provider Can Authenticate JCE 1.2.1 and Do Self-Integrity Checking

 
Further Implementation Details and Requirements
Algorithm Aliases
Service Interdependencies
Default Initializations
Diffie-Hellman Interfaces and their Required Implementations
Algorithm Parameter Specification Classes
Key Specification Classes Required by Key Factories
Secret-Key Generation
Ensuring Exportablity
How to Implement an Exemption Mechanism
Appendix A: The "IBMJCE" Provider's Master Class

 
Appendix B: The java.security Master Properties File

Introduction

The JavaTM Cryptography Extension (JCE) 1.2.1 is an optional package (formerly known as an "extension") to the JavaTM2 Platform. JCE 1.2.1 supplements the cryptographic services defined in the JavaTM 2 SDK (J2SDK), Standard Edition, v 1.2 by adding support for ciphers, key agreement, and message authentication codes.

JCE 1.2.1 is based on the same design principles as J2SDK v 1.2, which uses the notion of a Cryptographic Service Provider, or "provider" for short. This term refers to a package (or a set of packages) that supply a concrete implementation of a subset of the cryptography aspects of the Java Security API in J2SDK v 1.2.

JCE 1.2.1 extends the list of cryptographic services of which a provider can supply implementations. A provider could, for example, contain an implementation of one or more digital signature algorithms and one or more cipher algorithms.

A program wishing to use cryptography functionality may simply request a particular type of object (such as a Cipher object) implementing a particular algorithm (such as DES) and get an implementation from one of the installed providers. If an implementation from a particular provider is desired, the program can request that provider by name, along with the algorithm desired.

Each J2SDK installation has one or more provider packages installed. Each provider package supplies implementations of cryptographic services defined in J2SDK v 1.3 and/or JCE 1.2.1. Only when the JCE 1.2.1 framework is installed can the JCE-specific implementations supplied by provider packages be accessed.

Clients may configure their runtimes with different providers, and specify a preference order for each of them. The preference order is the order in which providers are searched for requested algorithms when no particular provider is requested.

The JCE 1.2.1 release comes standard with a provider named "IBMJCE", which supplies the following services:

New providers may be added statically or dynamically. Clients may also query which providers are currently installed.

The different implementations may have different characteristics. Some may be software-based, while others may be hardware-based. Some may be platform-independent, while others may be platform-specific. Some provider source code may be available for review and evaluation, while some may not.

Who Should Read This Document

Programmers that only need to use the Java Security API to access existing cryptography algorithms and other services do not need to read this document.

This document is intended for developers of cryptographic service providers. It documents what you need to do in order to integrate your provider into Java Security so that your algorithms and other services can be found when Java Security API clients request them.

Only providers signed by a trusted entity can be plugged into the JCE 1.2.1 framework.

Related Documentation

This document assumes you have already read the following documents: It also discusses various classes and interfaces in the Java Security API. The complete reference documentation for the relevant Security API packages can be found in:

A Note on Terminology

The JCE 1.2.1 release includes two software components: Throughout this document, the term "JCE 1.2.1" refers to the JCE 1.2.1 framework. If the full release is mentioned, it will be referred to as "the JCE 1.2.1 release."

What's New in JCE 1.2.1

There are two categories of differences between JCE 1.2 and JCE 1.2.1 that affect providers, one substantial and one minor (and optionally implemented):

Exportability

The primary difference between JCE 1.2 and JCE 1.2.1 is that the JCE 1.2.1 framework and provider cryptography implementations are now exportable outside the U.S. and Canada if certain conditions are satisfied.

Export control restrictions by the U.S. Commerce Department prohibit frameworks for encryption services from being exported, unless appropriate mechanisms have been implemented to ensure that only qualified providers can be plugged into the framework. (Qualified providers include those approved for export and those certified for domestic use only. Qualified providers are signed by a trusted entity.)

The JCE 1.2.1 framework contains such mechanisms and is thus now exportable. It is transparent to application developers how providers are authenticated, and only qualified providers can be plugged into JCE 1.2.1.

The JCE 1.2.1 framework also enforces restrictions regarding the cryptographic algorithms and maximum cryptographic strength available to applets/applications in different jurisdiction contexts (locations). This makes the JCE 1.2.1 framework worldwide exportable and worldwide importable.

The cryptographic services your provider supplies can now be used throughout the world, if certain conditions are met. You can write just one version of your provider software, implementing cryptography of maximum strength. It is up to JCE 1.2.1, not your provider, to enforce restrictions regarding the cryptographic algorithms and maximum cryptographic strengths available. For further information as to how this affects applets and applications, see Application Exportability in the JavaTM Cryptography Extension 1.2.1 API Specification & Reference.

In order to be able to plug into JCE 1.2.1, a provider should follow certain implementation guidelines and must be signed by an entity trusted by JCE 1.2.1, as described in Ensuring Exportability.

A provider may also optionally implement one or more exemption mechanisms (such as key recovery) which, if utilized by an application (or applet), allows that application to use cryptography of greater strength than that allowed by default. See How to Implement an Exemption Mechanism.

Key Wrapping for Secure Transport

Another difference between JCE 1.2 and JCE 1.2.1 that affects providers is the addition of engineWrap and engineUnwrap methods in the CipherSpi class. Providers can optionally implement these methods for "wrapping" and "unwrapping" keys.

"Wrapping" a key enables secure transfer of the key from one place to another.

Implementation by a provider of the new CipherSpi engineWrap and engineUnwrap methods is optional.

Further information about wrapping and unwrapping keys is provided in the Wrapping and Unwrapping Keys section of the JavaTM Cryptography Extension 1.2.1 API Specification & Reference.

Engine Classes and Corresponding SPI Classes

An "engine class" defines a cryptographic service in an abstract fashion (without a concrete implementation).

A cryptographic service is always associated with a particular algorithm, and it either provides cryptographic operations (like those for ciphers or key agreement protocols), or generates or supplies the cryptographic material (keys or parameters) required for cryptographic operations. For example, two of the engine classes are the Cipher and KeyAgreement classes. The Cipher class provides access to the functionality of an encryption algorithm (such as DES), and the KeyAgreement class provides access to the functionality of a key agreement protocol (such as Diffie-Hellman).

The Java Cryptography Architecture encompasses the classes of the J2SE Java Security package related to cryptography, including the engine classes. Users of the API request and utilize instances of the engine classes to carry out corresponding operations.

JCE 1.2.1 is provided as a standard optional package (also known as an "extension") to the Java 2 Platform, and supplements the cryptographic services defined in J2SDK v 1.2 by adding the following engine classes:

An engine class provides the interface to the functionality of a specific type of cryptographic service (independent of a particular cryptographic algorithm). It defines "Application Programming Interface" (API) methods that allow applications to access the specific type of cryptographic service it provides. The actual implementations (from one or more providers) are those for specific algorithms. The Cipher engine class, for example, provides access to the functionality of a cipher algorithm. The actual implementation supplied in a CipherSpi subclass (see next paragraph) would be that for a specific kind of encryption algorithm, such as DES or Triple DES.

The application interfaces supplied by an engine class are implemented in terms of a "Service Provider Interface" (SPI). That is, for each engine class, there is a corresponding abstract SPI class, which defines the Service Provider Interface methods that cryptographic service providers must implement.

An instance of an engine class, the "API object", encapsulates (as a private field) an instance of the corresponding SPI class, the "SPI object". All API methods of an API object are declared "final", and their implementations invoke the corresponding SPI methods of the encapsulated SPI object. An instance of an engine class (and of its corresponding SPI class) is created by a call to the getInstance factory method of the engine class.

The name of each SPI class is the same as that of the corresponding engine class, followed by "Spi". For example, the SPI class corresponding to the Cipher engine class is the CipherSpi class.

Each SPI class is abstract. To supply the implementation of a particular type of service, for a specific algorithm, a provider must subclass the corresponding SPI class and provide implementations for all the abstract methods.

Another example of an engine class is the KeyAgreement class, which provides access to a key agreement (key exchange) algorithm. Its implementations, in KeyAgreementSpi subclasses, may be those of various key agreement algorithms such as Diffie-Hellman.

As a final example, the SecretKeyFactory engine class supports the conversion from opaque secret keys to transparent key specifications, and vice versa. (See Key Specification Classes Required by Key Factories.) The actual implementation supplied in a SecretKeyFactorySpi subclass would be that for a specific type of secret keys, e.g., DES keys.

Steps to Implement and Integrate a Provider

The steps required in order to implement a provider and integrate it into JCE 1.2.1 are the following:
Step 1: Write your Service Implementation Code
Step 2: Give your Provider a Name
Step 3: Write your "Master Class," a subclass of Provider
Step 4: Compile your Code
Step 5: Prepare for Testing
Step 5a: Get a Code-Signing Certificate for Testing
Step 5b: Place Your Provider in a JAR File
Step 5c: Sign Your Provider for Testing
Step 5d: Install the Provider
Step 6: Write and Compile Test Programs
Step 7: Run your Test Programs
Step 8: Document your Provider and its Supported Services
Step 9: Prepare for Production
Step 9a (only for providers that may be exported): Apply for U.S. Government Export Approval
Step 9b: Get a Code-Signing Certificate for Production
Step 9c: Place Your Provider in a New JAR File
Step 9d: Sign Your Provider for Production
Step 10: Run your Test Programs Again
Step 11: Make Your Provider Software and Documentation Available to Clients

Step 1: Write your Service Implementation Code

The first thing you need to do is write the code supplying algorithm-specific implementations of the cryptographic services you want to support.

Note that your provider may supply implementations of cryptographic services defined in JCE 1.2.1 in addition to implementations of cryptographic services defined in J2SDK v 1.2.

In JCE 1.2.1, you can supply cipher, key agreement and MAC algorithms, as well as secret-key factories, secret-key generation services, and exemption mechanism implementations.

In J2SDK v 1.2, you can supply signature, message digest, key pair generation, and (pseudo-)random number generation algorithms, as well as key and certificate factories and keystore creation and management, algorithm parameter management, and algorithm parameter generation services.

For each cryptographic service in JCE 1.2.1 or J2SDK, you need to create a subclass of the appropriate SPI class. JCE 1.2.1 defines the following engine classes, in addition to the engine classes defined in J2SDK v 1.2 CipherSpi, KeyAgreementSpi, KeyGeneratorSpi, MacSpi, SecretKeyFactorySpi, and ExemptionMechanismSpi. (See "JCE 1.2.1 Engine Classes and Corresponding SPI Classes" and "JDK 1.2 Engine Classes and Corresponding SPI Classes".)

In your subclass, you need to

  1. supply implementations for the abstract methods, whose names usually begin with "engine". See Further Implementation Details and Requirements for additional information.
  2. ensure there is a public constructor without any arguments. Here's why: When one of your services is requested, Java Security looks up the subclass implementing that service, as specified by a property in your "master class" (see Step 3). Java Security then creates the Class object associated with your subclass, and creates an instance of your subclass by calling the newInstance method on that Class object. newInstance requires your subclass to have a public constructor without any parameters.
  3.  A default constructor without arguments will automatically be generated if your subclass doesn't have any constructors. But if your subclass defines any constructors, you must explicitly define a public constructor without arguments.

Additional JCE 1.2.1 Provider Requirements and Recommendations

When instantiating a provider's implementation (class) of a JCE service, the JCE 1.2.1 framework will determine the provider's codebase (JAR file) and verify its signature. In this way, JCE 1.2.1 authenticates the provider and ensures that only providers signed by a trusted entity can be plugged into JCE 1.2.1. Thus, one new requirement for providers in JCE 1.2.1 is that they must be signed, as described in later steps.

Similarly, each SPI implementation class should authenticate the JCE 1.2.1 framework in its constructor. This assures authorized providers of the integrity and authenticity of the JCE 1.2.1 that they plug into. In addition, each provider should perform self-integrity checking to ensure that the JAR file containing its code has not been manipulated in an attempt to invoke provider methods directly rather than through JCE 1.2.1. For further information, see How a Provider Can Authenticate JCE 1.2.1 and Do Self-Integrity Checking.

In order for provider classes to become unusable if instantiated by an application directly, bypassing JCE 1.2.1, providers should implement the following:

  • All SPI implementation classes in a provider package should be declared final (so that they cannot be subclassed), and their (SPI) implementation methods should be declared protected.
  • All crypto-related helper classes in a provider package should have package-private scope, so that they cannot be accessed from outside the provider package.
For providers that may be exported outside the U.S. and Canada, CipherSpi implementations must include an implementation of the new engineGetKeySize method which, given a Key, returns the key size. Each Cipher initialization method executed in a non-domestic release calls engineGetKeySize and then compares the result with the maximum allowable key size for the particular location and circumstances of the applet or application being run. If the key size is too large, the initialization method throws an Exception.

Additional optional features that providers may implement for JCE 1.2.1 are

  • the new engineWrap and engineUnwrap CipherSpi methods. See Key Wrapping for Secure Transport.
  • one or more "exemption mechanisms." An exemption mechanism is something such as key recovery, key escrow, or key weakening which, if implemented and enforced, may enable reduced cryptographic restrictions for an application (or applet) that utilizes it. See How to Implement an Exemption Mechanism.

Step 2: Give your Provider a Name

Decide on a name for your provider. This is the name to be used by client applications to refer to your provider.

Step 3: Write your "Master Class", a subclass of Provider

The third step is to create a subclass of the java.security.Provider class.

Your subclass should be a final class, and its constructor should

For further master class property setting examples, see Appendix A to view the current JCE 1.2.1 IBMJCE.java source file. This shows how the IBM class constructor sets all the properties for the "IBMJCE" provider.

Note: The Provider subclass can get its information from wherever it wants. Thus, the information can be hard-wired in, or retrieved at runtime, e.g., from a file.

As mentioned above, in the case of a Cipher property, algName may actually represent a transformation. A transformation is a string that describes the operation (or set of operations) to be performed by a Cipher object on some given input. A transformation always includes the name of a cryptographic algorithm (e.g., DES), and may be followed by a mode and a padding scheme.

A transformation is of the form:

(In the latter case, provider-specific default values for the mode and padding scheme are used). For example, the following is a valid transformation:
    Cipher c = Cipher.getInstance("DES/CBC/PKCS5Padding");
When requesting a block cipher in stream cipher mode (e.g., DES in CFB or OFB mode), a client may optionally specify the number of bits to be processed at a time, by appending this number to the mode name as shown in the following sample transformations:
    Cipher c1 = Cipher.getInstance("DES/CFB8/NoPadding");
    Cipher c2 = Cipher.getInstance("DES/OFB32/PKCS5Padding");
If a stream cipher mode is not followed by a number, a provider-specific default is used. (For example, the "IBMJCE" provider uses a default of 64 bits.)

A provider may supply a separate class for each combination of algorithm/mode/padding, or may decide to provide more generic classes representing sub-transformations corresponding to algorithm or algorithm/mode or algorithm//padding (note the double slashes), in which case the requested mode and/or padding are set automatically by the getInstance methods of Cipher, which invoke the engineSetMode and engineSetPadding methods of the provider's subclass of CipherSpi.

That is, a Cipher property in a provider master class may have one of the following formats:

(See Appendix A of the Java Cryptography Extension 1.2.1 API Specification & Reference for the standard algorithm names, modes, and padding schemes that should be used.)

For example, a provider may supply a subclass of CipherSpi that implements DES/ECB/PKCS5Padding, one that implements DES/CBC/PKCS5Padding, one that implements DES/CFB/PKCS5Padding, and yet another one that implements DES/OFB/PKCS5Padding. That provider would have the following Cipher properties in its master class:

Another provider may implement a class for each of the above modes (i.e., one class for ECB, one for CBC, one for CFB, and one for OFB), one class for PKCS5Padding, and a generic DES class that subclasses from CipherSpi. That provider would have the following Cipher properties in its master class: The getInstance factory method of the Cipher engine class follows these rules in order to instantiate a provider's implementation of CipherSpi for a transformation of the form "algorithm":
  1. Check if the provider has registered a subclass of CipherSpi for the specified "algorithm".

  2. If the answer is YES, instantiate this class, for whose mode and padding scheme default values (as supplied by the provider) are used.
    If the answer is NO, throw a NoSuchAlgorithmException exception.
The getInstance factory method of the Cipher engine class follows these rules in order to instantiate a provider's implementation of CipherSpi for a transformation of the form "algorithm/mode/padding":
  1. Check if the provider has registered a subclass of CipherSpi for the specified "algorithm/mode/padding" transformation.

  2. If the answer is YES, instantiate it.
    If the answer is NO, go to the next step.
  3. Check if the provider has registered a subclass of CipherSpi for the sub-transformation "algorithm/mode".

  4. If the answer is YES, instantiate it, and call engineSetPadding(padding) on the new instance.
    If the answer is NO, go to the next step.
  5. Check if the provider has registered a subclass of CipherSpi for the sub-transformation "algorithm//padding" (note the double slashes).

  6. If the answer is YES, instantiate it, and call engineSetMode(mode) on the new instance.
    If the answer is NO, go to the next step.
  7. Check if the provider has registered a subclass of CipherSpi for the sub-transformation "algorithm".

  8. If the answer is YES, instantiate it, and call engineSetMode(mode) and engineSetPadding(padding) on the new instance.
    If the answer is NO, throw a NoSuchAlgorithmException exception.

Step 4: Compile your Code

After you have created your implementation code (Step 1), given your provider a name (Step 2), and created the master class (Step 3), use the Java compiler to compile your files.

Step 5: Prepare for Testing

Step 5a: Get a Code-Signing Certificate for Testing

Request a code-signing certificate for testing only. This certificate has a limited validity period (30 days). It is provided for testing only. You should not use it to sign your provider in the production environment.

Below are the steps you should use to get a code-signing certificate for testing. For more information on the keytool tool, see keytool (for Microsoft Windows).

  1. Use keytool to generate a DSA keypair.
  2.   keytool -genkey -alias <alias> -keyalg DSA -keysize 1024
        -dname "cn=<Company Name>,ou=JCE Code Signing CA, 
        o=IBM Corporation" 
        -keystore <keystore file name>
        -storepass <keystore password>
    (Please note: This must be typed as a single line. Multiple lines and indentation are used in the examples just for legibility purposes.)

    This will generate a DSA keypair (a public key and an associated private key) and store it in an entry in the specified keystore. The public key is stored in a self-signed certificate. The keystore entry can subsequently be accessed using the specified alias.

    The option values in angle brackets ("<" & ">") represent the actual values that must be supplied. For example, <alias> must be replaced with whatever alias name you wish to be used to refer to the newly-generated keystore entry in the future, and <keystore file name> must be replaced with the name of the keystore to be used. Note: Do not surround actual values with angle brackets. For example, if you want your alias to be myTestAlias, specify the -alias option as follows:

        -alias myTestAlias
    If you specify a keystore that doesn't yet exist, it will be created.

    Note: If command lines you type are not allowed to be as long as the keytool -genkey command you want to execute (for example, if you are typing to a Microsoft Windows DOS prompt), you can create and execute a plain-text batch file containing the command. That is, create a new text file that contains nothing but the full keytool -genkey command. (Remember to type it all on one line.) Save the file with a .bat extension. Then in your DOS window, type the file name (with its path, if necessary). This will cause the command in the batch file to be executed.
     
     

  3. Use keytool to generate a certificate signing request.
  4. keytool -certreq -alias <alias> -sigalg DSA 
            -file <csr file name> 
            -keystore <keystore file name> 
            -storepass <keystore password>
    Here, <alias> is the alias for the DSA keypair entry created in the previous step. This command generates a Certificate Signing Request (CSR), using the PKCS#10 format. It stores the CSR in the file whose name is specified in <csr file name>.
  5. Send the CSR and contact information to the JCE Code Signing Certification Authority.

  6. Send, via email, the CSR and contact information to javasec@us.ibm.com. Put the following in the Subject line of your email message:

      Request a Certificate for Signing a JCE Provider (Testing)
    Put the contact information in the body of the message and send the CSR file as a plain text attachment to the message. If your mail tool has an option for specifying the encoding format to be used for attachments, select the "MIME" option. Note: The CSR file is just a plain text file, in Base 64 encoding. Only the first and last lines are human-readable.

    Include the following contact information in the body of your message:

    Company Name
    Street Address (Not a post office box)
    City
    State/Province
    Country
    Company Telephone Number
    Company Fax Number
    Company Home Page URL 
    Requester Name
    Requester Telephone Number
    Requester Email Address
    Brief description of your company (size, 
        line of business, etc.)
    All of the above information is required, except for the home page URL.

    The JCE Code Signing Certification Authority will authenticate you, the requester. Then it will create and sign a code-signing certificate for testing, valid for 30 days. It will send you an email message containing two plain-text file attachments: one file containing this code-signing certificate for testing and another file containing its own CA certificate, which authenticates its public key.
     

  7. Use keytool to import the certificates received from the CA.

  8. Once you have received the two certificates from the JCE Code Signing Certification Authority, you can use keytool to import them into your keystore.

    First import the CA's certificate as a "trusted certificate":

    keytool -import -alias <alias for the CA cert> 
            -file <CA cert file name> 
            -keystore <keystore file name> 
            -storepass <keystore password>
    Then import the code-signing certificate:
    keytool -import -alias <alias> 
            -file <code-signing cert file name>
            -keystore <keystore file name> 
            -storepass <keystore password>
    Here, <alias> is the same alias as that which you created in step 1 where you generated a DSA keypair. This command replaces the self-signed certificate in the keystore entry specified by <alias> with the one signed by the JCE Code Signing Certification Authority.
Now that you have in your keystore a certificate from an entity trusted by JCE 1.2.1 (the JCE Code Signing Certification Authority), you can place your provider code in a JAR file (Step 5b) and then use that certificate to sign the JAR file (Step 5c)

Step 5b: Place Your Provider in a JAR File

Place your provider code in a JAR file, in preparation for signing it in the next step. For more information on the jar tool, see jar (for Microsoft Windows).
jar cvf <JAR file name> <list of classes, separated by spaces>
This command creates a JAR file with the specified name containing the specified classes.

Step 5c: Sign Your Provider for Testing

Sign the JAR file created in the previous step with the code-signing certificate obtained in Step 5a. For more information on the jarsigner tool, see jarsigner (for Microsoft Windows).
jarsigner -keystore <keystore file name> 
          -storepass <keystore password>
          <JAR file name> <alias>
Here, <alias> is the alias into the keystore for the entry containing the code-signing certificate received from the JCE Code Signing Certification Authority (the same alias as that specified in the commands in Step 5a).

You can test verification of the signature via the following:

jarsigner -verify <JAR file name>
The text "jar verified" will be displayed if the verification was successful.

Step 5d: Install the Provider

In order to prepare for testing your provider, you must install it in the same manner as will be done by clients wishing to use it. The installation enables Java Security to find your algorithm implementations when clients request them.

There are two parts to installing a provider: installing the provider package classes, and configuring the provider.

Installing the Provider Classes

The first thing you must do is make your classes available so that they can be found when requested. You ship your provider classes as a JAR (Java ARchive) file.

Note that in order for your provider to work, you must also have JCE 1.2.1 (more specifically: the jce1_2_1.jar extension) installed. Note: The jce1_2_1.jar file must always be supplied as an "installed" extension on the target Java Virtual Machine (JVM) where your provider classes will be accessed.

There are a couple possible ways of installing your provider classes:

  • Place a JAR file containing your own provider classes anywhere on your CLASSPATH.
  • Supply your own provider JAR file as an "installed" or "bundled" extension.
For more information on "installed" extensions, see Installed Extensions.

For more information on "bundled" extensions, see Bundled Extensions.

Configuring the Provider

The next step is to add the provider to your list of approved providers. This is done statically by editing the security properties file
<java-home>\lib\security\java.security         [Win32]
Here <java-home> refers to the directory where the JRE was installed. For example, if you have J2SDK v 1.2.2 installed on Windows in a directory named jdk1.2.2, you need to edit the following file:
jdk1.2.2/jre/lib/security/java.security
Similarly, if you have Java 2 Runtime Environment v 1.2 installed on Windows in a directory named jre1.2, you need to edit this file:
jre1.2/lib/security/java.security
For each provider, this file should have a statement of the following form:
    security.provider.n=masterClassName
This declares a provider, and specifies its preference order n. The preference order is the order in which providers are searched for requested algorithms when no specific provider is requested. The order is 1-based; 1 is the most preferred, followed by 2, and so on.

masterClassName must specify the fully qualified name of the provider's "master class", which you implemented in Step 3. This class is always a subclass of the Provider class.

J2SDK comes standard with a provider named "SUN", which is automatically configured as a static provider in the java.security properties file, as follows:

    security.provider.1=sun.security.provider.Sun
(The "SUN" provider's master class is the Sun class in the sun.security.provider package.)

Note that when you install JCE 1.2.1, its provider ("IBMJCE") is not automatically registered. That is, if you want to use the "IBMJCE" provider, you need to register it explicitly - either statically or dynamically, as described in this section.

Suppose that your master class is the CryptoX class in the com.cryptox.provider package, and that you would like to make your provider the second preferred provider. To do so, add the following line to the java.security file below the line for the "SUN" provider:

    security.provider.2=com.cryptox.provider.CryptoX
Note: Providers may also be registered dynamically. To do so, a program (such as your test program, to be written in Step 6) can call either the addProvider or insertProviderAt method in the Security class. This type of registration is not persistent and can only be done by "trusted" programs. See the Security class section of the Java Cryptography Architecture API Specification and Reference.

Step 6: Write and Compile your Test Programs

Write and compile one or more test programs that test your provider's incorporation into the Security API as well as the correctness of its algorithm(s). Create any supporting files needed, such as those for test data to be encrypted.

The first tests your program should perform are ones to ensure that your provider is found, and that its name, version number, and additional information is as expected. To do so, you could write code like the following, substituting your provider name for "MyPro":

    import java.security.*;

    Provider p = Security.getProvider("MyPro");
    
    System.out.println("MyPro provider name is " + p.getName());
    System.out.println("MyPro provider version # is " + p.getVersion());
    System.out.println("MyPro provider info is " + p.getInfo());
Next, you should ensure that your services are found. For instance, if you implemented the DES encryption algorithm, you could check to ensure it's found when requested by using the following code (again substituting your provider name for "MyPro"):
    Cipher c = Cipher.getInstance("DES", "MyPro");

    System.out.println("My Cipher algorithm name is " + c.getAlgorithm());
If you don't specify a provider name in the call to getInstance, all registered providers will be searched, in preference order (see Configuring the Provider), until one implementing the algorithm is found.

If your provider implements an exemption mechanism, you should write a test applet or application that utilizes the exemption mechanism. Such an applet/application also needs to be signed, and needs to have a "permission policy file" bundled with it. See How to Make Applications "Exempt" from Cryptographic Restrictions. in the JavaTM Cryptography Extension 1.2.1 API Specification & Reference for complete information about creating and testing such an application.

Step 7: Run your Test Programs

Run your test program(s). Debug your code and continue testing as needed. If the Java Security API cannot seem to find one of your algorithms, review the steps above and ensure they are all completed.

Step 8: Document your Provider and its Supported Services

The next step is to write documentation for your clients. At the minimum, you need to specify In addition, your documentation should specify anything else of interest to clients, such as any default algorithm parameters.

MACs

For each MAC algorithm, tell whether or not your implementation is cloneable. This is not technically necessary, but it may save clients some time and coding by telling them whether or not intermediate "message authentication codes" (MACs) may be possible through cloning. Clients who do not know whether or not a MAC implementation is cloneable can find out by attempting to clone the Mac object and catching the potential exception, as illustrated by the following example:
try {
    // try and clone it
    /* compute the MAC for i1 */
    mac.update(i1); 
    byte[] i1Mac = mac.clone().doFinal();

    /* compute the MAC for i1 and i2 */
    mac.update(i2); 
    byte[] i12Mac = mac.clone().doFinal(); 

    /* compute the MAC for i1, i2 and i3 */
    mac.update(i3); 
    byte[] i123Mac = mac.doFinal();
} catch (CloneNotSupportedException cnse) {
  // have to use an approach not involving cloning
}
where

Key Pair Generators

For a key pair generator algorithm, in case the client does not explicitly initialize the key pair generator (via a call to an initialize method), each provider must supply and document a default initialization. For example, the Diffie-Hellman key pair generator supplied by the "IBMJCE" provider uses a default prime modulus size (keysize) of 1024 bits.

Key Factories

A provider should document all the key specifications supported by its (secret-)key factory.

Algorithm Parameter Generators

In case the client does not explicitly initialize the algorithm parameter generator (via a call to an init method in the AlgorithmParameterGenerator engine class), each provider must supply and document a default initialization. For example, the "IBMJCE" provider uses a default prime modulus size (keysize) of 1024 bits for the generation of Diffie-Hellman parameters.

Step 9: Prepare for Production

Step 9a (only for providers that may be exported): Apply for U.S. Government Export Approval

Provider vendors whose providers may be exported outside the U.S. and Canada should apply to the Bureau of Export Administration in the U.S. Department of Commerce for export approval. Please consult your export counsel for more information.

Here are a couple URLs that may be of use to you:

Step 9b: Get a Code-Signing Certificate for Production

Below are the steps you should use to get a code-signing certificate for production. Note that they are basically the same as the steps used to get a code-signing certificate for testing (Step 5a), except that the certificate you receive for production will last a lot longer than the temporary certificate you received for testing, and before you will be issued a code-signing certificate for production you will need to either show proof of export approval or include a notarized statement that your provider will only be distributed inside the U.S. and Canada.
  1. Use keytool to generate a DSA keypair.
  2.   keytool -genkey -alias <prAlias> -keyalg DSA -keysize 1024
        -dname "cn=<Company Name>,ou=JCE Code Signing CA, 
        o=IBM Corporation" 
        -keystore <keystore file name>
        -storepass <keystore password>
    (Please note: This must be typed as a single line. Multiple lines and indentation are used in the examples just for legibility purposes.)

    This will generate a DSA keypair (a public key and an associated private key) and store it in an entry in the specified keystore. The public key is stored in a self-signed certificate. The keystore entry can subsequently be accessed using the alias specified in <prAlias>.

    As mentioned in the corresponding step for obtaining a code-signing certificate for testing (Step 5a), the option values in angle brackets ("<" & ">") represent the actual values that must be supplied. Also, if command lines you type are not allowed to be as long as the keytool -genkey command you want to execute, you can create and execute a plain-text batch file containing the command.
     
     

  3. Use keytool to generate a certificate signing request.
  4. keytool -certreq -alias <prAlias> -sigalg DSA 
            -file <csr file name> 
            -keystore <keystore file name> 
            -storepass <keystore password>
    Here, <prAlias> is the alias for the DSA keypair entry created in the previous step. This command generates a Certificate Signing Request (CSR), using the PKCS#10 format. It stores the CSR in the file whose name is specified in <csr file name>.
  5. Send the CSR, contact information, and proof of export approval or domestic-only certification to the JCE Code Signing Certification Authority.

  6. Send, via email, the CSR and contact information to javasec@us.ibm.com.

    Put the following in the Subject line of your email message:

      Request a Certificate for Signing a JCE Provider (Production)
    Put the contact information in the body of the message and send the CSR file as a plain text attachment to the message. If your mail tool has an option for specifying the encoding format to be used for attachments, select the "MIME" option. Note: The CSR file is just a plain text file, in Base 64 encoding. Only the first and last lines are human-readable.

    Include the following contact information in the body of your message:

    Company Name
    Street Address (Not a post office box)
    City
    State/Province
    Country
    Company Telephone Number
    Company Fax Number
    Company Home Page URL 
    Requester Name
    Requester Telephone Number
    Requester Email Address
    Brief description of your company (size, 
        line of business, etc.)
    After the JCE Code Signing Certification Authority has received your email message, it will send you a request number, also via email. After receiving this request number, mail required document(s) to the address specified below. The documents you need to send depend on whether or not the provider may be exported outside the U.S. and Canada.
     
     
    • If the provider may be exported, mail a copy of your proof of export approval received from the U.S. government in Step 9a.
    • If the provider will not be exported, mail a notarized letter stating that the provider will only be distributed inside the U.S. and Canada.
    In both cases, the document(s) should be mailed to the address below and you should include in your mailing a note indicating the request number so that your hardcopy mailing can be matched to your email message containing the CSR and contact information. The documents and request number should be mailed to
    Corporate Export
    Attn: Encryption export
    901 San Antonio Road, UPAL01-541
    Palo Alto, CA 94303
    After the JCE Code Signing Certification Authority has received both your email message and either the proof of export approval or domestic-only certification, it will authenticate you, the requester. Then it will create and sign a code-signing certificate for production, valid for 5 years. It will send you an email message containing two plain-text file attachments: one file containing this code-signing certificate for production and another file containing its own CA certificate, which authenticates its public key.
     
     
  7. Use keytool to import the certificates received from the CA.

  8. Once you have received the two certificates from the JCE Code Signing Certification Authority, you can use keytool to import them into your keystore.

    If you are using the same keystore as that used for testing, you will already have imported the CA's certificate as a "trusted certificate" in the fourth substep of Step 5a: Get a Code-Signing Certificate for Testing, so you don't need to do that again.

    If you are using a different keystore, you need to import the CA's certificate into your keystore as a "trusted certificate":

    keytool -import -alias <alias for the CA cert> 
            -file <CA cert file name> 
            -keystore <keystore file name> 
            -storepass <keystore password>
    In any case, then you should import the code-signing certificate:
    keytool -import -alias <prAlias> 
            -file <code-signing cert file name>
            -keystore <keystore file name> 
            -storepass <keystore password>
    Here, <prAlias> is the same alias as that which you created in step 1 where you generated a DSA keypair. This command replaces the self-signed certificate in the keystore entry specified by <prAlias> with the one signed by the JCE Code Signing Certification Authority.
Now that you have in your keystore a code-signing certificate for production from an entity trusted by JCE 1.2.1 (the JCE Code Signing Certification Authority), you can place your provider code in a JAR file (Step 9c) and then use that certificate to sign the JAR file (Step 9d)

Step 9c: Place Your Provider in a New JAR File

Create a new JAR file containing the provider code, to be signed in the next step using the official code-signing certificate.
jar cvf <JAR file name> <list of classes, separated by spaces>
This command creates a JAR file with the specified name containing the specified classes.

For efficiency reasons, it is best to create this new JAR file rather than simply adding the official production signature to the JAR file created in Step 5b that was signed using the testing certificate obtained in Step 5a. If the JAR file used in production still contains signatures generated by the test certificate, JCE 1.2.1 may waste time trying to verify those signatures.

Step 9d: Sign your Provider for Production

Sign the JAR file created in the previous step with the code-signing certificate obtained in Step 9b.
jarsigner -keystore <keystore file name> 
          -storepass <keystore password>
          <JAR file name> <prAlias>
Here, <prAlias> is the alias into the keystore for the entry containing the code-signing certificate for production received from the JCE Code Signing Certification Authority (the same alias as that specified in the commands in Step 9b).

You can test verification of the signature via the following:

jarsigner -verify <JAR file name>
The text "jar verified" will be displayed if the verification was successful.

Step 10: Run your Test Programs Again

Your provider should have been thoroughly tested in Step 7. However, we suggest you run your test programs again to make sure that everything still works fine since what you tested in Step 7 is signed by a different certificate.

Step 11: Make your Provider Software and Documentation Available to Clients

The final step is to make your provider software and documentation available to your customers.

How a Provider Can Authenticate JCE 1.2.1 and Do Self-Integrity Checking

In order to prevent unauthorized providers from plugging into the JCE 1.2.1 framework (herein referred to as "JCE 1.2.1"), and to assure authorized providers of the integrity and authenticity of the JCE 1.2.1 that they plug into, JCE 1.2.1 and its providers will engage in mutual authentication. Only providers that have authenticated JCE 1.2.1, and who in turn have been authenticated by JCE 1.2.1, will become usable in the JCE 1.2.1 environment.

In addition, each provider should do self-integrity checking to ensure that the JAR file containing its code has not been tampered with.

The JCE 1.2.1 framework will be digitally signed. Providers that provide implementations for JCE 1.2.1 services must also be digitally signed. Authentication includes verification of those signatures and ensuring the signatures were generated by trusted entities. Certain Certificate Authorities will be deemed to be "trusted" and any code signed using a certificate that can be traced up a certificate chain to a certificate for one of the trusted Certificate Authorities will be considered trusted. Both JCE 1.2.1 and provider packages should embed within themselves the bytes for the certificates for the relevant trusted Certificate Authorities. At runtime, the embedded certificates will be used in determining whether or not code is authentic. Currently, the trusted Certification Authority is IBM Corporations' JCE Code Signing CA. Please refer to Step 5a (for testing) and Step 9b (for production) for detailed information about how to get a code-signing certificate from IBM Corporations' JCE Code Signing CA and the certificate of that CA.

The mutual authentication between JCE 1.2.1 and its providers will not take place when the providers are registered, but later, when JCE 1.2.1 tries to instantiate a provider's implementation of a JCE 1.2.1 cryptographic service. That way, providers with implementations of cryptographic services defined in both J2SE and JCE 1.2.1 (e.g., providers with an implementation of java.security.SignatureSpi for the "DSA" algorithm and an implementation of javax.crypto.CipherSpi for "Blowfish") will be usable in the J2SE environment without having to engage in mutual authentication. Only those cryptographic services related to JCE 1.2.1 will have to engage in mutual authentication with the JCE 1.2.1 framework, and will become usable (in the JCE 1.2.1 environment) only after such authentication has taken place.

The constructor of each SPI implementation class must

Sample code for each of these is provided below.

How a Provider Can Authenticate JCE 1.2.1

Each SPI implementation class must authenticate the JCE 1.2.1 framework in its constructor, by
  1. determining the codebase (URL) of the JCE 1.2.1 JAR file and
  2. verifying its digital signature
Each of these steps is described below. For each, initially simplified sample basic code is provided, followed by a complete coding example. Here is an outline for the sections:
Finding the JCE 1.2.1 JAR File: Basics
Finding the Cipher Class
Extracting the JAR URL
Creating a JarFile Referring to the JAR File
Finding the JCE 1.2.1 JAR File: Complete Example

 
Verifying the JCE 1.2.1 JAR File: Basics
Verification Setup
JAR File Signature Check
Verifying Signatures
Ensuring Signers Are Trusted
Getting the List of Signer Certificates
Determining the Roots of the Signer Certificate Chains
Determining Whether Any of the Root Certificates is Trusted
Verifying the JCE 1.2.1 JAR File: Complete Example

Finding the JCE 1.2.1 JAR File: Basics

The basic idea for a sample method to determine the URL for the JCE 1.2.1 framework is the following: An authentic JCE 1.2.1 framework must contain the javax.crypto.Cipher class. So you can find the URL for that class and extract the subpart of that URL which tells the JAR file the class is in, that is, the JAR file for the JCE 1.2.1 framework.

Finding the Cipher Class

How do you find the URL for the Cipher class? You know what the current Class (the SPI implementation class) is:
Class c = this.getClass();
From the current class you can determine the class loader for the class:
ClassLoader cl = c.getClassLoader();
You can then call that class loader's getResource method to find the URL for Cipher.class:
URL url = cl.getResource("javax/crypto/Cipher.class");
Why does this work? The JCE 1.2.1 framework is always deployed as an installed extension, while the provider can be either installed or bundled. The "javax/crypto/Cipher.class" resource will have been loaded either by the same class loader as the provider (if the provider is installed) or by the delegation parent of the provider's class loader (if the provider is bundled). In either case, the provider's class loader can always find the "javax/crypto/Cipher.class" resource, provided the provider has sufficient permissions.

Extracting the JAR URL

The syntax of a JAR URL is:
jar:<URL_for_jar_file>!/<entry>
Thus, the URL returned by the above getResource should be in the following format:
jar:<URL_for_jce_jar_file>!/javax/crypto/Cipher.class
The URL returned by getResource is the URL for one entry in the JCE 1.2.1 JAR file. What we really need is the URL for the whole JAR file, which is the full string preceding javax/crypto/Cipher.class, that is the substring from "jar:" through "!/", inclusive. Suppose slash is the index of the "/" character at the end of the URL for the full JAR file. Then you can extract the JAR file URL via the following:
URL jceJarURL = new URL(url.toString().substring(0, 
                     slash + 1));
(Recall that the substring method returns the substring beginning at the index specified by its first parameter and ending at the index specified by its second parameter minus 1.)

The URL for JCE 1.2.1 should have the protocol "file" since JCE 1.2.1 is expected to be deployed as an installed extension. The complete coding example for finding the JCE 1.2.1 JAR file will show how to ensure this is the case.

Creating a JarFile Referring to the JAR File

Once you have the URL for the JCE 1.2.1 JAR file, you can create a java.util.jar.JarFile referring to the JAR file. This is needed in the step for verifying the JCE 1.2.1 JAR file. How can you create the JarFile? First, open a connection to the specified URL by calling its openConnection method. Since the URL is a JAR URL, the result is a java.net.JarURLConnection:
JarURLConnection jc = 
    (JarURLConnection)jceJarURL.openConnection();
Now that you have a JarURLConnection, you can call its getJarFile method to get the JAR file:
JarFile jf = (JarFile)jc.getJarFile();

Finding the JCE 1.2.1 JAR File: Complete Example

The above code is just the basic code required to determine the JCE 1.2.1 Codebase (JAR File URL). In reality, there are additional coding requirements that are briefly described here and that are included in the complete example below.

A SecurityException should be thrown if anything is not as it should be, for example if:

  • The Cipher class cannot be found.
  • The URL returned for the Cipher class is not in the proper format.
  • The URL for JCE 1.2.1 does not have the protocol "file".
Below is a complete example of determining the URL of the JCE 1.2.1 JAR file. It includes all the basic code described above, plus further details such as error handling and use of AccessController.doPrivileged where needed. (See API For Privileged Blocks for information on the use of doPrivileged.) Be sure to substitute your provider name for all occurrences of myProvider.
// For efficiency, keep track of whether or not the JCE 
// has already been verified to avoid doing it
// multiple times unnecessarily.
private static boolean verifiedJce = false;

private static final synchronized void doVerifyJceJar()
{
    // If the JCE has already previously been verified, just return.
    // Otherwise, verify the JCE.
    if (verifiedJce) {
        return;
    }

    // Find out the URL for the JCE framework.
    URL jceJarURL = null;
    try {
        Class c = this.getClass();
        final ClassLoader cl = c.getClassLoader();

        // An authentic JCE framework must
        // contain the javax.crypto.Cipher class. So we can
        // rely on it to find out the URL for the JCE
        // JAR file. 
        // As explained in the JCE 1.2.1 API Specification &  
        // Reference, the JCE framework is always deployed as an 
        // installed extension. The provider can be either 
        // installed or bundled. So the provider will always be
        // able to determine the "javax/crypto/Cipher.class"
        // resource (provided it has sufficient permissions),
        // because that resource will have been loaded by
        // the same classloader as the provider (if the 
        // provider is installed) or its delegation parent
        // (if the provider is bundled).
        URL url = (URL)AccessController.doPrivileged(
                        new PrivilegedAction() {
          public Object run() {
            return cl.getResource("javax/crypto/Cipher.class");
          }
        });

        if (url == null) {
        throw new SecurityException("Cannot get the JCE framework " +
                        "URL. Check that this provider can " +
                        "read the JCE framework");
        }

        // The URL for "javax/crypto/Cipher.class" should be in
        // the following format:
        // jar:<URL_for_jce_jar_file>!/javax/crypto/Cipher.class

        String urlStr = url.toString();

        // Determine the location of the slash at the end of the
        // JAR specification.
        // First, get index of period in last occurrence of ".jar!/"
        int per = urlStr.lastIndexOf(".jar!/);
        if per = -1 {
            // No such substring was found
            throw new SecurityException(
                        "The JCE framework is invalid");
        }
        // Get index of the slash at the end of ".jar!/"
        int slash = per + 5;

        // Determine the full JAR URL 
        jceJarURL = new URL(urlStr.substring(0, slash + 1));

        // The URL for JCE should have the protocol "file"
        // since we expect JCE to be deployed as an installed
        // extension. Ensure this is the case.
        // Skip the "jar:" in the beginning.
        int beginIndex = 4;

        URL jceURL = 
            new URL(urlStr.substring(beginIndex, slash - 1));
        if (!jceURL.getProtocol().equalsIgnoreCase("file")) {
            throw new SecurityException("JCE should be deployed " +
                        "as an installed extension");
        }
    } catch (IOException ioe) {
        throw new SecurityException("The provider myProvider cannot " +
                        "authenticate the JCE framework" + ioe);
    }

    // Read the JCE framework JAR file.
    JarFile jf;

    try {
        final URL url = jceJarURL;
        jf = (JarFile)AccessController.doPrivileged(
                       new PrivilegedExceptionAction() {
          public Object run() throws Exception {
            return ((JarURLConnection)
                    url.openConnection()).getJarFile();
          }
        });
    } catch (java.security.PrivilegedActionException pae) {
        throw new SecurityException("Cannot authenticate " +
                    "the JCE framework " + pae);
    }

    try {
        verifySingleJarFile(jf);  // see next section
    } catch (Exception e) {
        throw new SecurityException("Cannot authenticate " +
                    "JCE framework " + e);
    }

    verifiedJce = true;
}

Verifying the JCE 1.2.1 JAR File: Basics

Once you have determined the URL for the JCE 1.2.1 JAR file and you have created a JarFile referring to the JAR file, as shown in the Finding the JCE 1.2.1 JAR File: Complete Example above, you can then verify the file.

The basic approach is:

  1. Ensure the JAR file is a signed JAR file,
  2. Go through all the entries in the JAR file and ensure the signature on each one verifies correctly, and
  3. Ensure at least one of each entry's signer's certificates can be traced back to a trusted Certification Authority.
Sample code for each of these steps is presented and described below, followed by the complete code example. Here is an outline for the sections, starting with the current one:
Verifying the JCE 1.2.1 JAR File: Basics
Verification Setup
JAR File Signature Check
Verifying Signatures
Ensuring Signers Are Trusted
Getting the List of Signer Certificates
Determining the Roots of the Signer Certificate Chains
Determining Whether Any of the Root Certificates is Trusted
Verifying the JCE 1.2.1 JAR File: Complete Example

Verification Setup

Our approach will define a method verifySingleJarFile which takes as a parameter the JarFile created in the previous step.

verifySingleJarFile will basically go through the JAR file entries twice. As an efficiency measure, it will allocate a Vector entriesVec and the first time through the loop processing the JAR file entries, it will add each entry to entriesVec, for use by the second loop. Here is the first part of our method:

private static void verifySingleJarFile(JarFile jf)
    throws IOException, CertificateException {
    Vector entriesVec = new Vector();

JAR File Signature Check

An authentic JCE 1.2.1 JAR file is signed. So the JAR file has been tampered with if it isn't signed:
    Manifest man = jf.getManifest();
    if (man == null)
        throw new SecurityException(
                    "The JCE framework is not signed");

Verifying Signatures

The next step is to go through all the entries in the JAR file and ensure the signature on each one verifies correctly. One possible way to verify the signature on a JAR file entry is to simply read the file. If a JAR file is signed, the read method itself automatically performs the signature verification. Here is sample code:
    byte[] buffer = new byte[8192];
    Enumeration entries = jf.entries();

    while (entries.hasMoreElements()) {
        JarEntry je = (JarEntry)entries.nextElement();
        entriesVec.addElement(je);
        InputStream is = jf.getInputStream(je);
        int n;
        while ((n = is.read(buffer, 0, buffer.length)) != -1) {
            // we just read. this will throw a SecurityException
            // if  a signature/digest check fails.
        }
        is.close();
    }
    jf.close();

Ensuring Signers Are Trusted

The code in the previous section verified the signatures of all the JCE 1.2.1 JAR file entries. The fact that they all verify correctly is a requirement, but it is not sufficient to verify the authenticity of the JAR file. A final requirement is that the signatures were generated by an entity that JCE 1.2.1 trusts. In order to test that, we can again go through each entry in the JAR file (this time using the entriesVec built in the previous step), and for each entry that must be signed (that is, each entry that is not a directory and that is not in the META-INF directory):
  1. Get the list of signer certificates for the entry.
  2. Determine the roots of the signer certificate chains.
  3. Determine whether any of the root certificates is trusted. At least one of the signers must be trusted.
The loop setup is the following:
    Enumeration e = entriesVec.elements();
    while (e.hasMoreElements()) {
        JarEntry je = (JarEntry) e.nextElement();

Getting the List of Signer Certificates

The certificates for the signers of a JAR file entry JarEntry can be obtained simply by calling the JarEntry getCertificates method:
    Certificate[] certs = je.getCertificates();
Adding this to the previous loop setup code, and adding code to ignore directories and files in the META-INF directory gives us:
Enumeration e = entriesVec.elements();
while (e.hasMoreElements()) {
    JarEntry je = (JarEntry) e.nextElement();
    
    if (je.isDirectory())
        continue;
    // Every file must be signed - except
    // files in META-INF
    Certificate[] certs = je.getCertificates();
    if ((certs == null) || (certs.length == 0)) {
        if (!je.getName().startsWith("META-INF"))
            throw new SecurityException("The JCE framework " +
                    "has unsigned " +
                    "class files.");
    } else {
    // Check whether the file
    // is signed as expected.
    . . .
    }

Determining the Roots of the Signer Certificate Chains

The certificate array returned by the JarEntry getCertificates method contains one or more certificate chains. There is one chain per signer of the entry. Each chain contains one or more certificates. Each certificate in a chain authenticates the public key in the previous certificate. The first certificate in a chain is the certificate containing the public key corresponding to the private key actually used to sign the entry. Each subsequent certificate is a certificate for the entity who signed the previous certificate. The last certificate in a chain is either
  • the certificate for a Certification Authority, or
  • a certificate issued by a Certification Authority
We need to go through the array of certificate chains to determine the final certificate in each chain, referred to as the "root" certificate:
        Certificate[] chainRoots = getChainRoots(certs);
The getChainRoots method is defined as follows:
private static Certificate[] 
            getChainRoots(Certificate[] certs) {
  Vector result = new Vector(3);
    // choose a Vector size that seems reasonable
  for (int i = 0; i < certs.length - 1; i++) {
    if (!((X509Certificate)certs[i + 1]).getSubjectDN().equals(
      ((X509Certificate)certs[i]).getIssuerDN())) {
        // We've reached the end of a chain
        result.addElement(certs[i]);
    }
  }
  // The final entry in the certs array is always 
  // a "root" certificate
  result.addElement(certs[certs.length - 1]);
  Certificate[] ret = new Certificate[result.size()];
  result.copyInto(ret);

  return ret;
}

Determining Whether Any of the Root Certificates is Trusted

Now that we have (in the chainRoots array) the list of root certificates for all the signers of a JCE 1.2.1 JAR file entry, we can go through that array and determine whether or not any of the root certificates is a certificate for a "trusted" entity. At least one of the root certificates for each JAR file entry must be trusted. Assume that trustedCaCerts is an array of certificates for trusted Certificate Authorities. Then sample code is the following:
        boolean signedAsExpected = false;

        for (int i = 0; i < chainRoots.length; i++) {
            if (isTrusted((X509Certificate)chainRoots[i],
                            trustedCaCerts)) {
                signedAsExpected = true;
                break;
            }
        }

        if (!signedAsExpected) {
            throw new SecurityException("The JCE framework " +
                        "is not signed by a " +
                        "trusted signer");
        }
A root certificate is considered "trusted" if one of the following is true:
  • It is the certificate for a trusted Certification Authority. (In the following sample code, this is true if it is the same as a certificate in the trustedCaCerts array.)
  • It is a certificate issued by a trusted Certification Authority. This is true if the certificate's IssuerDN is the same as the SubjectDN of one of the trustedCaCerts certificates and you can verify the signature. (The IssuerDN of a certificate is the name of the entity that signed the certificate, while the SubjectDN identifies the entity whose public key is being identified by the certificate.)
Here is sample code:
private static boolean isTrusted(X509Certificate cert, 
                     X509Certificate[] trustedCaCerts)
    {
    // Return true iff either of the following is true:
    // 1) the cert is in the trustedCaCerts.
    // 2) the cert is issued by a trusted CA.

    // Check whether the cert is in the trustedCaCerts
    for (int i = 0; i < trustedCaCerts.length; i++) {
        // If the cert has the same SubjectDN
        // as a trusted CA, check whether
        // the two certs are the same.
        if (cert.getSubjectDN().equals(
                trustedCaCerts[i].getSubjectDN())) {
            if (cert.equals(trustedCaCerts[i])) {
               return true;
        } 
        }
    }

    // Check whether the cert is issued by a trusted CA.
    // Signature verification is expensive. So we check 
    // whether the cert is issued
    // by one of the trusted CAs iff the above loop failed.
    for (int i = 0; i < trustedCaCerts.length; i++) {
        // If the issuer of the cert has the same name as 
        // a trusted CA, check whether that trusted CA 
        // actually issued the cert.
        if (cert.getIssuerDN().equals(
                trustedCaCerts[i].getSubjectDN())) {
            try {
                cert.verify(trustedCaCerts[i].getPublicKey());
                return true;
            } catch (Exception e) {
            // Do nothing.
            }
        }        
    }
       
    return false;
}

Verifying the JCE 1.2.1 JAR File: Complete Example

Below is the complete code for verifying the JCE 1.2.1 JAR file. It assumes the parameter jf refers to a JarFile that is open (and thus needs a close). It also assumes trustedCaCerts is the array of Certificates for trusted Certificate Authorities.
private static void verifySingleJarFile(JarFile jf)
    throws IOException, CertificateException {
    Vector entriesVec = new Vector();

    // Ensure there is a manifest file
    Manifest man = jf.getManifest();
    if (man == null)
        throw new SecurityException(
                    "The JCE framework is not signed");

    // Ensure all the entries' signatures verify correctly
    byte[] buffer = new byte[8192];
    Enumeration entries = jf.entries();

    while (entries.hasMoreElements()) {
        JarEntry je = (JarEntry)entries.nextElement();
        entriesVec.addElement(je);
        InputStream is = jf.getInputStream(je);
        int n;
        while ((n = is.read(buffer, 0, buffer.length)) != -1) {
            // we just read. this will throw a SecurityException
            // if  a signature/digest check fails.
        }
        is.close();
    }
    jf.close();

    // Get the list of signer certificates 
    Enumeration e = entriesVec.elements();
    while (e.hasMoreElements()) {
        JarEntry je = (JarEntry) e.nextElement();
        
        if (je.isDirectory())
            continue;
        // Every file must be signed - except
        // files in META-INF
        Certificate[] certs = je.getCertificates();
        if ((certs == null) || (certs.length == 0)) {
            if (!je.getName().startsWith("META-INF"))
                throw new SecurityException("The JCE framework " +
                        "has unsigned " +
                        "class files.");
        } else {
            // Check whether the file
            // is signed as expected.
            // The framework may be signed by
            // multiple signers. At least one of
            // the signers must be a trusted signer.
    
            // First, determine the roots of the certificate chains
            Certificate[] chainRoots = getChainRoots(certs);
            boolean signedAsExpected = false;
    
            for (int i = 0; i < chainRoots.length; i++) {
                if (isTrusted((X509Certificate)chainRoots[i],
                                trustedCaCerts)) {
                    signedAsExpected = true;
                    break;
                }
            }
    
            if (!signedAsExpected) {
                throw new SecurityException("The JCE framework " +
                            "is not signed by a " +
                            "trusted signer");
            }
        }
    }
}

private static boolean isTrusted(X509Certificate cert, 
                     X509Certificate[] trustedCaCerts)
    {
    // Return true iff either of the following is true:
    // 1) the cert is in the trustedCaCerts.
    // 2) the cert is issued by a trusted CA.

    // Check whether the cert is in the trustedCaCerts
    for (int i = 0; i < trustedCaCerts.length; i++) {
        // If the cert has the same SubjectDN
        // as a trusted CA, check whether
        // the two certs are the same.
        if (cert.getSubjectDN().equals(
                trustedCaCerts[i].getSubjectDN())) {
            if (cert.equals(trustedCaCerts[i])) {
               return true;
        } 
        }
    }

    // Check whether the cert is issued by a trusted CA.
    // Signature verification is expensive. So we check 
    // whether the cert is issued
    // by one of the trusted CAs iff the above loop failed.
    for (int i = 0; i < trustedCaCerts.length; i++) {
        // If the issuer of the cert has the same name as 
        // a trusted CA, check whether that trusted CA 
        // actually issued the cert.
        if (cert.getIssuerDN().equals(
                trustedCaCerts[i].getSubjectDN())) {
            try {
                cert.verify(trustedCaCerts[i].getPublicKey());
                return true;
            } catch (Exception e) {
            // Do nothing.
            }
        }        
    }
       
    return false;
}    

private static Certificate[] 
            getChainRoots(Certificate[] certs) {
  Vector result = new Vector(3);
    // choose a Vector size that seems reasonable
  for (int i = 0; i < certs.length - 1; i++) {
    if (!((X509Certificate)certs[i + 1]).getSubjectDN().equals(
      ((X509Certificate)certs[i]).getIssuerDN())) {
        // We've reached the end of a chain
        result.addElement(certs[i]);
    }
  }
  // The final entry in the certs array is always 
  // a "root" certificate
  result.addElement(certs[certs.length - 1]);
  Certificate[] ret = new Certificate[result.size()];
  result.copyInto(ret);

  return ret;
}

How a Provider Can Do Self-Integrity Checking

Each SPI implementation class constructor should do self-integrity checking to ensure that the provider has not been tampered with.

The basic approach a provider can use to check its own integrity is essentially the same as the approach taken to authenticate JCE 1.2.1:

  1. Determine the URL of the JAR file containing the code (in this case the provider code, not the JCE 1.2.1 code), and
  2. Verify the JAR file's digital signatures to ensure at least one signer of each entry of the JAR file is trusted.
Code for the second step is exactly the same as the code in Verifying the JCE 1.2.1 JAR File: Complete Example. That example contains a verifySingleJarFile method that verifies the JAR file passed to it as an argument.

Let's define a sample method called selfIntegrityChecking that returns a boolean value telling whether or not the integrity check succeeds. Each SPI implementation class constructor must call this method and must throw a SecurityException if it returns false. Here's the method declaration:

private static final synchronized boolean selfIntegrityChecking()

Determining the Provider's JAR File URL

The URL for the provider's JAR file can be obtained by determining the provider's CodeSource and then calling the getLocation method on the CodeSource. The basic idea is:
    Class cc = this.getClass();
    CodeSource s1 = 
        cc.getProtectionDomain().getCodeSource();
    URL providerURL = s1.getLocation();

Creating a JarFile Referring to the JAR File

Once you have the URL for the provider's JAR file, you can create a java.util.jar.JarFile referring to the JAR file. This is needed by the verifySingleJarFile method. How can you create the JarFile? First, open a connection to the specified URL by calling its openConnection method. Since the URL is a JAR URL, the result is a java.net.JarURLConnection. The basic code is:
JarURLConnection jc = 
    (JarURLConnection)providerURL.openConnection();
Now that you have a JarURLConnection, you can call its getJarFile method to get the JAR file:
JarFile jf = (JarFile)jc.getJarFile();
Then you can call the verifySingleJarFile method shown in Verifying the JCE 1.2.1 JAR File: Complete Example, which verifies the JAR file passed to it as an argument.
verifySingleJarFile(jf);

Provider Self-Integrity Checking Complete Example

The full code for our selfIntegrityChecking method appears below. It includes a getProviderURL method for step 1 and other code that utilizes AccessController.doPrivileged as needed. The example also includes a call to the verifySingleJarFile method shown in Verifying the JCE 1.2.1 JAR File: Complete Example.
// For efficiency, keep track of whether or not the provider 
// has already been verified to avoid doing it
// multiple times unnecessarily.
private static boolean verifiedSelfIntegrity = false;

// Provider self-integrity checking.
// Call the following method in the constructor
// of your SPI implementation classes.

private static final synchronized boolean selfIntegrityChecking()
    {
    if (verifiedSelfIntegrity) {
        return true;
    }

    URL providerURL = getProviderURL(this.getClass());
    if (providerURL == null) {
        return false;
    }

    // Open a connnection to the provider JAR file
    JarFile jf;

    try {
        final URL url = providerURL;
        jf = (JarFile)AccessController.doPrivileged(
                       new PrivilegedExceptionAction() {
          public Object run() throws Exception {
            return ((JarURLConnection)
                    url.openConnection()).getJarFile();
          }
        });
    } catch (java.security.PrivilegedActionException pae) {
        return false;
    }

    // Verify the provider JAR file 
    try {
        verifySingleJarFile(jf);  
    } catch (Exception e) {
        return false;
    }

    verifiedSelfIntegrity = true;

    return true;    
}

/*
 * Returns the URL of the JAR file containing the specified class.
 */
private static URL getProviderURL(Class c) {
    final Class cc = c;

    return (URL)AccessController.doPrivileged(
                     new PrivilegedAction() {
      public Object run() {
        CodeSource s1 = cc.getProtectionDomain().getCodeSource();
        return s1.getLocation();
      }
    });
}

Further Implementation Details and Requirements

Algorithm Aliases

For many cryptographic algorithms, there is a single official "standard name". The standard names defined by JCE 1.2.1 are listed in Appendix A of the Java Cryptography Extension 1.2.1 API Specification & Reference.

For example, "DiffieHellman" is the standard name for the Diffie-Hellman key agreement algorithm defined in PKCS #3.

JCE 1.2.1 uses the same aliasing scheme for algorithm names as J2SDK v 1.2. That scheme enables clients to use aliases when referring to algorithms, rather than their standard names. For example, the "IBMJCE" provider's master class (IBMJCE.java) defines the alias "DH" for the key agreement whose standard name is "DiffieHellman". Thus, the following statements are equivalent:

    KeyAgreement ka = KeyAgreement.getInstance("DiffieHellman", "IBMJCE");

    KeyAgreement ka = KeyAgreement.getInstance("DH", "IBMJCE");
Aliases can be defined in your "master class" (see Step 3). To define an alias, create a property named
    Alg.Alias.engineClassName.aliasName
where engineClassName is either Cipher, KeyAgreement, KeyGenerator, Mac, SecretKeyFactory, or ExemptionMechanism, and aliasName is your alias name. For all but ExemptionMechanism, the value of the property must be the standard algorithm name for the algorithm being aliased. For ExemptionMechanism, the value must be the exemption mechanism name ("KeyRecovery", "KeyEscrow", or "KeyWeakening").

As an example, the "IBMJCE" provider defines the alias "DH" for the key agreement algorithm whose standard name is "DiffieHellman" by setting a property named Alg.Alias.KeyAgreement.DH to have the value DiffieHellman via the following:

    put("Alg.Alias.KeyAgreement.DH", "DiffieHellman");
Currently, aliases defined by the "IBMJCE" provider are available to all clients, no matter which provider clients request. For example, if you create a provider named "MyPro" that implements the Diffie-Hellman algorithm, then even if you don't define any aliases for it, the "DH" alias defined by "IBMJCE" can be used to refer to your provider's Diffie-Hellman implementation as follows:
    KeyAgreement ka = KeyAgreement.getInstance("DH", "MyPro");

Service Interdependencies

Some algorithms require the use of other types of algorithms. For example, a PBE algorithm usually needs to use a message digest algorithm in order to transform a password into a key.

If you are implementing one type of algorithm that requires another, you can do one of the following:

  1. Provide your own implementations for both.

  2.  
  3. Let your implementation of one algorithm use an instance of the other type of algorithm, as supplied by the default "SUN" provider that is included with every Java 2 Platform installation. For example, if you are implementing a PBE algorithm that requires a message digest algorithm, you can obtain an instance of a class implementing the MD5 message digest algorithm by calling
  4.     MessageDigest.getInstance("MD5", "IBM")
  5. Let your implementation of one algorithm use an instance of the other type of algorithm, as supplied by another specific provider. This is only appropriate if you are sure that all clients who will use your provider will also have the other provider installed.
  6. Let your implementation of one algorithm use an instance of the other type of algorithm, as supplied by another (unspecified) provider. That is, you can request an algorithm by name, but without specifying any particular provider, as in
  7.     MessageDigest.getInstance("MD5")
    This is only appropriate if you are sure that there will be at least one implementation of the requested algorithm (in this case, MD5) installed on each Java platform where your provider will be used.

Default Initializations

In case the client does not explicitly initialize a key pair generator or an algorithm parameter generator, each provider of such a service must supply (and document) a default initialization. For example, the "IBMJCE" provider uses a default modulus size (keysize) of 1024 bits for the generation of Diffie-Hellman parameters.

Diffie-Hellman Interfaces and their Required Implementations

JCE 1.2.1 contains the following interfaces (in the javax.crypto.interfaces package) for the convenience of programmers implementing Diffie-Hellman services: The following sections discuss requirements for implementations of these interfaces.

DHPrivateKey and DHPublicKey Implementations

If you implement a Diffie-Hellman key pair generator or key factory, you need to create classes implementing the DHPrivateKey and DHPublicKey interfaces.

If you implement a Diffie-Hellman key pair generator, your generateKeyPair method (in your KeyPairGeneratorSpi subclass) will return instances of your implementations of those interfaces.

If you implement a Diffie-Hellman key factory, your engineGeneratePrivate method (in your KeyFactorySpi subclass) will return an instance of your DHPrivateKey implementation, and your engineGeneratePublic method will return an instance of your DHPublicKey implementation.

Also, your engineGetKeySpec and engineTranslateKey methods will expect the passed-in key to be an instance of a DHPrivateKey or DHPublicKey implementation. The getParams method provided by the interface implementations is useful for obtaining and extracting the parameters from the keys and then using the parameters, for example as parameters to the DHParameterSpec constructor called to create a parameter specification from parameter values that could be used to initialize a KeyPairGenerator object for Diffie-Hellman.

If you implement the Diffie-Hellman key agreement algorithm, your engineInit method (in your KeyAgreementSpi subclass) will expect to be passed a DHPrivateKey and your engineDoPhase method will expect to be passed a DHPublicKey.

Please note: The DHPublicKey and DHPrivateKey interfaces define a very generic, provider-independent interface to Diffie-Hellman public and private keys, respectively. The engineGetKeySpec and engineTranslateKey methods (in your KeyFactorySpi subclass) could additionally check if the passed-in key is actually an instance of their provider's own implementation of DHPrivateKey or DHPublicKey, e.g., to take advantage of provider-specific implementation details. The same is true for the Diffie-Hellman algorithm engineInit and engineDoPhase methods (in your KeyAgreementSpi subclass).

To see what methods need to be implemented by classes that implement the DHPublicKey and DHPrivateKey interfaces, first note the following interface signatures:

  In the javax.crypto.interfaces package:

    public interface DHPrivateKey extends DHKey, 
                                     java.security.PrivateKey

    public interface DHPublicKey extends DHKey, 
                                    java.security.PublicKey

    public interface DHKey

  In the java.security package:

    public interface PrivateKey extends Key

    public interface PublicKey extends Key

    public interface Key extends java.io.Serializable
In order to implement the DHPrivateKey and DHPublicKey interfaces, you must implement the methods they define as well as those defined by interfaces they extend, directly or indirectly.

Thus, for private keys, you need to supply a class that implements

  • the getX method from the DHPrivateKey interface.
  • the getParams method from the javax.crypto.interfaces.DHKey interface, since DHPrivateKey extends DHKey.
  • the getAlgorithm, getEncoded, and getFormat methods from the java.security.Key interface, since DHPrivateKey extends java.security.PrivateKey, and PrivateKey extends Key.
Similarly, for public Diffie-Hellman keys, you need to supply a class that implements
  • the getY method from the DHPublicKey interface.
  • the getParams method from the javax.crypto.interfaces.DHKey interface, since DHPublicKey extends DHKey.
  • the getAlgorithm, getEncoded, and getFormat methods from the java.security.Key interface, since DHPublicKey extends java.security.PublicKey, and PublicKey extends Key.

Algorithm Parameter Specification Classes

An algorithm parameter specification is a transparent representation of the sets of parameters used with an algorithm.

A transparent representation of parameters means that you can access each value individually, through one of the "get" methods defined in the corresponding specification class (e.g., DHParameterSpec defines getP, getG, and getL methods, to access the p, g, and l parameters, respectively).

This is contrasted with an opaque representation, as supplied by the AlgorithmParameters engine class, in which you have no direct access to the key material values; you can only get the name of the algorithm associated with the parameter set (via getAlgorithm) and some kind of encoding for the parameter set (via getEncoded).

If you supply an AlgorithmParametersSpi, AlgorithmParameterGeneratorSpi, or KeyPairGeneratorSpi implementation, you must utilize the AlgorithmParameterSpec interface, since each of those classes contain methods that take an AlgorithmParameterSpec parameter. Such methods need to determine which actual implementation of that interface has been passed in, and act accordingly.

JCE 1.2.1 contains a number of AlgorithmParameterSpec implementations for the most frequently used cipher and key agreement algorithm parameters. If you are operating on algorithm parameters that should be for a different type of algorithm not provided by JCE 1.2.1, you will need to supply your own AlgorithmParameterSpec implementation appropriate for that type of algorithm.

JCE 1.2.1 defines the following algorithm parameter specification classes in the javax.crypto.spec package:

The IvParameterSpec Class

This class (which implements the AlgorithmParameterSpec interface) specifies the initialization vector (IV) used with a cipher in feedback mode. It has the following method:
    public byte[] getIV()
This method returns the IV.

The PBEParameterSpec Class

This class (which implements the AlgorithmParameterSpec interface) specifies the set of parameters used with a password-based encryption (PBE) algorithm. It has the following methods:
    public byte[] getSalt()

    public int getIterationCount()
These methods return the PBE parameters: the salt and the iteration count.

The RC2ParameterSpec Class

This class (which implements the AlgorithmParameterSpec interface) specifies the set of parameters used with the RC2 algorithm. It has the following methods:
    public int getEffectiveKeyBits()

    public byte[] getIV()
These methods return the RC2 algorithm parameters: the effective key size (in bits) and the IV.

The RC5ParameterSpec Class

This class (which implements the AlgorithmParameterSpec interface) specifies the set of parameters used with the RC5 algorithm. It has the following methods:
    public int getVersion()

    public int getRounds()

    public int getWordSize()

    public byte[] getIV()
These methods return the RC5 algorithm parameters: the version number, number of rounds, word size (in bits), and the IV.

The DHParameterSpec Class

This class (which implements the AlgorithmParameterSpec interface) specifies the set of parameters used with the Diffie-Hellman algorithm. It has the following methods:
    public BigInteger getP()

    public BigInteger getG()

    public int getL()
These methods return the Diffie-Hellman algorithm parameters: the prime modulus p, the base generator g, and the size in bits of the random exponent (private value), l.

Many types of Diffie-Hellman services will find this class useful - for example, it is utilized by the Diffie-Hellman key agreement, key pair generator, algorithm parameter generator, and algorithm parameters classes implemented by the "IBMJCE" provider. As a specific example, an algorithm parameters implementation must include an implementation for the getParameterSpec method, which returns an AlgorithmParameterSpec. The Diffie-Hellman algorithm parameters implementation supplied by "IBMJCE" returns an instance of the DHParameterSpec class.

Key Specification Classes Required by Key Factories

Key specifications are transparent representations of the key material that constitutes a key. JCE 1.2.1 defines the following key specification classes in the javax.crypto.spec package: DHPrivateKeySpec, DHPublicKeySpec, DESKeySpec, and DESedeKeySpec.

The DHPrivateKeySpec Class

This class (which implements the KeySpec interface) specifies a Diffie-Hellman private key with its associated parameters. It has the following methods:
    public BigInteger getX()

    public BigInteger getP()

    public BigInteger getG()
These methods return the private value x, and the Diffie-Hellman algorithm parameters used to calculate it: the prime modulus p and the base generator g.

The DHPublicKeySpec Class

This class (which implements the KeySpec interface) specifies a Diffie-Hellman public key with its associated parameters. It has the following methods:
    public BigInteger getY()

    public BigInteger getP()

    public BigInteger getG()
These methods return the public value y, and the Diffie-Hellman algorithm parameters used to calculate it: the prime modulus p and the base generator g.

The DESKeySpec Class

This class (which implements the KeySpec interface) specifies a DES key. It has the following methods:
    public byte[] getKey()

    public static boolean isParityAdjusted(byte[] key, int offset)

    public static boolean isWeak(byte[] key, int offset)
The first method returns the DES key bytes. The other (class) methods check if a given DES key is parity adjusted or weak, respectively.

The DESedeKeySpec Class

This class (which implements the KeySpec interface) specifies a DES-EDE (Triple DES) key. It has the following methods:
    public byte[] getKey()

    public static boolean isParityAdjusted(byte[] key, int offset)
The first method returns the DES-EDE key bytes. The other (class) method checks if a given DES-EDE key is parity adjusted.

The SecretKeySpec Class

This class implements the KeySpec interface. Since it also implements the SecretKey interface, it can be used to construct a SecretKey object in a provider-independent fashion, i.e., without having to go through a provider-based SecretKeyFactory. It has the following methods:
    public SecretKeySpec(byte[] key, String algorithm)

    public SecretKeySpec(byte[] key, int offset, int len, 
        String algorithm)

    public String getAlgorithm()
    
    public String getFormat()

    public byte[] getEncoded()
The two constructors take a byte array that constitutes the secret-key material, and the name of the associated secret-key algorithm.

The other methods return the secret-key algorithm name, key format (given as the string "RAW"), and secret-key bytes, respectively.

Secret-Key Generation

If you provide a secret-key generator (subclass of javax.crypto.KeyGeneratorSpi) for a particular secret-key algorithm, you may return the generated secret-key object (which must be an instance of javax.crypto.SecretKey, see engineGenerateKey) in one of the following ways:
  • You implement a class whose instances represent secret-keys of the algorithm associated with your key generator. Your key generator implementation returns instances of that class. This approach is useful if the keys generated by your key generator have provider-specific properties.
  • Your key generator returns an instance of SecretKeySpec, which already implements the javax.crypto.SecretKey interface. You pass the (raw) key bytes and the name of the secret-key algorithm associated with your key generator to the SecretKeySpec constructor. This approach is useful if the underlying (raw) key bytes can be represented as a byte array and have no key-parameters associated with them.

Ensuring Exportability

The key feature of the JCE 1.2.1 release is the exportability of the JCE 1.2.1 framework and of the provider cryptography implementations if certain conditions are met.

As noted elsewhere, you can write just one version of your provider software, implementing cryptography of maximum strength. It is up to JCE 1.2.1, not your provider, to enforce restrictions regarding the cryptographic algorithms and maximum cryptographic strengths available to applets/applications in different locations.

The conditions that must be met by your provider in order to enable it to be plugged into JCE 1.2.1 are the following:

How to Implement an Exemption Mechanism

               The steps required in order to implement an exemption mechanism provider and integrate it into Java Security are the same as those described in the text above.

 Step 1: Write your Exemption Mechanism Service Implementation Code

          The first thing you need to do is write the code supplying algorithm-specific implementations of the exemption mechanism services you
          want to support.

          For each exemption mechanism service in JCE 1.2.1, you need to create a subclass of the exemption mechanism SPI class.(See "JCE
          1.2.1 Engine Classes and Corresponding SPI Classes" and "JDK 1.2 Engine Classes and Corresponding SPI Classes".)

          In your subclass, you need to

             1.supply implementations for the abstract methods, whose names usually begin with "engine". See Further Implementation Details and
               Requirements for additional information.

             2.ensure there is a public constructor without any arguments. Here's why: When one of your services is requested, Java Security
               looks up the subclass implementing that service, as specified by a property in your "master class" (see Step 3). Java Security then
               creates the Class object associated with your subclass, and creates an instance of your subclass by calling the newInstance
               method on that Class object. newInstance requires your subclass to have a public constructor without any parameters.

               A default constructor without arguments will automatically be generated if your subclass doesn't have any constructors. But if your
               subclass defines any constructors, you must explicitly define a public constructor without arguments.

  Step 2: Give your Exemption Mechanism Provider a Name

          Decide on a name for your exemption mechanism provider. This is the name to be used by client applications to refer to your exemption
          mechanism provider.

 Step 3: Write your "Master Class", a subclass of Provider

          The third step is to create a subclass of the java.security.Provider class.

          Your subclass should be a final class, and its constructor should

               call super, specifying the provider name (see Step 2), version number, and a string of information about the provider and
               algorithms it supports. For example:

                   super("ExemptionX", 1.0, "X provider v1.0, implementing " +
                       "Key Recovery, Key Escrow and Key Weakening exemption mechanism algorithms.");

               set the values of various properties that are required for the Java Security API to look up the cryptographic services implemented
               by the provider. For each exemption mechanism service implemented by the provider, ExemptionMechanism must be the name of
               the service followed by a period and the name of the exemption algorithm to which the service applies. The property value must
               specify the fully qualified name of the class implementing the service.
                        ExemptionMechanism.algName

               algName is the "standard" name of the algorithm.

               The value of each property must be the fully qualified name of the class implementing the specified algorithm. That is, it must be the
               package name followed by the class name, where the two are separated by a period.

               As an example, the "IBMEM" provider implements the key recovery algorithm in a class named KeyRecovery in the
               com.ibm.crypto.provider package. Its subclass of Provider (which is the IBMEM class in the com.ibm.crypto.provider
               package) sets the ExemptionMechanism.KeyRecovery property to have the value "com.ibm.crypto.provider.KeyRecovery" via
               the following:

                   put("ExemptionMechanism.KeyRecovery",
                         "com.ibm.crypto.provider.KeyRecovery")

          For further master class property setting examples, see Appendix A to view the current JCE 1.2.1 IBMJCE.java source file. This shows
          how the IBMJCE class constructor sets all the properties for the "IBMJCE" provider.

          Note: The Provider subclass can get its information from wherever it wants. Thus, the information can be hard-wired in, or retrieved at
          runtime, e.g., from a file.

 Step 4: Compile your Code

               After you have created your implementation code (Step 1), given your provider a name (Step 2), and created the master
               class (Step 3), use the Java compiler to compile your files.

 Step 5: Sign your Exemption Mechanism Provider for Testing

               Request a code signing certificate for testing only. This certificate has a limited validity period. It is provided for testing only.
               You should not use it to sign your product in the production environment. Note: The JCE provider signing policies have yet
               to be finalized.

  Step 6: Prepare for Testing: Install the Exemption Mechanism Provider

               In order to prepare for testing your provider, you must install it in the same manner as will be done by clients wishing to use
               it. The installation enables Java Security to find your algorithm implementations when clients request them.

               There are two parts to installing a provider: installing the provider package classes, and configuring the provider.

               Installing the Exemption Mechanism Provider Classes

               The first thing you must do is make your classes available so that they can be found when requested. You ship your provider
               classes as a JAR (Java ARchive) or ZIP file.

               Note that in order for your provider to work, you must also have JCE 1.2.1 (more specifically: the ibmjcefw.jar
               extension) installed.

               There are a couple possible ways of installing your provider classes:

                    Place a JAR or ZIP file containing your own provider classes anywhere on your CLASSPATH. Place the
                    ibmjcefw.jar file on the same CLASSPATH, or supply it as an "installed" extension.

                    Supply your own provider JAR file as an "installed" or "bundled" extension.

                    When you supply your own provider JAR file as an "installed" extension, the ibmjcefw.jar file must also be
                    supplied as an "installed" extension.

                    When you supply your own provider JAR file as a "bundled" extension, you must either bundle the jce1_2-do.jar
                    file with it (i.e., in the manifest of your own extension's JAR file you specify the relative URL referring to the
                    ibmjcefw.jar JAR file via the Class-Path attribute), or you must supply the ibmjcefw.jar file as an
                    "installed" extension on the target Java Virtual Machine (JVM) where your provider classes will be accessed. Since
                    you may not always have access to the target JVM, it is safest to always bundle the ibmjcefw.jar JAR file with
                    your own provider JAR file when you deploy your provider classes as a "bundled" extension.

               For more information on how to deploy an extension, see Extension Deployment.

               For more information on "installed" extensions, see Installed Extensions.

               For more information on "bundled" extensions, see Bundled Extensions.

               Configuring the Provider

                    The next step is to add the provider to your list of approved providers. This is done statically by editing the
                    security properties file

                         <java-home>\lib\security\java.security         [Win32]
 

                    Here <java-home> refers to the directory where the JRE was installed. For example, if you have JDK 1.2
                    installed on Windows, you need to edit the following file:

                         jdk1.2/jre/lib/security/java.security
 

                    For each provider, this file should have a statement of the following form:

                        security.provider.n=masterClassName

                    This declares a provider, and specifies its preference order n. The preference order is the order in which
                    providers are searched for requested algorithms when no specific provider is requested. The order is 1-based;
                    1 is the most preferred, followed by 2, and so on.

                    masterClassName must specify the fully qualified name of the provider's "master class", which you
                    implemented in Step 3. This class is always a subclass of the Provider class.

                    JDK 1.2 comes standard with a provider named "SUN", which is automatically configured as a static provider
                    in the java.security properties file, as follows:

                        security.provider.1=sun.security.provider.Sun

                    (The "SUN" provider's master class is the Sun class in the sun.security.provider package.)

                    Note that when you install JCE 1.2.1, its provider ("IBMJCE") is not automatically installed, i.e., if you want to
                    use the "IBMJCE" provider, you need to register it explicitly - either statically or dynamically, as described in
                    this section.

                    Suppose that your master class is the ExemptionX class in the com.ExemptionX.provider package, and
                    that you would like to make your provider the second preferred provider. To do so, add the following line to
                    the java.security file below the line for the "SUN" provider:

                        security.provider.2=com.cryptox.provider.ExemptionX

                    Note: Providers may also be registered dynamically. To do so, a program (such as your test program, to be
                    written in Step 7) can call either the addProvider or insertProviderAt method in the Security class.
                    This type of registration is not persistent and can only be done by "trusted" programs. See the Security class
                    section of the Java Cryptography Architecture API Specification and Reference.
 

 Step 7: Write and Compile your Test Programs

               Write and compile one or more test programs that test your provider's incorporation into the Security API as well as the
               correctness of its exemption mechanism algorithm(s). Create any supporting files needed, such as those for test data to be
               encrypted.

               The first tests your program should perform are ones to ensure that your provider is found, and that its name, version
               number, and additional information is as expected. To do so, you could write code like the following, substituting your
               provider name for "MyPro":

                   import java.security.*;

                   Provider p = Security.getProvider("MyPro");

                   System.out.println("MyPro provider name is " + p.getName());
                   System.out.println("MyPro provider version # is " + p.getVersion());
                   System.out.println("MyPro provider info is " + p.getInfo());

               Next, you should ensure that your services are found. For instance, if you implemented the key recovery algorithm, you
               could check to ensure it's found when requested by using the following code (again substituting your provider name for
               "MyPro"):

                   ExemptionMechanism em = ExemptionMechanism.getInstance("KeyRecovery", "MyPro");

                   System.out.println("My Cipher algorithm name is " + em.getAlgorithm());

               If you don't specify a provider name in the call to getInstance, all registered providers will be searched, in preference
               order (see Configuring the Provider), until one implementing the algorithm is found.

 Step 8: Run your Test Programs

               Run your test program(s). Debug your code and continue testing as needed. If the Java Security API cannot seem to find
               one of your algorithms, review the steps above and ensure they are all completed.

 Step 9: Document your Exemption Mechanism Provider and its Supported Services

               The next step is to write documentation for your clients. At the minimum, you need to specify

                    the name programs should use to refer to your provider. Please note: As of this writing, provider name searches are
                    case-sensitive. That is, if your master class specifies your provider name as "ExemptionX" but a user requests
                    "EXEMPTIONx", your provider will not be found. This behavior may change in the future, but for now be sure to
                    warn your clients to use the exact case you specify.

                    the types of algorithms and other services implemented by your provider.

                    instructions for installing the provider, similar to those provided in Step 6, except that the information and examples
                    should be specific to your provider.

               In addition, your documentation should specify anything else of interest to clients, such as algorithm parameters is needed.

               Algorithm Parameter Generators

               In case the client does not explicitly initialize the algorithm parameter generator (via a call to an init method in the
               AlgorithmParameterGenerator engine class), each provider must supply and document a default initialization. For example,
               the "IBMJCE" provider uses a default prime modulus size (keysize) of 1024 bits for the generation of Diffie-Hellman
               parameters.

 Step 10: Attach the cryptoPerms to your provider

               Some exemption mechanism may involve usuage of some crypto algorithm. In order to use those crypto algorithm, a
               cryptoPerms file should be included in the exemption mechanism provider JAR or ZIP file. Different level of permission for
               the exemption mechanism provider is defined the cryptoPerms file. A typical all-permission file is as follow:
 

// Permission policy file for provider

               grant {
                   permission javax.crypto.CryptoAllPermission;
               };

               Or a restricted policy file would be like:

               // A more restricted permission policy file for provider
               grant {
                   permission javax.crypto.CryptoPermission "DES", 64;
                   permission javax.crypto.CryptoPermission "RC2", 64,
                   permission javax.crypto.CryptoPermission "RC5", 64,
                   permission javax.crypto.CryptoPermission *, 64;
               };

 Step 11: Sign your Exemption Mechanism Provider for Production

               Note: The JCE provider signing policies have yet to be finalized.

 Step 12: Run your Test Programs Again

               Your provider should have been thoroughly tested in Step 8. However, we suggest you run your test programs again to
               make sure that everything still works fine since what you tested in Step 8 is signed by a different certificate.

 Step 13: Make your Exemption Mechanism Provider Software and Documentation Available to Clients

               The final step is to make your provider software and documentation available to your customers.

Appendix A: The "IBMJCE" Provider's Master Class

Below is an edited version of the IBMJCE.java file, which contains a class named IBMJCE that is the master class for the provider named "IBMJCE". (Although that provider is supplied with every JCE 1.2.1 installation, it still needs to be configured.)

As with all master classes, this class is a subclass of Provider. It specifies the class names and package locations of all the cryptographic service implementations supplied by the "IBMJCE" provider. This information is used by the getInstance methods of the engine classes to look up the various algorithms and other services when they are requested.

This code is supplied as an example of a provider master class.

package com.ibm.crypto.provider;

import java.security.AccessController;
import java.security.Provider;

/**
 * The "IBMJCE" Cryptographic Service Provider.
 *
 * @version 1.30, 02/02/99
 */

/**
 * Defines the "IBMJCE" provider.
 *
 * Supported algorithms and their names:
 *
 * - DES (ECB, CBC, CFB, OFB, PCBC)
 *
 * - DES-EDE (ECB, CBC, CFB, OFB, PCBC)
 *
 * - Password-based Encryption (PBE)
 *
 * - Blowfish
 *
 * - Diffie-Hellman Key Agreement
 *
 * - HMAC-MD5, HMAC-SHA1
 *
 * - PKCS5Padding
 */

public final class IBMJCE extends Provider {

    private static String info = "IBMJCE Provider " + 
    "(implements DES, Triple DES, Blowfish, PBE, Diffie-Hellman, HMAC-MD5, "
    + "HMAC-SHA1)";

    public IBMJCE() {
        /* We are the "IBMJCE" provider */
        super("IBMJCE", 1.3, info);

        AccessController.doPrivileged(new java.security.PrivilegedAction() {
            public Object run() {

                /*
                 * Cipher engines 
                 */
                put("Cipher.DES", "com.ibm.crypto.provider.DESCipher");

                put("Cipher.DESede", "com.ibm.crypto.provider.DESedeCipher");
                put("Alg.Alias.Cipher.TripleDES", "DESede");

                put("Cipher.PBEWithMD5AndDES",
                    "com.ibm.crypto.provider.PBEWithMD5AndDESCipher");
                put("Cipher.PBEWithMD5AndTripleDES",
                    "com.ibm.crypto.provider.PBEWithMD5AndTripleDESCipher");

                put("Cipher.Blowfish",
                    "com.ibm.crypto.provider.BlowfishCipher");

                /*
                 *  Key(pair) Generator engines 
                 */
                put("KeyGenerator.DES", 
                    "com.ibm.crypto.provider.DESKeyGenerator");

                put("KeyGenerator.DESede", 
                    "com.ibm.crypto.provider.DESedeKeyGenerator");
                put("Alg.Alias.KeyGenerator.TripleDES", "DESede");

                put("KeyGenerator.Blowfish", 
                    "com.ibm.crypto.provider.BlowfishKeyGenerator");

                put("KeyGenerator.HmacMD5", 
                    "com.ibm.crypto.provider.HmacMD5KeyGenerator");

                put("KeyGenerator.HmacSHA1", 
                    "com.ibm.crypto.provider.HmacSHA1KeyGenerator");

                put("KeyPairGenerator.DiffieHellman", 
                    "com.ibm.crypto.provider.DHKeyPairGenerator");
                put("Alg.Alias.KeyPairGenerator.DH", "DiffieHellman");

                /*
                 * Algorithm parameter generation engines
                 */
                put("AlgorithmParameterGenerator.DiffieHellman",
                    "com.ibm.crypto.provider.DHParameterGenerator");
                put("Alg.Alias.AlgorithmParameterGenerator.DH",
                    "DiffieHellman");

                /* 
                 * Key Agreement engines 
                 */
                put("KeyAgreement.DiffieHellman",
                    "com.ibm.crypto.provider.DHKeyAgreement");
                put("Alg.Alias.KeyAgreement.DH", "DiffieHellman");

                /* 
                 * Algorithm Parameter engines 
                 */
                put("AlgorithmParameters.DiffieHellman",
                    "com.ibm.crypto.provider.DHParameters");
                put("Alg.Alias.AlgorithmParameters.DH", "DiffieHellman");

                put("AlgorithmParameters.DES",
                    "com.ibm.crypto.provider.DESParameters");

                put("AlgorithmParameters.DESede",
                    "com.ibm.crypto.provider.DESedeParameters");
                put("Alg.Alias.AlgorithmParameters.TripleDES", "DESede");

                put("AlgorithmParameters.PBE",
                    "com.ibm.crypto.provider.PBEParameters");
                put("Alg.Alias.AlgorithmParameters.PBEWithMD5AndDES", "PBE");

                put("AlgorithmParameters.Blowfish",
                    "com.ibm.crypto.provider.BlowfishParameters");

                /*
                 * Key factories
                 */
                put("KeyFactory.DiffieHellman",
                    "com.ibm.crypto.provider.DHKeyFactory");
                put("Alg.Alias.KeyFactory.DH", "DiffieHellman");

                /*
                 * Secret-key factories
                 */
                put("SecretKeyFactory.DES", 
                    "com.ibm.crypto.provider.DESKeyFactory");

                put("SecretKeyFactory.DESede",
                    "com.ibm.crypto.provider.DESedeKeyFactory");
                put("Alg.Alias.SecretKeyFactory.TripleDES", "DESede");

                put("SecretKeyFactory.PBEWithMD5AndDES",
                    "com.ibm.crypto.provider.PBEKeyFactory");

                /*
                 * MAC
                 */
                put("Mac.HmacMD5", "com.ibm.crypto.provider.HmacMD5");
                put("Mac.HmacSHA1", "com.ibm.crypto.provider.HmacSHA1");

                /*
                 * KeyStore
                 */
                put("KeyStore.JCEKS", "com.ibm.crypto.provider.JceKeyStore");

                return null;
            }
        });
    }
}

Appendix B: The java.security Properties File

Below is a copy of the java.security file that appears in every JRE installation. This file appears at
<java-home>\lib\security\java.security         [Win32]
Here <java-home> refers to the directory where the JRE was installed. Thus, if you have J2SDK v 1.2 installed on Windows in a directory named jdk1.2, the file would be
jdk1.3/jre/lib/security/java.security
See Step 5d for an example of adding information about your provider to this file.
#
# This is the "master security properties file".
#
# In this file, various security properties are set for use by
# java.security classes. This is where users can statically register 
# Cryptography Package Providers ("providers" for short). The term 
# "provider" refers to a package or set of packages that supply a 
# concrete implementation of a subset of the cryptography aspects of 
# the Java Security API. A provider may, for example, implement one or 
# more digital signature algorithms or message digest algorithms.
#
# Each provider must implement a subclass of the Provider class.
# To register a provider in this master security properties file, 
# specify the Provider subclass name and priority in the format
#
#    security.provider.n=className 
#
# This declares a provider, and specifies its preference 
# order n. The preference order is the order in which providers are 
# searched for requested algorithms (when no specific provider is 
# requested). The order is 1-based; 1 is the most preferred, followed 
# by 2, and so on.
#
# className must specify the subclass of the Provider class whose 
# constructor sets the values of various properties that are required
# for the Java Security API to look up the algorithms or other 
# facilities implemented by the provider.
# 
# There must be at least one provider specification in java.security. 
# There is a default provider that comes standard with the JDK. It
# is called the "SUN" provider, and its Provider subclass
# named Sun appears in the sun.security.provider package. Thus, the
# "SUN" provider is registered via the following:
#
#    security.provider.1=sun.security.provider.Sun 
#
# (The number 1 is used for the default provider.) 
#
# Note: Statically registered Provider subclasses are instantiated 
# when the system is initialized. Providers can be dynamically 
# registered instead by calls to either the addProvider or 
# insertProviderAt method in the Security class.

#
# List of providers and their preference orders (see above):
#
security.provider.1=sun.security.provider.Sun

#
# Class to instantiate as the system Policy. This is the name of the class
# that will be used as the Policy object.
#
policy.provider=sun.security.provider.PolicyFile

# The default is to have a single system-wide policy file, 
# and a policy file in the user's home directory.
policy.url.1=file:${java.home}/lib/security/java.policy
policy.url.2=file:${user.home}/.java.policy

# whether or not we expand properties in the policy file
# if this is set to false, properties (${...}) will not be expanded in policy
# files.
policy.expandProperties=true

# whether or not we allow an extra policy to be passed on the command line
# with -Djava.security.policy=somefile. Comment out this line to disable
# this feature.
policy.allowSystemProperty=true

# whether or not we look into the IdentityScope for trusted Identities
# when encountering a 1.1 signed JAR file. If the identity is found
# and is trusted, we grant it AllPermission.
policy.ignoreIdentityScope=false

#
# Default keystore type.
#
keystore.type=jks

#
# Class to instantiate as the system scope:
#
system.scope=sun.security.provider.IdentityDatabase