mirror of
https://github.com/openjdk/jdk.git
synced 2026-07-20 07:58:55 +00:00
8340327: A common framework to support public key algorithms with standard parameter sets
Reviewed-by: ascarpino, mullan
This commit is contained in:
parent
0a57fe1df6
commit
3f53d57134
143
src/java.base/share/classes/sun/security/pkcs/NamedPKCS8Key.java
Normal file
143
src/java.base/share/classes/sun/security/pkcs/NamedPKCS8Key.java
Normal file
@ -0,0 +1,143 @@
|
||||
/*
|
||||
* Copyright (c) 2024, Oracle and/or its affiliates. All rights reserved.
|
||||
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
|
||||
*
|
||||
* This code is free software; you can redistribute it and/or modify it
|
||||
* under the terms of the GNU General Public License version 2 only, as
|
||||
* published by the Free Software Foundation. Oracle designates this
|
||||
* particular file as subject to the "Classpath" exception as provided
|
||||
* by Oracle in the LICENSE file that accompanied this code.
|
||||
*
|
||||
* This code is distributed in the hope that it will be useful, but WITHOUT
|
||||
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
|
||||
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
|
||||
* version 2 for more details (a copy is included in the LICENSE file that
|
||||
* accompanied this code).
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License version
|
||||
* 2 along with this work; if not, write to the Free Software Foundation,
|
||||
* Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
|
||||
*
|
||||
* Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
|
||||
* or visit www.oracle.com if you need additional information or have any
|
||||
* questions.
|
||||
*/
|
||||
|
||||
package sun.security.pkcs;
|
||||
|
||||
import sun.security.util.DerInputStream;
|
||||
import sun.security.util.DerValue;
|
||||
import sun.security.x509.AlgorithmId;
|
||||
|
||||
import javax.security.auth.DestroyFailedException;
|
||||
import java.io.IOException;
|
||||
import java.io.InvalidObjectException;
|
||||
import java.io.ObjectInputStream;
|
||||
import java.io.Serial;
|
||||
import java.security.InvalidKeyException;
|
||||
import java.security.NoSuchAlgorithmException;
|
||||
import java.security.ProviderException;
|
||||
import java.security.spec.NamedParameterSpec;
|
||||
import java.util.Arrays;
|
||||
|
||||
/// Represents a private key from an algorithm family that is specialized
|
||||
/// with a named parameter set.
|
||||
///
|
||||
/// This key is generated by either a [sun.security.provider.NamedKeyPairGenerator]
|
||||
/// or [sun.security.provider.NamedKeyFactory]. Its [#getAlgorithm] method
|
||||
/// returns the algorithm family name, while its [#getParams()] method returns
|
||||
/// the parameter set name as a [NamedParameterSpec] object. The algorithm
|
||||
/// identifier in the PKCS #8 encoding of the key is always a single OID derived
|
||||
/// from the parameter set name.
|
||||
///
|
||||
/// @see sun.security.provider.NamedKeyPairGenerator
|
||||
public final class NamedPKCS8Key extends PKCS8Key {
|
||||
@Serial
|
||||
private static final long serialVersionUID = 1L;
|
||||
|
||||
private final String fname;
|
||||
private final transient NamedParameterSpec paramSpec;
|
||||
private final byte[] rawBytes;
|
||||
|
||||
private transient boolean destroyed = false;
|
||||
|
||||
/// Ctor from family name, parameter set name, raw key bytes.
|
||||
/// Key bytes won't be cloned, caller must relinquish ownership
|
||||
public NamedPKCS8Key(String fname, String pname, byte[] rawBytes) {
|
||||
this.fname = fname;
|
||||
this.paramSpec = new NamedParameterSpec(pname);
|
||||
try {
|
||||
this.algid = AlgorithmId.get(pname);
|
||||
} catch (NoSuchAlgorithmException e) {
|
||||
throw new ProviderException(e);
|
||||
}
|
||||
this.rawBytes = rawBytes;
|
||||
|
||||
DerValue val = new DerValue(DerValue.tag_OctetString, rawBytes);
|
||||
try {
|
||||
this.key = val.toByteArray();
|
||||
} finally {
|
||||
val.clear();
|
||||
}
|
||||
}
|
||||
|
||||
/// Ctor from family name, and PKCS #8 bytes
|
||||
public NamedPKCS8Key(String fname, byte[] encoded) throws InvalidKeyException {
|
||||
super(encoded);
|
||||
this.fname = fname;
|
||||
try {
|
||||
paramSpec = new NamedParameterSpec(algid.getName());
|
||||
if (algid.getEncodedParams() != null) {
|
||||
throw new InvalidKeyException("algorithm identifier has params");
|
||||
}
|
||||
rawBytes = new DerInputStream(key).getOctetString();
|
||||
} catch (IOException e) {
|
||||
throw new InvalidKeyException("Cannot parse input", e);
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public String toString() {
|
||||
// Do not modify: this can be used by earlier JDKs that
|
||||
// do not have the getParams() method
|
||||
return paramSpec.getName() + " private key";
|
||||
}
|
||||
|
||||
/// Returns the reference to the internal key. Caller must not modify
|
||||
/// the content or keep a reference.
|
||||
public byte[] getRawBytes() {
|
||||
return rawBytes;
|
||||
}
|
||||
|
||||
@Override
|
||||
public NamedParameterSpec getParams() {
|
||||
return paramSpec;
|
||||
}
|
||||
|
||||
@Override
|
||||
public String getAlgorithm() {
|
||||
return fname;
|
||||
}
|
||||
|
||||
@java.io.Serial
|
||||
private void readObject(ObjectInputStream stream)
|
||||
throws IOException, ClassNotFoundException {
|
||||
throw new InvalidObjectException(
|
||||
"NamedPKCS8Key keys are not directly deserializable");
|
||||
}
|
||||
|
||||
@Override
|
||||
public void destroy() throws DestroyFailedException {
|
||||
Arrays.fill(rawBytes, (byte)0);
|
||||
Arrays.fill(key, (byte)0);
|
||||
if (encodedKey != null) {
|
||||
Arrays.fill(encodedKey, (byte)0);
|
||||
}
|
||||
destroyed = true;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean isDestroyed() {
|
||||
return destroyed;
|
||||
}
|
||||
}
|
||||
223
src/java.base/share/classes/sun/security/provider/NamedKEM.java
Normal file
223
src/java.base/share/classes/sun/security/provider/NamedKEM.java
Normal file
@ -0,0 +1,223 @@
|
||||
/*
|
||||
* Copyright (c) 2024, Oracle and/or its affiliates. All rights reserved.
|
||||
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
|
||||
*
|
||||
* This code is free software; you can redistribute it and/or modify it
|
||||
* under the terms of the GNU General Public License version 2 only, as
|
||||
* published by the Free Software Foundation. Oracle designates this
|
||||
* particular file as subject to the "Classpath" exception as provided
|
||||
* by Oracle in the LICENSE file that accompanied this code.
|
||||
*
|
||||
* This code is distributed in the hope that it will be useful, but WITHOUT
|
||||
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
|
||||
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
|
||||
* version 2 for more details (a copy is included in the LICENSE file that
|
||||
* accompanied this code).
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License version
|
||||
* 2 along with this work; if not, write to the Free Software Foundation,
|
||||
* Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
|
||||
*
|
||||
* Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
|
||||
* or visit www.oracle.com if you need additional information or have any
|
||||
* questions.
|
||||
*/
|
||||
|
||||
package sun.security.provider;
|
||||
|
||||
import sun.security.pkcs.NamedPKCS8Key;
|
||||
import sun.security.x509.NamedX509Key;
|
||||
|
||||
import javax.crypto.DecapsulateException;
|
||||
import javax.crypto.KEM;
|
||||
import javax.crypto.KEMSpi;
|
||||
import javax.crypto.SecretKey;
|
||||
import javax.crypto.spec.SecretKeySpec;
|
||||
import java.security.InvalidAlgorithmParameterException;
|
||||
import java.security.InvalidKeyException;
|
||||
import java.security.PrivateKey;
|
||||
import java.security.ProviderException;
|
||||
import java.security.PublicKey;
|
||||
import java.security.SecureRandom;
|
||||
import java.security.spec.AlgorithmParameterSpec;
|
||||
import java.security.spec.NamedParameterSpec;
|
||||
import java.util.Arrays;
|
||||
import java.util.Objects;
|
||||
|
||||
/// A base class for all `KEM` implementations that can be
|
||||
/// configured with a named parameter set. See [NamedKeyPairGenerator]
|
||||
/// for more details.
|
||||
public abstract class NamedKEM implements KEMSpi {
|
||||
|
||||
private final String fname; // family name
|
||||
private final String[] pnames; // allowed parameter set name (at least one)
|
||||
|
||||
/// Creates a new `NamedKEM` object.
|
||||
///
|
||||
/// @param fname the family name
|
||||
/// @param pnames the standard parameter set names, at least one is needed.
|
||||
protected NamedKEM(String fname, String... pnames) {
|
||||
if (fname == null) {
|
||||
throw new AssertionError("fname cannot be null");
|
||||
}
|
||||
if (pnames == null || pnames.length == 0) {
|
||||
throw new AssertionError("pnames cannot be null or empty");
|
||||
}
|
||||
this.fname = fname;
|
||||
this.pnames = pnames;
|
||||
}
|
||||
|
||||
@Override
|
||||
public EncapsulatorSpi engineNewEncapsulator(PublicKey publicKey,
|
||||
AlgorithmParameterSpec spec, SecureRandom secureRandom)
|
||||
throws InvalidAlgorithmParameterException, InvalidKeyException {
|
||||
if (spec != null) {
|
||||
throw new InvalidAlgorithmParameterException(
|
||||
"The " + fname + " algorithm does not take any parameters");
|
||||
}
|
||||
// translate also check the key
|
||||
var nk = (NamedX509Key) new NamedKeyFactory(fname, pnames)
|
||||
.engineTranslateKey(publicKey);
|
||||
var pk = nk.getRawBytes();
|
||||
return getKeyConsumerImpl(this, nk.getParams(), pk,
|
||||
implCheckPublicKey(nk.getParams().getName(), pk), secureRandom);
|
||||
}
|
||||
|
||||
@Override
|
||||
public DecapsulatorSpi engineNewDecapsulator(
|
||||
PrivateKey privateKey, AlgorithmParameterSpec spec)
|
||||
throws InvalidAlgorithmParameterException, InvalidKeyException {
|
||||
if (spec != null) {
|
||||
throw new InvalidAlgorithmParameterException(
|
||||
"The " + fname + " algorithm does not take any parameters");
|
||||
}
|
||||
// translate also check the key
|
||||
var nk = (NamedPKCS8Key) new NamedKeyFactory(fname, pnames)
|
||||
.engineTranslateKey(privateKey);
|
||||
var sk = nk.getRawBytes();
|
||||
return getKeyConsumerImpl(this, nk.getParams(), sk,
|
||||
implCheckPrivateKey(nk.getParams().getName(), sk), null);
|
||||
}
|
||||
|
||||
// We don't have a flag on whether key is public key or private key.
|
||||
// The correct method should always be called.
|
||||
private record KeyConsumerImpl(NamedKEM kem, String name, int sslen,
|
||||
int clen, byte[] key, Object k2, SecureRandom sr)
|
||||
implements KEMSpi.EncapsulatorSpi, KEMSpi.DecapsulatorSpi {
|
||||
@Override
|
||||
public SecretKey engineDecapsulate(byte[] encapsulation, int from, int to,
|
||||
String algorithm) throws DecapsulateException {
|
||||
if (encapsulation.length != clen) {
|
||||
throw new DecapsulateException("Invalid key encapsulation message length");
|
||||
}
|
||||
var ss = kem.implDecapsulate(name, key, k2, encapsulation);
|
||||
try {
|
||||
return new SecretKeySpec(ss,
|
||||
from, to - from, algorithm);
|
||||
} finally {
|
||||
Arrays.fill(ss, (byte)0);
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public KEM.Encapsulated engineEncapsulate(int from, int to, String algorithm) {
|
||||
var enc = kem.implEncapsulate(name, key, k2, sr);
|
||||
try {
|
||||
return new KEM.Encapsulated(
|
||||
new SecretKeySpec(enc[1],
|
||||
from, to - from, algorithm),
|
||||
enc[0],
|
||||
null);
|
||||
} finally {
|
||||
Arrays.fill(enc[1], (byte)0);
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public int engineSecretSize() {
|
||||
return sslen;
|
||||
}
|
||||
|
||||
@Override
|
||||
public int engineEncapsulationSize() {
|
||||
return clen;
|
||||
}
|
||||
}
|
||||
|
||||
private static KeyConsumerImpl getKeyConsumerImpl(NamedKEM kem,
|
||||
NamedParameterSpec nps, byte[] key, Object k2, SecureRandom sr) {
|
||||
String name = nps.getName();
|
||||
return new KeyConsumerImpl(kem, name, kem.implSecretSize(name), kem.implEncapsulationSize(name),
|
||||
key, k2, sr);
|
||||
}
|
||||
|
||||
/// User-defined encap function.
|
||||
///
|
||||
/// @param name parameter name
|
||||
/// @param pk public key in raw bytes
|
||||
/// @param pk2 parsed public key, `null` if none. See [#implCheckPublicKey].
|
||||
/// @param sr SecureRandom object, `null` if not initialized
|
||||
/// @return the key encapsulation message and the shared key (in this order)
|
||||
/// @throws ProviderException if there is an internal error
|
||||
protected abstract byte[][] implEncapsulate(String name, byte[] pk, Object pk2, SecureRandom sr);
|
||||
|
||||
/// User-defined decap function.
|
||||
///
|
||||
/// @param name parameter name
|
||||
/// @param sk private key in raw bytes
|
||||
/// @param sk2 parsed private key, `null` if none. See [#implCheckPrivateKey].
|
||||
/// @param encap the key encapsulation message
|
||||
/// @return the shared key
|
||||
/// @throws ProviderException if there is an internal error
|
||||
/// @throws DecapsulateException if there is another error
|
||||
protected abstract byte[] implDecapsulate(String name, byte[] sk, Object sk2, byte[] encap)
|
||||
throws DecapsulateException;
|
||||
|
||||
/// User-defined function returning shared secret key length.
|
||||
///
|
||||
/// @param name parameter name
|
||||
/// @return shared secret key length
|
||||
/// @throws ProviderException if there is an internal error
|
||||
protected abstract int implSecretSize(String name);
|
||||
|
||||
/// User-defined function returning key encapsulation message length.
|
||||
///
|
||||
/// @param name parameter name
|
||||
/// @return key encapsulation message length
|
||||
/// @throws ProviderException if there is an internal error
|
||||
protected abstract int implEncapsulationSize(String name);
|
||||
|
||||
/// User-defined function to validate a public key.
|
||||
///
|
||||
/// This method will be called in `newEncapsulator`. This gives the provider a chance to
|
||||
/// reject the key so an `InvalidKeyException` can be thrown earlier.
|
||||
/// An implementation can optionally return a "parsed key" as an `Object` value.
|
||||
/// This object will be passed into the [#implEncapsulate] method along with the raw key.
|
||||
///
|
||||
/// The default implementation returns `null`.
|
||||
///
|
||||
/// @param name parameter name
|
||||
/// @param pk public key in raw bytes
|
||||
/// @return a parsed key, `null` if none.
|
||||
/// @throws InvalidKeyException if the key is invalid
|
||||
protected Object implCheckPublicKey(String name, byte[] pk) throws InvalidKeyException {
|
||||
return null;
|
||||
}
|
||||
|
||||
/// User-defined function to validate a private key.
|
||||
///
|
||||
/// This method will be called in `newDecapsulator`. This gives the provider a chance to
|
||||
/// reject the key so an `InvalidKeyException` can be thrown earlier.
|
||||
/// An implementation can optionally return a "parsed key" as an `Object` value.
|
||||
/// This object will be passed into the [#implDecapsulate] method along with the raw key.
|
||||
///
|
||||
/// The default implementation returns `null`.
|
||||
///
|
||||
/// @param name parameter name
|
||||
/// @param sk private key in raw bytes
|
||||
/// @return a parsed key, `null` if none.
|
||||
/// @throws InvalidKeyException if the key is invalid
|
||||
protected Object implCheckPrivateKey(String name, byte[] sk) throws InvalidKeyException {
|
||||
return null;
|
||||
}
|
||||
}
|
||||
@ -0,0 +1,288 @@
|
||||
/*
|
||||
* Copyright (c) 2024, Oracle and/or its affiliates. All rights reserved.
|
||||
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
|
||||
*
|
||||
* This code is free software; you can redistribute it and/or modify it
|
||||
* under the terms of the GNU General Public License version 2 only, as
|
||||
* published by the Free Software Foundation. Oracle designates this
|
||||
* particular file as subject to the "Classpath" exception as provided
|
||||
* by Oracle in the LICENSE file that accompanied this code.
|
||||
*
|
||||
* This code is distributed in the hope that it will be useful, but WITHOUT
|
||||
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
|
||||
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
|
||||
* version 2 for more details (a copy is included in the LICENSE file that
|
||||
* accompanied this code).
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License version
|
||||
* 2 along with this work; if not, write to the Free Software Foundation,
|
||||
* Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
|
||||
*
|
||||
* Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
|
||||
* or visit www.oracle.com if you need additional information or have any
|
||||
* questions.
|
||||
*/
|
||||
|
||||
package sun.security.provider;
|
||||
|
||||
import sun.security.pkcs.NamedPKCS8Key;
|
||||
import sun.security.util.RawKeySpec;
|
||||
import sun.security.x509.NamedX509Key;
|
||||
|
||||
import java.security.AsymmetricKey;
|
||||
import java.security.InvalidKeyException;
|
||||
import java.security.Key;
|
||||
import java.security.KeyFactorySpi;
|
||||
import java.security.PrivateKey;
|
||||
import java.security.PublicKey;
|
||||
import java.security.spec.EncodedKeySpec;
|
||||
import java.security.spec.InvalidKeySpecException;
|
||||
import java.security.spec.KeySpec;
|
||||
import java.security.spec.NamedParameterSpec;
|
||||
import java.security.spec.PKCS8EncodedKeySpec;
|
||||
import java.security.spec.X509EncodedKeySpec;
|
||||
import java.util.Arrays;
|
||||
import java.util.Objects;
|
||||
|
||||
/// A base class for all `KeyFactory` implementations that can be
|
||||
/// configured with a named parameter set. See [NamedKeyPairGenerator]
|
||||
/// for more details.
|
||||
///
|
||||
/// This factory supports reading and writing to RAW formats:
|
||||
///
|
||||
/// 1. It reads from a RAW key using `translateKey` if `key.getFormat` is "RAW".
|
||||
/// 2. It writes to a RAW [EncodedKeySpec] if `getKeySpec(key, EncodedKeySpec.class)`
|
||||
/// is called. The format of the output is "RAW" and the algorithm is
|
||||
/// intentionally left unspecified.
|
||||
/// 3. It reads from and writes to the internal type [RawKeySpec].
|
||||
///
|
||||
/// When reading from a RAW format, it needs enough info to derive the
|
||||
/// parameter set name.
|
||||
public class NamedKeyFactory extends KeyFactorySpi {
|
||||
|
||||
private final String fname; // family name
|
||||
private final String[] pnames; // allowed parameter set name (at least one)
|
||||
|
||||
/// Creates a new `NamedKeyFactory` object.
|
||||
///
|
||||
/// @param fname the family name
|
||||
/// @param pnames the standard parameter set names, at least one is needed.
|
||||
protected NamedKeyFactory(String fname, String... pnames) {
|
||||
if (fname == null) {
|
||||
throw new AssertionError("fname cannot be null");
|
||||
}
|
||||
if (pnames == null || pnames.length == 0) {
|
||||
throw new AssertionError("pnames cannot be null or empty");
|
||||
}
|
||||
this.fname = fname;
|
||||
this.pnames = pnames;
|
||||
}
|
||||
|
||||
private String checkName(String name) throws InvalidKeyException {
|
||||
for (var pname : pnames) {
|
||||
if (pname.equalsIgnoreCase(name)) {
|
||||
// return the stored standard name
|
||||
return pname;
|
||||
}
|
||||
}
|
||||
throw new InvalidKeyException("Unsupported parameter set name: " + name);
|
||||
}
|
||||
|
||||
@Override
|
||||
protected PublicKey engineGeneratePublic(KeySpec keySpec)
|
||||
throws InvalidKeySpecException {
|
||||
if (keySpec instanceof X509EncodedKeySpec xspec) {
|
||||
try {
|
||||
return fromX509(xspec.getEncoded());
|
||||
} catch (InvalidKeyException e) {
|
||||
throw new InvalidKeySpecException(e);
|
||||
}
|
||||
} else if (keySpec instanceof RawKeySpec rks) {
|
||||
if (pnames.length == 1) {
|
||||
return new NamedX509Key(fname, pnames[0], rks.getKeyArr());
|
||||
} else {
|
||||
throw new InvalidKeySpecException("Parameter set name unavailable");
|
||||
}
|
||||
} else if (keySpec instanceof EncodedKeySpec espec
|
||||
&& espec.getFormat().equalsIgnoreCase("RAW")) {
|
||||
if (pnames.length == 1) {
|
||||
return new NamedX509Key(fname, pnames[0], espec.getEncoded());
|
||||
} else {
|
||||
throw new InvalidKeySpecException("Parameter set name unavailable");
|
||||
}
|
||||
} else {
|
||||
throw new InvalidKeySpecException("Unsupported keyspec: " + keySpec);
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
protected PrivateKey engineGeneratePrivate(KeySpec keySpec)
|
||||
throws InvalidKeySpecException {
|
||||
if (keySpec instanceof PKCS8EncodedKeySpec pspec) {
|
||||
var bytes = pspec.getEncoded();
|
||||
try {
|
||||
return fromPKCS8(bytes);
|
||||
} catch (InvalidKeyException e) {
|
||||
throw new InvalidKeySpecException(e);
|
||||
} finally {
|
||||
Arrays.fill(bytes, (byte) 0);
|
||||
}
|
||||
} else if (keySpec instanceof RawKeySpec rks) {
|
||||
if (pnames.length == 1) {
|
||||
var bytes = rks.getKeyArr();
|
||||
try {
|
||||
return new NamedPKCS8Key(fname, pnames[0], bytes);
|
||||
} finally {
|
||||
Arrays.fill(bytes, (byte) 0);
|
||||
}
|
||||
} else {
|
||||
throw new InvalidKeySpecException("Parameter set name unavailable");
|
||||
}
|
||||
} else if (keySpec instanceof EncodedKeySpec espec
|
||||
&& espec.getFormat().equalsIgnoreCase("RAW")) {
|
||||
if (pnames.length == 1) {
|
||||
var bytes = espec.getEncoded();
|
||||
try {
|
||||
return new NamedPKCS8Key(fname, pnames[0], bytes);
|
||||
} finally {
|
||||
Arrays.fill(bytes, (byte) 0);
|
||||
}
|
||||
} else {
|
||||
throw new InvalidKeySpecException("Parameter set name unavailable");
|
||||
}
|
||||
} else {
|
||||
throw new InvalidKeySpecException("Unsupported keyspec: " + keySpec);
|
||||
}
|
||||
}
|
||||
|
||||
private PrivateKey fromPKCS8(byte[] bytes)
|
||||
throws InvalidKeyException, InvalidKeySpecException {
|
||||
var k = new NamedPKCS8Key(fname, bytes);
|
||||
checkName(k.getParams().getName());
|
||||
return k;
|
||||
}
|
||||
|
||||
private PublicKey fromX509(byte[] bytes)
|
||||
throws InvalidKeyException, InvalidKeySpecException {
|
||||
var k = new NamedX509Key(fname, bytes);
|
||||
checkName(k.getParams().getName());
|
||||
return k;
|
||||
}
|
||||
|
||||
private static class RawEncodedKeySpec extends EncodedKeySpec {
|
||||
public RawEncodedKeySpec(byte[] encodedKey) {
|
||||
super(encodedKey);
|
||||
}
|
||||
|
||||
@Override
|
||||
public String getFormat() {
|
||||
return "RAW";
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
protected <T extends KeySpec> T engineGetKeySpec(Key key, Class<T> keySpec)
|
||||
throws InvalidKeySpecException {
|
||||
try {
|
||||
key = engineTranslateKey(key);
|
||||
} catch (InvalidKeyException e) {
|
||||
throw new InvalidKeySpecException(e);
|
||||
}
|
||||
// key is now either NamedPKCS8Key or NamedX509Key of permitted param set
|
||||
if (key instanceof NamedPKCS8Key nk) {
|
||||
byte[] bytes = null;
|
||||
try {
|
||||
if (keySpec == PKCS8EncodedKeySpec.class) {
|
||||
return keySpec.cast(
|
||||
new PKCS8EncodedKeySpec(bytes = key.getEncoded()));
|
||||
} else if (keySpec == RawKeySpec.class) {
|
||||
return keySpec.cast(new RawKeySpec(nk.getRawBytes()));
|
||||
} else if (keySpec.isAssignableFrom(EncodedKeySpec.class)) {
|
||||
return keySpec.cast(
|
||||
new RawEncodedKeySpec(nk.getRawBytes()));
|
||||
} else {
|
||||
throw new InvalidKeySpecException("Unsupported type: " + keySpec);
|
||||
}
|
||||
} finally {
|
||||
if (bytes != null) {
|
||||
Arrays.fill(bytes, (byte)0);
|
||||
}
|
||||
}
|
||||
} else if (key instanceof NamedX509Key nk) {
|
||||
if (keySpec == X509EncodedKeySpec.class
|
||||
&& key.getFormat().equalsIgnoreCase("X.509")) {
|
||||
return keySpec.cast(new X509EncodedKeySpec(key.getEncoded()));
|
||||
} else if (keySpec == RawKeySpec.class) {
|
||||
return keySpec.cast(new RawKeySpec(nk.getRawBytes()));
|
||||
} else if (keySpec.isAssignableFrom(EncodedKeySpec.class)) {
|
||||
return keySpec.cast(new RawEncodedKeySpec(nk.getRawBytes()));
|
||||
} else {
|
||||
throw new InvalidKeySpecException("Unsupported type: " + keySpec);
|
||||
}
|
||||
}
|
||||
throw new AssertionError("No " + keySpec.getName() + " for " + key.getClass());
|
||||
}
|
||||
|
||||
@Override
|
||||
protected Key engineTranslateKey(Key key) throws InvalidKeyException {
|
||||
if (key == null) {
|
||||
throw new InvalidKeyException("Key must not be null");
|
||||
}
|
||||
if (key instanceof NamedX509Key nk) {
|
||||
checkName(nk.getParams().getName());
|
||||
return key;
|
||||
}
|
||||
if (key instanceof NamedPKCS8Key nk) {
|
||||
checkName(nk.getParams().getName());
|
||||
return key;
|
||||
}
|
||||
var format = key.getFormat();
|
||||
if (format == null) {
|
||||
throw new InvalidKeyException("Unextractable key");
|
||||
} else if (format.equalsIgnoreCase("RAW")) {
|
||||
var kAlg = key.getAlgorithm();
|
||||
if (key instanceof AsymmetricKey pk) {
|
||||
String name;
|
||||
// Three cases that we can find the parameter set name from a RAW key:
|
||||
// 1. getParams() returns one
|
||||
// 2. getAlgorithm() returns param set name (some provider does this)
|
||||
// 3. getAlgorithm() returns family name but this KF is for param set name
|
||||
if (pk.getParams() instanceof NamedParameterSpec nps) {
|
||||
name = checkName(nps.getName());
|
||||
} else {
|
||||
if (kAlg.equalsIgnoreCase(fname)) {
|
||||
if (pnames.length == 1) {
|
||||
name = pnames[0];
|
||||
} else {
|
||||
throw new InvalidKeyException("No parameter set info");
|
||||
}
|
||||
} else {
|
||||
name = checkName(kAlg);
|
||||
}
|
||||
}
|
||||
return key instanceof PrivateKey
|
||||
? new NamedPKCS8Key(fname, name, key.getEncoded())
|
||||
: new NamedX509Key(fname, name, key.getEncoded());
|
||||
} else {
|
||||
throw new InvalidKeyException("Unsupported key type: " + key.getClass());
|
||||
}
|
||||
} else if (format.equalsIgnoreCase("PKCS#8") && key instanceof PrivateKey) {
|
||||
var bytes = key.getEncoded();
|
||||
try {
|
||||
return fromPKCS8(bytes);
|
||||
} catch (InvalidKeySpecException e) {
|
||||
throw new InvalidKeyException("Invalid PKCS#8 key", e);
|
||||
} finally {
|
||||
Arrays.fill(bytes, (byte) 0);
|
||||
}
|
||||
} else if (format.equalsIgnoreCase("X.509") && key instanceof PublicKey) {
|
||||
try {
|
||||
return fromX509(key.getEncoded());
|
||||
} catch (InvalidKeySpecException e) {
|
||||
throw new InvalidKeyException("Invalid X.509 key", e);
|
||||
}
|
||||
} else {
|
||||
throw new InvalidKeyException("Unsupported key format: " + key.getFormat());
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -0,0 +1,177 @@
|
||||
/*
|
||||
* Copyright (c) 2024, Oracle and/or its affiliates. All rights reserved.
|
||||
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
|
||||
*
|
||||
* This code is free software; you can redistribute it and/or modify it
|
||||
* under the terms of the GNU General Public License version 2 only, as
|
||||
* published by the Free Software Foundation. Oracle designates this
|
||||
* particular file as subject to the "Classpath" exception as provided
|
||||
* by Oracle in the LICENSE file that accompanied this code.
|
||||
*
|
||||
* This code is distributed in the hope that it will be useful, but WITHOUT
|
||||
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
|
||||
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
|
||||
* version 2 for more details (a copy is included in the LICENSE file that
|
||||
* accompanied this code).
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License version
|
||||
* 2 along with this work; if not, write to the Free Software Foundation,
|
||||
* Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
|
||||
*
|
||||
* Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
|
||||
* or visit www.oracle.com if you need additional information or have any
|
||||
* questions.
|
||||
*/
|
||||
|
||||
package sun.security.provider;
|
||||
|
||||
import sun.security.pkcs.NamedPKCS8Key;
|
||||
import sun.security.x509.NamedX509Key;
|
||||
|
||||
import java.security.InvalidAlgorithmParameterException;
|
||||
import java.security.InvalidParameterException;
|
||||
import java.security.KeyPair;
|
||||
import java.security.KeyPairGeneratorSpi;
|
||||
import java.security.ProviderException;
|
||||
import java.security.SecureRandom;
|
||||
import java.security.spec.AlgorithmParameterSpec;
|
||||
import java.security.spec.NamedParameterSpec;
|
||||
import java.util.Objects;
|
||||
|
||||
/// A base class for all `KeyPairGenerator` implementations that can be
|
||||
/// configured with a named parameter set.
|
||||
///
|
||||
/// Together with [NamedKeyFactory], [NamedKEM], and [NamedSignature], these
|
||||
/// classes form a compact framework designed to support any public key
|
||||
/// algorithm standardized with named parameter sets. In this scenario,
|
||||
/// the algorithm name is the "family name" and each standardized parameter
|
||||
/// set has a "parameter set name". Implementations of these classes are able
|
||||
/// to instantiate a `KeyPairGenerator`, `KeyFactory`, or `KEM` or `Signature`
|
||||
/// object using either the family name or a parameter set name. All keys used
|
||||
/// in this context will be of the type [NamedPKCS8Key] or [NamedX509Key],
|
||||
/// with `getAlgorithm` returning the family name, and `getParams` returning
|
||||
/// the parameter set name as a [NamedParameterSpec] object.
|
||||
///
|
||||
/// An implementation must include a zero-argument public constructor that
|
||||
/// calls `super(fname, pnames)`, where `fname` is the family name of the
|
||||
/// algorithm and `pnames` are its supported parameter set names. `pnames`
|
||||
/// must contain at least one element. For an implementation of
|
||||
/// `NamedKeyPairGenerator`, the first element becomes its default parameter
|
||||
/// set, i.e. the parameter set to be used in key pair generation unless
|
||||
/// [#initialize(AlgorithmParameterSpec, java.security.SecureRandom)]
|
||||
/// is called on a different parameter set.
|
||||
///
|
||||
/// An implementation must implement all abstract methods. For all these
|
||||
/// methods, the implementation must relinquish any "ownership" of any input
|
||||
/// and output array argument. Precisely, the implementation must not retain
|
||||
/// any reference to a returning array so that it won't be able to modify its
|
||||
/// content later. Similarly, the implementation must not modify any input
|
||||
/// array argument and must not retain any reference to an input array argument
|
||||
/// after the call.
|
||||
///
|
||||
/// Also, an implementation must not keep any extra copy of a private key.
|
||||
/// For key generation, the only copy is the one returned in the
|
||||
/// [#implGenerateKeyPair] call. For all other methods, it must not make
|
||||
/// a copy of the input private key. A `KEM` implementation also must not
|
||||
/// keep a copy of the shared secret key, no matter if it's an encapsulator
|
||||
/// or a decapsulator. Only the code that owns these sensitive data can
|
||||
/// choose to perform cleanup when it determines they are no longer needed.
|
||||
///
|
||||
/// The `NamedSignature` and `NamedKEM` classes provide `implCheckPublicKey`
|
||||
/// and `implCheckPrivateKey` methods that allow an implementation to validate
|
||||
/// a key before using it. An implementation may return a parsed key in
|
||||
/// a local type, and this parsed key will be passed to an operational method
|
||||
/// (For example, `implSign`) later. An implementation must not retain
|
||||
/// a reference of the parsed key.
|
||||
///
|
||||
/// When constructing a [NamedX509Key] or [NamedPKCS8Key] object from raw key
|
||||
/// bytes, the key bytes are directly referenced within the object, so the
|
||||
/// caller must not modify them afterward. Similarly, the key's `getRawBytes`
|
||||
/// method returns direct references to the underlying raw key bytes, meaning
|
||||
/// the caller must not alter the contents of the returned value.
|
||||
///
|
||||
/// Together, these measures ensure the classes are as efficient as possible,
|
||||
/// preventing unnecessary array cloning and potential data leaks. While these
|
||||
/// classes should not be considered immutable, strictly adhering to the rules
|
||||
/// above will ensure data integrity is maintained.
|
||||
///
|
||||
/// Note: A limitation of `NamedKeyPairGenerator` and `NamedKeyFactory` is
|
||||
/// that the keys generated by their implementations will always be of type
|
||||
/// `NamedX509Key` or `NamedPKCS8Key`. Existing implementations of algorithms
|
||||
/// like EdDSA and XDH have been generating keys implementing `EdECKey` or
|
||||
/// `XECKey` interfaces, and they are not rewritten with this framework.
|
||||
/// `NamedParameterSpec` fields not implemented with this framework include
|
||||
/// Ed25519, Ed448, X25519, and X448.
|
||||
public abstract class NamedKeyPairGenerator extends KeyPairGeneratorSpi {
|
||||
|
||||
private final String fname; // family name
|
||||
private final String[] pnames; // allowed parameter set name (at least one)
|
||||
|
||||
protected String name; // init as
|
||||
private SecureRandom secureRandom;
|
||||
|
||||
/// Creates a new `NamedKeyPairGenerator` object.
|
||||
///
|
||||
/// @param fname the family name
|
||||
/// @param pnames supported parameter set names, at least one is needed.
|
||||
/// If multiple, the first one becomes the default parameter set name.
|
||||
protected NamedKeyPairGenerator(String fname, String... pnames) {
|
||||
if (fname == null) {
|
||||
throw new AssertionError("fname cannot be null");
|
||||
}
|
||||
if (pnames == null || pnames.length == 0) {
|
||||
throw new AssertionError("pnames cannot be null or empty");
|
||||
}
|
||||
this.fname = fname;
|
||||
this.pnames = pnames;
|
||||
}
|
||||
|
||||
private String checkName(String name) throws InvalidAlgorithmParameterException {
|
||||
for (var pname : pnames) {
|
||||
if (pname.equalsIgnoreCase(name)) {
|
||||
// return the stored standard name
|
||||
return pname;
|
||||
}
|
||||
}
|
||||
throw new InvalidAlgorithmParameterException(
|
||||
"Unsupported parameter set name: " + name);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void initialize(AlgorithmParameterSpec params, SecureRandom random)
|
||||
throws InvalidAlgorithmParameterException {
|
||||
if (params instanceof NamedParameterSpec spec) {
|
||||
name = checkName(spec.getName());
|
||||
} else {
|
||||
throw new InvalidAlgorithmParameterException(
|
||||
"Unsupported AlgorithmParameterSpec: " + params);
|
||||
}
|
||||
this.secureRandom = random;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void initialize(int keysize, SecureRandom random) {
|
||||
if (keysize != -1) {
|
||||
// User can call initialize(-1, sr) to provide a SecureRandom
|
||||
// without touching the parameter set currently used
|
||||
throw new InvalidParameterException("keysize not supported");
|
||||
}
|
||||
this.secureRandom = random;
|
||||
}
|
||||
|
||||
@Override
|
||||
public KeyPair generateKeyPair() {
|
||||
String pname = name != null ? name : pnames[0];
|
||||
var keys = implGenerateKeyPair(pname, secureRandom);
|
||||
return new KeyPair(new NamedX509Key(fname, pname, keys[0]),
|
||||
new NamedPKCS8Key(fname, pname, keys[1]));
|
||||
}
|
||||
|
||||
/// User-defined key pair generator.
|
||||
///
|
||||
/// @param pname parameter set name
|
||||
/// @param sr `SecureRandom` object, `null` if not initialized
|
||||
/// @return public key and private key (in this order) in raw bytes
|
||||
/// @throws ProviderException if there is an internal error
|
||||
protected abstract byte[][] implGenerateKeyPair(String pname, SecureRandom sr);
|
||||
}
|
||||
@ -0,0 +1,222 @@
|
||||
/*
|
||||
* Copyright (c) 2024, Oracle and/or its affiliates. All rights reserved.
|
||||
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
|
||||
*
|
||||
* This code is free software; you can redistribute it and/or modify it
|
||||
* under the terms of the GNU General Public License version 2 only, as
|
||||
* published by the Free Software Foundation. Oracle designates this
|
||||
* particular file as subject to the "Classpath" exception as provided
|
||||
* by Oracle in the LICENSE file that accompanied this code.
|
||||
*
|
||||
* This code is distributed in the hope that it will be useful, but WITHOUT
|
||||
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
|
||||
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
|
||||
* version 2 for more details (a copy is included in the LICENSE file that
|
||||
* accompanied this code).
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License version
|
||||
* 2 along with this work; if not, write to the Free Software Foundation,
|
||||
* Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
|
||||
*
|
||||
* Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
|
||||
* or visit www.oracle.com if you need additional information or have any
|
||||
* questions.
|
||||
*/
|
||||
|
||||
package sun.security.provider;
|
||||
|
||||
import sun.security.pkcs.NamedPKCS8Key;
|
||||
import sun.security.x509.NamedX509Key;
|
||||
|
||||
import java.io.ByteArrayOutputStream;
|
||||
import java.security.AlgorithmParameters;
|
||||
import java.security.InvalidAlgorithmParameterException;
|
||||
import java.security.InvalidKeyException;
|
||||
import java.security.InvalidParameterException;
|
||||
import java.security.PrivateKey;
|
||||
import java.security.ProviderException;
|
||||
import java.security.PublicKey;
|
||||
import java.security.SecureRandom;
|
||||
import java.security.SignatureException;
|
||||
import java.security.SignatureSpi;
|
||||
import java.security.spec.AlgorithmParameterSpec;
|
||||
import java.util.Objects;
|
||||
|
||||
/// A base class for all `Signature` implementations that can be
|
||||
/// configured with a named parameter set. See [NamedKeyPairGenerator]
|
||||
/// for more details.
|
||||
///
|
||||
/// This class does not work with preHash signatures.
|
||||
public abstract class NamedSignature extends SignatureSpi {
|
||||
|
||||
private final String fname; // family name
|
||||
private final String[] pnames; // allowed parameter set name (at least one)
|
||||
|
||||
private final ByteArrayOutputStream bout = new ByteArrayOutputStream();
|
||||
|
||||
// init with...
|
||||
private String name;
|
||||
private byte[] secKey;
|
||||
private byte[] pubKey;
|
||||
|
||||
private Object sk2;
|
||||
private Object pk2;
|
||||
|
||||
/// Creates a new `NamedSignature` object.
|
||||
///
|
||||
/// @param fname the family name
|
||||
/// @param pnames the standard parameter set names, at least one is needed.
|
||||
protected NamedSignature(String fname, String... pnames) {
|
||||
if (fname == null) {
|
||||
throw new AssertionError("fname cannot be null");
|
||||
}
|
||||
if (pnames == null || pnames.length == 0) {
|
||||
throw new AssertionError("pnames cannot be null or empty");
|
||||
}
|
||||
this.fname = fname;
|
||||
this.pnames = pnames;
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void engineInitVerify(PublicKey publicKey) throws InvalidKeyException {
|
||||
// translate also check the key
|
||||
var nk = (NamedX509Key) new NamedKeyFactory(fname, pnames)
|
||||
.engineTranslateKey(publicKey);
|
||||
name = nk.getParams().getName();
|
||||
pubKey = nk.getRawBytes();
|
||||
pk2 = implCheckPublicKey(name, pubKey);
|
||||
secKey = null;
|
||||
bout.reset();
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void engineInitSign(PrivateKey privateKey) throws InvalidKeyException {
|
||||
// translate also check the key
|
||||
var nk = (NamedPKCS8Key) new NamedKeyFactory(fname, pnames)
|
||||
.engineTranslateKey(privateKey);
|
||||
name = nk.getParams().getName();
|
||||
secKey = nk.getRawBytes();
|
||||
sk2 = implCheckPrivateKey(name, secKey);
|
||||
pubKey = null;
|
||||
bout.reset();
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void engineUpdate(byte b) throws SignatureException {
|
||||
bout.write(b);
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void engineUpdate(byte[] b, int off, int len) throws SignatureException {
|
||||
bout.write(b, off, len);
|
||||
}
|
||||
|
||||
@Override
|
||||
protected byte[] engineSign() throws SignatureException {
|
||||
if (secKey != null) {
|
||||
var msg = bout.toByteArray();
|
||||
bout.reset();
|
||||
return implSign(name, secKey, sk2, msg, appRandom);
|
||||
} else {
|
||||
throw new SignatureException("No private key");
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
protected boolean engineVerify(byte[] sig) throws SignatureException {
|
||||
if (pubKey != null) {
|
||||
var msg = bout.toByteArray();
|
||||
bout.reset();
|
||||
return implVerify(name, pubKey, pk2, msg, sig);
|
||||
} else {
|
||||
throw new SignatureException("No public key");
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
@SuppressWarnings("deprecation")
|
||||
protected void engineSetParameter(String param, Object value)
|
||||
throws InvalidParameterException {
|
||||
throw new InvalidParameterException("setParameter() not supported");
|
||||
}
|
||||
|
||||
@Override
|
||||
@SuppressWarnings("deprecation")
|
||||
protected Object engineGetParameter(String param) throws InvalidParameterException {
|
||||
throw new InvalidParameterException("getParameter() not supported");
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void engineSetParameter(AlgorithmParameterSpec params)
|
||||
throws InvalidAlgorithmParameterException {
|
||||
if (params != null) {
|
||||
throw new InvalidAlgorithmParameterException(
|
||||
"The " + fname + " algorithm does not take any parameters");
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
protected AlgorithmParameters engineGetParameters() {
|
||||
return null;
|
||||
}
|
||||
|
||||
/// User-defined sign function.
|
||||
///
|
||||
/// @param name parameter name
|
||||
/// @param sk private key in raw bytes
|
||||
/// @param sk2 parsed private key, `null` if none. See [#implCheckPrivateKey].
|
||||
/// @param msg the message
|
||||
/// @param sr SecureRandom object, `null` if not initialized
|
||||
/// @return the signature
|
||||
/// @throws ProviderException if there is an internal error
|
||||
/// @throws SignatureException if there is another error
|
||||
protected abstract byte[] implSign(String name, byte[] sk, Object sk2,
|
||||
byte[] msg, SecureRandom sr) throws SignatureException;
|
||||
|
||||
/// User-defined verify function.
|
||||
///
|
||||
/// @param name parameter name
|
||||
/// @param pk public key in raw bytes
|
||||
/// @param pk2 parsed public key, `null` if none. See [#implCheckPublicKey].
|
||||
/// @param msg the message
|
||||
/// @param sig the signature
|
||||
/// @return true if verified
|
||||
/// @throws ProviderException if there is an internal error
|
||||
/// @throws SignatureException if there is another error
|
||||
protected abstract boolean implVerify(String name, byte[] pk, Object pk2,
|
||||
byte[] msg, byte[] sig) throws SignatureException;
|
||||
|
||||
/// User-defined function to validate a public key.
|
||||
///
|
||||
/// This method will be called in `initVerify`. This gives the provider a chance to
|
||||
/// reject the key so an `InvalidKeyException` can be thrown earlier.
|
||||
/// An implementation can optionally return a "parsed key" as an `Object` value.
|
||||
/// This object will be passed into the [#implVerify] method along with the raw key.
|
||||
///
|
||||
/// The default implementation returns `null`.
|
||||
///
|
||||
/// @param name parameter name
|
||||
/// @param pk public key in raw bytes
|
||||
/// @return a parsed key, `null` if none.
|
||||
/// @throws InvalidKeyException if the key is invalid
|
||||
protected Object implCheckPublicKey(String name, byte[] pk) throws InvalidKeyException {
|
||||
return null;
|
||||
}
|
||||
|
||||
/// User-defined function to validate a private key.
|
||||
///
|
||||
/// This method will be called in `initSign`. This gives the provider a chance to
|
||||
/// reject the key so an `InvalidKeyException` can be thrown earlier.
|
||||
/// An implementation can optionally return a "parsed key" as an `Object` value.
|
||||
/// This object will be passed into the [#implSign] method along with the raw key.
|
||||
///
|
||||
/// The default implementation returns `null`.
|
||||
///
|
||||
/// @param name parameter name
|
||||
/// @param sk private key in raw bytes
|
||||
/// @return a parsed key, `null` if none.
|
||||
/// @throws InvalidKeyException if the key is invalid
|
||||
protected Object implCheckPrivateKey(String name, byte[] sk) throws InvalidKeyException {
|
||||
return null;
|
||||
}
|
||||
}
|
||||
@ -184,13 +184,13 @@ public final class KeyUtil {
|
||||
*/
|
||||
public static final String fullDisplayAlgName(Key key) {
|
||||
String result = key.getAlgorithm();
|
||||
if (key instanceof ECKey) {
|
||||
ECParameterSpec paramSpec = ((ECKey) key).getParams();
|
||||
if (key instanceof AsymmetricKey ak) {
|
||||
AlgorithmParameterSpec paramSpec = ak.getParams();
|
||||
if (paramSpec instanceof NamedCurve nc) {
|
||||
result += " (" + nc.getNameAndAliases()[0] + ")";
|
||||
} else if (paramSpec instanceof NamedParameterSpec nps) {
|
||||
result = nps.getName();
|
||||
}
|
||||
} else if (key instanceof EdECKey) {
|
||||
result = ((EdECKey) key).getParams().getName();
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
@ -33,6 +33,7 @@ import java.security.interfaces.RSAKey;
|
||||
import java.security.spec.*;
|
||||
import java.util.Locale;
|
||||
|
||||
import sun.security.pkcs.NamedPKCS8Key;
|
||||
import sun.security.rsa.RSAUtil;
|
||||
import jdk.internal.access.SharedSecrets;
|
||||
import sun.security.x509.AlgorithmId;
|
||||
@ -274,7 +275,7 @@ public class SignatureUtil {
|
||||
return signatureAlgorithm.substring(0, with);
|
||||
} else {
|
||||
throw new IllegalArgumentException(
|
||||
"Unknown algorithm: " + signatureAlgorithm);
|
||||
"Cannot extract digest algorithm from " + signatureAlgorithm);
|
||||
}
|
||||
}
|
||||
|
||||
@ -390,8 +391,8 @@ public class SignatureUtil {
|
||||
public static AlgorithmId fromSignature(Signature sigEngine, PrivateKey key)
|
||||
throws SignatureException {
|
||||
try {
|
||||
if (key instanceof EdECKey) {
|
||||
return AlgorithmId.get(((EdECKey) key).getParams().getName());
|
||||
if (key.getParams() instanceof NamedParameterSpec nps) {
|
||||
return AlgorithmId.get(nps.getName());
|
||||
}
|
||||
|
||||
AlgorithmParameters params = null;
|
||||
@ -431,6 +432,14 @@ public class SignatureUtil {
|
||||
public static void checkKeyAndSigAlgMatch(PrivateKey key, String sAlg) {
|
||||
String kAlg = key.getAlgorithm().toUpperCase(Locale.ENGLISH);
|
||||
sAlg = checkName(sAlg);
|
||||
if (key instanceof NamedPKCS8Key n8k) {
|
||||
if (!sAlg.equalsIgnoreCase(n8k.getAlgorithm())
|
||||
&& !sAlg.equalsIgnoreCase(n8k.getParams().getName())) {
|
||||
throw new IllegalArgumentException(
|
||||
"key algorithm not compatible with signature algorithm");
|
||||
}
|
||||
return;
|
||||
}
|
||||
switch (sAlg) {
|
||||
case "RSASSA-PSS" -> {
|
||||
if (!kAlg.equals("RSASSA-PSS")
|
||||
@ -495,8 +504,10 @@ public class SignatureUtil {
|
||||
case "EDDSA" -> k instanceof EdECPrivateKey
|
||||
? ((EdECPrivateKey) k).getParams().getName()
|
||||
: kAlg;
|
||||
default -> kAlg; // All modern signature algorithms,
|
||||
// RSASSA-PSS, ED25519, ED448, HSS/LMS, etc
|
||||
default -> kAlg.contains("KEM") ? null : kAlg;
|
||||
// All modern signature algorithms use the same name across
|
||||
// key algorithms and signature algorithms, for example,
|
||||
// RSASSA-PSS, ED25519, ED448, HSS/LMS, etc
|
||||
};
|
||||
}
|
||||
|
||||
|
||||
120
src/java.base/share/classes/sun/security/x509/NamedX509Key.java
Normal file
120
src/java.base/share/classes/sun/security/x509/NamedX509Key.java
Normal file
@ -0,0 +1,120 @@
|
||||
/*
|
||||
* Copyright (c) 2024, Oracle and/or its affiliates. All rights reserved.
|
||||
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
|
||||
*
|
||||
* This code is free software; you can redistribute it and/or modify it
|
||||
* under the terms of the GNU General Public License version 2 only, as
|
||||
* published by the Free Software Foundation. Oracle designates this
|
||||
* particular file as subject to the "Classpath" exception as provided
|
||||
* by Oracle in the LICENSE file that accompanied this code.
|
||||
*
|
||||
* This code is distributed in the hope that it will be useful, but WITHOUT
|
||||
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
|
||||
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
|
||||
* version 2 for more details (a copy is included in the LICENSE file that
|
||||
* accompanied this code).
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License version
|
||||
* 2 along with this work; if not, write to the Free Software Foundation,
|
||||
* Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
|
||||
*
|
||||
* Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
|
||||
* or visit www.oracle.com if you need additional information or have any
|
||||
* questions.
|
||||
*/
|
||||
|
||||
package sun.security.x509;
|
||||
|
||||
import sun.security.util.BitArray;
|
||||
|
||||
import java.io.IOException;
|
||||
import java.io.InvalidObjectException;
|
||||
import java.io.ObjectInputStream;
|
||||
import java.io.Serial;
|
||||
import java.security.InvalidKeyException;
|
||||
import java.security.KeyRep;
|
||||
import java.security.NoSuchAlgorithmException;
|
||||
import java.security.ProviderException;
|
||||
import java.security.spec.NamedParameterSpec;
|
||||
|
||||
/// Represents a public key from an algorithm family that is specialized
|
||||
/// with a named parameter set.
|
||||
///
|
||||
/// This key is generated by either a [sun.security.provider.NamedKeyPairGenerator]
|
||||
/// or [sun.security.provider.NamedKeyFactory]. Its [#getAlgorithm] method
|
||||
/// returns the algorithm family name, while its [#getParams()] method returns
|
||||
/// the parameter set name as a [NamedParameterSpec] object. The algorithm
|
||||
/// identifier in the X.509 encoding of the key is always a single OID derived
|
||||
/// from the parameter set name.
|
||||
///
|
||||
/// @see sun.security.provider.NamedKeyPairGenerator
|
||||
public final class NamedX509Key extends X509Key {
|
||||
@Serial
|
||||
private static final long serialVersionUID = 1L;
|
||||
|
||||
private final String fname;
|
||||
private final transient NamedParameterSpec paramSpec;
|
||||
private final byte[] rawBytes;
|
||||
|
||||
/// Ctor from family name, parameter set name, raw key bytes.
|
||||
/// Key bytes won't be cloned, caller must relinquish ownership
|
||||
public NamedX509Key(String fname, String pname, byte[] rawBytes) {
|
||||
this.fname = fname;
|
||||
this.paramSpec = new NamedParameterSpec(pname);
|
||||
try {
|
||||
this.algid = AlgorithmId.get(pname);
|
||||
} catch (NoSuchAlgorithmException e) {
|
||||
throw new ProviderException(e);
|
||||
}
|
||||
this.rawBytes = rawBytes;
|
||||
|
||||
setKey(new BitArray(rawBytes.length * 8, rawBytes));
|
||||
}
|
||||
|
||||
/// Ctor from family name, and X.509 bytes
|
||||
public NamedX509Key(String fname, byte[] encoded) throws InvalidKeyException {
|
||||
this.fname = fname;
|
||||
decode(encoded);
|
||||
this.paramSpec = new NamedParameterSpec(algid.getName());
|
||||
if (algid.encodedParams != null) {
|
||||
throw new InvalidKeyException("algorithm identifier has params");
|
||||
}
|
||||
this.rawBytes = getKey().toByteArray();
|
||||
}
|
||||
|
||||
@Override
|
||||
public String toString() {
|
||||
// Do not modify: this can be used by earlier JDKs that
|
||||
// do not have the getParams() method
|
||||
return paramSpec.getName() + " public key";
|
||||
}
|
||||
|
||||
/// Returns the reference to the internal key. Caller must not modify
|
||||
/// the content or keep a reference.
|
||||
public byte[] getRawBytes() {
|
||||
return rawBytes;
|
||||
}
|
||||
|
||||
@Override
|
||||
public NamedParameterSpec getParams() {
|
||||
return paramSpec;
|
||||
}
|
||||
|
||||
@Override
|
||||
public String getAlgorithm() {
|
||||
return fname;
|
||||
}
|
||||
|
||||
@java.io.Serial
|
||||
private Object writeReplace() throws java.io.ObjectStreamException {
|
||||
return new KeyRep(KeyRep.Type.PUBLIC, getAlgorithm(), getFormat(),
|
||||
getEncoded());
|
||||
}
|
||||
|
||||
@java.io.Serial
|
||||
private void readObject(ObjectInputStream stream)
|
||||
throws IOException, ClassNotFoundException {
|
||||
throw new InvalidObjectException(
|
||||
"NamedX509Key keys are not directly deserializable");
|
||||
}
|
||||
}
|
||||
239
test/jdk/sun/security/provider/NamedEdDSA.java
Normal file
239
test/jdk/sun/security/provider/NamedEdDSA.java
Normal file
@ -0,0 +1,239 @@
|
||||
/*
|
||||
* Copyright (c) 2024, Oracle and/or its affiliates. All rights reserved.
|
||||
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
|
||||
*
|
||||
* This code is free software; you can redistribute it and/or modify it
|
||||
* under the terms of the GNU General Public License version 2 only, as
|
||||
* published by the Free Software Foundation.
|
||||
*
|
||||
* This code is distributed in the hope that it will be useful, but WITHOUT
|
||||
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
|
||||
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
|
||||
* version 2 for more details (a copy is included in the LICENSE file that
|
||||
* accompanied this code).
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License version
|
||||
* 2 along with this work; if not, write to the Free Software Foundation,
|
||||
* Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
|
||||
*
|
||||
* Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
|
||||
* or visit www.oracle.com if you need additional information or have any
|
||||
* questions.
|
||||
*/
|
||||
|
||||
/*
|
||||
* @test
|
||||
* @bug 8340327
|
||||
* @modules java.base/sun.security.ec.ed
|
||||
* java.base/sun.security.ec.point
|
||||
* java.base/sun.security.jca
|
||||
* java.base/sun.security.provider
|
||||
* @library /test/lib
|
||||
*/
|
||||
|
||||
import jdk.test.lib.Asserts;
|
||||
import jdk.test.lib.Utils;
|
||||
import sun.security.ec.ed.EdDSAOperations;
|
||||
import sun.security.ec.ed.EdDSAParameters;
|
||||
import sun.security.ec.point.AffinePoint;
|
||||
import sun.security.jca.JCAUtil;
|
||||
import sun.security.provider.NamedKeyFactory;
|
||||
import sun.security.provider.NamedKeyPairGenerator;
|
||||
import sun.security.provider.NamedSignature;
|
||||
|
||||
import java.security.*;
|
||||
import java.security.spec.EdDSAParameterSpec;
|
||||
import java.security.spec.NamedParameterSpec;
|
||||
import java.util.Arrays;
|
||||
import java.util.List;
|
||||
|
||||
public class NamedEdDSA {
|
||||
|
||||
public static class ProviderImpl extends Provider {
|
||||
public ProviderImpl() {
|
||||
super("Named", "0", "");
|
||||
put("KeyPairGenerator.EdDSA", EdDSAKeyPairGenerator.class.getName());
|
||||
put("KeyPairGenerator.Ed25519", EdDSAKeyPairGenerator.Ed25519.class.getName());
|
||||
put("KeyPairGenerator.Ed448", EdDSAKeyPairGenerator.Ed448.class.getName());
|
||||
put("KeyFactory.EdDSA", EdDSAKeyFactory.class.getName());
|
||||
put("KeyFactory.Ed25519", EdDSAKeyFactory.Ed25519.class.getName());
|
||||
put("KeyFactory.Ed448", EdDSAKeyFactory.Ed448.class.getName());
|
||||
put("Signature.EdDSA", EdDSASignature.class.getName());
|
||||
put("Signature.Ed25519", EdDSASignature.Ed25519.class.getName());
|
||||
put("Signature.Ed448", EdDSASignature.Ed448.class.getName());
|
||||
}
|
||||
}
|
||||
|
||||
public static class EdDSASignature extends NamedSignature {
|
||||
public EdDSASignature() {
|
||||
super("EdDSA", "Ed25519", "Ed448");
|
||||
}
|
||||
|
||||
protected EdDSASignature(String pname) {
|
||||
super("EdDSA", pname);
|
||||
}
|
||||
|
||||
public static class Ed25519 extends EdDSASignature {
|
||||
public Ed25519() {
|
||||
super("Ed25519");
|
||||
}
|
||||
}
|
||||
|
||||
public static class Ed448 extends EdDSASignature {
|
||||
public Ed448() {
|
||||
super("Ed448");
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public byte[] implSign(String name, byte[] sk, Object sk2, byte[] msg, SecureRandom sr) throws SignatureException {
|
||||
return getOps(name).sign(plain, sk, msg);
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean implVerify(String name, byte[] pk, Object pk2, byte[] msg, byte[] sig) throws SignatureException {
|
||||
return getOps(name).verify(plain, (AffinePoint) pk2, pk, msg, sig);
|
||||
}
|
||||
|
||||
@Override
|
||||
public Object implCheckPublicKey(String name, byte[] pk) throws InvalidKeyException {
|
||||
return getOps(name).decodeAffinePoint(InvalidKeyException::new, pk);
|
||||
}
|
||||
}
|
||||
|
||||
public static class EdDSAKeyFactory extends NamedKeyFactory {
|
||||
public EdDSAKeyFactory() {
|
||||
super("EdDSA", "Ed25519", "Ed448");
|
||||
}
|
||||
|
||||
protected EdDSAKeyFactory(String pname) {
|
||||
super("EdDSA", pname);
|
||||
}
|
||||
|
||||
public static class Ed25519 extends EdDSAKeyFactory {
|
||||
public Ed25519() {
|
||||
super("Ed25519");
|
||||
}
|
||||
}
|
||||
|
||||
public static class Ed448 extends EdDSAKeyFactory {
|
||||
public Ed448() {
|
||||
super("Ed448");
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public static class EdDSAKeyPairGenerator extends NamedKeyPairGenerator {
|
||||
public EdDSAKeyPairGenerator() {
|
||||
super("EdDSA", "Ed25519", "Ed448");
|
||||
}
|
||||
|
||||
protected EdDSAKeyPairGenerator(String pname) {
|
||||
super("EdDSA", pname);
|
||||
}
|
||||
|
||||
public static class Ed25519 extends EdDSAKeyPairGenerator {
|
||||
public Ed25519() {
|
||||
super("Ed25519");
|
||||
}
|
||||
}
|
||||
|
||||
public static class Ed448 extends EdDSAKeyPairGenerator {
|
||||
public Ed448() {
|
||||
super("Ed448");
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public byte[][] implGenerateKeyPair(String pname, SecureRandom sr) {
|
||||
sr = sr == null ? JCAUtil.getDefSecureRandom() : sr;
|
||||
var op = getOps(pname);
|
||||
var sk = op.generatePrivate(sr);
|
||||
var point = op.computePublic(sk);
|
||||
byte[] encodedPoint = point.getY().toByteArray();
|
||||
reverse(encodedPoint);
|
||||
// array may be too large or too small, depending on the value
|
||||
encodedPoint = Arrays.copyOf(encodedPoint, op.getParameters().getKeyLength());
|
||||
// set the high-order bit of the encoded point
|
||||
byte msb = (byte) (point.isXOdd() ? 0x80 : 0);
|
||||
encodedPoint[encodedPoint.length - 1] |= msb;
|
||||
return new byte[][] { encodedPoint, sk };
|
||||
}
|
||||
|
||||
private static void swap(byte[] arr, int i, int j) {
|
||||
byte tmp = arr[i];
|
||||
arr[i] = arr[j];
|
||||
arr[j] = tmp;
|
||||
}
|
||||
|
||||
private static void reverse(byte [] arr) {
|
||||
int i = 0;
|
||||
int j = arr.length - 1;
|
||||
|
||||
while (i < j) {
|
||||
swap(arr, i, j);
|
||||
i++;
|
||||
j--;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private static EdDSAOperations getOps(String pname) {
|
||||
var op = switch (pname) {
|
||||
case "Ed25519" -> e2;
|
||||
case "Ed448" -> e4;
|
||||
default -> throw new AssertionError("unknown pname " + pname);
|
||||
};
|
||||
return op;
|
||||
}
|
||||
|
||||
static final EdDSAParameterSpec plain = new EdDSAParameterSpec(false);
|
||||
static final EdDSAOperations e2, e4;
|
||||
static {
|
||||
try {
|
||||
e2 = new EdDSAOperations(EdDSAParameters.getBySize(AssertionError::new, 255));
|
||||
e4 = new EdDSAOperations(EdDSAParameters.getBySize(AssertionError::new, 448));
|
||||
} catch (Exception e) {
|
||||
throw new AssertionError(e);
|
||||
}
|
||||
}
|
||||
|
||||
public static void main(String[] args) throws Exception {
|
||||
var ps = List.of(new ProviderImpl(), Security.getProvider("SunEC"));
|
||||
for (var p1 : ps) {
|
||||
for (var p2 : ps) {
|
||||
for (var p3 : ps) {
|
||||
test(p1, p2, p3);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
static void test(Provider p1, Provider p2, Provider p3) throws Exception {
|
||||
System.out.println(p1.getName() + " " + p2.getName() + " " + p3.getName());
|
||||
var g = KeyPairGenerator.getInstance("EdDSA", p1);
|
||||
g.initialize(NamedParameterSpec.ED448);
|
||||
var kp = g.generateKeyPair();
|
||||
var s1 = Signature.getInstance("EdDSA", p2);
|
||||
var s2 = Signature.getInstance("EdDSA", p3);
|
||||
var f1 = KeyFactory.getInstance("EdDSA", p2);
|
||||
var f2 = KeyFactory.getInstance("EdDSA", p3);
|
||||
var sk = (PrivateKey) f1.translateKey(kp.getPrivate());
|
||||
var pk = (PublicKey) f2.translateKey(kp.getPublic());
|
||||
// sign and verify twice to make sure the key is intact
|
||||
s1.initSign(sk);
|
||||
var sig1 = s1.sign();
|
||||
s1.initSign(sk);
|
||||
var sig2 = s1.sign();
|
||||
// EdDSA signing is deterministic
|
||||
Asserts.assertEqualsByteArray(sig1, sig2);
|
||||
s2.initVerify(pk);
|
||||
Asserts.assertTrue(s2.verify(sig1));
|
||||
s2.initVerify(pk);
|
||||
Asserts.assertTrue(s2.verify(sig2));
|
||||
// No parameters defined
|
||||
s1.setParameter(null);
|
||||
Utils.runAndCheckException(() -> s1.setParameter(NamedParameterSpec.ED448),
|
||||
InvalidAlgorithmParameterException.class);
|
||||
}
|
||||
}
|
||||
261
test/jdk/sun/security/provider/NamedKeyFactoryTest.java
Normal file
261
test/jdk/sun/security/provider/NamedKeyFactoryTest.java
Normal file
@ -0,0 +1,261 @@
|
||||
/*
|
||||
* Copyright (c) 2024, Oracle and/or its affiliates. All rights reserved.
|
||||
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
|
||||
*
|
||||
* This code is free software; you can redistribute it and/or modify it
|
||||
* under the terms of the GNU General Public License version 2 only, as
|
||||
* published by the Free Software Foundation.
|
||||
*
|
||||
* This code is distributed in the hope that it will be useful, but WITHOUT
|
||||
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
|
||||
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
|
||||
* version 2 for more details (a copy is included in the LICENSE file that
|
||||
* accompanied this code).
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License version
|
||||
* 2 along with this work; if not, write to the Free Software Foundation,
|
||||
* Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
|
||||
*
|
||||
* Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
|
||||
* or visit www.oracle.com if you need additional information or have any
|
||||
* questions.
|
||||
*/
|
||||
|
||||
/*
|
||||
* @test
|
||||
* @bug 8340327
|
||||
* @modules java.base/sun.security.x509
|
||||
* java.base/sun.security.pkcs
|
||||
* java.base/sun.security.provider
|
||||
* java.base/sun.security.util
|
||||
* @library /test/lib
|
||||
*/
|
||||
import jdk.test.lib.Asserts;
|
||||
import jdk.test.lib.Utils;
|
||||
import jdk.test.lib.security.SeededSecureRandom;
|
||||
import sun.security.pkcs.NamedPKCS8Key;
|
||||
import sun.security.provider.NamedKeyFactory;
|
||||
import sun.security.provider.NamedKeyPairGenerator;
|
||||
import sun.security.util.RawKeySpec;
|
||||
import sun.security.x509.NamedX509Key;
|
||||
|
||||
import java.security.*;
|
||||
import java.security.spec.*;
|
||||
|
||||
public class NamedKeyFactoryTest {
|
||||
|
||||
private static final SeededSecureRandom RAND = SeededSecureRandom.one();
|
||||
|
||||
public static void main(String[] args) throws Exception {
|
||||
Security.addProvider(new ProviderImpl());
|
||||
|
||||
var g = KeyPairGenerator.getInstance("sHA");
|
||||
var g2 = KeyPairGenerator.getInstance("ShA-256");
|
||||
var g5 = KeyPairGenerator.getInstance("SHa-512");
|
||||
var kf = KeyFactory.getInstance("ShA");
|
||||
var kf2 = KeyFactory.getInstance("Sha-256");
|
||||
var kf5 = KeyFactory.getInstance("Sha-512");
|
||||
|
||||
checkKeyPair(g.generateKeyPair(), "SHA", "SHA-256");
|
||||
checkKeyPair(g2.generateKeyPair(), "SHA", "SHA-256");
|
||||
checkKeyPair(g5.generateKeyPair(), "SHA", "SHA-512");
|
||||
|
||||
checkKeyPair(g.generateKeyPair(), "SHA", "SHA-256");
|
||||
checkKeyPair(g2.generateKeyPair(), "SHA", "SHA-256");
|
||||
checkKeyPair(g5.generateKeyPair(), "SHA", "SHA-512");
|
||||
|
||||
Utils.runAndCheckException(() -> g.initialize(NamedParameterSpec.ED448),
|
||||
InvalidAlgorithmParameterException.class); // wrong pname
|
||||
Utils.runAndCheckException(() -> g.initialize(new NamedParameterSpec("SHA-384")),
|
||||
InvalidAlgorithmParameterException.class); // wrong pname
|
||||
|
||||
Utils.runAndCheckException(() -> g5.initialize(new NamedParameterSpec("SHA-256")),
|
||||
InvalidAlgorithmParameterException.class); // diff pname
|
||||
g5.initialize(new NamedParameterSpec("SHA-512"));
|
||||
|
||||
g.initialize(new NamedParameterSpec("sHA-512"));
|
||||
checkKeyPair(g.generateKeyPair(), "SHA", "SHA-512");
|
||||
g.initialize(new NamedParameterSpec("ShA-256"));
|
||||
checkKeyPair(g.generateKeyPair(), "SHA", "SHA-256");
|
||||
|
||||
var pk = new NamedX509Key("sHa", "ShA-256", RAND.nBytes(2));
|
||||
var sk = new NamedPKCS8Key("sHa", "SHa-256", RAND.nBytes(2));
|
||||
checkKey(pk, "sHa", "ShA-256");
|
||||
checkKey(sk, "sHa", "SHa-256");
|
||||
|
||||
Asserts.assertEquals("X.509", pk.getFormat());
|
||||
Asserts.assertEquals("PKCS#8", sk.getFormat());
|
||||
|
||||
var pkSpec = kf.getKeySpec(pk, X509EncodedKeySpec.class);
|
||||
var skSpec = kf.getKeySpec(sk, PKCS8EncodedKeySpec.class);
|
||||
|
||||
kf2.getKeySpec(pk, X509EncodedKeySpec.class);
|
||||
kf2.getKeySpec(sk, PKCS8EncodedKeySpec.class);
|
||||
Utils.runAndCheckException(() -> kf5.getKeySpec(pk, X509EncodedKeySpec.class),
|
||||
InvalidKeySpecException.class); // wrong KF
|
||||
Utils.runAndCheckException(() -> kf5.getKeySpec(sk, PKCS8EncodedKeySpec.class),
|
||||
InvalidKeySpecException.class);
|
||||
Utils.runAndCheckException(() -> kf.getKeySpec(pk, PKCS8EncodedKeySpec.class),
|
||||
InvalidKeySpecException.class); // wrong KeySpec
|
||||
Utils.runAndCheckException(() -> kf.getKeySpec(sk, X509EncodedKeySpec.class),
|
||||
InvalidKeySpecException.class);
|
||||
|
||||
checkKey(kf.generatePublic(pkSpec), "SHA", "SHA-256");
|
||||
Utils.runAndCheckException(() -> kf.generatePrivate(pkSpec),
|
||||
InvalidKeySpecException.class);
|
||||
|
||||
checkKey(kf.generatePrivate(skSpec), "SHA", "SHA-256");
|
||||
Utils.runAndCheckException(() -> kf.generatePublic(skSpec),
|
||||
InvalidKeySpecException.class);
|
||||
|
||||
checkKey(kf2.generatePrivate(skSpec), "SHA", "SHA-256");
|
||||
checkKey(kf2.generatePublic(pkSpec), "SHA", "SHA-256");
|
||||
|
||||
Utils.runAndCheckException(() -> kf5.generatePublic(pkSpec),
|
||||
InvalidKeySpecException.class); // wrong KF
|
||||
Utils.runAndCheckException(() -> kf5.generatePublic(skSpec),
|
||||
InvalidKeySpecException.class);
|
||||
|
||||
// The private RawKeySpec and unnamed RAW EncodedKeySpec
|
||||
var prk = kf.getKeySpec(pk, RawKeySpec.class);
|
||||
Asserts.assertEqualsByteArray(prk.getKeyArr(), pk.getRawBytes());
|
||||
var prk2 = kf.getKeySpec(pk, EncodedKeySpec.class);
|
||||
Asserts.assertEquals("RAW", prk2.getFormat());
|
||||
Asserts.assertEqualsByteArray(prk.getKeyArr(), prk2.getEncoded());
|
||||
|
||||
Asserts.assertEqualsByteArray(kf2.generatePublic(prk).getEncoded(), pk.getEncoded());
|
||||
Utils.runAndCheckException(() -> kf.generatePublic(prk), InvalidKeySpecException.class); // no pname
|
||||
Asserts.assertEqualsByteArray(kf2.generatePublic(prk2).getEncoded(), pk.getEncoded());
|
||||
Utils.runAndCheckException(() -> kf.generatePublic(prk2), InvalidKeySpecException.class); // no pname
|
||||
|
||||
var srk = kf.getKeySpec(sk, RawKeySpec.class);
|
||||
Asserts.assertEqualsByteArray(srk.getKeyArr(), sk.getRawBytes());
|
||||
var srk2 = kf.getKeySpec(sk, EncodedKeySpec.class);
|
||||
Asserts.assertEquals("RAW", srk2.getFormat());
|
||||
Asserts.assertEqualsByteArray(srk2.getEncoded(), sk.getRawBytes());
|
||||
|
||||
Asserts.assertEqualsByteArray(kf2.generatePrivate(srk).getEncoded(), sk.getEncoded());
|
||||
Utils.runAndCheckException(() -> kf.generatePrivate(srk), InvalidKeySpecException.class); // no pname
|
||||
Asserts.assertEqualsByteArray(kf2.generatePrivate(srk2).getEncoded(), sk.getEncoded());
|
||||
Utils.runAndCheckException(() -> kf.generatePrivate(srk2), InvalidKeySpecException.class); // no pname
|
||||
|
||||
var pk1 = new PublicKey() {
|
||||
public String getAlgorithm() { return "SHA"; }
|
||||
public String getFormat() { return "RAW"; }
|
||||
public byte[] getEncoded() { return RAND.nBytes(2); }
|
||||
};
|
||||
var pk2 = new PublicKey() {
|
||||
public String getAlgorithm() { return "sHA-256"; }
|
||||
public String getFormat() { return "RAW"; }
|
||||
public byte[] getEncoded() { return RAND.nBytes(2); }
|
||||
};
|
||||
var pk3 = new PublicKey() {
|
||||
public String getAlgorithm() { return "SHA"; }
|
||||
public String getFormat() { return "RAW"; }
|
||||
public byte[] getEncoded() { return RAND.nBytes(2); }
|
||||
public AlgorithmParameterSpec getParams() { return new NamedParameterSpec("sHA-256"); }
|
||||
};
|
||||
|
||||
checkKey(kf2.translateKey(pk1), "SHA", "SHA-256");
|
||||
checkKey(kf.translateKey(pk2), "SHA", "SHA-256");
|
||||
checkKey(kf.translateKey(pk3), "SHA", "SHA-256");
|
||||
|
||||
Utils.runAndCheckException(() -> kf.translateKey(pk1), InvalidKeyException.class);
|
||||
Utils.runAndCheckException(() -> kf5.translateKey(pk2), InvalidKeyException.class);
|
||||
Utils.runAndCheckException(() -> kf5.translateKey(pk3), InvalidKeyException.class);
|
||||
|
||||
var sk1 = new PrivateKey() {
|
||||
public String getAlgorithm() { return "SHA"; }
|
||||
public String getFormat() { return "RAW"; }
|
||||
public byte[] getEncoded() { return RAND.nBytes(2); }
|
||||
};
|
||||
var sk2 = new PrivateKey() {
|
||||
public String getAlgorithm() { return "sHA-256"; }
|
||||
public String getFormat() { return "RAW"; }
|
||||
public byte[] getEncoded() { return RAND.nBytes(2); }
|
||||
};
|
||||
var sk3 = new PrivateKey() {
|
||||
public String getAlgorithm() { return "SHA"; }
|
||||
public String getFormat() { return "RAW"; }
|
||||
public byte[] getEncoded() { return RAND.nBytes(2); }
|
||||
public AlgorithmParameterSpec getParams() { return new NamedParameterSpec("sHA-256"); }
|
||||
};
|
||||
|
||||
checkKey(kf2.translateKey(sk1), "SHA", "SHA-256");
|
||||
checkKey(kf.translateKey(sk2), "SHA", "SHA-256");
|
||||
checkKey(kf.translateKey(sk3), "SHA", "SHA-256");
|
||||
|
||||
Utils.runAndCheckException(() -> kf.translateKey(sk1), InvalidKeyException.class);
|
||||
Utils.runAndCheckException(() -> kf5.translateKey(sk2), InvalidKeyException.class);
|
||||
Utils.runAndCheckException(() -> kf5.translateKey(sk3), InvalidKeyException.class);
|
||||
}
|
||||
|
||||
static void checkKeyPair(KeyPair kp, String algName, String toString) {
|
||||
checkKey(kp.getPrivate(), algName, toString);
|
||||
checkKey(kp.getPublic(), algName, toString);
|
||||
}
|
||||
|
||||
static void checkKey(Key k, String algName, String pname) {
|
||||
Asserts.assertEquals(algName, k.getAlgorithm());
|
||||
Asserts.assertTrue(k.toString().contains(pname));
|
||||
if (k instanceof AsymmetricKey ak && ak.getParams() instanceof NamedParameterSpec nps) {
|
||||
Asserts.assertEquals(pname, nps.getName());
|
||||
}
|
||||
}
|
||||
|
||||
// Provider
|
||||
|
||||
public static class ProviderImpl extends Provider {
|
||||
public ProviderImpl() {
|
||||
super("P", "1", "...");
|
||||
put("KeyFactory.SHA", KF.class.getName());
|
||||
put("KeyFactory.SHA-256", KF1.class.getName());
|
||||
put("KeyFactory.SHA-512", KF2.class.getName());
|
||||
put("KeyPairGenerator.SHA", KPG.class.getName());
|
||||
put("KeyPairGenerator.SHA-256", KPG1.class.getName());
|
||||
put("KeyPairGenerator.SHA-512", KPG2.class.getName());
|
||||
}
|
||||
}
|
||||
public static class KF extends NamedKeyFactory {
|
||||
public KF() {
|
||||
super("SHA", "SHA-256", "SHA-512");
|
||||
}
|
||||
}
|
||||
public static class KF1 extends NamedKeyFactory {
|
||||
public KF1() {
|
||||
super("SHA", "SHA-256");
|
||||
}
|
||||
}
|
||||
public static class KF2 extends NamedKeyFactory {
|
||||
public KF2() {
|
||||
super("SHA", "SHA-512");
|
||||
}
|
||||
}
|
||||
public static class KPG extends NamedKeyPairGenerator {
|
||||
public KPG() {
|
||||
super("SHA", "SHA-256", "SHA-512");
|
||||
}
|
||||
|
||||
public KPG(String pname) {
|
||||
super("SHA", pname);
|
||||
}
|
||||
|
||||
@Override
|
||||
public byte[][] implGenerateKeyPair(String name, SecureRandom sr) {
|
||||
var out = new byte[2][];
|
||||
out[0] = RAND.nBytes(name.endsWith("256") ? 2 : 4);
|
||||
out[1] = RAND.nBytes(name.endsWith("256") ? 2 : 4);
|
||||
return out;
|
||||
}
|
||||
}
|
||||
public static class KPG1 extends KPG {
|
||||
public KPG1() {
|
||||
super("SHA-256");
|
||||
}
|
||||
}
|
||||
public static class KPG2 extends KPG {
|
||||
public KPG2() {
|
||||
super("SHA-512");
|
||||
}
|
||||
}
|
||||
}
|
||||
Loading…
x
Reference in New Issue
Block a user