Re: Fw: My opinions on Key, KeySpec, KeyFactory, KeyGenerator, et. al.

George Chung (gchung@openhorizon.com)
Fri, 13 Mar 1998 15:31:18 -0800

From: "George Chung" <gchung@openhorizon.com>
To: "Jan Luehe" <Jan.Luehe@Eng>, <java-security@javasoft.com>
Subject: Re: Fw: My opinions on Key, KeySpec, KeyFactory, KeyGenerator, et. al.
Date: Fri, 13 Mar 1998 15:31:18 -0800

Dear Jan,

>In the Java Crypto Architecture (JCA), keys are generated and passed
>around as opaque Key objects, of which only generic properties
>can be retrieved, such as the key's algorithm, type, and possibly
>its encoding (see the methods defined in the java.security.Key
>interface). In some cases, the Key object may also implement some
>specialized key interfaces, such as the interfaces for DSA public
>and private keys.

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.

With a), the Cipher provider must be able to process the format type and
convert the encoded format into one of _his_ opaque Key's for encrypting.
With b), the Cipher provider must provide a KeyFactory implementation to
convert a KeySpec into one of _his_ opaque Key's for encrypting. What's the
difference? In your DESKey ctor, why not provide the following ctor?

DESKey(byte encoded[], String format)

instead of (or in addition to)

DESKey(DESKeySpec spec)

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.

Now if we consider the encoding/format equivalent in concept to the KeySpec
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.

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.

>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

>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?

>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. The programmer should only deal
with vendor _neutral_ representation. 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.

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!

>You described a scenario where Key objects returned from a key factory from
>one provider ("FOO") are used to initialize a Cipher object from a
different
>provider ("BAR"), and you were wondering if this usage is supported by the
JCA.
>The answer is "yes". Key factories have a method "translate", which
attempts
>to convert a Key object from one provider to the corresponding Key object
from
>a different provider (the provider of the key factory). This conversion may
fail,
>in which case an "InvalidKeyException" is thrown.

Yes, I saw this after I sent the original email.

>To avoid this problem, you may always specify the name of a provider when
>you request a Cipher and key factory object. To avoid conversion problems,
>you would specify the same provider in both cases.

If Cipher providers were required to handle an init call with a KeySpec
parameter, then we could instead have the init call throw an
"InvalidKeySpecException".

>Hope this answers your questions.

Thanks for taking the time to explain your position. I still maintain that
you already mush the idea of Key and KeySpec together by having Key return a
vendor neutral encoding and format name. I think that KeySpec is a much more
formalized and elegant way of dealing with Key's in a vendor neutral way. I
think most provider's would agree that just dealing with the documented
KeySpec's as opposed to the undocumented encodings is a much easier
proposition. And that having the Cipher simply create its own implementation
specific key based on the KeySpec passed into the init() call is quite an
easy thing to do also. The other added benefit is that the JDK could provide
final classes that convert from the documented KeySpec's to the encoding
formats that are important. You would be able to remove this yoke from the
providers!

I know first hand, because I'm trying to implement security provider ala the
JCE and I see a lot of work that I have to do with the current semantics!
:-)

Just by 2 cents.

Thanks for listening.