8046724: XML Signature ECKeyValue elements cannot be marshalled or unmarshalled
Reviewed-by: mullan
--- a/jdk/src/java.xml.crypto/share/classes/org/jcp/xml/dsig/internal/dom/DOMKeyValue.java Fri Jan 09 16:54:17 2015 +0100
+++ b/jdk/src/java.xml.crypto/share/classes/org/jcp/xml/dsig/internal/dom/DOMKeyValue.java Fri Jan 09 11:58:34 2015 -0800
@@ -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,20 +33,19 @@
import javax.xml.crypto.dsig.*;
import javax.xml.crypto.dsig.keyinfo.KeyValue;
-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;
@@ -54,6 +53,7 @@
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;
@@ -325,55 +325,112 @@
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<Void>() {
- 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.util.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.util.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,
@@ -391,14 +448,11 @@
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/",
@@ -422,21 +476,6 @@
("unable to create EC KeyFactory: " + e.getMessage());
}
}
- try {
- AccessController.doPrivileged(
- new PrivilegedExceptionAction<Void>() {
- 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")) {
@@ -447,14 +486,9 @@
// 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");
@@ -464,24 +498,43 @@
}
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.util.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 {
--- a/jdk/test/javax/xml/crypto/dsig/GenerationTests.java Fri Jan 09 16:54:17 2015 +0100
+++ b/jdk/test/javax/xml/crypto/dsig/GenerationTests.java Fri Jan 09 11:58:34 2015 -0800
@@ -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
@@ -24,6 +24,7 @@
/**
* @test
* @bug 4635230 6283345 6303830 6824440 6867348 7094155 8038184 8038349 8046949
+ * 8046724
* @summary Basic unit tests for generating XML Signatures with JSR 105
* @compile -XDignore.symbol.file KeySelectors.java SignatureValidator.java
* X509KeySelector.java GenerationTests.java
@@ -45,6 +46,13 @@
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.*;
@@ -81,9 +89,10 @@
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;
@@ -121,6 +130,7 @@
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();
@@ -175,6 +185,8 @@
(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);
@@ -182,6 +194,8 @@
("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();
@@ -342,6 +356,13 @@
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);
@@ -1168,7 +1189,42 @@
"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);
@@ -1197,6 +1253,14 @@
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);
@@ -1223,6 +1287,12 @@
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"; }
--- a/jdk/test/javax/xml/crypto/dsig/KeySelectors.java Fri Jan 09 16:54:17 2015 +0100
+++ b/jdk/test/javax/xml/crypto/dsig/KeySelectors.java Fri Jan 09 11:58:34 2015 -0800
@@ -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
@@ -166,7 +166,6 @@
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) ||
@@ -181,6 +180,10 @@
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;
}
--- a/jdk/test/javax/xml/crypto/dsig/ValidationTests.java Fri Jan 09 16:54:17 2015 +0100
+++ b/jdk/test/javax/xml/crypto/dsig/ValidationTests.java Fri Jan 09 11:58:34 2015 -0800
@@ -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
@@ -90,6 +90,7 @@
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),
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/jdk/test/javax/xml/crypto/dsig/data/signature-enveloping-p256-sha1.xml Fri Jan 09 11:58:34 2015 -0800
@@ -0,0 +1,3 @@
+<?xml version="1.0" encoding="UTF-8" standalone="no"?><Signature xmlns="http://www.w3.org/2000/09/xmldsig#"><SignedInfo><CanonicalizationMethod Algorithm="http://www.w3.org/TR/2001/REC-xml-c14n-20010315"/><SignatureMethod Algorithm="http://www.w3.org/2001/04/xmldsig-more#ecdsa-sha1"/><Reference URI="#object"><DigestMethod Algorithm="http://www.w3.org/2000/09/xmldsig#sha1"/><DigestValue>7/XTsHaBSOnJ/jXD5v0zL6VKYsk=</DigestValue></Reference></SignedInfo><SignatureValue>WiF/Hd0s7BiH36Ds/1iJcbKiXOUVBSGFteuTjXwBbezR43NAwpMmMX5c1su0A9hG9rVVzE/1DOlO
+vuDVLBBblg==</SignatureValue><KeyInfo><KeyValue><ECKeyValue xmlns="http://www.w3.org/2009/xmldsig11#"><NamedCurve URI="urn:oid:1.2.840.10045.3.1.7"/><PublicKey>BAds672US3sCYunM2k2bEQLbuRxdQlNTvq+5fitOpDMe0mBdZV4J3yZaG0taziYIuAT9GJGfds+q
+xtXOCNWe/60=</PublicKey></ECKeyValue></KeyValue></KeyInfo><Object Id="object">some text</Object></Signature>
\ No newline at end of file