Tuesday, December 15, 2009

The PKCS #12 Format


















































The PKCS #12 Format



The
PKCS #12 format is defined in RSA Security's PKCS #12 standard and was
primarily designed as a means of encoding personal credentials that
consisted of private keys and certificates. It can be used in several
ways that are based around the combination of two privacy modes and two
integrity modes. The privacy mode can be one of password-based
encryption (PBE), or through public key encryption. Likewise the
integrity mode can be one of an HMAC based on a password and PBE or a
digital signature. The format is important, as it is the most common
way of dealing with private keys when you need to import them into
another application such as a Web browser to introduce personal
credentials or some other application that allows you to receive
encrypted data or send signed information.


The encoding specified for the format is ASN.1 using the BER rules and the file structure is built around the PFX structure, which is defined as follows:


    PFX ::= SEQUENCE {
version INTEGER {v3(3)}(v3,...),
authSafe ContentInfo,
macData MacData OPTIONAL }

MacData ::= SEQUENCE {
mac DigestInfo,
macSalt OCTET STRING,
iterations INTEGER DEFAULT 1 }

The MacData structure is present only if PBE is used for the integrity mode of the PFX. It is very rare to see iterations set to its default value; normally it will be over 1,000.


The ContentInfo structure is defined in PKCS #7 as follows:



    ContentInfo ::= SEQUENCE {
contentType ContentType,
content [0] EXPLICIT CONTENTS.&Type({Contents}{@contentType}) OPTIONAL }


which, in case you've forgotten, translates into
English as: What you will find in the content field depends on what the
value is in the contentType field. The type of the contentType field, ContentType, is derived from a set of OIDs and reduces to a restricted version of the following:


    ContentType ::= OBJECT IDENTIFIER

You will have a closer look at ContentInfo in the next chapter
when you read about Cryptographic Message Syntax (CMS), but for the
sake of PKCS #12, two object identifiers are recognized in the contentType field of the ContentInfo structure contained in the PFX structure: data and signedData. These two object identifiers have the following definition:



    pkcs-7       OBJECT IDENTIFIER ::=
{ iso(1) member-body(2) us(840) rsadsi(113549) pkcs(1) 7 }
data OBJECT IDENTIFIER ::= { pkcs-7 1 }
signedData OBJECT IDENTIFIER ::= { pkcs-7 2 }


and which one you will find in the contentType field depends on whether the content of the PFX is integrity-protected using a password—in which case it will be data, or integrity-protected using a digital signature—in which case it will be signedData.



I will leave the signedData content type alone for the moment because you'll be looking at it in the next chapter and in practice you will only see PFX objects using both password-based integrity mode and password-based privacy mode. In any case, whether you encounter signedData, or just the data type the actual structure that contains the key and certificate information present in the file is an AuthenticatedSafe, which is defined as follows:


    AuthenticatedSafe ::= SEQUENCE OF ContentInfo

And it is the encoding of the AuthenticatedSafe that is present in the content field of the relevant ContentInfo structure as an ASN.1 OCTET STRING. In the case of the password-based files, the relevant ContentInfo structure is the one contained in the PFX structure. The content field of each ContentInfo structure can contain either plaintext, password-encrypted data, or public key-encrypted data as OCTET STRING values based on the BER encoding of another structure—the SafeContents.


The SafeContents structure and its associated structures are defined as follows:



    SafeContents ::= SEQUENCE OF SafeBag

SafeBag ::= SEQUENCE {
bagId BAG-TYPE.&id ({PKCS12BagSet})
bagValue [0] EXPLICIT BAG-TYPE.&Type({PKCS12BagSet}{@bagId}),
bagAttributes SET OF PKCS12Attribute OPTIONAL }

PKCS12Attribute ::= SEQUENCE {
attrId ATTRIBUTE.&id ({PKCS12AttrSet}),
attrValues SET OF ATTRIBUTE.&Type ({PKCS12AttrSet}{@attrId}) }


And as you might imagine, there is a range of bagId values that indicate the bagValue is an object containing a certificate, key, a PKCS #8 encrypted private key, or a CRL. As you can see, the PKCS12Attribute structure is pretty much the same as the Attribute structure you saw in Chapter 5, the only difference being a name change and a syntax update to reflect the removal of the ANY type from ASN.1 in 1994. There are two common attributes that appear in relation to SafeBag attributes, both of which are defined in PKCS #9: the "friendly name," which is a BMPString identifier associated with the object in the SafeBag, and the local key ID, which is an OCTET STRING associated with the key or certificate in the SafeBag the attribute is attached to.


