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 /* 24 * Copyright (c) 2005, 2013, Oracle and/or its affiliates. All rights reserved. 25 */ 26 /* 27 * $Id: DOMKeyValue.java 1333415 2012-05-03 12:03:51Z coheigea $ 28 */ 29 package org.jcp.xml.dsig.internal.dom; 30 31 import javax.xml.crypto.*; 32 import javax.xml.crypto.dom.DOMCryptoContext; 33 import javax.xml.crypto.dsig.*; 34 import javax.xml.crypto.dsig.keyinfo.KeyValue; 35 36 // import java.io.IOException; 37 import java.lang.reflect.InvocationTargetException; 38 import java.lang.reflect.Method; 39 import java.security.AccessController; 40 import java.security.KeyException; 41 import java.security.KeyFactory; 42 import java.security.NoSuchAlgorithmException; 43 import java.security.PrivilegedActionException; 44 import java.security.PrivilegedExceptionAction; 45 import java.security.PublicKey; 46 import java.security.interfaces.DSAParams; 47 import java.security.interfaces.DSAPublicKey; 48 import java.security.interfaces.ECPublicKey; 49 import java.security.interfaces.RSAPublicKey; 50 import java.security.spec.DSAPublicKeySpec; 51 import java.security.spec.ECParameterSpec; 52 import java.security.spec.ECPoint; 53 import java.security.spec.ECPublicKeySpec; 54 import java.security.spec.EllipticCurve; 55 import java.security.spec.InvalidKeySpecException; 56 import java.security.spec.KeySpec; 57 import java.security.spec.RSAPublicKeySpec; 58 import org.w3c.dom.Document; 59 import org.w3c.dom.Element; 60 import org.w3c.dom.Node; 61 62 import com.sun.org.apache.xml.internal.security.exceptions.Base64DecodingException; 63 import com.sun.org.apache.xml.internal.security.utils.Base64; 64 65 /** 66 * DOM-based implementation of KeyValue. 67 * 68 * @author Sean Mullan 69 */ 70 public abstract class DOMKeyValue extends DOMStructure implements KeyValue { 71 72 private static final String XMLDSIG_11_XMLNS 73 = "http://www.w3.org/2009/xmldsig11#"; 74 private final PublicKey publicKey; 75 76 public DOMKeyValue(PublicKey key) throws KeyException { 77 if (key == null) { 78 throw new NullPointerException("key cannot be null"); 79 } 80 this.publicKey = key; 81 } 82 83 /** 84 * Creates a <code>DOMKeyValue</code> from an element. 85 * 86 * @param kvtElem a KeyValue child element 87 */ 88 public DOMKeyValue(Element kvtElem) throws MarshalException { 89 this.publicKey = unmarshalKeyValue(kvtElem); 90 } 91 92 static KeyValue unmarshal(Element kvElem) throws MarshalException { 93 Element kvtElem = DOMUtils.getFirstChildElement(kvElem); 94 if (kvtElem.getLocalName().equals("DSAKeyValue")) { 95 return new DSA(kvtElem); 96 } else if (kvtElem.getLocalName().equals("RSAKeyValue")) { 97 return new RSA(kvtElem); 98 } else if (kvtElem.getLocalName().equals("ECKeyValue")) { 99 return new EC(kvtElem); 100 } else { 101 return new Unknown(kvtElem); 102 } 103 } 104 105 public PublicKey getPublicKey() throws KeyException { 106 if (publicKey == null) { 107 throw new KeyException("can't convert KeyValue to PublicKey"); 108 } else { 109 return publicKey; 110 } 111 } 112 113 public void marshal(Node parent, String dsPrefix, DOMCryptoContext context) 114 throws MarshalException 115 { 116 Document ownerDoc = DOMUtils.getOwnerDocument(parent); 117 118 // create KeyValue element 119 Element kvElem = DOMUtils.createElement(ownerDoc, "KeyValue", 120 XMLSignature.XMLNS, dsPrefix); 121 marshalPublicKey(kvElem, ownerDoc, dsPrefix, context); 122 123 parent.appendChild(kvElem); 124 } 125 126 abstract void marshalPublicKey(Node parent, Document doc, String dsPrefix, 127 DOMCryptoContext context) throws MarshalException; 128 129 abstract PublicKey unmarshalKeyValue(Element kvtElem) 130 throws MarshalException; 131 132 private static PublicKey generatePublicKey(KeyFactory kf, KeySpec keyspec) { 133 try { 134 return kf.generatePublic(keyspec); 135 } catch (InvalidKeySpecException e) { 136 //@@@ should dump exception to log 137 return null; 138 } 139 } 140 141 @Override 142 public boolean equals(Object obj) { 143 if (this == obj) { 144 return true; 145 } 146 if (!(obj instanceof KeyValue)) { 147 return false; 148 } 149 try { 150 KeyValue kv = (KeyValue)obj; 151 if (publicKey == null ) { 152 if (kv.getPublicKey() != null) { 153 return false; 154 } 155 } else if (!publicKey.equals(kv.getPublicKey())) { 156 return false; 157 } 158 } catch (KeyException ke) { 159 // no practical way to determine if the keys are equal 160 return false; 161 } 162 163 return true; 164 } 165 166 @Override 167 public int hashCode() { 168 int result = 17; 169 if (publicKey != null) { 170 result = 31 * result + publicKey.hashCode(); 171 } 172 173 return result; 174 } 175 176 static final class RSA extends DOMKeyValue { 177 // RSAKeyValue CryptoBinaries 178 private DOMCryptoBinary modulus, exponent; 179 private KeyFactory rsakf; 180 181 RSA(PublicKey key) throws KeyException { 182 super(key); 183 RSAPublicKey rkey = (RSAPublicKey)key; 184 exponent = new DOMCryptoBinary(rkey.getPublicExponent()); 185 modulus = new DOMCryptoBinary(rkey.getModulus()); 186 } 187 188 RSA(Element elem) throws MarshalException { 189 super(elem); 190 } 191 192 void marshalPublicKey(Node parent, Document doc, String dsPrefix, 193 DOMCryptoContext context) throws MarshalException { 194 Element rsaElem = DOMUtils.createElement(doc, "RSAKeyValue", 195 XMLSignature.XMLNS, 196 dsPrefix); 197 Element modulusElem = DOMUtils.createElement(doc, "Modulus", 198 XMLSignature.XMLNS, 199 dsPrefix); 200 Element exponentElem = DOMUtils.createElement(doc, "Exponent", 201 XMLSignature.XMLNS, 202 dsPrefix); 203 modulus.marshal(modulusElem, dsPrefix, context); 204 exponent.marshal(exponentElem, dsPrefix, context); 205 rsaElem.appendChild(modulusElem); 206 rsaElem.appendChild(exponentElem); 207 parent.appendChild(rsaElem); 208 } 209 210 PublicKey unmarshalKeyValue(Element kvtElem) 211 throws MarshalException 212 { 213 if (rsakf == null) { 214 try { 215 rsakf = KeyFactory.getInstance("RSA"); 216 } catch (NoSuchAlgorithmException e) { 217 throw new RuntimeException 218 ("unable to create RSA KeyFactory: " + e.getMessage()); 219 } 220 } 221 Element modulusElem = DOMUtils.getFirstChildElement(kvtElem, 222 "Modulus"); 223 modulus = new DOMCryptoBinary(modulusElem.getFirstChild()); 224 Element exponentElem = DOMUtils.getNextSiblingElement(modulusElem, 225 "Exponent"); 226 exponent = new DOMCryptoBinary(exponentElem.getFirstChild()); 227 RSAPublicKeySpec spec = new RSAPublicKeySpec(modulus.getBigNum(), 228 exponent.getBigNum()); 229 return generatePublicKey(rsakf, spec); 230 } 231 } 232 233 static final class DSA extends DOMKeyValue { 234 // DSAKeyValue CryptoBinaries 235 private DOMCryptoBinary p, q, g, y, j; //, seed, pgen; 236 private KeyFactory dsakf; 237 238 DSA(PublicKey key) throws KeyException { 239 super(key); 240 DSAPublicKey dkey = (DSAPublicKey) key; 241 DSAParams params = dkey.getParams(); 242 p = new DOMCryptoBinary(params.getP()); 243 q = new DOMCryptoBinary(params.getQ()); 244 g = new DOMCryptoBinary(params.getG()); 245 y = new DOMCryptoBinary(dkey.getY()); 246 } 247 248 DSA(Element elem) throws MarshalException { 249 super(elem); 250 } 251 252 void marshalPublicKey(Node parent, Document doc, String dsPrefix, 253 DOMCryptoContext context) 254 throws MarshalException 255 { 256 Element dsaElem = DOMUtils.createElement(doc, "DSAKeyValue", 257 XMLSignature.XMLNS, 258 dsPrefix); 259 // parameters J, Seed & PgenCounter are not included 260 Element pElem = DOMUtils.createElement(doc, "P", XMLSignature.XMLNS, 261 dsPrefix); 262 Element qElem = DOMUtils.createElement(doc, "Q", XMLSignature.XMLNS, 263 dsPrefix); 264 Element gElem = DOMUtils.createElement(doc, "G", XMLSignature.XMLNS, 265 dsPrefix); 266 Element yElem = DOMUtils.createElement(doc, "Y", XMLSignature.XMLNS, 267 dsPrefix); 268 p.marshal(pElem, dsPrefix, context); 269 q.marshal(qElem, dsPrefix, context); 270 g.marshal(gElem, dsPrefix, context); 271 y.marshal(yElem, dsPrefix, context); 272 dsaElem.appendChild(pElem); 273 dsaElem.appendChild(qElem); 274 dsaElem.appendChild(gElem); 275 dsaElem.appendChild(yElem); 276 parent.appendChild(dsaElem); 277 } 278 279 PublicKey unmarshalKeyValue(Element kvtElem) 280 throws MarshalException 281 { 282 if (dsakf == null) { 283 try { 284 dsakf = KeyFactory.getInstance("DSA"); 285 } catch (NoSuchAlgorithmException e) { 286 throw new RuntimeException 287 ("unable to create DSA KeyFactory: " + e.getMessage()); 288 } 289 } 290 Element curElem = DOMUtils.getFirstChildElement(kvtElem); 291 // check for P and Q 292 if (curElem.getLocalName().equals("P")) { 293 p = new DOMCryptoBinary(curElem.getFirstChild()); 294 curElem = DOMUtils.getNextSiblingElement(curElem, "Q"); 295 q = new DOMCryptoBinary(curElem.getFirstChild()); 296 curElem = DOMUtils.getNextSiblingElement(curElem); 297 } 298 if (curElem.getLocalName().equals("G")) { 299 g = new DOMCryptoBinary(curElem.getFirstChild()); 300 curElem = DOMUtils.getNextSiblingElement(curElem, "Y"); 301 } 302 y = new DOMCryptoBinary(curElem.getFirstChild()); 303 curElem = DOMUtils.getNextSiblingElement(curElem); 304 if (curElem != null && curElem.getLocalName().equals("J")) { 305 j = new DOMCryptoBinary(curElem.getFirstChild()); 306 // curElem = DOMUtils.getNextSiblingElement(curElem); 307 } 308 /* 309 if (curElem != null) { 310 seed = new DOMCryptoBinary(curElem.getFirstChild()); 311 curElem = DOMUtils.getNextSiblingElement(curElem); 312 pgen = new DOMCryptoBinary(curElem.getFirstChild()); 313 } 314 */ 315 //@@@ do we care about j, pgenCounter or seed? 316 DSAPublicKeySpec spec = new DSAPublicKeySpec(y.getBigNum(), 317 p.getBigNum(), 318 q.getBigNum(), 319 g.getBigNum()); 320 return generatePublicKey(dsakf, spec); 321 } 322 } 323 324 static final class EC extends DOMKeyValue { 325 // ECKeyValue CryptoBinaries 326 private byte[] ecPublicKey; 327 private KeyFactory eckf; 328 private ECParameterSpec ecParams; 329 private Method encodePoint, decodePoint, getCurveName, 330 getECParameterSpec; 331 332 EC(PublicKey key) throws KeyException { 333 super(key); 334 ECPublicKey ecKey = (ECPublicKey)key; 335 ECPoint ecPoint = ecKey.getW(); 336 ecParams = ecKey.getParams(); 337 try { 338 AccessController.doPrivileged( 339 new PrivilegedExceptionAction<Void>() { 340 public Void run() throws 341 ClassNotFoundException, NoSuchMethodException 342 { 343 getMethods(); 344 return null; 345 } 346 } 347 ); 348 } catch (PrivilegedActionException pae) { 349 throw new KeyException("ECKeyValue not supported", 350 pae.getException()); 351 } 352 Object[] args = new Object[] { ecPoint, ecParams.getCurve() }; 353 try { 354 ecPublicKey = (byte[])encodePoint.invoke(null, args); 355 } catch (IllegalAccessException iae) { 356 throw new KeyException(iae); 357 } catch (InvocationTargetException ite) { 358 throw new KeyException(ite); 359 } 360 } 361 362 EC(Element dmElem) throws MarshalException { 363 super(dmElem); 364 } 365 366 void getMethods() throws ClassNotFoundException, NoSuchMethodException { 367 Class<?> c = Class.forName("sun.security.ec.ECParameters"); 368 Class<?>[] params = new Class<?>[] { ECPoint.class, 369 EllipticCurve.class }; 370 encodePoint = c.getMethod("encodePoint", params); 371 params = new Class<?>[] { ECParameterSpec.class }; 372 getCurveName = c.getMethod("getCurveName", params); 373 params = new Class<?>[] { byte[].class, EllipticCurve.class }; 374 decodePoint = c.getMethod("decodePoint", params); 375 c = Class.forName("sun.security.ec.NamedCurve"); 376 params = new Class<?>[] { String.class }; 377 getECParameterSpec = c.getMethod("getECParameterSpec", params); 378 } 379 380 void marshalPublicKey(Node parent, Document doc, String dsPrefix, 381 DOMCryptoContext context) 382 throws MarshalException 383 { 384 String prefix = DOMUtils.getNSPrefix(context, XMLDSIG_11_XMLNS); 385 Element ecKeyValueElem = DOMUtils.createElement(doc, "ECKeyValue", 386 XMLDSIG_11_XMLNS, 387 prefix); 388 Element namedCurveElem = DOMUtils.createElement(doc, "NamedCurve", 389 XMLDSIG_11_XMLNS, 390 prefix); 391 Element publicKeyElem = DOMUtils.createElement(doc, "PublicKey", 392 XMLDSIG_11_XMLNS, 393 prefix); 394 Object[] args = new Object[] { ecParams }; 395 try { 396 String oid = (String) getCurveName.invoke(null, args); 397 DOMUtils.setAttribute(namedCurveElem, "URI", "urn:oid:" + oid); 398 } catch (IllegalAccessException iae) { 399 throw new MarshalException(iae); 400 } catch (InvocationTargetException ite) { 401 throw new MarshalException(ite); 402 } 403 String qname = (prefix == null || prefix.length() == 0) 404 ? "xmlns" : "xmlns:" + prefix; 405 namedCurveElem.setAttributeNS("http://www.w3.org/2000/xmlns/", 406 qname, XMLDSIG_11_XMLNS); 407 ecKeyValueElem.appendChild(namedCurveElem); 408 String encoded = Base64.encode(ecPublicKey); 409 publicKeyElem.appendChild 410 (DOMUtils.getOwnerDocument(publicKeyElem).createTextNode(encoded)); 411 ecKeyValueElem.appendChild(publicKeyElem); 412 parent.appendChild(ecKeyValueElem); 413 } 414 415 PublicKey unmarshalKeyValue(Element kvtElem) 416 throws MarshalException 417 { 418 if (eckf == null) { 419 try { 420 eckf = KeyFactory.getInstance("EC"); 421 } catch (NoSuchAlgorithmException e) { 422 throw new RuntimeException 423 ("unable to create EC KeyFactory: " + e.getMessage()); 424 } 425 } 426 try { 427 AccessController.doPrivileged( 428 new PrivilegedExceptionAction<Void>() { 429 public Void run() throws 430 ClassNotFoundException, NoSuchMethodException 431 { 432 getMethods(); 433 return null; 434 } 435 } 436 ); 437 } catch (PrivilegedActionException pae) { 438 throw new MarshalException("ECKeyValue not supported", 439 pae.getException()); 440 } 441 ECParameterSpec ecParams = null; 442 Element curElem = DOMUtils.getFirstChildElement(kvtElem); 443 if (curElem.getLocalName().equals("ECParameters")) { 444 throw new UnsupportedOperationException 445 ("ECParameters not supported"); 446 } else if (curElem.getLocalName().equals("NamedCurve")) { 447 String uri = DOMUtils.getAttributeValue(curElem, "URI"); 448 // strip off "urn:oid" 449 if (uri.startsWith("urn:oid:")) { 450 String oid = uri.substring(8); 451 try { 452 Object[] args = new Object[] { oid }; 453 ecParams = (ECParameterSpec) 454 getECParameterSpec.invoke(null, args); 455 } catch (IllegalAccessException iae) { 456 throw new MarshalException(iae); 457 } catch (InvocationTargetException ite) { 458 throw new MarshalException(ite); 459 } 460 } else { 461 throw new MarshalException("Invalid NamedCurve URI"); 462 } 463 } else { 464 throw new MarshalException("Invalid ECKeyValue"); 465 } 466 curElem = DOMUtils.getNextSiblingElement(curElem, "PublicKey"); 467 ECPoint ecPoint = null; 468 try { 469 Object[] args = new Object[] { Base64.decode(curElem), 470 ecParams.getCurve() }; 471 ecPoint = (ECPoint)decodePoint.invoke(null, args); 472 } catch (Base64DecodingException bde) { 473 throw new MarshalException("Invalid EC PublicKey", bde); 474 } catch (IllegalAccessException iae) { 475 throw new MarshalException(iae); 476 } catch (InvocationTargetException ite) { 477 throw new MarshalException(ite); 478 } 479 /* 480 ecPoint = sun.security.ec.ECParameters.decodePoint( 481 Base64.decode(curElem), ecParams.getCurve()); 482 */ 483 ECPublicKeySpec spec = new ECPublicKeySpec(ecPoint, ecParams); 484 return generatePublicKey(eckf, spec); 485 } 486 } 487 488 static final class Unknown extends DOMKeyValue { 489 private javax.xml.crypto.dom.DOMStructure externalPublicKey; 490 Unknown(Element elem) throws MarshalException { 491 super(elem); 492 } 493 PublicKey unmarshalKeyValue(Element kvElem) throws MarshalException { 494 externalPublicKey = new javax.xml.crypto.dom.DOMStructure(kvElem); 495 return null; 496 } 497 void marshalPublicKey(Node parent, Document doc, String dsPrefix, 498 DOMCryptoContext context) 499 throws MarshalException 500 { 501 parent.appendChild(externalPublicKey.getNode()); 502 } 503 } 504 }