1 /*
2  * reserved comment block
3  * DO NOT REMOVE OR ALTER!
4  */
5 /**
6  * Licensed to the Apache Software Foundation (ASF) under one
7  * or more contributor license agreements. See the NOTICE file
8  * distributed with this work for additional information
9  * regarding copyright ownership. The ASF licenses this file
10  * to you under the Apache License, Version 2.0 (the
11  * "License"); you may not use this file except in compliance
12  * with the License. You may obtain a copy of the License at
13  *
14  * http://www.apache.org/licenses/LICENSE-2.0
15  *
16  * Unless required by applicable law or agreed to in writing,
17  * software distributed under the License is distributed on an
18  * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
19  * KIND, either express or implied. See the License for the
20  * specific language governing permissions and limitations
21  * under the License.
22  */
23 package com.sun.org.apache.xml.internal.security.keys.content.keyvalues;
24 
25 import java.io.IOException;
26 import java.math.BigInteger;
27 import java.security.Key;
28 import java.security.KeyFactory;
29 import java.security.NoSuchAlgorithmException;
30 import java.security.PublicKey;
31 import java.security.interfaces.ECPublicKey;
32 import java.security.spec.ECField;
33 import java.security.spec.ECFieldFp;
34 import java.security.spec.ECParameterSpec;
35 import java.security.spec.ECPoint;
36 import java.security.spec.ECPublicKeySpec;
37 import java.security.spec.EllipticCurve;
38 import java.security.spec.InvalidKeySpecException;
39 import java.util.Arrays;
40 
41 import javax.xml.crypto.MarshalException;
42 
43 import org.w3c.dom.Document;
44 import org.w3c.dom.Element;
45 import org.w3c.dom.Node;
46 import org.w3c.dom.Text;
47 
48 import com.sun.org.apache.xml.internal.security.exceptions.XMLSecurityException;
49 import com.sun.org.apache.xml.internal.security.utils.Constants;
50 import com.sun.org.apache.xml.internal.security.utils.I18n;
51 import com.sun.org.apache.xml.internal.security.utils.Signature11ElementProxy;
52 import com.sun.org.apache.xml.internal.security.utils.XMLUtils;
53 
54 public class ECKeyValue extends Signature11ElementProxy implements KeyValueContent {
55 
56     /* Supported curve, secp256r1 */
57     private static final Curve SECP256R1 = initializeCurve(
58         "secp256r1 [NIST P-256, X9.62 prime256v1]",
59         "1.2.840.10045.3.1.7",
60         "FFFFFFFF00000001000000000000000000000000FFFFFFFFFFFFFFFFFFFFFFFF",
61         "FFFFFFFF00000001000000000000000000000000FFFFFFFFFFFFFFFFFFFFFFFC",
62         "5AC635D8AA3A93E7B3EBBD55769886BC651D06B0CC53B0F63BCE3C3E27D2604B",
63         "6B17D1F2E12C4247F8BCE6E563A440F277037D812DEB33A0F4A13945D898C296",
64         "4FE342E2FE1A7F9B8EE7EB4A7C0F9E162BCE33576B315ECECBB6406837BF51F5",
65         "FFFFFFFF00000000FFFFFFFFFFFFFFFFBCE6FAADA7179E84F3B9CAC2FC632551",
66         1
67     );
68 
69     /* Supported curve secp384r1 */
70     private static final Curve SECP384R1 = initializeCurve(
71         "secp384r1 [NIST P-384]",
72         "1.3.132.0.34",
73         "FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFEFFFFFFFF0000000000000000FFFFFFFF",
74         "FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFEFFFFFFFF0000000000000000FFFFFFFC",
75         "B3312FA7E23EE7E4988E056BE3F82D19181D9C6EFE8141120314088F5013875AC656398D8A2ED19D2A85C8EDD3EC2AEF",
76         "AA87CA22BE8B05378EB1C71EF320AD746E1D3B628BA79B9859F741E082542A385502F25DBF55296C3A545E3872760AB7",
77         "3617DE4A96262C6F5D9E98BF9292DC29F8F41DBD289A147CE9DA3113B5F0B8C00A60B1CE1D7E819D7A431D7C90EA0E5F",
78         "FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFC7634D81F4372DDF581A0DB248B0A77AECEC196ACCC52973",
79         1
80     );
81 
82     /* Supported curve secp521r1 */
83     private static final Curve SECP521R1 = initializeCurve(
84         "secp521r1 [NIST P-521]",
85         "1.3.132.0.35",
86         "01FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF",
87         "01FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFC",
88         "0051953EB9618E1C9A1F929A21A0B68540EEA2DA725B99B315F3B8B489918EF109E156193951EC7E937B1652C0BD3BB1BF073573DF883D2C34F1EF451FD46B503F00",
89         "00C6858E06B70404E9CD9E3ECB662395B4429C648139053FB521F828AF606B4D3DBAA14B5E77EFE75928FE1DC127A2FFA8DE3348B3C1856A429BF97E7E31C2E5BD66",
90         "011839296A789A3BC0045C8A5FB42C7D1BD998F54449579B446817AFBD17273E662C97EE72995EF42640C550B9013FAD0761353C7086A272C24088BE94769FD16650",
91         "01FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFA51868783BF2F966B7FCC0148F709A5D03BB5C9B8899C47AEBB6FB71E91386409",
92         1
93     );
94 
initializeCurve(String name, String oid, String sfield, String a, String b, String x, String y, String n, int h)95     private static Curve initializeCurve(String name, String oid,
96             String sfield, String a, String b,
97             String x, String y, String n, int h) {
98         BigInteger p = bigInt(sfield);
99         ECField field = new ECFieldFp(p);
100         EllipticCurve curve = new EllipticCurve(field, bigInt(a),
101                                                 bigInt(b));
102         ECPoint g = new ECPoint(bigInt(x), bigInt(y));
103         return new Curve(name, oid, curve, g, bigInt(n), h);
104     }
105 
106 
107     /**
108      * Constructor DSAKeyValue
109      *
110      * @param element
111      * @param baseURI
112      * @throws XMLSecurityException
113      */
ECKeyValue(Element element, String baseURI)114     public ECKeyValue(Element element, String baseURI) throws XMLSecurityException {
115         super(element, baseURI);
116     }
117 
118     /**
119      * Constructor DSAKeyValue
120      *
121      * @param doc
122      * @param key
123      * @throws IllegalArgumentException
124      */
ECKeyValue(Document doc, Key key)125     public ECKeyValue(Document doc, Key key) throws IllegalArgumentException {
126         super(doc);
127 
128         addReturnToSelf();
129 
130         if (key instanceof ECPublicKey) {
131             ECParameterSpec ecParams = ((ECPublicKey)key).getParams();
132 
133             // NamedCurve
134             String oid = getCurveOid(ecParams);
135             if (oid == null) {
136                 throw new IllegalArgumentException("Invalid ECParameterSpec");
137             }
138 
139             Element namedCurveElement = XMLUtils.createElementInSignature11Space(getDocument(), "NamedCurve");
140             namedCurveElement.setAttributeNS(null, "URI", "urn:oid:" + oid);
141             appendSelf(namedCurveElement);
142             addReturnToSelf();
143 
144             // PublicKey
145             ECPoint ecPoint = ((ECPublicKey)key).getW();
146             byte[] secPublicKey = encodePoint(ecPoint, ecParams.getCurve());
147             String encoded = XMLUtils.encodeToString(secPublicKey);
148             Element publicKeyElement = XMLUtils.createElementInSignature11Space(getDocument(), "PublicKey");
149             Text text = getDocument().createTextNode(encoded);
150 
151             publicKeyElement.appendChild(text);
152 
153             appendSelf(publicKeyElement);
154             addReturnToSelf();
155 
156         } else {
157             Object[] exArgs = { Constants._TAG_ECKEYVALUE, key.getClass().getName() };
158 
159             throw new IllegalArgumentException(I18n.translate("KeyValue.IllegalArgument", exArgs));
160         }
161     }
162 
163     /** {@inheritDoc} */
getPublicKey()164     public PublicKey getPublicKey() throws XMLSecurityException {
165         try {
166             ECParameterSpec ecParams = null;
167             Element curElem = getFirstChildElement(getElement());
168             if (curElem == null) {
169                 throw new MarshalException("KeyValue must contain at least one type");
170             }
171 
172             if ("ECParameters".equals(curElem.getLocalName())
173                 && Constants.SignatureSpec11NS.equals(curElem.getNamespaceURI())) {
174                 throw new UnsupportedOperationException
175                     ("ECParameters not supported");
176             } else if ("NamedCurve".equals(curElem.getLocalName())
177                 && Constants.SignatureSpec11NS.equals(curElem.getNamespaceURI())) {
178                 String uri = null;
179                 if (curElem.hasAttributeNS(null, "URI")) {
180                     uri = curElem.getAttributeNS(null, "URI");
181                 }
182                 // strip off "urn:oid"
183                 if (uri.startsWith("urn:oid:")) {
184                     String oid = uri.substring("urn:oid:".length());
185                     ecParams = getECParameterSpec(oid);
186                     if (ecParams == null) {
187                         throw new MarshalException("Invalid curve OID");
188                     }
189                 } else {
190                     throw new MarshalException("Invalid NamedCurve URI");
191                 }
192             } else {
193                 throw new MarshalException("Invalid ECKeyValue");
194             }
195             curElem = getNextSiblingElement(curElem, "PublicKey", Constants.SignatureSpec11NS);
196             ECPoint ecPoint = null;
197 
198             try {
199                 String content = XMLUtils.getFullTextChildrenFromNode(curElem);
200                 ecPoint = decodePoint(XMLUtils.decode(content), ecParams.getCurve());
201             } catch (IOException ioe) {
202                 throw new MarshalException("Invalid EC Point", ioe);
203             }
204 
205             ECPublicKeySpec spec = new ECPublicKeySpec(ecPoint, ecParams);
206             return KeyFactory.getInstance("EC").generatePublic(spec);
207         } catch (NoSuchAlgorithmException ex) {
208             throw new XMLSecurityException(ex);
209         } catch (InvalidKeySpecException ex) {
210             throw new XMLSecurityException(ex);
211         } catch (MarshalException ex) {
212             throw new XMLSecurityException(ex);
213         }
214     }
215 
216     /** {@inheritDoc} */
getBaseLocalName()217     public String getBaseLocalName() {
218         return Constants._TAG_ECKEYVALUE;
219     }
220 
getFirstChildElement(Node node)221     private static Element getFirstChildElement(Node node) {
222         Node child = node.getFirstChild();
223         while (child != null && child.getNodeType() != Node.ELEMENT_NODE) {
224             child = child.getNextSibling();
225         }
226         return (Element)child;
227     }
228 
getNextSiblingElement(Node node, String localName, String namespaceURI)229     private static Element getNextSiblingElement(Node node, String localName, String namespaceURI)
230         throws MarshalException
231     {
232         return verifyElement(getNextSiblingElement(node), localName, namespaceURI);
233     }
234 
getNextSiblingElement(Node node)235     private static Element getNextSiblingElement(Node node) {
236         Node sibling = node.getNextSibling();
237         while (sibling != null && sibling.getNodeType() != Node.ELEMENT_NODE) {
238             sibling = sibling.getNextSibling();
239         }
240         return (Element)sibling;
241     }
242 
verifyElement(Element elem, String localName, String namespaceURI)243     private static Element verifyElement(Element elem, String localName, String namespaceURI)
244         throws MarshalException
245     {
246         if (elem == null) {
247             throw new MarshalException("Missing " + localName + " element");
248         }
249         String name = elem.getLocalName();
250         String namespace = elem.getNamespaceURI();
251         if (!name.equals(localName) || namespace == null && namespaceURI != null
252             || namespace != null && !namespace.equals(namespaceURI)) {
253             throw new MarshalException("Invalid element name: " +
254                 namespace + ":" + name + ", expected " + namespaceURI + ":" + localName);
255         }
256         return elem;
257     }
258 
getCurveOid(ECParameterSpec params)259     private static String getCurveOid(ECParameterSpec params) {
260         // Check that the params represent one of the supported
261         // curves. If there is a match, return the object identifier
262         // of the curve.
263         Curve match;
264         if (matchCurve(params, SECP256R1)) {
265             match = SECP256R1;
266         } else if (matchCurve(params, SECP384R1)) {
267             match = SECP384R1;
268         } else if (matchCurve(params, SECP521R1)) {
269             match = SECP521R1;
270         } else {
271             return null;
272         }
273         return match.getObjectId();
274     }
275 
matchCurve(ECParameterSpec params, Curve curve)276     private static boolean matchCurve(ECParameterSpec params, Curve curve) {
277         int fieldSize = params.getCurve().getField().getFieldSize();
278         return curve.getCurve().getField().getFieldSize() == fieldSize
279             && curve.getCurve().equals(params.getCurve())
280             && curve.getGenerator().equals(params.getGenerator())
281             && curve.getOrder().equals(params.getOrder())
282             && curve.getCofactor() == params.getCofactor();
283     }
284 
decodePoint(byte[] data, EllipticCurve curve)285     private static ECPoint decodePoint(byte[] data, EllipticCurve curve)
286         throws IOException {
287         if (data.length == 0 || data[0] != 4) {
288             throw new IOException("Only uncompressed point format " +
289                 "supported");
290         }
291         // Per ANSI X9.62, an encoded point is a 1 byte type followed by
292         // ceiling(LOG base 2 field-size / 8) bytes of x and the same of y.
293         int n = (data.length - 1) / 2;
294         if (n != (curve.getField().getFieldSize() + 7) >> 3) {
295             throw new IOException("Point does not match field size");
296         }
297 
298         byte[] xb = Arrays.copyOfRange(data, 1, 1 + n);
299         byte[] yb = Arrays.copyOfRange(data, n + 1, n + 1 + n);
300 
301         return new ECPoint(new BigInteger(1, xb), new BigInteger(1, yb));
302     }
303 
encodePoint(ECPoint point, EllipticCurve curve)304     private static byte[] encodePoint(ECPoint point, EllipticCurve curve) {
305         // get field size in bytes (rounding up)
306         int n = (curve.getField().getFieldSize() + 7) >> 3;
307         byte[] xb = trimZeroes(point.getAffineX().toByteArray());
308         byte[] yb = trimZeroes(point.getAffineY().toByteArray());
309         if (xb.length > n || yb.length > n) {
310             throw new RuntimeException("Point coordinates do not " +
311                                        "match field size");
312         }
313         byte[] b = new byte[1 + (n << 1)];
314         b[0] = 4; // uncompressed
315         System.arraycopy(xb, 0, b, n - xb.length + 1, xb.length);
316         System.arraycopy(yb, 0, b, b.length - yb.length, yb.length);
317         return b;
318     }
319 
trimZeroes(byte[] b)320     private static byte[] trimZeroes(byte[] b) {
321         int i = 0;
322         while (i < b.length - 1 && b[i] == 0) {
323             i++;
324         }
325         if (i == 0) {
326             return b;
327         }
328         return Arrays.copyOfRange(b, i, b.length);
329     }
330 
getECParameterSpec(String oid)331     private static ECParameterSpec getECParameterSpec(String oid) {
332         if (oid.equals(SECP256R1.getObjectId())) {
333             return SECP256R1;
334         } else if (oid.equals(SECP384R1.getObjectId())) {
335             return SECP384R1;
336         } else if (oid.equals(SECP521R1.getObjectId())) {
337             return SECP521R1;
338         } else {
339             return null;
340         }
341     }
342 
343     static final class Curve extends ECParameterSpec {
344         private final String name;
345         private final String oid;
346 
Curve(String name, String oid, EllipticCurve curve, ECPoint g, BigInteger n, int h)347         Curve(String name, String oid, EllipticCurve curve,
348               ECPoint g, BigInteger n, int h) {
349             super(curve, g, n, h);
350             this.name = name;
351             this.oid = oid;
352         }
353 
getName()354         private String getName() {
355             return name;
356         }
357 
getObjectId()358         private String getObjectId() {
359             return oid;
360         }
361     }
362 
bigInt(String s)363     private static BigInteger bigInt(String s) {
364         return new BigInteger(s, 16);
365     }
366 }
367