mirror of
https://github.com/openjdk/jdk.git
synced 2025-08-27 14:54:52 +02: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
10 changed files with 1693 additions and 9 deletions
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");
|
||||
}
|
||||
}
|
Loading…
Add table
Add a link
Reference in a new issue