Okay, let me go back to the beginning with some slightly weaker
assumptions, and a slightly modified design. I've attempted to
incorporate all the feedback I've received so far, but if you feel like
I missed something, please let me know.
Assumptions:
A) We don't need to expose curve domain parameters over the API in the
initial design/implementation. Curves can be specified using names (e.g.
"X25519") or OIDs. The underlying implementation will likely support
arbitrary Montgomery curves, but the JCA application will only be able
to use the supported named curves. Support for arbitrary curve
parameters in the API is something that can be considered as a separate
feature in the future.
B) We don't need specs and interfaces that are specific to RFC 7748
public/private keys. To allow interoperability between providers, and to
make it easy to specify key values, we can use more general purpose spec
classes and interfaces that can be reused by multiple algorithms. In
particular, we should be able to reuse these interfaces/classes between
RFC 7748 and RFC 8032.
Here is a high-level description of the proposed JCA API. A more
detailed description will be provided during the JEP review.
1) The string "XDH" will be used in getInstance() to refer to all
services related to RFC 7748 (KeyAgreement, KeyFactory,
KeyPairGenerator, etc). This is a departure from the ECDH API that used
"EC" for key generation (shared with ECDSA) and "ECDH" for KeyAgreement,
and makes the RFC 7748 API more like "DiffieHellman" and other
algorithms that use the same name for all services.
2) The new class java.security.spec.NamedParameterSpec (which implements
AlgorithmParameterSpec) will be used to specify curves for RFC 7748.
This class has a single String member which holds the name of the curve
("X25519" or "X448"). This parameter spec class can be reused by other
crypto algorithms that similarly identify parameter sets using names
(e.g. FFDHE3072 in DiffieHellman). This new class can be inserted into
the hierarchy above ECGenParameterSpec.
3) There will be no classes in java.security.spec specific to RFC 7748
public keys and private keys. A new class called ByteArrayKeySpec will
be added to java.security.spec. This class will hold an algorithm name
String, an AlgorithmParameterSpec, and a byte array containing the raw
key bytes. This class is suitable for keys in RFC 7748, RFC 8032, and
possibly other algorithms. ByteArrayKeySpec can be viewed as a
generalization of SecretKeySpec, and it will be used in a similar
manner. The existing classes X509EncodedKeySpec and PKCS8EncodedKeySpec
can also be used for RFC 7748 public and private key specs, respectively.
4) There will be no interfaces in java.security.interfaces specific to
RFC 7748 public/private keys. A new interface called ByteArrayKey will
be added to java.security.interfaces. This interface will expose an
AlgorithmParameterSpec and a byte array containing the raw key value.
Implementations of keys in RFC 7748, 8032 (and possibly other
algorithms) can implement this interface to allow other providers to
access the required information about the keys. Implementations can also
achieve interoperability by using encoded representations of keys.
Here is how the API will be implemented in the SunEC provider:
1) The public key and private key implementation classes will extend
sun.security.ec.X509Key and sun.security.ec.PKCS8Key, respectively. This
is similar to ECPublicKeyImpl and ECPrivateKeyImpl. They will also both
implement ByteArrayKey.
2) The KeyFactory for RFC 7748 will support translation from opaque keys
and ByteArrayKey to X509EncodedKeySpec/PKCS8EncodedKeySpec and
ByteArrayKeySpec (and vice-versa).
Example code:
KeyPairGenerator kpg = KeyPairGenerator.getInstance("XDH");
NamedParameterSpec paramSpec = new NamedParameterSpec("X25519");
kpg.initialize(paramSpec); // equivalent to kpg.initialize(255)
KeyPair kp = kpg.generateKeyPair();
KeyFactory kf = KeyFactory.getInstance("XDH");
byte[] rawKeyBytes = ...
ByteArrayKeySpec pubSpec = new ByteArrayKeySpec(
"XDH",
new NamedParameterSpec("X25519"),
rawKeyBytes);
PublicKey pubKey = kf.generatePublic(pubSpec);
KeyAgreement ka = KeyAgreement.getInstance("XDH");
ka.init(kp.getPrivate());
ka.doPhase(pubKey, true);
byte[] secret = ka.generateSecret();
Additional notes:
1) The ability to specify curve domain parameters over the API is a
useful feature, and this design accommodates the addition of this
feature in the future. Both ByteArrayKeySpec and ByteArrayKey hold an
AlgorithmParameterSpec rather than a more concrete type. In addition to
using NamedParameterSpec, the parameters could be specified using some
other type that holds domain parameters.
2) Applications that need to choose their behavior based on the type of
(for example) public key can do so for RFC 7748 keys by calling
Key::getAlgorithm() to determine the algorithm and then using
ByteArrayKey to extract the required information.
3) This design supports Xuelei's general EC proposal[1]. We can use
names like "XDHWithX25519" or perhaps simply "X25519" to specify the
"XDH" algorithm initialized with the "X25519" NamedParameterSpec.
[1]
http://mail.openjdk.java.net/pipermail/security-dev/2017-August/016194.html