8305310: Calculate PublicKey from PrivateKey

Reviewed-by: mullan
This commit is contained in:
Weijun Wang 2023-04-10 00:55:16 +00:00
parent 50d7335206
commit 97276859ab
7 changed files with 166 additions and 39 deletions
src
java.base/share/classes/sun/security
jdk.crypto.ec/share/classes/sun/security/ec
test/jdk/sun/security/util/InternalPrivateKey

@ -1,5 +1,5 @@
/*
* Copyright (c) 1996, 2022, Oracle and/or its affiliates. All rights reserved.
* Copyright (c) 1996, 2023, 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
@ -58,7 +58,7 @@ import sun.security.util.*;
*
* We support this format but do not parse attributes and publicKey now.
*/
public class PKCS8Key implements PrivateKey {
public class PKCS8Key implements PrivateKey, InternalPrivateKey {
/** use serialVersionUID from JDK 1.1. for interoperability */
@java.io.Serial

@ -0,0 +1,41 @@
/*
* Copyright (c) 2023, 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.util;
import java.security.PublicKey;
/**
* Extra private methods on a private key.
*/
public interface InternalPrivateKey {
/**
* Calculates a matching public key.
* @return the public key
* @throws UnsupportedOperationException if not supported
*/
default PublicKey calculatePublicKey() {
throw new UnsupportedOperationException();
}
}

@ -191,15 +191,10 @@ public final class ECKeyPairGenerator extends KeyPairGeneratorSpi {
int seedSize = (seedBits + 7) / 8;
byte[] privArr = generatePrivateScalar(random, ops, seedSize);
Point pub = ops.multiply(ecParams.getGenerator(), privArr);
AffinePoint affPub = pub.asAffine();
PrivateKey privateKey = new ECPrivateKeyImpl(privArr, ecParams);
ECPrivateKeyImpl privateKey = new ECPrivateKeyImpl(privArr, ecParams);
Arrays.fill(privArr, (byte)0);
ECPoint w = new ECPoint(affPub.getX().asBigInteger(),
affPub.getY().asBigInteger());
PublicKey publicKey = new ECPublicKeyImpl(w, ecParams);
PublicKey publicKey = privateKey.calculatePublicKey();
return Optional.of(new KeyPair(publicKey, privateKey));
}

@ -1,5 +1,5 @@
/*
* Copyright (c) 2006, 2022, Oracle and/or its affiliates. All rights reserved.
* Copyright (c) 2006, 2023, 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
@ -33,6 +33,8 @@ import java.security.interfaces.*;
import java.security.spec.*;
import java.util.Arrays;
import sun.security.ec.point.AffinePoint;
import sun.security.ec.point.MutablePoint;
import sun.security.util.*;
import sun.security.x509.AlgorithmId;
import sun.security.pkcs.PKCS8Key;
@ -148,11 +150,15 @@ public final class ECPrivateKeyImpl extends PKCS8Key implements ECPrivateKey {
return s;
}
public byte[] getArrayS() {
private byte[] getArrayS0() {
if (arrayS == null) {
arrayS = ECUtil.sArray(getS(), params);
}
return arrayS.clone();
return arrayS;
}
public byte[] getArrayS() {
return getArrayS0().clone();
}
// see JCA doc
@ -195,4 +201,21 @@ public final class ECPrivateKeyImpl extends PKCS8Key implements ECPrivateKey {
throw new InvalidKeyException("Invalid EC private key", e);
}
}
@Override
public PublicKey calculatePublicKey() {
ECParameterSpec ecParams = getParams();
ECOperations ops = ECOperations.forParameters(ecParams)
.orElseThrow(ProviderException::new);
MutablePoint pub = ops.multiply(ecParams.getGenerator(), getArrayS0());
AffinePoint affPub = pub.asAffine();
ECPoint w = new ECPoint(affPub.getX().asBigInteger(),
affPub.getY().asBigInteger());
try {
return new ECPublicKeyImpl(w, ecParams);
} catch (InvalidKeyException e) {
throw new ProviderException(
"Unexpected error calculating public key", e);
}
}
}

@ -1,5 +1,5 @@
/*
* Copyright (c) 2018, 2022, Oracle and/or its affiliates. All rights reserved.
* Copyright (c) 2018, 2023, 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
@ -40,7 +40,7 @@ public final class XDHPrivateKeyImpl extends PKCS8Key implements XECPrivateKey {
private static final long serialVersionUID = 1L;
@SuppressWarnings("serial") // Type of field is not Serializable
private final AlgorithmParameterSpec paramSpec;
private final NamedParameterSpec paramSpec;
private byte[] k;
XDHPrivateKeyImpl(XECParameters params, byte[] k)
@ -100,5 +100,19 @@ public final class XDHPrivateKeyImpl extends PKCS8Key implements XECPrivateKey {
public Optional<byte[]> getScalar() {
return Optional.of(getK());
}
@Override
public PublicKey calculatePublicKey() {
XECParameters params = paramSpec.getName().equals("X25519")
? XECParameters.X25519
: XECParameters.X448;
try {
return new XDHPublicKeyImpl(params,
new XECOperations(params).computePublic(k.clone()));
} catch (InvalidKeyException e) {
throw new ProviderException(
"Unexpected error calculating public key", e);
}
}
}

@ -39,6 +39,9 @@ import sun.security.x509.AlgorithmId;
public class XECParameters {
static final XECParameters X25519;
static final XECParameters X448;
static ParametersMap<XECParameters> namedParams = new ParametersMap<>();
// Naming/identification parameters
@ -114,41 +117,27 @@ public class XECParameters {
Map<String, XECParameters> byName = new HashMap<>();
// set up X25519
try {
BigInteger p = TWO.pow(255).subtract(BigInteger.valueOf(19));
addParameters(255, p, 121665, (byte)0x09, 3,
KnownOIDs.X25519.value(), NamedParameterSpec.X25519.getName(),
bySize, byOid, byName);
} catch (IOException ex) {
// Unable to set X25519 parameters---it will be disabled
}
BigInteger p2 = TWO.pow(255).subtract(BigInteger.valueOf(19));
X25519 = addParameters(255, p2, 121665, (byte)0x09, 3,
KnownOIDs.X25519, NamedParameterSpec.X25519.getName());
// set up X448
try {
BigInteger p = TWO.pow(448).subtract(TWO.pow(224))
.subtract(BigInteger.ONE);
addParameters(448, p, 39081, (byte)0x05, 2,
KnownOIDs.X448.value(), NamedParameterSpec.X448.getName(),
bySize, byOid, byName);
} catch (IOException ex) {
// Unable to set X448 parameters---it will be disabled
}
BigInteger p4 = TWO.pow(448).subtract(TWO.pow(224))
.subtract(BigInteger.ONE);
X448 = addParameters(448, p4, 39081, (byte)0x05, 2,
KnownOIDs.X448, NamedParameterSpec.X448.getName());
namedParams.fix();
}
private static void addParameters(int bits, BigInteger p, int a24,
byte basePoint, int logCofactor, String objectId, String name,
Map<Integer, XECParameters> bySize,
Map<ObjectIdentifier, XECParameters> byOid,
Map<String, XECParameters> byName) throws IOException {
private static XECParameters addParameters(int bits, BigInteger p, int a24,
byte basePoint, int logCofactor, KnownOIDs koid, String name) {
ObjectIdentifier oid = ObjectIdentifier.of(objectId);
ObjectIdentifier oid = ObjectIdentifier.of(koid);
XECParameters params =
new XECParameters(bits, p, a24, basePoint, logCofactor, oid, name);
namedParams.put(name.toLowerCase(), oid, bits, params);
return params;
}
boolean oidEquals(XECParameters other) {

@ -0,0 +1,65 @@
/*
* Copyright (c) 2023, 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.
*/
import jdk.test.lib.Asserts;
import sun.security.util.InternalPrivateKey;
import java.security.*;
import java.security.spec.ECGenParameterSpec;
import java.util.Arrays;
import java.util.List;
/*
* @test
* @bug 8305310
* @library /test/lib
* @summary Calculate PublicKey from PrivateKey
* @modules java.base/sun.security.util
*/
public class Correctness {
public static void main(String[] args) throws Exception {
for (String alg : List.of("secp256r1", "secp384r1", "secp521r1",
"X25519", "X448")) {
KeyPairGenerator g;
if (alg.startsWith("X")) {
g = KeyPairGenerator.getInstance(alg);
} else {
g = KeyPairGenerator.getInstance("EC");
g.initialize(new ECGenParameterSpec(alg));
}
KeyPair kp = g.generateKeyPair();
PublicKey p1 = kp.getPublic();
PrivateKey s1 = kp.getPrivate();
if (s1 instanceof InternalPrivateKey ipk) {
PublicKey p2 = ipk.calculatePublicKey();
Asserts.assertTrue(Arrays.equals(p2.getEncoded(), p1.getEncoded()));
Asserts.assertEQ(p2.getAlgorithm(), p1.getAlgorithm());
Asserts.assertEQ(p2.getFormat(), p1.getFormat());
} else {
throw new RuntimeException("Not an InternalPrivateKey: "
+ s1.getClass());
}
}
}
}