public class PKCS12KeyStore
extends java.security.KeyStoreSpi
KeyStore
Service Provider Interface
(SPI) to load and save PKCS#12
files, which contain private key entries (private keys and certificate chains)
and certificate entries (X.509 certificates).
Note that there has been a change in the API from version IAIK-JCE 5.2 to IAIK-JCE 5.3. The PKCS12KeyStore implementation of version 5.2 (or lower) only supported private key entries (private keys and certificate chains) as this is the most widely used format for saving private keys with certificates in files (supported by Microsoft WindowsTM, Microsoft Internet ExplorerTM, Microsoft OutlookTM, Mozilla Firefox, Mozilla Thunderbird,...).
If you want to instantiate a KeyStore
, specify keystore type and
provider.
KeyStore ks = KeyStore.getInstance("PKCS12", "IAIK");Before a
KeyStore
can be used, it must be loaded.
ks.load(inputStream, password);You must even load the
KeyStore
in case you instantiate a new
empty KeyStore
object. In this case, the parameters shall be
null
.
ks.load(null, null);
Implementation Details
This implementation of theKeyStoreSpi
loads and stores
files according to the PKCS#12 v1.1: Personal Information Exchange
Syntax (RFC 7292).
The keystore loads private keys and certificate chains from a
password-encrypted PKCS#12-file (e.g. .pfx, .p12). Inside the PKCS#12 file,
the localKeyID is used to match the private key with the corresponding
certificate. KeyEntries, set with method
KeyStore.setKeyEntry(String, byte[], Certificate[])
or
KeyStore.setKeyEntry(String, Key, char[], Certificate[])
use the current time (Milliseconds since 1970) as key ID. When loading a
file, the creation date is extracted from this ID if possible. With method
KeyStore.store(OutputStream, char[])
, all keys and
certificate chains are saved in a PKCS#12 file.
This implementation supports multiple keys and certificate chains within one PKCS#12 file, and also multiple certificate entries.
PKCS#12 carries certificates in CertBags, and keys in KeyBags. The relationship between a certificate contained in a CertBag and the corresponding key contained in a KeyBag may be established by giving Cert- and KeyBag the same lokaleKeyId and/or friendlyName attribute. This KeyStore implementation follows the following strategy for getting/setting the certificates for Key- and Certificate Entries from/for a PKCS#12 KeyStore:
keyStore.getCertificateChain()
this certificate is located
at index 0 of the returned certificate chain. The remaining certificates
of the certificate chain are got from all other CertBags (regardless
of their attributes) by trying to build a certificate path for the
"index-0" certificate based on IssuerDN/SubjecDN
and AuthorityKeyIdentifier/SubjectKeyIdentifier (if available) relationship.
When storing
a KeyStore by default
HMAC_SHA256
is used as mac algorithm for protecting the
integrity of the KeyStore and the PKCS#5 PBES2 scheme PBES2WithHmacSHA256AndAES256
is used for encrypting (Shrouded)KeyBags contained in
unencrypted AuthenticatedSafe objects and CertBags contained in encrypted AuthenticatedSafe objects.
You can change
the default algorithms to be used. However,
be careful when your PKCS#12 KeyStore(s) must be readable with other PKCS#12 applications, too.
Although we have tested the algorithms to work with well established PKCS#12 applications, it even might
happen that an application may only be able to read PKCS#12 KeyStores protected with the legacy
algorithm set that uses HMAC_SHA1
as mac algorithm, and
the PKCS#5 PBES1 schemes PBEWithSHAAnd40BitRC2_CBC
for encrypting CertBags contained in encrypted AuthenticatedSafe objects and
PBEWithSHAAnd3_KeyTripleDES_CBC
for
encrypting (Shrouded)KeyBags contained in unencrypted AuthenticatedSafe objects. Of course the
legacy set should be used with care because it provides less security than the default
algorithm set. For interoperability reasons the legacy algorithm set has been used as default algorithm
set until IAIK-JCE version 5.62. If you want to convert a legacy PBES1 using PKCS12KeyStore to
a new PBES2 using PKCS12KeyStore you either may create PKCS12KeyStore objects and copy each entry
from the first to the second PKCS12KeyStore. Or you read the legacy PKCS12KeyStore and call
before storing it again.
PKCS12Algorithms.setEnforceDefault(true)
When using Java 8 or later it is possible to specify protection parameters when having to use different than the default algorithms for adding keys/certificates to and/or storing a specific PKCS#12 KeyStore, e.g.:
// the protection algorithm set name String protectionAlg = "PBES1"; // the keystore password char[] password = ...; // create a new PKCS12 KeyStore KeyStore ks = KeyStore.getInstance("PKCS12", "IAIK"); ks.load(null, null); // add a key entry PrivateKey privateKey = ...; X509Certificate[] certChain = ...; String keyAlias = ...; KeyStore.PrivateKeyEntry keyEntry = new KeyStore.PrivateKeyEntry(privateKey, certChain); P12PasswordProtection pwdProtection = new P12PasswordProtection(password, protectionAlg); ks.setEntry(keyAlias, keyEntry, pwdProtection); // add a cert entry X509Certificate trustedCert = ...; String certAlias = ...; KeyStore.TrustedCertificateEntry certEntry = new KeyStore.TrustedCertificateEntry(trustedCert); P12CertEntryProtection p12Protection = new P12CertEntryProtection(protectionAlg); ks.setEntry(certAlias, certEntry, p12Protection); // store keystore OutputStream os = ...; P12StoreParameter storeParams = new P12StoreParameter(os, password, protectionAlg); ks.store(storeParams);Although it is possible to specify a specific (or even different) PBE algorithm for each separate key or certificate entry and a specific MAC algorithm when storing the keystore, for interoperability reasons it is recommended to use the name of one of the two PKCS#12 algorithm sets
"PBES2"
or "PBES1"
only. For security reasons
it is recommended to use "PBES2".Constructor and Description |
---|
PKCS12KeyStore()
Creates a new PKCS#12
KeyStore object. |
Modifier and Type | Method and Description |
---|---|
static void |
addSunTrustedKeyUsageAttributeToCertificateBags(boolean add)
Decides whether to add the proprietary Oracle
TrustedKeyUsage attribute to PKCS12
CertificateBags to "label" them as trusted KeyStore certificate entries. |
java.util.Enumeration |
engineAliases() |
boolean |
engineContainsAlias(java.lang.String alias)
Checks if the given alias exists in this keystore.
|
void |
engineDeleteEntry(java.lang.String alias)
Deletes the entry identified by the given alias from this keystore.
|
java.security.cert.Certificate |
engineGetCertificate(java.lang.String alias)
Returns the certificate associated with the given alias.
|
java.lang.String |
engineGetCertificateAlias(java.security.cert.Certificate cert)
Returns the (alias) name of the first keystore entry whose certificate
matches the given certificate.
|
java.security.cert.Certificate[] |
engineGetCertificateChain(java.lang.String alias)
Returns the certificate chain associated with the given alias.
|
java.util.Date |
engineGetCreationDate(java.lang.String alias)
Returns the creation date of the entry identified by the given alias or -
if not available - the date on which the certificate became valid.
|
java.security.Key |
engineGetKey(java.lang.String alias,
char[] password)
Returns the key associated with the given alias, using the given password
to recover it.
|
boolean |
engineIsCertificateEntry(java.lang.String alias)
Checks if the entry with the given alias represents a certificate entry.
|
boolean |
engineIsKeyEntry(java.lang.String alias)
Returns true if the entry identified by the given alias was created by a
call to setKeyEntry, or created by a call to setEntry with a
PrivateKeyEntry or a SecretKeyEntry.
|
void |
engineLoad(java.io.InputStream stream,
char[] password)
Loads the keystore from the given input stream.
|
void |
engineSetCertificateEntry(java.lang.String alias,
java.security.cert.Certificate cert)
Assigns the given certificate to the given alias.
|
void |
engineSetKeyEntry(java.lang.String alias,
byte[] key,
java.security.cert.Certificate[] chain)
Assigns the given key (that has already been protected) to the given alias.
|
void |
engineSetKeyEntry(java.lang.String alias,
java.security.Key key,
char[] password,
java.security.cert.Certificate[] chain)
Assigns the given key to the given alias, protecting it with the given
password.
|
int |
engineSize() |
void |
engineStore(java.io.OutputStream stream,
char[] password)
Stores this keystore to the given output stream, and protects its integrity
with the given password.
|
static boolean |
getUseJKSFallBack()
Gets whether the JKS fallback mechanism is enabled when parsing a PKCS#12 KeyStore.
|
static void |
setUseJKSFallBack(boolean useJKSFallBack)
Sets whether to enable the JKS fallback mechanism when parsing a PKCS#12 KeyStore.
|
public static final void setUseJKSFallBack(boolean useJKSFallBack)
Oracle has changed the JDK default KeyStore format from "JKS" to "PKCS12",
but still uses the JKS format for its cacerts
default KeyStore.
When, for instance, an application uses JSSE to connect to some TLS/HTTPS server
(and does not have explicitly set some trust store) JSSE tries to read the
certificates from the default cacerts
KeyStore by instantiating a
KeyStore with the default format ("PKCS12"). When IAIK is installed as
first provider the PKCS12 KeyStore of the IAIK provider is instantiated and tries
to parse the cacerts
KeyStore. This, however, must fail since
cacerts
is a JKS KeyStore which cannot be read by the IAIK PKCS12KeyStore
(that, of course, expects a KeyStore in PKCS12 format). The TLS/HTTPS connection
attempt will fail with an Exception saying that the trust store cannot be
accessed because of a KeyStore parsing problem.
There are several work arounds for solving this problem (and keeping IAIK as first provider):
java -Djavax.net.ssl.trustStoreType=jks -Djavax.net.ssl.keyStoreType=jks …
setUseJKSFallBack()
to true:
PKCS12KeyStore.setUSEJKSFallBack(true);
PKCS12KeyStore.setUSEJKSFallBack(true);
)
the IAIK PKCS12KeyStore will be advised to try the JKS format anytime it fails
to parse a PKCS#12 KeyStore. This may cause some overhead. For that reason -- and
because you may not notice that you read a JKS KeyStore while you are expecting to
read a PKCS#12 KeyStore) the JKS fallback mechanism is disabled by default and
has to be explicitly enabled by calling PKCS12KeyStore.setUSEJKSFallBack(true);
.useJKSFallBack
- whether to enable the JKS fallback mechanism (default: false
).public static final boolean getUseJKSFallBack()
false
).setUseJKSFallBack(boolean)
public static void addSunTrustedKeyUsageAttributeToCertificateBags(boolean add)
TrustedKeyUsage
attribute to PKCS12
CertificateBags to "label" them as trusted KeyStore certificate entries.
When building
a PKCS#12 object from a PKCS12 KeyStore
there is no standardized way to distinguish between CA certificates that belong to the
certificate chain of a KeyEntry
and trusted certificate entries
.
When creating a CertificateBag
from a trusted certificate entry the
PKCS#12 KeyStore implementation of the Sun provider therefore adds a proprietary TrustedKeyUsage
attribute to the CertificateBag.
The IAIK PKCS12KeyStore implementation by default does not add the Oracle
TrustedKeyUsage
attribute to PKCS12 CertificateBags because it is a
proprietary attribute that may cause problems with other PKCS12 applications that
do not allow proprietary attributes (although they should accept them). However, for
interoperability reasons with the Sun PKCS12 KeyStore implementation you may decide that
the IAIK PKCS12KeyStrore shall add the Oracle TrustedKeyUsage
attribute to
PKCS12 CertificateBags:
PKCS12KeyStore.addSunTrustedKeyUsageAttributeToCertificateBags(true);In this case not only the Oracle
TrustedKeyUsage
attribute will be added to
PKCS12 CertificateBags, but also only CertificateBags containing this attribute will
be recognized as trusted cert entries.add
- whether to add the Oracle TrustedKeyUsage
attribute
to PKCS12 CertificateBagspublic void engineLoad(java.io.InputStream stream, char[] password) throws java.io.IOException, java.security.NoSuchAlgorithmException, java.security.cert.CertificateException
Set both parameters to null
to initialize an new empty
keystore.
engineLoad
in class java.security.KeyStoreSpi
stream
- the input stream from which the keystore is loaded, or nullpassword
- the password used to check the integrity of the keystore, the
password used to unlock the keystore, or nulljava.io.IOException
- if there is an I/O or format problem with the keystore data, if a
password is required but not given, or if the given password was
incorrectjava.security.NoSuchAlgorithmException
- if the algorithm used to check the integrity of the keystore
cannot be foundjava.security.cert.CertificateException
- if any of the certificates in the keystore could not be loadedpublic java.util.Enumeration engineAliases()
engineAliases
in class java.security.KeyStoreSpi
public boolean engineContainsAlias(java.lang.String alias)
engineContainsAlias
in class java.security.KeyStoreSpi
alias
- The alias name.true
if the alias exists, false
otherwise.public void engineDeleteEntry(java.lang.String alias) throws java.security.KeyStoreException
engineDeleteEntry
in class java.security.KeyStoreSpi
alias
- The alias name.java.security.KeyStoreException
- If the entry cannot be removed.public java.security.cert.Certificate engineGetCertificate(java.lang.String alias)
engineGetCertificate
in class java.security.KeyStoreSpi
alias
- the alias namepublic java.lang.String engineGetCertificateAlias(java.security.cert.Certificate cert)
engineGetCertificateAlias
in class java.security.KeyStoreSpi
cert
- the certificate to match with.public java.security.cert.Certificate[] engineGetCertificateChain(java.lang.String alias)
engineGetCertificateChain
in class java.security.KeyStoreSpi
alias
- the alias namepublic java.util.Date engineGetCreationDate(java.lang.String alias)
engineGetCreationDate
in class java.security.KeyStoreSpi
alias
- the alias namepublic java.security.Key engineGetKey(java.lang.String alias, char[] password) throws java.security.NoSuchAlgorithmException, java.security.UnrecoverableKeyException
engineGetKey
in class java.security.KeyStoreSpi
alias
- the alias namepassword
- the password for recovering the keyjava.security.NoSuchAlgorithmException
- if the algorithm for recovering the key cannot be foundjava.security.UnrecoverableKeyException
- if the key cannot be recovered (e.g. the given password is
wrong).public boolean engineIsCertificateEntry(java.lang.String alias)
engineIsCertificateEntry
in class java.security.KeyStoreSpi
alias
- the alias for the keystore entry to be checkedpublic boolean engineIsKeyEntry(java.lang.String alias)
engineIsKeyEntry
in class java.security.KeyStoreSpi
alias
- the alias for the keystore entry to be checkedpublic void engineSetCertificateEntry(java.lang.String alias, java.security.cert.Certificate cert) throws java.security.KeyStoreException
engineSetCertificateEntry
in class java.security.KeyStoreSpi
alias
- the alias namecert
- the certificate to be associated with the aliasjava.security.KeyStoreException
- if this operation fails, e.g. the given alias was already
used for a KeyEntry of this keystorepublic void engineSetKeyEntry(java.lang.String alias, byte[] key, java.security.cert.Certificate[] chain) throws java.security.KeyStoreException
engineSetKeyEntry
in class java.security.KeyStoreSpi
alias
- the alias namekey
- the key (in protected format) to be associated with the aliaschain
- the certificate chain for the corresponding public key (only
useful if the protected key is of type java.security.PrivateKey).java.security.KeyStoreException
- if this operation fails, e.g. the given alias was already
used for a CertEntry of this keystorepublic void engineSetKeyEntry(java.lang.String alias, java.security.Key key, char[] password, java.security.cert.Certificate[] chain) throws java.security.KeyStoreException
PrivateKey
, it
must be accompanied by a certificate chain certifying the corresponding
public key. This certificate chain must be arranged having the end-user
certificate at index 0 and the top-CA-certificate at the end of the chain.
If the given alias already exists and identifies another key entry, the
keystore information associated with it is overridden by the given key
and certificate chain. However, if an already exiting alias identifies
a cert entry, a KeyStore exception is thrown to not allow using the
same alias for key- and cert-entries.
The provided password is ignored. Only the password provided to the
engineStore(OutputStream, char[])
is used.
engineSetKeyEntry
in class java.security.KeyStoreSpi
alias
- the alias namekey
- the key to be associated with the aliaspassword
- the password to encrypt the key withchain
- the certificate chain for the corresponding public key (only
required if the given key is of type java.security.PrivateKey).java.security.KeyStoreException
- if the given key cannot be protected, or this operation fails for
some other reason, e.g. the given alias was already
used for a CertEntry of this keystorepublic int engineSize()
engineSize
in class java.security.KeyStoreSpi
public void engineStore(java.io.OutputStream stream, char[] password) throws java.io.IOException, java.security.NoSuchAlgorithmException, java.security.cert.CertificateException
engineStore
in class java.security.KeyStoreSpi
stream
- the output stream to which this keystore is written.password
- the password to generate the keystore integrity checkjava.io.IOException
- if there was an I/O problem with datajava.security.NoSuchAlgorithmException
- if the appropriate data integrity algorithm could not be foundjava.security.cert.CertificateException
- if any of the certificates included in the keystore data could
not be stored