From 210addb50706727b438b072f47e4d5d201311ff1 Mon Sep 17 00:00:00 2001 From: ebaron Date: Fri, 9 Jan 2015 11:58:34 -0800 Subject: [PATCH] 8046724: XML Signature ECKeyValue elements cannot be marshalled or unmarshalled Reviewed-by: phh, andrew --- .../xml/dsig/internal/dom/DOMKeyValue.java | 222 +++++++++++------- .../xml/crypto/dsig/GenerationTests.java | 75 +++++- test/javax/xml/crypto/dsig/KeySelectors.java | 7 +- .../xml/crypto/dsig/ValidationTests.java | 5 +- .../data/signature-enveloping-p256-sha1.xml | 3 + 5 files changed, 220 insertions(+), 92 deletions(-) create mode 100644 test/javax/xml/crypto/dsig/data/signature-enveloping-p256-sha1.xml diff --git a/src/share/classes/org/jcp/xml/dsig/internal/dom/DOMKeyValue.java b/src/share/classes/org/jcp/xml/dsig/internal/dom/DOMKeyValue.java index 61aa8a6b2..08aee02be 100644 --- a/src/share/classes/org/jcp/xml/dsig/internal/dom/DOMKeyValue.java +++ b/src/share/classes/org/jcp/xml/dsig/internal/dom/DOMKeyValue.java @@ -21,7 +21,7 @@ * under the License. */ /* - * Copyright (c) 2005, 2014, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2005, 2015, Oracle and/or its affiliates. All rights reserved. */ /* * $Id: DOMKeyValue.java 1333415 2012-05-03 12:03:51Z coheigea $ @@ -33,21 +33,19 @@ import javax.xml.crypto.dom.DOMCryptoContext; import javax.xml.crypto.dsig.*; import javax.xml.crypto.dsig.keyinfo.KeyValue; -// import java.io.IOException; -import java.lang.reflect.InvocationTargetException; -import java.lang.reflect.Method; -import java.security.AccessController; +import java.io.IOException; +import java.math.BigInteger; import java.security.KeyException; import java.security.KeyFactory; import java.security.NoSuchAlgorithmException; -import java.security.PrivilegedActionException; -import java.security.PrivilegedExceptionAction; import java.security.PublicKey; import java.security.interfaces.DSAParams; import java.security.interfaces.DSAPublicKey; import java.security.interfaces.ECPublicKey; import java.security.interfaces.RSAPublicKey; import java.security.spec.DSAPublicKeySpec; +import java.security.spec.ECField; +import java.security.spec.ECFieldFp; import java.security.spec.ECParameterSpec; import java.security.spec.ECPoint; import java.security.spec.ECPublicKeySpec; @@ -55,6 +53,7 @@ import java.security.spec.EllipticCurve; import java.security.spec.InvalidKeySpecException; import java.security.spec.KeySpec; import java.security.spec.RSAPublicKeySpec; +import java.util.Arrays; import org.w3c.dom.Document; import org.w3c.dom.Element; import org.w3c.dom.Node; @@ -326,55 +325,112 @@ public abstract class DOMKeyValue extends DOMStructure implements KeyValue { private byte[] ecPublicKey; private KeyFactory eckf; private ECParameterSpec ecParams; - private Method encodePoint, decodePoint, getCurveName, - getECParameterSpec; + + // The supported curve, secp256r1 + private static final Curve SECP256R1; + static { + final String name, oid, sfield, a, b, x, y, n; + name = "secp256r1 [NIST P-256, X9.62 prime256v1]"; + oid = "1.2.840.10045.3.1.7"; + sfield = + "FFFFFFFF00000001000000000000000000000000FFFFFFFFFFFFFFFFFFFFFFFF"; + a = + "FFFFFFFF00000001000000000000000000000000FFFFFFFFFFFFFFFFFFFFFFFC"; + b = + "5AC635D8AA3A93E7B3EBBD55769886BC651D06B0CC53B0F63BCE3C3E27D2604B"; + x = + "6B17D1F2E12C4247F8BCE6E563A440F277037D812DEB33A0F4A13945D898C296"; + y = + "4FE342E2FE1A7F9B8EE7EB4A7C0F9E162BCE33576B315ECECBB6406837BF51F5"; + n = + "FFFFFFFF00000000FFFFFFFFFFFFFFFFBCE6FAADA7179E84F3B9CAC2FC632551"; + final int h = 1; + + BigInteger p = bigInt(sfield); + ECField field = new ECFieldFp(p); + EllipticCurve curve = new EllipticCurve(field, bigInt(a), + bigInt(b)); + ECPoint g = new ECPoint(bigInt(x), bigInt(y)); + SECP256R1 = new Curve(name, oid, curve, g, bigInt(n), h); + } EC(PublicKey key) throws KeyException { super(key); ECPublicKey ecKey = (ECPublicKey)key; ECPoint ecPoint = ecKey.getW(); ecParams = ecKey.getParams(); - try { - AccessController.doPrivileged( - new PrivilegedExceptionAction() { - public Void run() throws - ClassNotFoundException, NoSuchMethodException - { - getMethods(); - return null; - } - } - ); - } catch (PrivilegedActionException pae) { - throw new KeyException("ECKeyValue not supported", - pae.getException()); - } - Object[] args = new Object[] { ecPoint, ecParams.getCurve() }; - try { - ecPublicKey = (byte[])encodePoint.invoke(null, args); - } catch (IllegalAccessException iae) { - throw new KeyException(iae); - } catch (InvocationTargetException ite) { - throw new KeyException(ite); - } + ecPublicKey = encodePoint(ecPoint, ecParams.getCurve()); } EC(Element dmElem) throws MarshalException { super(dmElem); } - void getMethods() throws ClassNotFoundException, NoSuchMethodException { - Class c = Class.forName("sun.security.ec.ECParameters"); - Class[] params = new Class[] { ECPoint.class, - EllipticCurve.class }; - encodePoint = c.getMethod("encodePoint", params); - params = new Class[] { ECParameterSpec.class }; - getCurveName = c.getMethod("getCurveName", params); - params = new Class[] { byte[].class, EllipticCurve.class }; - decodePoint = c.getMethod("decodePoint", params); - c = Class.forName("sun.security.ec.NamedCurve"); - params = new Class[] { String.class }; - getECParameterSpec = c.getMethod("getECParameterSpec", params); + private static ECPoint decodePoint(byte[] data, EllipticCurve curve) + throws IOException { + if ((data.length == 0) || (data[0] != 4)) { + throw new IOException("Only uncompressed point format " + + "supported"); + } + // Per ANSI X9.62, an encoded point is a 1 byte type followed by + // ceiling(log base 2 field-size / 8) bytes of x and the same of y. + int n = (data.length - 1) / 2; + if (n != ((curve.getField().getFieldSize() + 7) >> 3)) { + throw new IOException("Point does not match field size"); + } + + byte[] xb = Arrays.copyOfRange(data, 1, 1 + n); + byte[] yb = Arrays.copyOfRange(data, n + 1, n + 1 + n); + + return new ECPoint(new BigInteger(1, xb), new BigInteger(1, yb)); + } + + private static byte[] encodePoint(ECPoint point, EllipticCurve curve) { + // get field size in bytes (rounding up) + int n = (curve.getField().getFieldSize() + 7) >> 3; + byte[] xb = trimZeroes(point.getAffineX().toByteArray()); + byte[] yb = trimZeroes(point.getAffineY().toByteArray()); + if ((xb.length > n) || (yb.length > n)) { + throw new RuntimeException("Point coordinates do not " + + "match field size"); + } + byte[] b = new byte[1 + (n << 1)]; + b[0] = 4; // uncompressed + System.arraycopy(xb, 0, b, n - xb.length + 1, xb.length); + System.arraycopy(yb, 0, b, b.length - yb.length, yb.length); + return b; + } + + private static byte[] trimZeroes(byte[] b) { + int i = 0; + while ((i < b.length - 1) && (b[i] == 0)) { + i++; + } + if (i == 0) { + return b; + } + return Arrays.copyOfRange(b, i, b.length); + } + + private static String getCurveOid(ECParameterSpec params) { + // Check that the params represent the secp256r1 curve + // If so, return the object identifier of the curve + int fieldSize = params.getCurve().getField().getFieldSize(); + if (SECP256R1.getCurve().getField().getFieldSize() == fieldSize + && SECP256R1.getCurve().equals(params.getCurve()) + && SECP256R1.getGenerator().equals(params.getGenerator()) + && SECP256R1.getOrder().equals(params.getOrder()) + && SECP256R1.getCofactor() == params.getCofactor()) { + return SECP256R1.getObjectId(); + } + return null; + } + + private static ECParameterSpec getECParameterSpec(String oid) { + if (oid.equals(SECP256R1.getObjectId())) { + return SECP256R1; + } + return null; } void marshalPublicKey(Node parent, Document doc, String dsPrefix, @@ -392,14 +448,11 @@ public abstract class DOMKeyValue extends DOMStructure implements KeyValue { XMLDSIG_11_XMLNS, prefix); Object[] args = new Object[] { ecParams }; - try { - String oid = (String) getCurveName.invoke(null, args); - DOMUtils.setAttribute(namedCurveElem, "URI", "urn:oid:" + oid); - } catch (IllegalAccessException iae) { - throw new MarshalException(iae); - } catch (InvocationTargetException ite) { - throw new MarshalException(ite); + String oid = getCurveOid(ecParams); + if (oid == null) { + throw new MarshalException("Invalid ECParameterSpec"); } + DOMUtils.setAttribute(namedCurveElem, "URI", "urn:oid:" + oid); String qname = (prefix == null || prefix.length() == 0) ? "xmlns" : "xmlns:" + prefix; namedCurveElem.setAttributeNS("http://www.w3.org/2000/xmlns/", @@ -423,21 +476,6 @@ public abstract class DOMKeyValue extends DOMStructure implements KeyValue { ("unable to create EC KeyFactory: " + e.getMessage()); } } - try { - AccessController.doPrivileged( - new PrivilegedExceptionAction() { - public Void run() throws - ClassNotFoundException, NoSuchMethodException - { - getMethods(); - return null; - } - } - ); - } catch (PrivilegedActionException pae) { - throw new MarshalException("ECKeyValue not supported", - pae.getException()); - } ECParameterSpec ecParams = null; Element curElem = DOMUtils.getFirstChildElement(kvtElem); if (curElem.getLocalName().equals("ECParameters")) { @@ -448,14 +486,9 @@ public abstract class DOMKeyValue extends DOMStructure implements KeyValue { // strip off "urn:oid" if (uri.startsWith("urn:oid:")) { String oid = uri.substring(8); - try { - Object[] args = new Object[] { oid }; - ecParams = (ECParameterSpec) - getECParameterSpec.invoke(null, args); - } catch (IllegalAccessException iae) { - throw new MarshalException(iae); - } catch (InvocationTargetException ite) { - throw new MarshalException(ite); + ecParams = getECParameterSpec(oid); + if (ecParams == null) { + throw new MarshalException("Invalid curve OID"); } } else { throw new MarshalException("Invalid NamedCurve URI"); @@ -465,24 +498,43 @@ public abstract class DOMKeyValue extends DOMStructure implements KeyValue { } curElem = DOMUtils.getNextSiblingElement(curElem, "PublicKey"); ECPoint ecPoint = null; + try { - Object[] args = new Object[] { Base64.decode(curElem), - ecParams.getCurve() }; - ecPoint = (ECPoint)decodePoint.invoke(null, args); + ecPoint = decodePoint(Base64.decode(curElem), + ecParams.getCurve()); } catch (Base64DecodingException bde) { throw new MarshalException("Invalid EC PublicKey", bde); - } catch (IllegalAccessException iae) { - throw new MarshalException(iae); - } catch (InvocationTargetException ite) { - throw new MarshalException(ite); + } catch (IOException ioe) { + throw new MarshalException("Invalid EC Point", ioe); } -/* - ecPoint = sun.security.ec.ECParameters.decodePoint( - Base64.decode(curElem), ecParams.getCurve()); -*/ + ECPublicKeySpec spec = new ECPublicKeySpec(ecPoint, ecParams); return generatePublicKey(eckf, spec); } + + static final class Curve extends ECParameterSpec { + private final String name; + private final String oid; + + Curve(String name, String oid, EllipticCurve curve, + ECPoint g, BigInteger n, int h) { + super(curve, g, n, h); + this.name = name; + this.oid = oid; + } + + private String getName() { + return name; + } + + private String getObjectId() { + return oid; + } + } + } + + private static BigInteger bigInt(String s) { + return new BigInteger(s, 16); } static final class Unknown extends DOMKeyValue { diff --git a/test/javax/xml/crypto/dsig/GenerationTests.java b/test/javax/xml/crypto/dsig/GenerationTests.java index 4a3e1919a..b6f284be8 100644 --- a/test/javax/xml/crypto/dsig/GenerationTests.java +++ b/test/javax/xml/crypto/dsig/GenerationTests.java @@ -24,7 +24,7 @@ /** * @test * @bug 4635230 6283345 6303830 6824440 6867348 7094155 8038184 - * 8038349 8074784 8210736 + * 8038349 8046724 8074784 8210736 * @summary Basic unit tests for generating XML Signatures with JSR 105 * @compile -XDignore.symbol.file KeySelectors.java SignatureValidator.java * X509KeySelector.java GenerationTests.java @@ -54,6 +54,13 @@ import java.security.cert.X509CRL; import java.security.spec.KeySpec; import java.security.spec.DSAPrivateKeySpec; import java.security.spec.DSAPublicKeySpec; +import java.security.spec.ECField; +import java.security.spec.ECFieldFp; +import java.security.spec.ECParameterSpec; +import java.security.spec.ECPoint; +import java.security.spec.ECPrivateKeySpec; +import java.security.spec.ECPublicKeySpec; +import java.security.spec.EllipticCurve; import java.security.spec.RSAPrivateKeySpec; import java.security.spec.RSAPublicKeySpec; import java.util.*; @@ -91,9 +98,10 @@ public class GenerationTests { private static DocumentBuilder db; private static CanonicalizationMethod withoutComments; private static SignatureMethod dsaSha1, dsaSha256, rsaSha1, - rsaSha256, rsaSha384, rsaSha512; + rsaSha256, rsaSha384, rsaSha512, + ecdsaSha1; private static DigestMethod sha1, sha256, sha384, sha512; - private static KeyInfo dsa1024, dsa2048, rsa, rsa1024; + private static KeyInfo dsa1024, dsa2048, rsa, rsa1024, p256ki; private static KeySelector kvks = new KeySelectors.KeyValueKeySelector(); private static KeySelector sks; private static Key signingKey; @@ -201,6 +209,7 @@ public class GenerationTests { test_create_signature_enveloping_hmac_sha384(); test_create_signature_enveloping_hmac_sha512(); test_create_signature_enveloping_rsa(); + test_create_signature_enveloping_p256_sha1(); test_create_signature_external_b64_dsa(); test_create_signature_external_dsa(); test_create_signature_keyname(); @@ -346,6 +355,8 @@ public class GenerationTests { (kifac.newKeyValue(getPublicKey("RSA", 512)))); rsa1024 = kifac.newKeyInfo(Collections.singletonList (kifac.newKeyValue(getPublicKey("RSA", 1024)))); + p256ki = kifac.newKeyInfo(Collections.singletonList + (kifac.newKeyValue(getECPublicKey()))); rsaSha1 = fac.newSignatureMethod(SignatureMethod.RSA_SHA1, null); rsaSha256 = fac.newSignatureMethod ("http://www.w3.org/2001/04/xmldsig-more#rsa-sha256", null); @@ -353,6 +364,8 @@ public class GenerationTests { ("http://www.w3.org/2001/04/xmldsig-more#rsa-sha384", null); rsaSha512 = fac.newSignatureMethod ("http://www.w3.org/2001/04/xmldsig-more#rsa-sha512", null); + ecdsaSha1 = fac.newSignatureMethod + ("http://www.w3.org/2001/04/xmldsig-more#ecdsa-sha1", null); sks = new KeySelectors.SecretKeySelector("secret".getBytes("ASCII")); httpUd = new HttpURIDereferencer(); @@ -513,6 +526,13 @@ public class GenerationTests { System.out.println(); } + static void test_create_signature_enveloping_p256_sha1() throws Exception { + System.out.println("* Generating signature-enveloping-p256-sha1.xml"); + test_create_signature_enveloping(sha1, ecdsaSha1, p256ki, + getECPrivateKey(), kvks, false); + System.out.println(); + } + static void test_create_signature_external_b64_dsa() throws Exception { System.out.println("* Generating signature-external-b64-dsa.xml"); test_create_signature_external(dsaSha1, dsa1024, signingKey, kvks, true); @@ -1526,7 +1546,42 @@ public class GenerationTests { "237008997971129772408397621801631622129297063463868593083106979716" + "204903524890556839550490384015324575598723478554854070823335021842" + "210112348400928769"; + private static final String EC_X = + "335863644451761614592446380116804721648611739647823420286081723541" + + "6166183710"; + private static final String EC_Y = + "951559601159729477487064127150143688502130342917782252098602422796" + + "95457910701"; + private static final String EC_S = + "425976209773168452211813225517384419928639977904006759709292218082" + + "7440083936"; + private static final ECParameterSpec EC_PARAMS; + + static { + final String ec_sfield, ec_a, ec_b, ec_gx, ec_gy, ec_n; + ec_sfield = + "FFFFFFFF00000001000000000000000000000000FFFFFFFFFFFFFFFFFFFFFFFF"; + ec_a = + "FFFFFFFF00000001000000000000000000000000FFFFFFFFFFFFFFFFFFFFFFFC"; + ec_b = + "5AC635D8AA3A93E7B3EBBD55769886BC651D06B0CC53B0F63BCE3C3E27D2604B"; + ec_gx = + "6B17D1F2E12C4247F8BCE6E563A440F277037D812DEB33A0F4A13945D898C296"; + ec_gy = + "4FE342E2FE1A7F9B8EE7EB4A7C0F9E162BCE33576B315ECECBB6406837BF51F5"; + ec_n = + "FFFFFFFF00000000FFFFFFFFFFFFFFFFBCE6FAADA7179E84F3B9CAC2FC632551"; + final int ec_h = 1; + final ECField ec_field = new ECFieldFp(bigInt(ec_sfield)); + final EllipticCurve ec_curve = new EllipticCurve(ec_field, + bigInt(ec_a), bigInt(ec_b)); + final ECPoint ec_g = new ECPoint(bigInt(ec_gx), bigInt(ec_gy)); + EC_PARAMS = new ECParameterSpec(ec_curve, ec_g, bigInt(ec_n), ec_h); + } + private static BigInteger bigInt(String s) { + return new BigInteger(s, 16); + } private static PublicKey getPublicKey(String algo, int keysize) throws Exception { KeyFactory kf = KeyFactory.getInstance(algo); @@ -1555,6 +1610,14 @@ public class GenerationTests { return kf.generatePublic(kspec); } + private static PublicKey getECPublicKey() throws Exception { + KeyFactory kf = KeyFactory.getInstance("EC"); + KeySpec kspec = new ECPublicKeySpec(new ECPoint(new BigInteger(EC_X), + new BigInteger(EC_Y)), + EC_PARAMS); + return kf.generatePublic(kspec); + } + private static PrivateKey getPrivateKey(String algo, int keysize) throws Exception { KeyFactory kf = KeyFactory.getInstance(algo); @@ -1581,6 +1644,12 @@ public class GenerationTests { return kf.generatePrivate(kspec); } + private static PrivateKey getECPrivateKey() throws Exception { + KeyFactory kf = KeyFactory.getInstance("EC"); + KeySpec kspec = new ECPrivateKeySpec(new BigInteger(EC_S), EC_PARAMS); + return kf.generatePrivate(kspec); + } + private static SecretKey getSecretKey(final byte[] secret) { return new SecretKey() { public String getFormat() { return "RAW"; } diff --git a/test/javax/xml/crypto/dsig/KeySelectors.java b/test/javax/xml/crypto/dsig/KeySelectors.java index 290c55b89..2195b8ce2 100644 --- a/test/javax/xml/crypto/dsig/KeySelectors.java +++ b/test/javax/xml/crypto/dsig/KeySelectors.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2005, 2014, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2005, 2015, 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 @@ -172,7 +172,6 @@ class KeySelectors { throw new KeySelectorException("No KeyValue element found!"); } - //@@@FIXME: this should also work for key types other than DSA/RSA static boolean algEquals(String algURI, String algName) { if (algName.equalsIgnoreCase("DSA") && algURI.equals(SignatureMethod.DSA_SHA1) || @@ -187,6 +186,10 @@ class KeySelectors { algURI.equals ("http://www.w3.org/2001/04/xmldsig-more#rsa-sha512"))) { return true; + } else if (algName.equalsIgnoreCase("EC") && + (algURI.equals + ("http://www.w3.org/2001/04/xmldsig-more#ecdsa-sha1"))) { + return true; } else { return false; } diff --git a/test/javax/xml/crypto/dsig/ValidationTests.java b/test/javax/xml/crypto/dsig/ValidationTests.java index 278d64c32..d7384815f 100644 --- a/test/javax/xml/crypto/dsig/ValidationTests.java +++ b/test/javax/xml/crypto/dsig/ValidationTests.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2005, 2013, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2005, 2015, 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 @@ -23,7 +23,7 @@ /** * @test - * @bug 4635230 6365103 6366054 6824440 7131084 + * @bug 4635230 6365103 6366054 6824440 7131084 8046724 * @summary Basic unit tests for validating XML Signatures with JSR 105 * @compile -XDignore.symbol.file KeySelectors.java SignatureValidator.java * X509KeySelector.java ValidationTests.java @@ -99,6 +99,7 @@ public class ValidationTests { new Test("signature-enveloping-b64-dsa.xml", KVKS), new Test("signature-enveloping-dsa.xml", KVKS), new Test("signature-enveloping-rsa.xml", KVKS), + new Test("signature-enveloping-p256-sha1.xml", KVKS), new Test("signature-enveloping-hmac-sha1.xml", SKKS), new Test("signature-external-dsa.xml", KVKS), new Test("signature-external-b64-dsa.xml", KVKS), diff --git a/test/javax/xml/crypto/dsig/data/signature-enveloping-p256-sha1.xml b/test/javax/xml/crypto/dsig/data/signature-enveloping-p256-sha1.xml new file mode 100644 index 000000000..0ace1157b --- /dev/null +++ b/test/javax/xml/crypto/dsig/data/signature-enveloping-p256-sha1.xml @@ -0,0 +1,3 @@ +7/XTsHaBSOnJ/jXD5v0zL6VKYsk=WiF/Hd0s7BiH36Ds/1iJcbKiXOUVBSGFteuTjXwBbezR43NAwpMmMX5c1su0A9hG9rVVzE/1DOlO +vuDVLBBblg==BAds672US3sCYunM2k2bEQLbuRxdQlNTvq+5fitOpDMe0mBdZV4J3yZaG0taziYIuAT9GJGfds+q +xtXOCNWe/60=some text \ No newline at end of file -- GitLab