Date: Fri, 13 Mar 1998 17:53:36 -0800 (PST)
From: Jan Luehe <Jan.Luehe@Eng>
Subject: Re: Fw: My opinions on Key, KeySpec, KeyFactory, KeyGenerator, et. al.
To: java-security@javasoft.com, gchung@openhorizon.com
George:
> First of all, the basic problem is that I'm just not seeing the conceptual
> difference between:
> a) a Key's encoded format and format type and
> b) a KeySpec.
>
> And therefore, I'm not seeing the need for supporting both.
In the case of a secret key that can be represented as a byte array,
you're right: The encoding of the key corresponds to the key spec,
it's simply the byte array.
In all other cases, the encoding is different from the key spec.
For example, in the case of a DSA public key, the encoding is
a byte array representing an ASN.1 encoded SEQUENCE of the public value
y, the prime p, the sub-prime q, and the base g.
> So let's say I want to encrypt something with a DES key that I got over the
> net so that it's in byte array format...and...my DESCipher provider doesn't
> provide a DESKeyFactory. I need to do the following:
>
> 1. Create a DESKeySpec from the 8byte array
> 2. Create a vendor Foo specific opaque DESKey by constructing one directly,
> or using Foo's DESKeyFactory => fooDESKey
> 3. Create a vendor Bar specific DESCipher => barDESCipher
> 4. init barDESCipher with the above key: barDESCipher.init(fooDESKey);
> 5. barDESCipher.init() must translate fooDESKey to a barDESKey by calling
> getFormat() and getEncoded() on fooDESKey before it can use it.
>
> Here's the rub:
>
> 1. We created a vendor _neutral_ DESKeySpec from a 8byte array that was read
> from the network.
> 2. We create an opaque key fooDESKey from that 8byte array using a
> KeyFactory object from vendor Foo.
> 3. We pass fooDESKey into barDESCipher.init().
> 4. Inside barDESCipher.init(), the implementation extracts the 8byte array
> calling getEncoded() on fooDESKey.
> 5. Vendor Bar's implementation then new's a vendor _neutral_ DESKeySpec from
> that extracted 8 byte array.
> 6. Vendor Bar's implementation then new's a BarDESKey using the just new'ed
> DESKeySpec as a parameter.
>
> Hmmm. We went to a lot of trouble to get back the vendor neutral DESKeySpec
> in order to create a new BarDESKey simply because Cipher.init() takes a Key
> instead of a KeySpec.
Yes. What makes your example so complicated is that your provider
of DESCipher does NOT provide a key factory.
However, I agree with you that in the case of secret keys that are
stored in software, the key factory concept may be somewhat overkill.
This is why in the upcoming JCE 1.2 early access release (JCE1.2ea2),
you'll find a class (javax.crypto.spec.SecretKeySpec)
that does exactly what you're asking for. That class already implements
the "javax.crypto.SecretKey" interface, which itself subclasses off of
"java.security.Key".
In the accompanying API User's Guide document, you'll see the following
explanation under "The SecretKeyFactory Class":
[...]
The following is an example of how to use a SecretKeyFactory to
convert secret key data into a SecretKey object, which can be used
for a subsequent Cipher operation:
byte[] desKeyData = { (byte)0x01, (byte)0x02, ...};
DESKeySpec desKeySpec = new DESKeySpec(desKeyData);
SecretKeyFactory keyFactory = SecretKeyFactory.getInstance("DES");
SecretKey secretKey = keyFactory.generateSecret(desKeySpec);
In this case, the underlying implementation of secretKey is based on
the provider of keyFactory.
An alternative, provider-independent way of creating a
functionally equivalent SecretKey object from the same key material
is to use the SecretKeySpec class, which implements the
javax.crypto.SecretKey interface:
byte[] desKeyData = { (byte)0x01, (byte)0x02, ...};
SecretKeySpec secretKey = new SecretKeySpec(desKeyData, "DES");
[...]
This should make you happy. :-)
> Now if we consider the encoding/format equivalent in concept to the KeySpec
Only for secret keys!
> and the opaque version of the key is kept inside one's implementation of
> Cipher, then we can accomplish the above in far fewer steps...
>
> 1. We created a vendor _neutral_ DESKeySpec from an 8byte array that was
> read from the network.
> 2. We pass this DESKeySpec into the barDESCipher.init().
> 3. Vendor Bar's implementation then new's an _implementation specific_
> BarDESKey from the DESKeySpec for use with _his_ implementation of DES.
OK, the new SecretKeySpec class (that you will see in the next JCE 1.2
early access release) lets you do exactly that! It's a shortcut to
getting a SecretKey object from the key material.
> Second of all, with regards to opaque keys being passed around, I am
> questioning whether this actually is useful in practice. I just don't
> envision ever seeing some JCA provider coming to market with a super-duper
> DESKeyGenerator implementation without also providing the Cipher
> implementation. In other words one comes up with a DESKeyGenerator because
> it generates DESKey objects that work well with one's DESCipher
> implementation.
I agree.
>
> >In order to retrieve a specification of the key's underlying key
> >material in a suitable format, you use a key factory, which is
> >provider-based.
>
> Again, what's the difference in the amount of work that a Key provider has
> to do whether it:
> a) supports an interface that requires it to return a vendor _neutral_
> encoding format
> b) supports an interface that requires it to return a vendor _neutral_
> KeySpec
>
As a provider, you are not required to support any key specs
other than EncodedKeySpec (to which I am going to add a byte[]
constructor),
if you provide a complete feature list to your users, i.e., users who use
your provider do not have to use any other providers.
The algorithm-specific key specs are defined so that keys from
one provider can be used with a different provider, provided that both
providers supply conversion routines from their keys to the key specs,
and vice versa.
If your provider does not support any of the algorithm-specific
key specs (this would be the case if your provider supplied some
proprietary algorithms), it may still support a complete life-cycle
for its keys:
1. Key(Pair)Generator returns opaque key objects, which you can use with
the provider's implementations of the crypto services.
2. If you want to store your keys for later usage, you call the
key's getEncoded() method, which returns some kind of encoding for the
key, which may or may not be standards-based.
3. If you want to reuse your keys, you wrap their encoding into
an EncodedKeySpec, and pass that to your provider's key factory,
which will know how to parse the encoding.
> >Key factories are bi-directional.
> >You also use a key factory to build a Key object from a key
> >specification.
>
> What's the point when a Cipher must convert that Key back into an encoded
> format in order to convert it into a Key that it can use? If the Cipher must
> ultimately create an implementation specific Key that its cipher
> implementation can use, then pass it the KeySpec. The Cipher provider has
> much less work to do because the KeySpec is documented in the JCE. On the
> other hand, the encoded format and the String names for those formats are
> not documented anywhere. Where is "RAW" documented?
I'll need to document it. Thanks for the reminder!
> >Key objects are opaque, because you cannot tell how they are implemented.
> >The underlying implementation is provider-dependent, and may be
> >software or hardware based.
>
> Therefore, they should not be exposed to the programmer and should only be
> dealt with inside the Cipher implementation.
If my implementation of keys is hardware-based, and my private key
never leaves the token device, how do you expect me to construct a
DSAPrivateKeySpec, which expects as one of its parameters the
private key?
> The programmer should only deal
> with vendor _neutral_ representation.
That's not always possible, see above.
> If anything, there should be a class
> to convert KeySpec's to various encoded formats for transmission over the
> net.
>
> >If you have a key specification for a DSA public key, consisting of
> >y, p, q, and g, and you feed the same specification to DSA key factories
> >from different providers, the resulting PublicKey objects will most
> >likely have different underlying implementations. If the key specifications
> >already implemented the "Key" interface, and the concept of a key
> >factory were removed (as you suggested), you would remove that flexibility,
> >and providers would no longer be able to supply their own implementations
> >of keys.
>
> If I had a DSA public key spec consisting of y, p, q, and, g, I would rather
> init() the vendor specific DSASignature implementation with the
> DSAPublicKeySpec and let the vendor implementation create whatever DSAKey
> representation _it_ requires in order to carry out its algorithm.
Again, this may only work in software.
> If all Cipher/Signature providers understood the documented KeySpec's, then
> I believe we would have more flexibility (just my humble opinion). The
> KeySpec acts as a vendor neutral implementation. As it stands, yes, I can
> pass opaque Key's around, but the Cipher's have to be prepared to convert
> that opaque Key into a Key that it can actually use. How does it do this? By
> converting that opaque Key back into a KeySpec!
Only when you're switching from one provider to another you have to do
the conversion.
If one of the providers is hardware based, you may not even be able
to do the conversion.
Jan