You can recognize the friendly name and the local key ID by the following OIDs in the attrId field of the PKCS12Attribute structure:


  pkcs-9-at-friendlyName OBJECT IDENTIFIER ::= {pkcs-9 20}
pkcs-9-at-localKeyId OBJECT IDENTIFIER ::= {pkcs-9 21}

where pkcs-9 is defined as:



  pkcs-9 OBJECT IDENTIFIER ::= {
iso(1) member-body(2) us(840) rsadsi(113549) pkcs(1) 9 }


In the context of PKCS #12 the local key ID is the most
important of these, because it is generally used to tell the program
reading the file which end entity certificate is related to which
private key.



So, a complete PFX is a loosely defined nested structure, commonly with the kind of layout you see in Figure 8-1.
The definition affords a great deal of flexibility in implementation
and this is where you start to run into difficulties—if you think about
it for a while, there are only some ways of constructing one of these
files that will work with the Java KeyStore API, and many variations on
the way.






Figure 8-1

As I hope you can see, to deal with the import and
export issues you might run into with these files, it is a good idea to
keep in mind some idea of how they function internally. The myriad of
implementations out there often means the treating of PKCS #12 files as
a "black box" does not always work.




Using PKCS #12 with the KeyStore API


So if you are using a keystore implementation of PKCS #12, where are the dragons lurking?


If you are using another tool to create them and then
importing them into Java, the first point to be careful of is that,
ideally, any keys in the imported file will have the friendly name
attribute set, because some implementations rely on that to use as an
alias name. It's certainly a lot easier to use one if it has a
"friendly name" set; otherwise, the first thing you have to do is dump
the aliases for the KeyStore object to find out what name the provider has assigned to it if it has been able to.


Usually PKCS #12 files you import, and ones you are
planning to export, employ the same password for integrity checking as
they do for encryption of individual entries in the AuthenticatedSafe.
This is largely because PKCS #12 is meant for storing private
credentials. In the case of the Bouncy Castle implementation, you dealt
with this by ignoring individual entry passwords. Doing otherwise would
have meant people would have unwittingly generated files they could not
export. On the other hand, the JDK 1.5 implementation from Sun allows
you to do this. This does not mean that either implementation is
wrong—but whether you want to take advantage of the features of either
depends a lot on whether you are planning to export the files to other
applications and what those applications will cope with.


You'll also find that storing multiple private keys or
just trusted certificates in a PKCS #12 keystore might be problematic
depending on the provider. Although the PKCS #9 friendly name attribute
allows aliases to be added to certificates, not everyone supports this,
with the result that alias information on lone certificates may well be
lost, and they simply won't show up in the alias list for the keystore.
The implementation
might also refuse to store trusted certificates altogether. In any
case, some of the non-Java applications you will need to import PKCS
#12 files into may cope properly only with files that have a single
private key and certificate chain.


Another issue you might find is how certificate chains
get picked up from the PKCS #12 file. Some applications will happily
reconstruct a certificate chain using the subject and issuer DNs in the
certificates and whether the public key of one certificate validates
another. Others insist on the presence of the AuthorityKeyIdentifier and SubjectKeyIdentifier
extensions to correctly recover the chain. The safest bet when you are
creating your own chains is to make sure both extensions are present in
any certificates that aren't trust anchors as well—they don't do any
harm by being there.


A common problem you might encounter with a PKCS12
keystore is that not all software that claims to be able to read PKCS
#12 files can read BER-encoded data. If you are using the Bouncy Castle
PKCS12 implementation that does use BER, it may be necessary to
transform the keystore file from BER to DER to get it to import.
Fortunately, this is fairly easy to do, as the following code fragment
shows:



ByteArrayOutputStream bOut = new ByteArrayOutputStream();

pkcs12Store.store(bOut, password);

ASN1InputStream aIn = new ASN1InputStream(bOut.toByteArray());

bOut.reset();

DEROutputStream dOut = new DEROutputStream(bOut);

dOut.writeObject(aIn.readObject());

byte[] derPKCS12 = bOut.toByteArray();


where derPKCS12 will end up being the DER
encoding of the PKCS #12 PFX structure. If this is happening, it is
unlikely the application will tell you that this is what the problem
is—if you start having "bizarre" password issues, the BER/DER issue is
the most common cause.


Finally, you might find it simply impossible to read a
PKCS #12 store that has been generated by another application. This is
fairly unlikely now because the BC implementation is four years old and
the Sun one is about the same, but if it does, I guess the best advice
I can give is that it's time to start working with your favorite dump
utility and asking around. The org.bouncycastle.asn1.util.ASN1Dump
class is a good place to start, and it would be worth getting your
hands on the source of a PKCS #12 implementation, such as that found in
JDKPKCS12KeyStore.java in the org.bouncycastle.jce.provider package of the Bouncy Castle distribution.


