mirror of
https://github.com/openjdk/jdk.git
synced 2026-02-03 23:18:28 +00:00
289 lines
12 KiB
Java
289 lines
12 KiB
Java
/*
|
|
* 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());
|
|
}
|
|
}
|
|
}
|