The Java Platform, Standard Edition (Java SE) provides application developers with a large set of security APIs, tools, and implementations of commonly used security algorithms, mechanisms, and protocols. These security APIs span a wide range of areas, including cryptography, public key infrastructure, secure communication, authentication, and access control. In addition, the security tools facilitate the ability of users or administrators to securely deploy and manage Java platform applications.
As technology evolves, native platforms undergo many security improvements, for example, cryptographic accelerators, secure key management, more built-in security services, and so on. Leveraging the security offered by the native platform provides several significant benefits to the Java platform: They include but are not limited to the performance boost that cryptographic accelerators provide, a consistent behavior that matches what native applications have when they use the same native library, and the seamless sharing of users' native credentials.
This article will discuss important enhancements on the native security integration using JDK 6. Note: Any API additions or other enhancements to the Java SE platform specification are subject to review and approval by the JSR 270 Expert Group.
After an overview, this article will cover each feature in turn and illustrate the common usage scenarios with examples and sample code. To better understand the examples, you should familiarize yourself with existing security APIs and security provider frameworks. If you are new to Java security, see the Java Security Overview. Additional links are available in the For More Information section at the end of this article.
Note that the enhancements that this article discusses may not be available for all native platforms. The Appendix provides more details, including the first JDK release in which each feature was introduced as well as the native platforms that each feature supports.
Contents
- Access Microsoft CryptoAPI and Its Cryptographic Services
- Access PKCS#11 Cryptographic Services
- Access Native GSS-API
- Import and Export PKCS#12 Keystores
- Conclusion
- Appendix
- For More Information
On the Microsoft (MS) Windows operating system, the MS CryptoAPI (CAPI) defines a standard interface for performing cryptographic operations as well as accessing the user keys and certificates that Windows manages. The SunMSCAPI provider is layered on top of CAPI and helps Java platform applications access CAPI cryptographic services using existing Java technology security and cryptography APIs. This means that Java platform applications can now use the SunMSCAPI provider to do the following:
- Access private keys and certificates stored in CAPI
- Use CAPI's cryptographic algorithm implementations
Table 1 shows the complete list of cryptographic services that the SunMSCAPI provider supports.
Table 1. Cryptographic Services Supported by the SunMSCAPI Provider |
Service Type | Service Name | Service Description | ||
---|---|---|---|---|
| Generates RSA key pairs needed by other cryptographic services such as | |||
| Creates and validates signatures using various message digest and encryption algorithm as specified in the service name. | |||
| Performs RSA encryption and decryption. | |||
| Provides direct read-write access to MS Window's keystores. The | |||
| Generates random numbers for the random data that other cryptographic services need. | |||
Note that JDK 6 is preconfigured with multiple providers that are listed in order of preference -- with position 1 being most preferred -- as defined in the Java technology security properties file,
. More than one provider supports RSA cryptographic services: The SunRsaSign provider offers the RSA KeyPairGenerator
and Signature
services and the SunJCE provider offers the RSA Cipher
service. By default, the SunMSCAPI provider is installed with a preference lower than both to ensure maximum compatibility. A Java platform application that does not indicate which provider to use will get the implementation from the most preferred provider that supports the requested algorithm. You can find more details on provider lookup and management in the section "How Provider Implementations Are Requested and Supplied" of the Java Cryptography Architecture document.
Keys and certificates stored in MS Windows key containers and certificate stores, known as keystores, can be accessed by using the java.security.KeyStore
class and the SunMSCAPI provider. The same set of KeyStore APIs are used for accessing MS Windows keystores and other types of keystores, such as JKS or PKCS12
. However, because MS Windows keystores do not use passwords and cannot be imported or exported, values for password and input-output stream arguments should be null. By default, the SunMSCAPI provider silently ignores non-null values. In addition, changes are reflected immediately when making modifications to the keystore, such as KeyStore.setKeyEntry(...)
, KeyStore.deleteEntry(...)
.
For example, here is one way to sign data by using the RSA private key stored in the MS Windows keystore under the alias myRSA
and then to verify the generated signature:
KeyStore ks = KeyStore.getInstance("Windows-MY"); |
Note that keys produced by the SunMSCAPI provider are wrapper objects for the native handles. Thus, they may not be accepted by other providers and may behave somewhat differently than keys produced by pure-Java providers, such as SunJCE. In particular, the RSA private keys generated by the SunMSCAPI provider cannot be serialized and implement the java.security.PrivateKey
interface instead of the java.security.interfaces.RSAPrivateKey
interface.
If you are writing a Java platform application and want to use the keys and certificates from MS Windows keystores with Java Secure Socket Extension (JSSE), refer to the JSSE Reference Guide for instructions on how to customize the default key and trust stores.
PKCS#11, the Cryptographic Token Interface Standard, defines native programming interfaces to cryptographic tokens such as hardware cryptographic accelerators and smart cards. To help Java platform applications access native PKCS#11 tokens, a new provider was introduced in JDK 5.0 to act as a bridge between the native PKCS#11 tokens and the applications.
This means that Java platform applications can now use existing security and cryptography APIs in the Java platform to take advantage of benefits offered by the underlying PKCS#11 implementations, such as the following:
- Cryptographic smart cards for added security
- Hardware cryptographic accelerators for better performance
- Software implementations for more algorithms or for meeting certification requirements
Note that this provider does not come with its own PKCS#11 implementation and simply uses what it is configured with. On the Solaris 10 operating system, it is preconfigured to use the Solaris Cryptographic Framework. Thus, when the particular machine has cryptographic accelerators built in, all existing Java platform applications benefit automatically without requiring any changes to the configuration or code. To use other PKCS#11 tokens or implementations, the PKCS#11 provider requires a configuration file that supplies information on the PKCS#11 library, which most vendors provide along with their cryptographic devices, as well as a unique name identifier for differentiating this provider from other providers.
The configuration file is a text file that contains mappings of options and their values. Most of the options are for regular PKCS#11 libraries, except for the small number of options specific to Network Security Services (NSS). NSS is an open-source FIPS-140 certified cryptographic implementation used in a variety of products including Mozilla Firefox, AOL Communicator, and Sun Java Enterprise System (JES). NSS cryptographic APIs are based on PKCS#11, but they have special features outside of the PKCS#11 standard and thus require these special configuration options.
NSS is often used in one of the following two modes: as a cryptographic provider for its optimized cryptographic algorithm implementations or as an FIPS-140 compliant cryptographic token.
Assuming that NSS libraries are under the /opt/nss/lib
directory and that its key database files -- with the suffix .db
-- are under the /opt/nss/fipsdb
directory, the sample configurations for using NSS are as follows:
# Use NSS as a pure cryptography provider "SunPKCS11-NSScrypto". |
The configuration of non-NSS PKCS#11 libraries contains at least two options: a string that is concatenated with the prefix SunPKCS11-
to produce the name of the PKCS#11 provider as well as the full path to the native PKCS#11 libraries. The sample configuration for naming the PKCS#11 provider to SunPKCS11-Foo
using the PKCS#11 library at /opt/foo/lib/libpkcs11.so
is as follows:
name = Foo |
You can use other configuration options to further customize the provider. You can also use more than one PKCS#11 token or use multiple slots of the same PKCS#11 token by creating different PKCS#11 providers. Refer to the Java PKCS#11 Reference Guide for the supported options and their use.
Just like other providers, a PKCS#11 provider can be installed dynamically at runtime or statically in the Java technology security properties file, $JAVA_HOME/lib/security/java.security.
You can use the following code to programmatically create and install the PKCS#11 provider at runtime:
String configFileName = "/opt/foo/sunpkcs11-Foo.cfg"; |
To statically install the PKCS#11 provider, add the following provider preference entry to the Java technology security properties file:
security.provider.1=sun.security.pkcs11.SunPKCS11 /opt/foo/sunpkcs11-Foo.cfg |
You can find more details on provider lookup and management in the section "How Provider Implementations Are Requested and Supplied" of the Java Cryptography Architecture document.
With this new PKCS#11 provider, Java platform applications can access their PKCS#11 tokens and perform general cryptographic operations such as signature generation and verification through existing security APIs. For certain PKCS#11 features such as dynamically changing smart cards, JDK 6 contains a number of enhancements to better support those features, including added new APIs, updated tools, and so on.
For example, to access private keys on a PKCS#11 token and prompts for a personal identification number (PIN) at runtime, use this code:
// TextCallbackHandler prompts and reads the command line for |
For PKCS#11 tokens that require a PIN even for operations not related to a key, Sun's PKCS#11 provider extends the java.security.AuthProvider
class, which allows Java platform applications to supply PINs when needed using the registered callback handler. Note that the callback handler must be able to handle objects of type javax.security.auth.callback.PasswordCallback
:
AuthProvider myPKCS11Prov = |
To learn more about using JSSE with PKCS#11 cryptographic services, read the section "JCE and Hardware Acceleration/Smartcard Support" in the JSSE Reference Guide.
Security tools such as keytool
, jarsigner
, and policytool
are updated in JDK 6 to support PKCS#11 keystores as well. For example, to list the PKCS#11 keystore content using keytool
, use this code:
// When "SunPKCS11-Foo" provider is statically installed |
Note that keytool
will prompt users to enter the PIN information using the command line because the code in the previous sample has not supplied one using the option -storepass
. If such behavior is not desired in the application, for example, if the PKCS#11 token has its own dedicated PIN pad, you must specify the option -protected
to disable the prompting.
The jarsigner
tool supports PKCS#11 keystores with the same set of options as in the keytool
example and is quite straightforward. JDK 6 has enhanced the syntax of the keystore
entry in the default policy implementation to better support PKCS#11 keystores:
keystore "some_ks_url", "ks_type"[, "ks_provider"]; |
The elements inside the square brackets ([ ]) are optional. Using the PKCS11-Foo
provider from the previous examples and assuming its PIN is stored in a file named /opt/foo/sunpkcs11-Foo.passwd
, specify the following entry in the policy file:
keystore "NONE", "PKCS11", "SunPKCS11-Foo"; |
Note that the SunPKCS11-Foo
provider must be installed statically in order for policytool
to validate this keystore entry after it is entered.
The Generic Security Services API (GSS-API) defines a generic security API atop a variety of underlying cryptographic mechanisms including Kerberos version 5. With GSS-API, applications can authenticate a principal, delegate its rights to a peer, and apply security services such as confidentiality and integrity on a per-message basis. Request for Comment (RFC) 2744 and RFC 2853 specify the GSS-API language bindings for C and the Java programming language, respectively.
To help Java platform applications achieve seamless integration with native applications, JDK 6 enhances Java GSS-API to use native GSS-API instead of its own implementation of cryptographic mechanisms when configured to do so. When using the native GSS-API and its underlying native cryptographic mechanisms, the native credentials and settings in users' environment will be picked up automatically. This is different from the default case in which Java GSS-API uses its own implementation of cryptographic mechanisms. When using Kerberos, Java platform applications have to supply Kerberos configuration information using the designated Kerberos system properties in order for Java GSS-API to function. Existing Java GSS documentation covers the default case in great detail, so this section will focus on how to enable or configure Java GSS-API to use native GSS-API. At the end of this section, you will find sample programs that illustrate the point.
Before you enable Java GSS-API to use native GSS-API, it is important to ensure that native GSS-API and its underlying cryptographic mechanism are available and functioning with user settings. For example, you must ensure that native GSS libraries are installed at the appropriate directories with proper configurations, and the same applies to the Kerberos library and configurations. Note that native GSS-API assumes that before an application calls its APIs, it has already obtained and stored the mechanism-specific credentials in a location that the native mechanism implementation is aware of. Thus, when an application uses native GSS-API with Kerberos, it must already have obtained appropriate native credentials, such as Kerberos tickets and keys, using the kinit
tool, for example.
To make Java GSS-API use native GSS-API, Java platform applications must explicitly enable this behavior by setting one or more of the following system properties:
sun.security.jgss.native
(required): Set this totrue
to enable Java GSS-API to use native GSS-API.sun.security.jgss.lib
(optional): Set to the full path of the native GSS library. If this is not set, Java GSS-API will look for the native GSS library using the default Java library path. The default name varies by operating system: For the Solaris OS, uselibgss.so
; for Linux, uselibgssapi.so
.
Note: These two system properties are ignored when applications run on operating systems that do not yet support this feature, for example, MS Windows.
As mentioned previously, native GSS-API requires that the application has obtained these credentials and that they are accessible. Java platform applications can access these native credentials through Java GSS-API and use them for establishing GSS-API security contexts with peers. Note that when a Subject
is present, for example,
javax.security.auth.Subject.getSubject(AccessController.getContext()) != null |
Java GSS-API mandates that the credentials be obtained from the private or public credential sets of the current Subject
and that the Java GSS-API call must fail if the desired credential cannot be found. Thus, Java platform applications that execute the Java GSS-API calls inside a
Subject.doAs/doAsPrivileged(...) |
call should either populate the Subject
's credential sets with appropriate Java GSSCredential
objects that encapsulate the native credentials or explicitly set the system property javax.security.auth.useSubjectCredsOnly
to false
so that Java GSS-API can obtain credentials from other locations, for example, from native credential caches, in addition to the Subject
's credential sets.
When delegated to establish a GSS-API security context on behalf of others, Java platform applications can either specify the delegated credential, as returned by GSSContext.getDelegCred(),
explicitly in Java GSS-API calls or create a Subject
object with this delegated credential and execute the Java GSS-API calls inside the Subject.doAs/doAsPrivileged(...)
calls.
Lastly, once the native GSS-API is enabled, Java platform applications that indirectly call Java GSS-API through mechanisms or protocols such as Simple Authentication and Security Layer (SASL) will also use user's native settings and credentials.
Here is some sample code that helps demonstrate how to use Java GSS-API to establish GSS-API security contexts and securely exchange data between three parties: SampleClient
contacts FooServer
, which in turn contacts FooServer2
on behalf of SampleClient
. Note:
The sample code should be invoked with native GSS-API enabled. The Principal names
host@foo.sample.com
andhost@foo2.sample.com
are placeholders and should be replaced with actual principal names in your Kerberos database.When a security manager is installed, some Java GSS-API calls require that permissions be granted. Check the Java platform documentation of the following classes for more details:
To simplify the example, token exchanges between peers are represented by two pseudo methods:
SEND_TOKEN(byte[])
andREAD_TOKEN()
. Their actual implementation are application specific and thus not shown here.To reduce code duplication, context establishment code is referred by a pseudo method,
ESTABLISH_CONTEXT(GSSContext)
, in the code segments forSampleClient
,FooServer
, andFooServer2
.
Following is the implementation using Java GSS-API.
/** |
Following are the code segments for SampleClient
, FooServer
, and FooServer2
:
//================================================================ |
PKCS#12, the Personal Information Exchange Syntax Standard, defines a portable format for storing or transporting personal identity information, including private keys, certificates, and so on. This enables users to import, export, and thus effectively share their personal identity information among applications that support this standard. In particular, user credentials that browsers such as Microsoft IE or Mozilla Firefox generate can be exported in PKCS#12 format -- as files with the .pfx
or .p12
suffix -- and then accessed and used by Java platform applications.
The PKCS12
keystore implementation from the SunJSSE provider follows the syntax defined in PKCS#12. All entries are privacy protected, and the whole keystore is then integrity protected using a user-supplied password as defined in PKCS#12 in the Password privacy
and Password integrity
modes. To ensure maximum interoperability with other vendors' PKCS#12 keystore implementation, the JDK 6 PKCS12
keystore implementation only allows the storing of PrivateKey
and its corresponding certificate chain, because other types of entries such as SecretKey
or trusted certificate are generally not supported. In addition, although the PKCS12
implementation in JDK 6 is quite flexible and allows the use of different passwords for each private key entry and the integrity of the keystore, the resulting PKCS#12 keystore may not be imported into applications that use only a single password for the keystore and all its key entries.
For example, to import the PKCS12
keystore sample.p12
created by other applications using the password testpass
and listing its content, you have two options:
You can invoke the command line
keytool
:keytool -list -v -keystore sample.p12 -storepass testpass -storetype PKCS12
You can do this programmatically through the
java.security.KeyStore
APIs:KeyStore ks = KeyStore.getInstance("PKCS12");
FileInputStream fis = new FileInputStream("sample.p12");
char[] passwd = { 't', 'e', 's', 't', 'p', 'a', 's', 's' };
ks.load(fis, passwd);
// Retrieve the PrivateKey and its certificate chain using
// the java.security.KeyStore API.
Enumerationaliases = ks.aliases();
while (aliases.hasMoreElements()) {
String name = aliases.nextElement();
Key privKey = ks.getKey(name, passwd);
java.security.cert.Certificate[] certs =
ks.getCertificateChain(name);
// Print out the desired information using 'name',
// 'privKey', and 'certs'.
...
}
As for creating a PKCS12
keystore and exporting it to other applications, the most straightforward approach would probably be running the command line keytool
. For example, to create a PKCS12
keystore sample.p12
that contains a 1024-bit RSA key pair using the password testpass
, you could use the following:
keytool -genkeypair -alias myRSAKey -keyalg RSA -keysize 1024 -keystore sample.p12 -storepass testpass -storetype pkcs12 |
Once this command is executed, the file named sample.p12
contains a 1024-bit RSA private key accompanied with a self-signed certificate. For this exercise, users can now import this PKCS12
file into a browser. For real-world use, the self-signed certificate should be replaced with a certificate chain issued by a public certification authority (CA). There are keytool
options, for example, -certreq
and -import
, to generate the certificate request as well as to import the PKCS#7 reply from the CA. The keytool
documentation provides a complete list of options and their use. Lastly, you can repeat the previously given keytool
command with a different alias to add more PrivateKey
entries into the PKCS12
file if needed.
The Java SE platform provides developers a large set of security APIs, tools, and implementations of commonly used security algorithms, mechanisms, and protocols. The ability to leverage the security in the native platform offers several significant benefits: the performance boost that cryptographic accelerators provide, a consistent behavior that matches what native applications have, and the seamless sharing of users' native credentials and settings.
With these enhancements, Java platform applications can now access Microsoft CryptoAPI and its cryptographic services, PKCS#11 cryptographic tokens, and native GSS-API and its mechanisms including Kerberos, as well as sharing credentials such as PrivateKey
and Certificates
with native applications such as browsers. Furthermore, these enhancements are often implemented as providers that implement a set of services through existing security APIs. Thus, developers of Java platform applications do not have to learn new APIs and can simply reuse most of the existing code.
Table 2 lists information about the native security features this article has discussed and their availability in various operating systems.
Table 2. Native Security Features and Their Availability |
Feature | Starting JDK Release: Available OS | Note |
---|---|---|
Access MS CryptoAPI | JDK 6: 32-bit MS Windows | |
Access PKCS#11 cryptographic services | JDK 5.0: 32-bit and 64-bit Solaris (SPARC, x86), 32-bit Linux, 32-bit MS Windows JDK 6: All except 64-bit MS Windows | Requires PKCS#11 v. 2.0 or later Requires NSS v. 3.11.1 or later (JDK 6) |
Access Native GSS-API | JDK 6: Solaris, Linux | |
Import and export PKCS#12 keystores | JDK 1.4: All | Interoperable with MS IE, Netscape, NSS, and OpenSSL |
2 comments:
Thanks for good introduction into this subject.
Thanks for your very useful information.
Rao.
Post a Comment