Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
142 changes: 142 additions & 0 deletions src/java.base/share/classes/sun/security/pkcs/NamedPKCS8Key.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,142 @@
/*
* 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;
}

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 changes: 223 additions & 0 deletions src/java.base/share/classes/sun/security/provider/NamedKEM.java
Original file line number Diff line number Diff line change
@@ -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;
}
}
Loading