Still, after all, for the most part the PKCS #12
implementations that are available under Java do the job. So that being
said, let's have a look at an example.




Try It Out: Using a PKCS #12 Keystore






This example is very similar to the first one in
this chapter ("Try It Out: Using a JKS Keystore"), only this time it is
using a PKCS12 keystore type to store a trusted certificate entry and a private key and its associated certificate chain.



package chapter8;
import java.io.ByteArrayInputStream;
import java.io.ByteArrayOutputStream;
import java.security.KeyStore;
import java.security.cert.Certificate;
import java.util.Enumeration;

import javax.security.auth.x500.X500PrivateCredential;

/**
* Example of the creation of a PKCS #12 store
*/
public class PKCS12StoreExample
{
public static KeyStore createKeyStore()
throws Exception
{

KeyStore store = KeyStore.getInstance("PKCS12", "BC");

// initialize
store.load(null, null);

X500PrivateCredential rootCredential = Utils.createRootCredential();
X500PrivateCredential interCredential = Utils.createIntermediateCredential(
rootCredential.getPrivateKey(), rootCredential.getCertificate());
X500PrivateCredential endCredential = Utils.createEndEntityCredential(
interCredential.getPrivateKey(), interCredential.getCertificate());
Certificate[] chain = new Certificate[3];

chain[0] = endCredential.getCertificate();
chain[1] = interCredential.getCertificate();
chain[2] = rootCredential.getCertificate();

// set the entries
store.setCertificateEntry(
rootCredential.getAlias(), rootCredential.getCertificate());

store.setKeyEntry(
endCredential.getAlias(), endCredential.getPrivateKey(), null, chain);

return store;
}
public static void main(
String[] args)
throws Exception
{
KeyStore store = createKeyStore();
char[] password = "storePassword".toCharArray();

ByteArrayOutputStream bOut = new ByteArrayOutputStream();
store.store(bOut, password);


store = KeyStore.getInstance("PKCS12", "BC");


store.load(new ByteArrayInputStream(bOut.toByteArray()), password);

Enumeration en = store.aliases();
while (en.hasMoreElements())
{
String alias = (String)en.nextElement();
System.out.println("found " + alias
+ ", isCertificate? " + store.isCertificateEntry(alias));
}
}
}

Running the example produces the following output:


  found end, isCertificate? false
found root, isCertificate? true
















How It Works


There is not a lot to say about how this example works, but there are a few items of note.


The first is you will notice that the private key password is null.
The reason is that with the Bouncy Castle implementation the password
given when the keystore is saved is used both to generate the integrity
check and to encrypt the data contained in the keystore.


The second issue to think about is what will happen if
you are using JDK 1.5 and remove the specification of the BC provider.
If you change the line in the main driver to just be


    store = KeyStore.getInstance("PKCS12");

you will find the output changes to


   found end, isCertificate? false

Because the parser in the JDK-provided implementation
doesn't pick up the fact there is a certificate with the friendly name
attribute set on it.


If you go one step further and change the use of KeyStore.getInstance() in the createKeyStore()
method in the same fashion, you will find the example will throw an
exception when it tries to add the certificate to the keystore. Comment
that line out and you will find it throws an exception when it tries
adding the key because the password is null. Change the lines adding the key entry to read



        store.setKeyEntry(
endCredential.getAlias(), endCredential.getPrivateKey(),
"storePassword".toCharArray(), chain);


and you will find the example then works. Notice that I have used the same password as is used to save the KeyStore
object. The Sun implementation will let you use different passwords for
saving the key and saving the password. However, if you are creating
the file for import into another application, you will normally find it
will be unable to load the PKCS #12 file, because it will assume the
password for protecting the keys is the same one used to create the
HMAC integrity check.



As
you will realize from the outline I gave of the PKCS #12 PFX format
earlier, there is nothing in PKCS #12 that says the Sun implementation
is not compliant compared to the Bouncy Castle implementation—in some
ways it is more so; it just happens to be different. As previously
mentioned, you will find this is fairly common, especially when you
introduce PKCS #12 files generated by other applications. Consequently,
if you are using PKCS #12 files to pass credentials between
applications, the first step is to make sure that the Java mechanisms
you are using to load and save the files are compatible with the other
applications you are using and that your use of the features of a
particular Java implementation of PKCS #12 is restricted so that your
users can reliably produce PKCS #12 files that will work across
applications.


The JDK/JRE also comes with an application that
can be very useful for examining keystores for the purposes of
exporting keys, debugging, or just having a look at their contents. You
will look at that now.











































No comments: