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, 2008, 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 = new DOMCryptoBinary(modulusElem.getFirstChild()); 223 Element exponentElem = DOMUtils.getNextSiblingElement(modulusElem); 224 exponent = new DOMCryptoBinary(exponentElem.getFirstChild()); 225 RSAPublicKeySpec spec = new RSAPublicKeySpec(modulus.getBigNum(), 226 exponent.getBigNum()); 227 return generatePublicKey(rsakf, spec); 228 } 229 } 230 231 static final class DSA extends DOMKeyValue { 232 // DSAKeyValue CryptoBinaries 233 private DOMCryptoBinary p, q, g, y, j; //, seed, pgen; 234 private KeyFactory dsakf; 235 236 DSA(PublicKey key) throws KeyException { 237 super(key); 238 DSAPublicKey dkey = (DSAPublicKey) key; 239 DSAParams params = dkey.getParams(); 240 p = new DOMCryptoBinary(params.getP()); 241 q = new DOMCryptoBinary(params.getQ()); 242 g = new DOMCryptoBinary(params.getG()); 243 y = new DOMCryptoBinary(dkey.getY()); 244 } 245 246 DSA(Element elem) throws MarshalException { 247 super(elem); 248 } 249 250 void marshalPublicKey(Node parent, Document doc, String dsPrefix, 251 DOMCryptoContext context) 252 throws MarshalException 253 { 254 Element dsaElem = DOMUtils.createElement(doc, "DSAKeyValue", 255 XMLSignature.XMLNS, 256 dsPrefix); 257 // parameters J, Seed & PgenCounter are not included 258 Element pElem = DOMUtils.createElement(doc, "P", XMLSignature.XMLNS, 259 dsPrefix); 260 Element qElem = DOMUtils.createElement(doc, "Q", XMLSignature.XMLNS, 261 dsPrefix); 262 Element gElem = DOMUtils.createElement(doc, "G", XMLSignature.XMLNS, 263 dsPrefix); 264 Element yElem = DOMUtils.createElement(doc, "Y", XMLSignature.XMLNS, 265 dsPrefix); 266 p.marshal(pElem, dsPrefix, context); 267 q.marshal(qElem, dsPrefix, context); 268 g.marshal(gElem, dsPrefix, context); 269 y.marshal(yElem, dsPrefix, context); 270 dsaElem.appendChild(pElem); 271 dsaElem.appendChild(qElem); 272 dsaElem.appendChild(gElem); 273 dsaElem.appendChild(yElem); 274 parent.appendChild(dsaElem); 275 } 276 277 PublicKey unmarshalKeyValue(Element kvtElem) 278 throws MarshalException 279 { 280 if (dsakf == null) { 281 try { 282 dsakf = KeyFactory.getInstance("DSA"); 283 } catch (NoSuchAlgorithmException e) { 284 throw new RuntimeException 285 ("unable to create DSA KeyFactory: " + e.getMessage()); 286 } 287 } 288 Element curElem = DOMUtils.getFirstChildElement(kvtElem); 289 // check for P and Q 290 if (curElem.getLocalName().equals("P")) { 291 p = new DOMCryptoBinary(curElem.getFirstChild()); 292 curElem = DOMUtils.getNextSiblingElement(curElem); 293 q = new DOMCryptoBinary(curElem.getFirstChild()); 294 curElem = DOMUtils.getNextSiblingElement(curElem); 295 } 296 if (curElem.getLocalName().equals("G")) { 297 g = new DOMCryptoBinary(curElem.getFirstChild()); 298 curElem = DOMUtils.getNextSiblingElement(curElem); 299 } 300 y = new DOMCryptoBinary(curElem.getFirstChild()); 301 curElem = DOMUtils.getNextSiblingElement(curElem); 302 if (curElem != null && curElem.getLocalName().equals("J")) { 303 j = new DOMCryptoBinary(curElem.getFirstChild()); 304 // curElem = DOMUtils.getNextSiblingElement(curElem); 305 } 306 /* 307 if (curElem != null) { 308 seed = new DOMCryptoBinary(curElem.getFirstChild()); 309 curElem = DOMUtils.getNextSiblingElement(curElem); 310 pgen = new DOMCryptoBinary(curElem.getFirstChild()); 311 } 312 */ 313 //@@@ do we care about j, pgenCounter or seed? 314 DSAPublicKeySpec spec = new DSAPublicKeySpec(y.getBigNum(), 315 p.getBigNum(), 316 q.getBigNum(), 317 g.getBigNum()); 318 return generatePublicKey(dsakf, spec); 319 } 320 } 321 322 static final class EC extends DOMKeyValue { 323 // ECKeyValue CryptoBinaries 324 private byte[] ecPublicKey; 325 private KeyFactory eckf; 326 private ECParameterSpec ecParams; 327 private Method encodePoint, decodePoint, getCurveName, 328 getECParameterSpec; 329 330 EC(PublicKey key) throws KeyException { 331 super(key); 332 ECPublicKey ecKey = (ECPublicKey)key; 333 ECPoint ecPoint = ecKey.getW(); 334 ecParams = ecKey.getParams(); 335 try { 336 AccessController.doPrivileged( 337 new PrivilegedExceptionAction<Void>() { 338 public Void run() throws 339 ClassNotFoundException, NoSuchMethodException 340 { 341 getMethods(); 342 return null; 343 } 344 } 345 ); 346 } catch (PrivilegedActionException pae) { 347 throw new KeyException("ECKeyValue not supported", 348 pae.getException()); 349 } 350 Object[] args = new Object[] { ecPoint, ecParams.getCurve() }; 351 try { 352 ecPublicKey = (byte[])encodePoint.invoke(null, args); 353 } catch (IllegalAccessException iae) { 354 throw new KeyException(iae); 355 } catch (InvocationTargetException ite) { 356 throw new KeyException(ite); 357 } 358 } 359 360 EC(Element dmElem) throws MarshalException { 361 super(dmElem); 362 } 363 364 void getMethods() throws ClassNotFoundException, NoSuchMethodException { 365 Class c = Class.forName("sun.security.ec.ECParameters"); 366 Class[] params = new Class[] { ECPoint.class, EllipticCurve.class }; 367 encodePoint = c.getMethod("encodePoint", params); 368 params = new Class[] { ECParameterSpec.class }; 369 getCurveName = c.getMethod("getCurveName", params); 370 params = new Class[] { byte[].class, EllipticCurve.class }; 371 decodePoint = c.getMethod("decodePoint", params); 372 c = Class.forName("sun.security.ec.NamedCurve"); 373 params = new Class[] { String.class }; 374 getECParameterSpec = c.getMethod("getECParameterSpec", params); 375 } 376 377 void marshalPublicKey(Node parent, Document doc, String dsPrefix, 378 DOMCryptoContext context) 379 throws MarshalException 380 { 381 String prefix = DOMUtils.getNSPrefix(context, XMLDSIG_11_XMLNS); 382 Element ecKeyValueElem = DOMUtils.createElement(doc, "ECKeyValue", 383 XMLDSIG_11_XMLNS, 384 prefix); 385 Element namedCurveElem = DOMUtils.createElement(doc, "NamedCurve", 386 XMLDSIG_11_XMLNS, 387 prefix); 388 Element publicKeyElem = DOMUtils.createElement(doc, "PublicKey", 389 XMLDSIG_11_XMLNS, 390 prefix); 391 Object[] args = new Object[] { ecParams }; 392 try { 393 String oid = (String) getCurveName.invoke(null, args); 394 DOMUtils.setAttribute(namedCurveElem, "URI", "urn:oid:" + oid); 395 } catch (IllegalAccessException iae) { 396 throw new MarshalException(iae); 397 } catch (InvocationTargetException ite) { 398 throw new MarshalException(ite); 399 } 400 String qname = (prefix == null || prefix.length() == 0) 401 ? "xmlns" : "xmlns:" + prefix; 402 namedCurveElem.setAttributeNS("http://www.w3.org/2000/xmlns/", 403 qname, XMLDSIG_11_XMLNS); 404 ecKeyValueElem.appendChild(namedCurveElem); 405 String encoded = Base64.encode(ecPublicKey); 406 publicKeyElem.appendChild 407 (DOMUtils.getOwnerDocument(publicKeyElem).createTextNode(encoded)); 408 ecKeyValueElem.appendChild(publicKeyElem); 409 parent.appendChild(ecKeyValueElem); 410 } 411 412 PublicKey unmarshalKeyValue(Element kvtElem) 413 throws MarshalException 414 { 415 if (eckf == null) { 416 try { 417 eckf = KeyFactory.getInstance("EC"); 418 } catch (NoSuchAlgorithmException e) { 419 throw new RuntimeException 420 ("unable to create EC KeyFactory: " + e.getMessage()); 421 } 422 } 423 try { 424 AccessController.doPrivileged( 425 new PrivilegedExceptionAction<Void>() { 426 public Void run() throws 427 ClassNotFoundException, NoSuchMethodException 428 { 429 getMethods(); 430 return null; 431 } 432 } 433 ); 434 } catch (PrivilegedActionException pae) { 435 throw new MarshalException("ECKeyValue not supported", 436 pae.getException()); 437 } 438 ECParameterSpec ecParams = null; 439 Element curElem = DOMUtils.getFirstChildElement(kvtElem); 440 if (curElem.getLocalName().equals("ECParameters")) { 441 throw new UnsupportedOperationException 442 ("ECParameters not supported"); 443 } else if (curElem.getLocalName().equals("NamedCurve")) { 444 String uri = DOMUtils.getAttributeValue(curElem, "URI"); 445 // strip off "urn:oid" 446 if (uri.startsWith("urn:oid:")) { 447 String oid = uri.substring(8); 448 try { 449 Object[] args = new Object[] { oid }; 450 ecParams = (ECParameterSpec) 451 getECParameterSpec.invoke(null, args); 452 } catch (IllegalAccessException iae) { 453 throw new MarshalException(iae); 454 } catch (InvocationTargetException ite) { 455 throw new MarshalException(ite); 456 } 457 } else { 458 throw new MarshalException("Invalid NamedCurve URI"); 459 } 460 } else { 461 throw new MarshalException("Invalid ECKeyValue"); 462 } 463 curElem = DOMUtils.getNextSiblingElement(curElem); 464 ECPoint ecPoint = null; 465 try { 466 Object[] args = new Object[] { Base64.decode(curElem), 467 ecParams.getCurve() }; 468 ecPoint = (ECPoint)decodePoint.invoke(null, args); 469 } catch (Base64DecodingException bde) { 470 throw new MarshalException("Invalid EC PublicKey", bde); 471 } catch (IllegalAccessException iae) { 472 throw new MarshalException(iae); 473 } catch (InvocationTargetException ite) { 474 throw new MarshalException(ite); 475 } 476 /* 477 ecPoint = sun.security.ec.ECParameters.decodePoint( 478 Base64.decode(curElem), ecParams.getCurve()); 479 */ 480 ECPublicKeySpec spec = new ECPublicKeySpec(ecPoint, ecParams); 481 return generatePublicKey(eckf, spec); 482 } 483 } 484 485 static final class Unknown extends DOMKeyValue { 486 private javax.xml.crypto.dom.DOMStructure externalPublicKey; 487 Unknown(Element elem) throws MarshalException { 488 super(elem); 489 } 490 PublicKey unmarshalKeyValue(Element kvElem) throws MarshalException { 491 externalPublicKey = new javax.xml.crypto.dom.DOMStructure(kvElem); 492 return null; 493 } 494 void marshalPublicKey(Node parent, Document doc, String dsPrefix, 495 DOMCryptoContext context) 496 throws MarshalException 497 { 498 parent.appendChild(externalPublicKey.getNode()); 499 } 500 } 501 }