changeset 59396:066bb54fba95

8234465: Encoded elliptic curve private keys should include the public point Reviewed-by: xuelei
author weijun
date Tue, 17 Dec 2019 09:38:30 +0800
parents 8af520f2976a
children 359553a04f66
files src/jdk.crypto.ec/share/classes/sun/security/ec/ECKeyFactory.java src/jdk.crypto.ec/share/classes/sun/security/ec/ECKeyPairGenerator.java src/jdk.crypto.ec/share/classes/sun/security/ec/ECPrivateKeyImpl.java test/jdk/sun/security/ec/PublicKeyInPrivateKey.java
diffstat 4 files changed, 106 insertions(+), 21 deletions(-) [+]
line wrap: on
line diff
--- a/src/jdk.crypto.ec/share/classes/sun/security/ec/ECKeyFactory.java	Mon Dec 16 15:20:02 2019 -0800
+++ b/src/jdk.crypto.ec/share/classes/sun/security/ec/ECKeyFactory.java	Tue Dec 17 09:38:30 2019 +0800
@@ -1,5 +1,5 @@
 /*
- * Copyright (c) 2006, 2012, Oracle and/or its affiliates. All rights reserved.
+ * Copyright (c) 2006, 2019, 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
@@ -201,6 +201,7 @@
             ECPrivateKey ecKey = (ECPrivateKey)key;
             return new ECPrivateKeyImpl(
                 ecKey.getS(),
+                null,
                 ecKey.getParams()
             );
         } else if ("PKCS#8".equals(key.getFormat())) {
@@ -237,7 +238,7 @@
             return new ECPrivateKeyImpl(pkcsSpec.getEncoded());
         } else if (keySpec instanceof ECPrivateKeySpec) {
             ECPrivateKeySpec ecSpec = (ECPrivateKeySpec)keySpec;
-            return new ECPrivateKeyImpl(ecSpec.getS(), ecSpec.getParams());
+            return new ECPrivateKeyImpl(ecSpec.getS(), null, ecSpec.getParams());
         } else {
             throw new InvalidKeySpecException("Only ECPrivateKeySpec "
                 + "and PKCS8EncodedKeySpec supported for EC private keys");
--- a/src/jdk.crypto.ec/share/classes/sun/security/ec/ECKeyPairGenerator.java	Mon Dec 16 15:20:02 2019 -0800
+++ b/src/jdk.crypto.ec/share/classes/sun/security/ec/ECKeyPairGenerator.java	Tue Dec 17 09:38:30 2019 +0800
@@ -1,5 +1,5 @@
 /*
- * Copyright (c) 2009, 2018, Oracle and/or its affiliates. All rights reserved.
+ * Copyright (c) 2009, 2019, 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
@@ -199,11 +199,10 @@
         AffinePoint affGen = new AffinePoint(x, y);
         Point pub = ops.multiply(affGen, privArr);
         AffinePoint affPub = pub.asAffine();
-
-        PrivateKey privateKey = new ECPrivateKeyImpl(privArr, ecParams);
-
         ECPoint w = new ECPoint(affPub.getX().asBigInteger(),
             affPub.getY().asBigInteger());
+
+        PrivateKey privateKey = new ECPrivateKeyImpl(privArr, w, ecParams);
         PublicKey publicKey = new ECPublicKeyImpl(w, ecParams);
 
         return Optional.of(new KeyPair(publicKey, privateKey));
@@ -225,11 +224,12 @@
         // keyBytes[0] is the encoding of the native private key
         BigInteger s = new BigInteger(1, (byte[]) keyBytes[0]);
 
-        PrivateKey privateKey = new ECPrivateKeyImpl(s, ecParams);
-
         // keyBytes[1] is the encoding of the native public key
         byte[] pubKey = (byte[]) keyBytes[1];
         ECPoint w = ECUtil.decodePoint(pubKey, ecParams.getCurve());
+
+        PrivateKey privateKey = new ECPrivateKeyImpl(s, w, ecParams);
+
         PublicKey publicKey = new ECPublicKeyImpl(w, ecParams);
 
         return new KeyPair(publicKey, privateKey);
--- a/src/jdk.crypto.ec/share/classes/sun/security/ec/ECPrivateKeyImpl.java	Mon Dec 16 15:20:02 2019 -0800
+++ b/src/jdk.crypto.ec/share/classes/sun/security/ec/ECPrivateKeyImpl.java	Tue Dec 17 09:38:30 2019 +0800
@@ -1,5 +1,5 @@
 /*
- * Copyright (c) 2006, 2018, Oracle and/or its affiliates. All rights reserved.
+ * Copyright (c) 2006, 2019, 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
@@ -52,8 +52,8 @@
  * }
  * </pre>
  *
- * We currently ignore the optional parameters and publicKey fields. We
- * require that the parameters are encoded as part of the AlgorithmIdentifier,
+ * We currently ignore the optional parameters. We require that the
+ * parameters are encoded as part of the AlgorithmIdentifier,
  * not in the private key structure.
  *
  * @since   1.6
@@ -66,6 +66,7 @@
     private BigInteger s;       // private value
     private byte[] arrayS;      // private value as a little-endian array
     private ECParameterSpec params;
+    private ECPoint pub;        // the optional public key
 
     /**
      * Construct a key from its encoding. Called by the ECKeyFactory.
@@ -78,18 +79,20 @@
      * Construct a key from its components. Used by the
      * KeyFactory.
      */
-    ECPrivateKeyImpl(BigInteger s, ECParameterSpec params)
+    ECPrivateKeyImpl(BigInteger s, ECPoint pub, ECParameterSpec params)
             throws InvalidKeyException {
         this.s = s;
         this.params = params;
+        this.pub = pub;
         makeEncoding(s);
 
     }
 
-    ECPrivateKeyImpl(byte[] s, ECParameterSpec params)
+    ECPrivateKeyImpl(byte[] s, ECPoint pub, ECParameterSpec params)
             throws InvalidKeyException {
         this.arrayS = s.clone();
         this.params = params;
+        this.pub = pub;
         makeEncoding(s);
     }
 
@@ -102,6 +105,12 @@
             byte[] privBytes = s.clone();
             ArrayUtil.reverse(privBytes);
             out.putOctetString(privBytes);
+            if (pub != null) {
+                DerOutputStream pubDer = new DerOutputStream();
+                pubDer.putBitString(ECUtil.encodePoint(pub, params.getCurve()));
+                out.write(DerValue.createTag(DerValue.TAG_CONTEXT,
+                        true, (byte) 0x01), pubDer);
+            }
             DerValue val =
                 new DerValue(DerValue.tag_Sequence, out.toByteArray());
             key = val.toByteArray();
@@ -186,22 +195,25 @@
             byte[] privData = data.getOctetString();
             ArrayUtil.reverse(privData);
             arrayS = privData;
+            AlgorithmParameters algParams = this.algid.getParameters();
+            if (algParams == null) {
+                throw new InvalidKeyException("EC domain parameters must be "
+                        + "encoded in the algorithm identifier");
+            }
+            params = algParams.getParameterSpec(ECParameterSpec.class);
             while (data.available() != 0) {
                 DerValue value = data.getDerValue();
                 if (value.isContextSpecific((byte) 0)) {
-                    // ignore for now
+                    // ignore for now. Usually not encoded because
+                    // pkcs8 already has the params
                 } else if (value.isContextSpecific((byte) 1)) {
-                    // ignore for now
+                    pub = ECUtil.decodePoint(
+                            value.data.getUnalignedBitString().toByteArray(),
+                            params.getCurve());
                 } else {
                     throw new InvalidKeyException("Unexpected value: " + value);
                 }
             }
-            AlgorithmParameters algParams = this.algid.getParameters();
-            if (algParams == null) {
-                throw new InvalidKeyException("EC domain parameters must be "
-                    + "encoded in the algorithm identifier");
-            }
-            params = algParams.getParameterSpec(ECParameterSpec.class);
         } catch (IOException e) {
             throw new InvalidKeyException("Invalid EC private key", e);
         } catch (InvalidParameterSpecException e) {
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/test/jdk/sun/security/ec/PublicKeyInPrivateKey.java	Tue Dec 17 09:38:30 2019 +0800
@@ -0,0 +1,72 @@
+/*
+ * Copyright (c) 2019, 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 jdk.test.lib.security.DerUtils;
+import sun.security.util.DerValue;
+
+import java.security.KeyFactory;
+import java.security.KeyPair;
+import java.security.KeyPairGenerator;
+import java.security.spec.ECGenParameterSpec;
+import java.security.spec.PKCS8EncodedKeySpec;
+import java.util.Arrays;
+
+/*
+ * @test
+ * @bug 8234465
+ * @library /test/lib
+ * @modules java.base/sun.security.util
+ * @summary Encoded elliptic curve private keys should include the public point
+ */
+public class PublicKeyInPrivateKey {
+
+    public static void main(String[] args) throws Exception {
+
+        KeyPairGenerator kpg = KeyPairGenerator.getInstance("EC");
+        kpg.initialize(new ECGenParameterSpec("secp256r1"));
+        KeyPair kp = kpg.generateKeyPair();
+        byte[] pubBytes = kp.getPublic().getEncoded();
+        byte[] privBytes = kp.getPrivate().getEncoded();
+
+        // https://tools.ietf.org/html/rfc5480#section-2.
+        // subjectPublicKey is 2nd in SubjectPublicKeyInfo
+        DerValue pubPoint = DerUtils.innerDerValue(pubBytes, "1");
+
+        // https://tools.ietf.org/html/rfc5208#section-5.
+        // privateKey as an OCTET STRING is 3rd in PrivateKeyInfo
+        // https://tools.ietf.org/html/rfc5915#section-3
+        // publicKey as [1] is 3rd (we do not have parameters) in ECPrivateKey
+        DerValue pubPointInPriv = DerUtils.innerDerValue(privBytes, "2c20");
+
+        // The two public keys should be the same
+        Asserts.assertEQ(pubPoint, pubPointInPriv);
+
+        // And it's reloadable
+        KeyFactory kf = KeyFactory.getInstance("EC");
+        byte[] privBytes2 = kf.generatePrivate(
+                new PKCS8EncodedKeySpec(privBytes)).getEncoded();
+
+        Asserts.assertTrue(Arrays.equals(privBytes, privBytes2));
+    }
+}