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