How to Implement a Provider for the
JavaTM Cryptography Extension(JCE)
in the JavaTM 2 SDK, v 6.0
Last Modified: 21st November 2007
Copyright information
Note: Before using this information and the product it supports, be sure to read the general information under Notices.
(c) Copyright Sun Microsystems, Inc. 1998, 2005, 901 San Antonio Rd., Palo Alto, CA 94303 USA. All rights reserved.
(c) Copyright International Business Machines Corporation, 1998, 2007. All rights reserved.
U.S. Government Users Restricted Rights - Use, duplication or disclosure restricted by GSA ADP Schedule Contract with IBM Corp.
- Introduction
- Who Should Read This Document
- Related Documentation
- A Note on Terminology
- What's New
- JCE in the Java 2 SDK, v 6.0
- JCE in the Java 2 SDK, v 5.0 (previous release)
- JCE in the Java 2 SDK, v 1.4 (older release)
- JCE 1.2.1 (older release)
- 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 Do Self-Integrity Checking
- Finding the Provider JAR File
- Verifying the Provider JAR File
- Notes on the Sample Code
- How a Provider Authenticates The Framework(JCE 1.2.1 only)
- Finding the Framework JAR File
- Verifying the Framework JAR File
- Complete Example
- 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
- Notices
Introduction
The JavaTM Cryptography Extension (JCE) provides a framework and implementations for encryption, key generation and key agreement, and Message Authentication Code (MAC) algorithms. Support for encryption includes symmetric, asymmetric, block, and stream ciphers. The software also supports secure streams and sealed objects.
JCE was previously an optional package (extension) to the Java 2 SDK, Standard Edition (Java 2 SDK), versions 1.2.x and 1.3.x. JCE has now been integrated into the Java 2 SDK, v 1.4.
JCE is based on the same design principles as Java 2 SDK, 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 Java 2 SDK.
JCE 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 that needs to use cryptography functionality can simply request a particular type of object (such as a Cipher object) that implements 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 Java 2 SDK installation has one or more provider packages installed. Each provider package supplies implementations of cryptographic services defined in Java 2 SDK v 1.4 or JCE or both.
Clients can 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 Java 2 SDK v 1.4.2 comes standard with a provider named "IBMJCE", which supplies the following services:
- Implementation of Message Authentication Code (MAC): HmacSHA1, HmacMD5
- Implementation of Key Agreement algorithms: DiffieHellman
- Implementation of Message Digest: MD2, MD5, SHA-1, SHA-256, SHA-384, SHA-512
- Implementation of Signature algorithms: SHA1withDSA, SHA1withRSA, MD5withRSA, MD2withRSA
- Implementation of Key Factory: DiffieHellman, DSA, RSA
- Implementation of Secret Key Factory: AES, Blowfish, DES, TripleDES, Mars, RC2, RC4, RC5, Seal, PKCS5Key, PBKDF1 and PBKDF2(PKCS5Derived Key)
- Implementation of Cipher: AES, DES, TripleDES, PBEWithMD2AndDES, PBEWithMD2AndTripleDES, PBEWithMD2AndRC2,PBEWithMD5AndDES, PBEWithMD5AndTripleDES, PBEWithMD5AndRC2, PBEWithSHA1AndDES, PBEWithSHA1AndTripleDES, PBEWithSHA1AndRC2, Mars, RC2, RC4, RSA, Seal
- Implementation of Modes of operation for Block Ciphers: ECB, CBC, CFB, CTR, OFB, PCBC
- Implementation of Key PairGeneration: DSA, RSA, DiffieHellman
- Implementation of Key Generation: Blowfish, DiffieHellman, DES, TripleDES, HmacMD5, HmacSHA1, Mars, RC2, RC4, RC5, Seal
- Implementation of Algorithm Parameter Generator: DiffieHellman, DSA
- Implementation of KeyStore: JCEKS, PKCS12, JKS
New providers can be added statically or dynamically. Clients might also query which providers are currently installed. The different implementations can have different characteristics. Some can be software-based, while others can be hardware-based. Some can be platform-independent, while others an be platform-specific. Some provider source code might be available for review and evaluation, while some might not.
Who Should Read This Document If you only need to use the Java Security API to access existing cryptography algorithms and other services, you 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 framework.
Related Documentation This document assumes that 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 includes two software components:
- The framework that defines and supports cryptographic services that providers can supply implementations for. This framework includes everything in the javax.crypto package.
- A provider named "IBMJCE"
Throughout this document, the term "JCE" refers to the JCE framework. If the full release is mentioned, it will be referred to as "the JCE release."
What's New
JCE in Java 2 SDK, v 6.0
These are the differences between JCE in Java 2 SDK, v 5.0 and Java 2 SDK, v 6.0 that affect providers:
Algorithm names are now compared in the English Locale so as to avoid internationalization issues.
Caching of CryptoPermissions is now done in order to reduce lookup times in the policy files.
JCE in Java 2 SDK, v 5.0
These are the differences between JCE in Java 2 SDK, v 1.4 and Java 2 SDK, v 5.0 that affect providers:
Methods that take ByteBuffer arguments were added to the JCE API and SPI classes for processing bulk data. Providers can override the engine* methods if they can process ByteBuffers more efficiently than byte[].
In 5.0, new parameter classes were added to fully support OAEP.
To make EncyptedPrivateKeyInfo easier to use and to make its API consistent with javax.crypto.SealedObject, methods were added to javax.crypto.EncryptedPrivateKeyInfo.
Methods were added to javax.crypto.Cipher to enable dynamic maximum allowable key length determination.
JCE in Java 2 SDK, v 1.4
These are the differences between JCE 1.2.1 and the JCE in the Java 2 SDK, v 1.4 that affect providers:
JCE was previously an optional package (extension) to the Java 2 SDK, Standard Edition (Java 2 SDK), versions 1.2.x and 1.3.x. JCE has now been integrated into the Java 2 SDK, v 1.4. The IBMJCE provider is also included and is automatically registered in the java.security security properties file included with the Java 2 SDK, v 1.4.
Due to import control restrictions, the jurisdiction policy files included with the Java 2 SDK, v 1.4 allow "strong" but limited cryptography to be used. An "unlimited" version of these files indicating no restrictions on cryptographic strengths is available for those living in eligible countries (which is most countries). You can download this version and replace the strong cryptography versions supplied with the Java 2 SDK, v 1.4.2 with the unlimited ones. See the following web site for information indicating where to go to download the unlimited version. Note that the jurisdiction policy files from other JDK levels (e.g. 1.4.1) are not applicable to 1.4.2 and vice versa.
http://www6.software.ibm.com/dl/jcesdk/jcesdk-p
The jurisdiction policy files have been relocated to
<java-home>\lib\security [Windows] <java-home>/lib/security [Unix]
where <java-home> refers to the directory where the runtime software is installed, which is the top-level directory of the JavaTM 2 Runtime Environment (JRE) or the jre directory in the JavaTM 2 SDK (Java 2 SDK) software. They have been moved to this standard location so that it is easy to replace the strong cryptography versions that come with the Java 2 SDK, v 1.4 with the unlimited ones.
In JCE 1.2.1, providers needed to include code to authenticate the JCE framework to assure themselves of the integrity and authenticity of the JCE that they plugged into. Now that JCE is integrated into the Java 2 SDK, v 1.4, this is no longer necessary.
JCE 1.2.1 providers that follow the guidance in the section on "How a Provider Authenticates The Framework" will work with the JCE framework in the Java 2 SDK, v 1.4.
However, a provider whose framework authentication code locates the JCE framework using protection domain instead of following the recommendations in the aforementioned JCE 1.2.1 JCE provider document will not work in the Java 2 SDK, v 1.4. Now that JCE has been integrated into the Java 2 SDK, v 1.4, the JCE framework has a null code source just like any other class in the Java 2 SDK, v 1.4. You can either modify your provider to follow the recommended approach for authenticating the framework, or put in a condition so that the framework authentication code is executed only when the provider is being run with JCE 1.2.1.
There are two differences between JCE 1.2 and JCE 1.2.1 that affect providers, one substantial and one minor (and optionally implemented):
The primary difference between JCE 1.2 and JCE 1.2.1 is that the JCE 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 framework contains such mechanisms and is now exportable. It is transparent to application developers how providers are authenticated, and only qualified providers can be plugged into JCE.
The JCE 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 framework worldwide exportable and worldwide importable.
The cryptographic services that 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, not your provider, to enforce restrictions regarding the cryptographic algorithms and maximum cryptographic strengths available. For further information as to how this implementation affects applets and applications, see Application Exportability in the Java Cryptography Extension API Specification & Reference.
In order to be able to plug into JCE, a provider should follow certain implementation guidelines and must be signed by an entity trusted by JCE, as described in Ensuring Exportability.
A provider can 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.
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 Java Cryptography Extension API Specification & Reference.
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 was previously an optional package (also known as an "extension") to the Java 2 Platform, versions 1.2.x and 1.3.x. JCE has now been integrated into the Java 2 SDK, v 1.4 and supplements the cryptographic services defined since Java 2 SDK v 1.2 by adding the following engine classes:
Cipher - used to encrypt or decrypt some specified data.
KeyAgreement - used to execute a key agreement (key exchange) protocol between 2 or more parties.
KeyGenerator - used to generate a secret (symmetric) key suitable for a specified algorithm.
Mac - used to compute the message authentication code of some specified data.
SecretKeyFactory - used to convert opaque cryptographic keys of type SecretKey into key specifications (transparent representations of the underlying key material), and vice versa.
ExemptionMechanism - used to provide the functionality of an exemption mechanism, examples of which are key recovery, key weakening, and key escrow. Applications or applets that use an exemption mechanism can be granted stronger encryption capabilities than those that don't.
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, can 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, such as DES keys.
Steps to Implement and Integrate a Provider
The steps required in order to implement a provider and integrate it into JCE 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 in addition to implementations of cryptographic services defined in Java 2 SDK v 1.4.2.
In JCE, you can supply cipher, key agreement and MAC algorithms, as well as secret-key factories, secret-key generation services, and exemption mechanism implementations.
In Java 2 SDK 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 or Java 2 SDK, you need to create a subclass of the appropriate SPI class. JCE defines the engine classes: CipherSpi, KeyAgreementSpi, KeyGeneratorSpi, MacSpi, SecretKeyFactorySpi, and ExemptionMechanismSpi. (See "JCE Engine Classes and Corresponding SPI Classes")
In your subclass, you need to
- Supply implementations for the abstract methods, whose names usually begin with "engine". See Further Implementation Details and Requirements for additional information.
- Ensure there is a public constructor without any arguments. When one of your services is requested, Java Security looks up the subclass that implements 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.
Additional JCE Provider Requirements and Recommendations
When instantiating a provider's implementation (class) of a JCE service, the JCE framework will determine the provider's codebase (JAR file) and verify its signature. In this way, JCE authenticates the provider and ensures that only providers signed by a trusted entity can be plugged into JCE. Therefore, one new requirement for providers in JCE is that they must be signed, as described in later steps. Similarly, when using JCE1.2.1 framework, 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 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. For further information, see How a Provider Can Authenticate JCE and Do Self-Integrity Checking.
In order for provider classes to become unusable if instantiated by an application directly, bypassing JCE, 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 can 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 can implement for JCE are:
- The engineWrap and engineUnwrap CipherSpi methods. See Wrapping or Unwrapping Keys.
- One or more "exemption mechanisms." An exemption mechanism is something such as key recovery, key escrow, or key weakening which, if implemented and enforced, can 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 name will 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:
- 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("CryptoX", 1.0, "CryptoX provider v1.0, implementing " + "RSA encryption and key pair generation, and DES encryption.");
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 service implemented by the provider, there must be a property whose name is the type of service (Cipher, KeyAgreement, KeyGenerator, Mac, SecretKeyFactory, or ExemptionMechanism), followed by a period and the name of the algorithm to which the service applies. The property value must specify the fully qualified name of the class that implements the service.
The list below shows the various types of properties that must be defined for the various types of JCE services, where the actual algorithm name is substitued for algName:
Cipher.algName KeyAgreement.algName KeyGenerator.algName Mac.algName SecretKeyFactory.algName ExemptionMechanism.algName In all of these except ExemptionMechanism and Cipher, algName is the "standard" name of the algorithm. In the case of ExemptionMechanism, algName refers to the name of the exemption mechanism, which must be one of the following: "KeyRecovery", "KeyEscrow", or "KeyWeakening". Case does not matter.
In the case of Cipher, algName can actually represent a transformation, and can be composed of an algorithm name, a particular mode, and a padding scheme. (See Appendix A of the Java Cryptography Extension API Specification & Reference for the standard algorithm names that should be used.)
The value of each property must be the fully qualified name of the class that implements 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 "IBMJCE" provider implements the Diffie-Hellman key agreement algorithm in a class named DHKeyAgreement in the com.ibm.crypto.provider package. Its subclass of Provider (which is the IBMJCE class in the com.ibm.crypto.provider package) sets the KeyAgreement.DiffieHellman property to have the value "com.ibm.crypto.provider.DHKeyAgreement" using the following: put("KeyAgreement.DiffieHellman", "com.ibm.crypto.provider.DHKeyAgreement")
For further master class property setting examples, see Appendix A to view the current JCE 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, such as from a file.
As mentioned above, in the case of a Cipher property, algName can 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 (such as DES), and can be followed by a mode and a padding scheme.
A transformation is of the form:
- "algorithm/mode/padding" or
- "algorithm"
(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 (such as DES in CFB or OFB mode), a client might 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 might supply a separate class for each combination of algorithm/mode/padding, or might 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 or padding or both 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 can have one of the following formats:
// provider's subclass of "CipherSpi" implements "algName" with // pluggable mode and padding Cipher.algName
// provider's subclass of "CipherSpi" implements "algName" in the // specified "mode", with pluggable padding Cipher.algName/mode
// provider's subclass of "CipherSpi" implements "algName" with // the specified "padding", with pluggable mode Cipher.algName//padding
// provider's subclass of "CipherSpi" implements "algName" with // the specified "mode" and "padding" Cipher.algName/mode/padding
(See Appendix A of the Java Cryptography Extension API Specification & Reference for the standard algorithm names, modes, and padding schemes that should be used.) For example, a provider can 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:
Cipher.DES/ECB/PKCS5Padding
Cipher.DES/CBC/PKCS5Padding
Cipher.DES/CFB/PKCS5Padding
Cipher.DES/OFB/PKCS5Padding
Another provider may implement a class for each of the above modes (that is, 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: Cipher.DES 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":
- Check if the provider has registered a subclass of CipherSpi for the specified "algorithm".
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":
- Check if the provider has registered a subclass of CipherSpi for the specified "algorithm/mode/padding" transformation.
If the answer is YES, instantiate it. If the answer is NO, go to the next step. - Check if the provider has registered a subclass of CipherSpi for the sub-transformation "algorithm/mode".
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. - Check if the provider has registered a subclass of CipherSpi for the sub-transformation "algorithm//padding" (note the double slashes).
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. - Check if the provider has registered a subclass of CipherSpi for the sub-transformation "algorithm".
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(R) Windows(R)).
- Use keytool to generate a DSA keypair.
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>
(Note: This command must be typed as a single line. Multiple lines and indentation are used in the examples just for legibility purposes.) This command 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 want 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 using 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 to run the command in the batch file.
- Use the keytool to generate a certificate signing request.
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>. - Send the CSR and contact information to the JCE Code Signing Certification Authority.
Send, using email, the CSR and contact information to javasec@us.ibm.com. Put the following text 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.
- Use keytool to import the certificates received from the CA.
After 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 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 (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 using the following command:
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 that will be used by the clients that will 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. Your provider classes should be packaged as a JAR (Java ARchive) file. There are two 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.
The next step is to add the provider to your list of approved providers. Adding the provider is done statically by editing the security properties file <java-home>\lib\security\java.security [Windows] <java-home>/lib/security/java.security [Unix]
Here <java-home> refers to the directory where the JRE was installed. For example, if you have Java 2 SDK v 1.4 installed on Windows in a directory named jdk1.4, you need to edit the following file: C:\jdk1.4\jre\lib\security\java.security
For each provider, this file should have a statement of the following form: security.provider.n=masterClassName
This statement 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.
Java 2 SDK v 1.4 comes standard with a number of providers that are automatically configured as static providers in the java.security properties file, as follows:
security.provider.1=com.ibm.jsse.IBMJSSEProvider security.provider.2=com.ibm.crypto.provider.IBMJCE security.provider.3=com.ibm.security.jgss.IBMJGSSProvider security.provider.4=com.ibm.security.cert.IBMCertPath
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 fifth preferred provider. To do so, add the following line to the java.security file below the line for the "IBMCertPath" provider: security.provider.5=com.cryptox.provider.CryptoX Note: Providers can 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 be done only by "trusted" programs. See the Security class section of the Java Cryptography Architecture API Specification and Reference. For instance, if the provider code of a provider with name "MyJCE" is in myjce_provider.jar in the myDir directory: grant codeBase "file:/myDir/myjce_provider.jar" { permission java.security.SecurityPermission "insertProvider.MyJCE"; };
Step 5e: Set Provider Permissions
Whenever JCE providers are not installed extensions, permissions must be granted for when applets or applications using JCE are run while a security manager is installed. There is typically a security manager installed whenever an applet is running, and a security manager can be installed for an application either using code in the application itself or via a command-line argument. Permissions do not need to be granted to installed extensions, because the default system policy file grants all permissions to installed extensions. Whenever a client does not install your provider as an installed extension, your provider might need the following permissions granted to it in the client environment:
- java.lang.RuntimePermission to get class protection domains. The provider might need to get its own protection domain in the process of doing self-integrity checking.
- java.security.SecurityPermission to set provider properties.
To ensure that your provider works when a security manager is installed and that the provider is not an installed extension, you need to test such an installation and execution environment. In addition, prior to testing you need to grant appropriate permissions to your provider and to any other providers it uses. For example, a sample statement granting permissions to a provider whose name is "MyJCE" and whose code is in myjce_provider.jar appears below. Such a statement could appear in a policy file. In this example, the myjce_provider.jar file is assumed to be in the /myDir directory. grant codeBase "file:/myDir/myjce_provider.jar" { permission java.lang.RuntimePermission "getProtectionDomain"; permission java.security.SecurityPermission "putProviderProperty.MyJCE"; };
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 algorithms. 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 is 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 a provider that is 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 or 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 Java Cryptography Extension API Specification & Reference for complete information about creating and testing such an application.
Step 7: Run your Test Programs
Run your test programs. 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 that they are all completed.
Step 8: Document Your Provider and Its Supported Services
The next step is to write documentation for your customers. At the minimum, you need to specify
- The name programs should use to refer to your provider. Note: Provider name searches are case-sensitive. That is, if your master class specifies your provider name as "CryptoX" but a user requests "CRYPTOx", your provider will not be found.
- The types of algorithms and other services implemented by your provider.
- Instructions for installing the provider, similar to those provided in Step 5d, except that the information and examples should be specific to your provider.
In addition, your documentation should specify anything else of interest related to using your provider such as any default algorithm parameters. MACs For each MAC algorithm, tell whether or not your implementation is cloneable, to save your customers some time and coding. Customers 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
- mac is the MAC object they received when they requested one using a call to Mac.getInstance,
- i1,i2 and i3 are input byte arrays, and
- they want to calculate separate hashes for:
- i1
- i1 and i2
- i1, i2, and i3
Key Pair Generators For a key pair generator algorithm, in case the customer does not explicitly initialize the key pair generator (using 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 (using 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 can 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. Consult your export counsel for more information. The following URLs might 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 be valid for a longer than the temporary certificate you received for testing. 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 be distributed only inside the U.S. and Canada.
- Use keytool to generate a DSA keypair.
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>
(Note: This command must be typed as a single line. Multiple lines and indentation are used in the examples just for legibility purposes.) This command 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.
- Use keytool to generate a certificate signing request.
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>. - Send the CSR, contact information, and proof of export approval or domestic-only certification to the JCE Code Signing Certification Authority.
Send, using email, the CSR and contact information to javasec@us.ibm.com.
Put the following text in the Subject line of your e-mail 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 e-mail message, it will send you a request number, also using email. After receiving this request number, mail required documents to the address specified below. The documents you need to send depend on whether or not the provider can be exported outside the U.S. and Canada.
- If the provider can 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 be distributed only inside the U.S. and Canada.
In both cases, the documents 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 e-mail 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.
- Use keytool to import the certificates received from the CA.
After 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 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 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 (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 might 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 using the following command:
jarsigner -verify <JAR file name> The text "jar verified" will be displayed if the verification was successful.
Step 10: Run Your Test Programs Again
You should have thoroughly tested your provider in Step 7. However, we suggest that you run your test programs again to make sure that the provider works correctly because 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 Do Self-Integrity Checking
Each provider should do self-integrity checking to ensure that the JAR file containing its code has not been tampered with, for example in an attempt to invoke provider methods directly rather than through JCE. Integrity checking takes place when the JCE framework tries to instantiate a provider's implementation of a JCE cryptographic service (not when the provider is registered). That way, providers with implementations of cryptographic services defined in both J2SE and JCE (for example, 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 authentication. Only those cryptographic services related to JCE will have to perform self-checking, and will become usable (in the JCE environment) only after such checking has taken place.
Providers that provide implementations for JCE services must be digitally signed with a certificate that is issued by "trusted" Certification Authorities. Currently, the following two Certification Authorities are considered "trusted":
- IBM JCE Code Signing CA, and
- Sun Microsystems' JCE Code Signing CA.
Refer to Step 5a (for testing) and Step 9b (for production) for detailed information about how to get a code-signing certificate from the IBM JCE Code Signing CA and the certificate of that CA. The basic approach that a provider can use to check its own integrity is:
- Determine the URL of the JAR file containing the provider code, and
- Verify the JAR file's digital signatures to ensure that at least one signer of each entry of the JAR file is trusted.
These steps and their substeps are described in the following sections:
- Finding the Provider JAR File
- Determining the Provider's JAR File URL
- Creating a JarFile Referring to the JAR File
- Verifying the Provider JAR File
- Verification Setup
- JAR File Signature Check
- Verifying Signatures
- Ensuring Signers Are Trusted
- Getting the List of Certificates
- Identifying Each of the Signers and Determining If One is Trusted
- Notes on the Sample Code
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.
URL providerURL = (URL) AccessController.doPrivileged( new PrivilegedAction) { public Object run() { CodeSource cs = MyProvider.class.getProtectionDomain(). getCodeSource(); return cs.getLocation(); } });
After you have the URL for the provider's JAR file, you can create a java.util.jar.JarFile that refers to the JAR file. This instance is needed in the step for verifying the Provider JAR file.
To create the JAR file, first open a connection to the specified URL by calling its openConnection method. Because the URL is a JAR URL, the type is java.net.JarURLConnection. Here's the basic code:
// Prep the url with the appropriate protocol. jarURL = url.getProtocol().equalsIgnoreCase("jar") ? url : new URL("jar:" + url.toString() + "!/"); // Retrieve the jar file using JarURLConnection JarFile jf = (JarFile) AccessController.doPrivileged( new PrivilegedExceptionAction() { public Object run() throws Exception { JarURLConnection conn = (JarURLConnection) jarURL.openConnection(); ... Now that you have a JarURLConnection, you can call its getJarFile method to get the JAR file: // Always get a fresh copy, so we don't have to // worry about the stale file handle when the // cached jar is closed by some other application. conn.setUseCaches(false); jf = conn.getJarFile();
After you have determined the URL for your provider JAR file and you have created a JarFile that refers to the JAR file, as shown in the steps above, you can then verify the file.
The basic approach is:
- Ensure that at least one of each entry's signer certificates is equal to the provider's own code signing certificate.
- Go through all the entries in the JAR file and ensure that the signature on each one verifies correctly.
- Ensure that at least one of each entry's signer certificates can be traced back to a trusted Certification Authority.
Sample code for each of these steps is presented and described in the following sections:
- Verification Setup
- JAR File Signature Check
- Verifying Signatures
- Ensuring Signers Are Trusted
- Getting the List of Certificates
- Identifying Each of the Signers and Determining If One is Trusted
The approach in this example is to define a class JarVerifier to handle the retrieval of a JAR file from a given URL and verify whether the JAR file is signed with the specified certificate.
The constructor of JarVerifier takes the provider URL as a parameter, which will be used to retrieve the JAR file later.
The actual jar verification is implemented in the verify method, which takes the provider code signing certificate as a parameter.
public void verify(X509Certificate targetCert) throws IOException { // variable 'jarFile' is a JarFile object created // from the provider's Jar URL. ... Vector entriesVec = new Vector();
Basically the verify method will go through the JAR file entries twice: the first time checking the signature on each entry and the second time verifying that the signer is trusted. Note: In these code snippets, the jarFile variable is the JarFile object of the provider's jar file.
An authentic provider JAR file is signed. If the JAR file is not signed, it has been tempered with:
// Ensure the jar file is signed. Manifest man = jarFile.getManifest(); if (man == null) { throw new SecurityException("The provider is not signed"); }
The next step is to go through all the entries in the JAR file and ensure that 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:
// Ensure all the entries' signatures verify correctly byte[] buffer = new byte[8192]; Enumeration entries = jarFile.entries(); while (entries.hasMoreElements()) { JarEntry je = (JarEntry) entries.nextElement();
// Skip directories. if (je.isDirectory()) continue; entriesVec.addElement(je); InputStream is = jarFile.getInputStream(je);
// Read in each jar entry. A security exception will // be thrown if a signature/digest check fails. int n; while ((n = is.read(buffer, 0, buffer.length)) != -1) { // Don't care } is.close(); }
The code in the previous section verified the signatures of all the provider 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 the same entity as the one that developed this provider. To test that the signatures are trusted, 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):
- Get the list of signer certificates for the entry.
- Identify each of the certificate chains and determine whether any of the certificate chains are trusted. At least one of the certificate chains must be trusted.
The loop setup is the following: Enumeration e = entriesVec.elements(); while (e.hasMoreElements()) { JarEntry je = (JarEntry) e.nextElement(); ... }
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 line of code to the previous loop setup code, and adding code to ignore directories and files in the META-INF directory results in:
while (e.hasMoreElements()) { JarEntry je = (JarEntry) e.nextElement();
// 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 provider " + "has unsigned " + "class files."); } else { // Check whether the file is signed by the expected // signer. The jar may be signed by multiple signers. // See if one of the signers is 'targetCert'. ... } ...
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 signer's certificate, which contains the public key corresponding to the private key that was actually used to sign the entry. Each subsequent certificate is a certificate for the issuer of the previous certificate. Because the self-integrity check is based on whether the JAR file is signed with the provider's signing cert, the trust decision will be made upon only the first certificate, the signer's certificate.
Next to go through the array of certificate chains and check each chain and the associated signers until you find a trusted entity. For each JAR file entry, at least one of the signers must be trusted. A signer is considered "trusted" if and only if its certificate is equals to the embedded provider signing certificate.
The following sample code loops through all the certificate chains, compares the first certificate in a chain to the embedded provider signing certificate, and only returns true if a match is found.
int startIndex = 0; X509Certificate[] certChain; boolean signedAsExpected = false; while ((certChain = getAChain(certs, startIndex)) != null) { if (certChain[0].equals(targetCert)) { // Stop since one trusted signer is found. signedAsExpected = true; break; } // Proceed to the next chain. startIndex += certChain.length; } if (!signedAsExpected) { throw new SecurityException("The provider " + "is not signed by a " + "trusted signer"); }
The getAChain method is defined as follows: /** * Extracts ONE certificate chain from the specified certificate array * which may contain multiple certificate chains, starting from index * 'startIndex'. */ private static X509Certificate[] getAChain(Certificate[] certs, int startIndex) { if (startIndex > certs.length - 1) return null; int i; // Keep going until the next certificate is not the // issuer of this certificate. for (i = startIndex; i < certs.length - 1; i++) { if (!((X509Certificate)certs[i + 1]).getSubjectDN(). equals(((X509Certificate)certs[i]).getIssuerDN())) { break; } }
// Construct and return the found certificate chain. int certChainSize = (i-startIndex) + 1; X509Certificate[] ret = new X509Certificate[certChainSize]; for (int j = 0; j < certChainSize; j++ ) { ret[j] = (X509Certificate) certs[startIndex + j]; } return ret; }
The sample code, MyProvider.java, is a sample provider that has a method selfIntegrityChecking, which performs self-integrity checking. It first determines the URL of its own provider JAR file and then verifies that the provider JAR file is signed with the embedded code-signing certificate. Note: The method selfIntegrityChecking should be called by all the constructors of its cryptographic engine classes to ensure that its integrity is not compromised.
Provider MyProvider performs self-integrity checking in the following steps:
- Determine the URL to access the provider JAR file using its own class,
MyProvider.class.
- Instantiate a
JarVerifier object with the provider URL in Step 1.
- Create a
X509Certificate object from the embedded byte array bytesOfProviderCert.
- Call the
JarVerifier.verify method to verify that all entries in the provider JAR file are signed and that they are signed with the same certificate that was instantiated in Step 3.
Note: The class JarVerifier will retrieve the JAR file from the given URL and make sure that the JAR file is signed, all entries have valid signatures, and all entries are signed with the specified X509Certificate. A security exception is thrown by JarVerifier.verify in several cases:
- The certificate passed to
verify is null (invalid).
- When unable to retrieve the JAR file from the given URL.
- The provider is not signed. (The jar has no manifest.)
- The provider has unsigned class files.
- The provider is not signed with the specified certificate.
The MyProvider.java sample code is comprised of the code snippets shown above. In addition, it includes error handling, sample code signing certificate bytes, and code for instantiating a X509Certificate object from the embedded sample code signing certificate bytes.
Regarding the use of AccessController.doPrivileged, see API For Privileged Blocks for information on the use of doPrivileged.
JCE 1.2.1 was an optional extension to Java 2 v 1.3 and as such the JCE framework was packaged in a separate jar file that was digitally signed. Providers that provide implementations for JCE services, which need to work with JCE 1.2.1, need to authenticate that the JCE framework is valid and has not been tampered with. Each SPI implementation class, in addition to performing self-integrity chekcing must authenticate the JCE framework in its constructor, by:
- Determining the codebase (URL) of the JCE JAR file and
- Verifying its digital signature
These steps and their substeps are 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 framework JAR File
- Finding the Cipher Class
- Extracting the JAR URL
- Creating a JarFile Referring to the JAR File
- Complete Example
- Verifying the framework JAR File
- 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
- Complete Example
The basic idea for a sample method to determine the URL for the JCE framework is the following: An authentic JCE framework must contain the javax.crypto.Cipher class, you can find the URL for that class and extract the subpart of that URL which identifies the JAR file that the class is in. This JAR file is also the JAR file for the JCE framework. To find the URL for the Cipher class, you begin with what you know about the current class (the SPI implementation class): 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");
This method works because the JCE 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, if the provider has sufficient permissions. IMPORTANT NOTE: If you want your provider to work both with JCE 1.2.1 and with the JCE in the Java 2 SDK, v 1.4, then you will need to add a conditional statement. This way the provider code to authenticate the JCE framework is executed only when the provider is run with JCE 1.2.1. The following is sample code:
Class cipherCls = Class.forName("javax.crypto.Cipher"); CodeSource cs = cipherCls.getProtectionDomain().getCodeSource(); if (cs != null) { // Authenticate JCE framework . . . }
Extracting the JAR URL The syntax of a JAR URL is: jar:<URL_for_jar_file>!/<entry>
Therefore, the URL returned by the getResource call should be in the following format: jar:<URL_for_jce_jar_file>!/javax/crypto/Cipher.class
|