diff --git a/src/java.base/share/classes/sun/security/pkcs/PKCS8Key.java b/src/java.base/share/classes/sun/security/pkcs/PKCS8Key.java index d0d7c69d1a0..bc560aa1772 100644 --- a/src/java.base/share/classes/sun/security/pkcs/PKCS8Key.java +++ b/src/java.base/share/classes/sun/security/pkcs/PKCS8Key.java @@ -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 diff --git a/src/java.base/share/classes/sun/security/util/InternalPrivateKey.java b/src/java.base/share/classes/sun/security/util/InternalPrivateKey.java new file mode 100644 index 00000000000..ccbea5c3f96 --- /dev/null +++ b/src/java.base/share/classes/sun/security/util/InternalPrivateKey.java @@ -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(); + } +} diff --git a/src/jdk.crypto.ec/share/classes/sun/security/ec/ECKeyPairGenerator.java b/src/jdk.crypto.ec/share/classes/sun/security/ec/ECKeyPairGenerator.java index 126800613d2..69c57b95bc4 100644 --- a/src/jdk.crypto.ec/share/classes/sun/security/ec/ECKeyPairGenerator.java +++ b/src/jdk.crypto.ec/share/classes/sun/security/ec/ECKeyPairGenerator.java @@ -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)); } diff --git a/src/jdk.crypto.ec/share/classes/sun/security/ec/ECPrivateKeyImpl.java b/src/jdk.crypto.ec/share/classes/sun/security/ec/ECPrivateKeyImpl.java index e984f8c9523..a2a54391c1f 100644 --- a/src/jdk.crypto.ec/share/classes/sun/security/ec/ECPrivateKeyImpl.java +++ b/src/jdk.crypto.ec/share/classes/sun/security/ec/ECPrivateKeyImpl.java @@ -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); + } + } } diff --git a/src/jdk.crypto.ec/share/classes/sun/security/ec/XDHPrivateKeyImpl.java b/src/jdk.crypto.ec/share/classes/sun/security/ec/XDHPrivateKeyImpl.java index c5893080b58..8dd718c1d3b 100644 --- a/src/jdk.crypto.ec/share/classes/sun/security/ec/XDHPrivateKeyImpl.java +++ b/src/jdk.crypto.ec/share/classes/sun/security/ec/XDHPrivateKeyImpl.java @@ -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 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); + } + } } diff --git a/src/jdk.crypto.ec/share/classes/sun/security/ec/XECParameters.java b/src/jdk.crypto.ec/share/classes/sun/security/ec/XECParameters.java index 9f84a7c1be3..86555321ec8 100644 --- a/src/jdk.crypto.ec/share/classes/sun/security/ec/XECParameters.java +++ b/src/jdk.crypto.ec/share/classes/sun/security/ec/XECParameters.java @@ -39,6 +39,9 @@ import sun.security.x509.AlgorithmId; public class XECParameters { + static final XECParameters X25519; + static final XECParameters X448; + static ParametersMap namedParams = new ParametersMap<>(); // Naming/identification parameters @@ -114,41 +117,27 @@ public class XECParameters { Map 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 bySize, - Map byOid, - Map 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) { diff --git a/test/jdk/sun/security/util/InternalPrivateKey/Correctness.java b/test/jdk/sun/security/util/InternalPrivateKey/Correctness.java new file mode 100644 index 00000000000..eb16c605664 --- /dev/null +++ b/test/jdk/sun/security/util/InternalPrivateKey/Correctness.java @@ -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()); + } + } + } +}