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

Cannot use 3rd party EC implementation with SECG curves with optional seed

XMLWordPrintable

    • Icon: Bug Bug
    • Resolution: Cannot Reproduce
    • Icon: P3 P3
    • 9
    • 6u13
    • security-libs

      FULL PRODUCT VERSION :
      java version "1.6.0_13"
      Java(TM) SE Runtime Environment (build 1.6.0_13-b03)
      Java HotSpot(TM) Client VM (build 11.3-b02, mixed mode, sharing)

      ADDITIONAL OS VERSION INFORMATION :
      Microsoft Windows XP [Version 5.1.2600]

      A DESCRIPTION OF THE PROBLEM :
      The documentation for the SunJSSE provider indicates that the EC ciphersuites are supported if a JCE crypto provider is installed that meets certain EC related criteria (see http://java.sun.com/javase/6/docs/technotes/guides/security/SunProviders.html#SunJSSEProvider). Furthermore, it indicates that "the crypto provider should support all SECG curves". What it does not state is that if the implementation returns the ECParameterSpec with the optional curve seed (a parameter of the curve), the JSSE will fail to recognize them as valid.

      STEPS TO FOLLOW TO REPRODUCE THE PROBLEM :
      1. Obtain a 3rd party JCE provider that supports EC as detailed in http://java.sun.com/javase/6/docs/technotes/guides/security/SunProviders.html#SunJSSEProvider. The ECPublicKey and ECPrivateKey that this provider uses should return an ECParameterSpec with the seed set in the curve when the keys are decoded from named curve representation.

      2. Attempt to establish a TLS connection between a client and a server using the following ciphersuite: TLS_ECDH_ECDSA_WITH_AES_128_CBC_SHA


      EXPECTED VERSUS ACTUAL BEHAVIOR :
      EXPECTED -
      JSSE SSL transaction should succeed
      ACTUAL -
      Caused by: javax.net.ssl.SSLException: No available certificate or key corresponds to the SSL cipher suites which are enabled.
      at com.sun.net.ssl.internal.ssl.SSLServerSocketImpl.checkEnabledSuites(SSLServerSocketImpl.java:307)
      at com.sun.net.ssl.internal.ssl.SSLServerSocketImpl.accept(SSLServerSocketImpl.java:253)
      at SslTest_EC$SslServer.run(SslTest_EC.java:176)

      REPRODUCIBILITY :
      This bug can be reproduced always.

      ---------- BEGIN SOURCE ----------
      The low-level JSSE calls in particular that are problematic are sun.security.ec.ECParameters.getCurveName(ECParameterSpec) and sun.security.ec.ECParameters.getNamedCurve(ECParameterSpec). When they are passed an ECParameterSpec that does represent one of the SECG curves, but it has the seed in the EllipticCurve set (set to the seed value specified in the standard), the curve is not recognized as named.

      For example, the following source code demonstrates with the P-256 curve (1.2.840.10045.3.1.7). In the first case an ECParameterSpec that contains the seed is provided, in the second case it is not.

      import java.math.BigInteger;
      import java.security.spec.ECFieldFp;
      import java.security.spec.ECParameterSpec;
      import java.security.spec.ECPoint;
      import java.security.spec.EllipticCurve;

      import sun.security.ec.ECParameters;

      public class EcProblem
      {
          public static void main(String[] args)
          {
              System.out.println(ECParameters.getCurveName(ansix9p256r1_with_seed()));
              System.out.println(ECParameters.getCurveName(ansix9p256r1_without_seed()));
          }

          private static final ECParameterSpec ansix9p256r1_with_seed() {
                  return new ECParameterSpec(
                      new EllipticCurve(new ECFieldFp(new BigInteger("FFFFFFFF00000001000000000000000000000000FFFFFFFFFFFFFFFFFFFFFFFF", 16)),
                              new BigInteger("FFFFFFFF00000001000000000000000000000000FFFFFFFFFFFFFFFFFFFFFFFC",16),
                              new BigInteger("5AC635D8AA3A93E7B3EBBD55769886BC651D06B0CC53B0F63BCE3C3E27D2604B",16),
                              new byte[] { (byte)0xC4, (byte)0x9D, (byte)0x36, (byte)0x08, (byte)0x86, (byte)0xE7, (byte)0x04, (byte)0x93,
                                           (byte)0x6A, (byte)0x66, (byte)0x78, (byte)0xE1, (byte)0x13, (byte)0x9D, (byte)0x26, (byte)0xB7,
                                           (byte)0x81, (byte)0x9F, (byte)0x7E, (byte)0x90 }),
                      new ECPoint(new BigInteger("6B17D1F2E12C4247F8BCE6E563A440F277037D812DEB33A0F4A13945D898C296", 16),
                                  new BigInteger("4FE342E2FE1A7F9B8EE7EB4A7C0F9E162BCE33576B315ECECBB6406837BF51F5", 16)),
                      new BigInteger("FFFFFFFF00000000FFFFFFFFFFFFFFFFBCE6FAADA7179E84F3B9CAC2FC632551", 16),
                      1);
          }

          private static final ECParameterSpec ansix9p256r1_without_seed() {
              return new ECParameterSpec(
                  new EllipticCurve(new ECFieldFp(new BigInteger("FFFFFFFF00000001000000000000000000000000FFFFFFFFFFFFFFFFFFFFFFFF", 16)),
                          new BigInteger("FFFFFFFF00000001000000000000000000000000FFFFFFFFFFFFFFFFFFFFFFFC",16),
                          new BigInteger("5AC635D8AA3A93E7B3EBBD55769886BC651D06B0CC53B0F63BCE3C3E27D2604B",16)),
                  new ECPoint(new BigInteger("6B17D1F2E12C4247F8BCE6E563A440F277037D812DEB33A0F4A13945D898C296", 16),
                              new BigInteger("4FE342E2FE1A7F9B8EE7EB4A7C0F9E162BCE33576B315ECECBB6406837BF51F5", 16)),
                  new BigInteger("FFFFFFFF00000000FFFFFFFFFFFFFFFFBCE6FAADA7179E84F3B9CAC2FC632551", 16),
                  1);
          }
      }


      Expected output at system.out:
      null
      1.2.840.10045.3.1.7

      Actual output at system.out:
      1.2.840.10045.3.1.7
      1.2.840.10045.3.1.7

      Solution:
      The fix is to the ECParameters.getNamedCurve(ECParameterSpec params) API to perform a better compare. For example, something like the following:

                  if (namedCurve.getCurve().getField().equals(params.getCurve().getField()) == false) {
                      continue;
                  }
                  if (namedCurve.getCurve().getA().equals(params.getCurve().getA()) == false) {
                      continue;
                  }
                  if (namedCurve.getCurve().getB().equals(params.getCurve().getB()) == false) {
                      continue;
                  }
                  if (namedCurve.getCurve().getSeed() != null && params.getCurve().getSeed() != null && namedCurve.getCurve().getSeed().equals(params.getCurve().getSeed() ) == false) {
                      continue;
                  }

      Currently, the code is as follows. I took this from OpenJDK since Sun does not provide the source for the JSSE. Note: it is very difficult to debug issues like this without source code. It would be very helpful if Sun would provide the JSSE source code as well (not sure why you currently do not)

          // Convert the given ECParameterSpec object to a NamedCurve object.
          // If params does not represent a known named curve, return null.
          // Used by SunPKCS11.
          public static NamedCurve getNamedCurve(ECParameterSpec params) {
              if ((params instanceof NamedCurve) || (params == null)) {
                  return (NamedCurve)params;
              }
              // This is a hack to allow SunJSSE to work with 3rd party crypto
              // providers for ECC and not just SunPKCS11.
              // This can go away once we decide how to expose curve names in the
              // public API.
              // Note that it assumes that the 3rd party provider encodes named
              // curves using the short form, not explicitly. If it did that, then
              // the SunJSSE TLS ECC extensions are wrong, which could lead to
              // interoperability problems.
              int fieldSize = params.getCurve().getField().getFieldSize();
              for (ECParameterSpec namedCurve : NamedCurve.knownECParameterSpecs()) {
                  // ECParameterSpec does not define equals, so check all the
                  // components ourselves.
                  // Quick field size check first
                  if (namedCurve.getCurve().getField().getFieldSize() != fieldSize) {
                      continue;
                  }
                  if (namedCurve.getCurve().equals(params.getCurve()) == false) {
                      continue;
                  }
                  if (namedCurve.getGenerator().equals(params.getGenerator()) == false) {
                      continue;
                  }
                  if (namedCurve.getOrder().equals(params.getOrder()) == false) {
                      continue;
                  }
                  if (namedCurve.getCofactor() != params.getCofactor()) {
                      continue;
                  }
                  // everything matches our named curve, return it
                  return (NamedCurve)namedCurve;
              }
              // no match found
              return null;
          }

      ---------- END SOURCE ----------

      CUSTOMER SUBMITTED WORKAROUND :
      The only workaround is for the 3rd party EC implementation to never set the seed in the EllipticCurve object of its ECParameterSpec. If it does, it will not be recognized as a supported named curve, even though it might be.

      This is bad because there may be very legitimate reasons to want to include the seed (interop with products that may perform EC domain parameter validation, which makes use of the seed).

            vinnie Vincent Ryan
            wetmore Bradford Wetmore
            Votes:
            0 Vote for this issue
            Watchers:
            0 Start watching this issue

              Created:
              Updated:
              Resolved:
              Imported:
              Indexed: