Uploaded image for project: 'JDK'
  1. JDK
  2. JDK-8261502

ECDHKeyAgreement: Allows alternate ECPrivateKey impl and revised exception handling

    XMLWordPrintable

    Details

    • Subcomponent:
    • Introduced In Build:
      b17
    • Introduced In Version:
      15

      Description

      A DESCRIPTION OF THE PROBLEM :
      After updating from OpenJDK 13.0.2 to OpenJDK 15.0.2 our code that generates an ECDH key agreement fails with error "Legacy SunEC curve disabled".

      However we are not using a legacy curve, we are using secp256r1 which is supported. This message appears to be caused by this piece of code which is failing any PrivateKey that doesn't extend ECPrivateKeyImpl:
      https://github.com/openjdk/jdk/blob/b0245c2b54f709857ad73d5530f76a33b1979c6a/src/jdk.crypto.ec/share/classes/sun/security/ec/ECDHKeyAgreement.java#L231-L233 - and then prints an inaccurate error message.

      REGRESSION : Last worked in version 13

      STEPS TO FOLLOW TO REPRODUCE THE PROBLEM :
      Run test code supplied in report. Verify it passes with earlier version of the JDK, and fails with JDK 15.

      EXPECTED VERSUS ACTUAL BEHAVIOR :
      EXPECTED -
      No output and exit code of 0.
      ACTUAL -
      Stack trace and exit code of non-zero:

      xception in thread "main" java.lang.IllegalStateException: java.security.InvalidAlgorithmParameterException: Legacy SunEC curve disabled, one or both keys: Private: secp256r1 [NIST P-256,X9.62 prime256v1] (1.2.840.10045.3.1.7), PublicKey:secp256r1 [NIST P-256,X9.62 prime256v1] (1.2.840.10045.3.1.7)
              at jdk.crypto.ec/sun.security.ec.ECDHKeyAgreement.engineGenerateSecret(ECDHKeyAgreement.java:184)
              at java.base/javax.crypto.KeyAgreement.generateSecret(KeyAgreement.java:604)
              at JDK15Bug.generateECDHSecret(JDK15Bug.java:31)
              at JDK15Bug.main(JDK15Bug.java:20)
      Caused by: java.security.InvalidAlgorithmParameterException: Legacy SunEC curve disabled, one or both keys: Private: secp256r1 [NIST P-256,X9.62 prime256v1] (1.2.840.10045.3.1.7), PublicKey:secp256r1 [NIST P-256,X9.62 prime256v1] (1.2.840.10045.3.1.7)
              ... 4 more

      ---------- BEGIN SOURCE ----------
      public class JDK15Bug {

          public static void main(String []args) throws Exception {
              // make secp256r1 key pair generator
              java.security.KeyPairGenerator g = java.security.KeyPairGenerator.getInstance("EC");
              java.security.spec.ECGenParameterSpec spec = new java.security.spec.ECGenParameterSpec("secp256r1");
              g.initialize(spec);

              // generate a private and a public EC key
              java.security.interfaces.ECPrivateKey privKey = (java.security.interfaces.ECPrivateKey) g.generateKeyPair().getPrivate();
              java.security.interfaces.ECPublicKey pubKey = (java.security.interfaces.ECPublicKey) g.generateKeyPair().getPublic();

              // this works fine in JDK 15
              generateECDHSecret(privKey, pubKey);
              
              // this breaks in JDK 15 with:
              // Caused by: java.security.InvalidAlgorithmParameterException: Legacy SunEC curve disabled, one or both keys: Private: secp256r1 [NIST P-256,X9.62 prime256v1] (1.2.840.10045.3.1.7), PublicKey:secp256r1 [NIST P-256,X9.62 prime256v1] (1.2.840.10045.3.1.7)
              // even though this is not a legacy curve
              // This has worked in prior version (e.g. 11, 13)
              generateECDHSecret(new MyECPrivateKey(privKey), pubKey);
          }

          // given an ECPrivateKey and ECPublicKey, return the ECDH exchange generated secret
          private static byte[] generateECDHSecret(
              java.security.interfaces.ECPrivateKey privKey,
              java.security.interfaces.ECPublicKey pubKey
          ) throws Exception {
              javax.crypto.KeyAgreement ka = javax.crypto.KeyAgreement.getInstance("ECDH");
              ka.init(privKey);
              ka.doPhase(pubKey, true);
              return ka.generateSecret();
          }

          // this class implements ECPrivateKey, wrapping a supplied ECPrivateKey
          private static class MyECPrivateKey implements java.security.interfaces.ECPrivateKey {
              private java.security.interfaces.ECPrivateKey p;

              MyECPrivateKey(java.security.interfaces.ECPrivateKey p) {
                  this.p = p;
              }

              public java.math.BigInteger getS() { return this.p.getS(); }
              public byte[] getEncoded() { return this.p.getEncoded(); }
              public String getFormat() { return this.p.getFormat(); }
              public String getAlgorithm() { return this.p.getAlgorithm(); }
              public java.security.spec.ECParameterSpec getParams() { return this.p.getParams(); }
          }
      }
      ---------- END SOURCE ----------

      CUSTOMER SUBMITTED WORKAROUND :
      We have rolled back to previous version (13) of the JDK while we evaluate options. Much of our code base passes around our own class which implements ECPrivateKey (and includes additional metadata such as who the key belongs to etc), so if not fixed we will need to invest in a substantial refactor to accommodate what feels like an unnecessarily new tight coupling to a private implementation class.

      If the JDK code (https://github.com/openjdk/jdk/blob/b0245c2b54f709857ad73d5530f76a33b1979c6a/src/jdk.crypto.ec/share/classes/sun/security/ec/ECDHKeyAgreement.java#L231-L233) instead checked for "instanceof ECPrivateKey" and called "getS()" instead of the private implementation version "getArrayS()", then things would work again.

      FREQUENCY : always


        Attachments

          Issue Links

            Activity

              People

              Assignee:
              ascarpino Anthony Scarpino
              Reporter:
              webbuggrp Webbug Group
              Votes:
              0 Vote for this issue
              Watchers:
              6 Start watching this issue

                Dates

                Created:
                Updated:
                Resolved: