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, EllipticCurve.class };
 369             encodePoint = c.getMethod("encodePoint", params);
 370             params = new Class[] { ECParameterSpec.class };
 371             getCurveName = c.getMethod("getCurveName", params);
 372             params = new Class[] { byte[].class, EllipticCurve.class };
 373             decodePoint = c.getMethod("decodePoint", params);
 374             c  = Class.forName("sun.security.ec.NamedCurve");
 375             params = new Class[] { String.class };
 376             getECParameterSpec = c.getMethod("getECParameterSpec", params);
 377         }
 378 
 379         void marshalPublicKey(Node parent, Document doc, String dsPrefix,
 380                               DOMCryptoContext context)
 381             throws MarshalException
 382         {
 383             String prefix = DOMUtils.getNSPrefix(context, XMLDSIG_11_XMLNS);
 384             Element ecKeyValueElem = DOMUtils.createElement(doc, "ECKeyValue",
 385                                                             XMLDSIG_11_XMLNS,
 386                                                             prefix);
 387             Element namedCurveElem = DOMUtils.createElement(doc, "NamedCurve",
 388                                                             XMLDSIG_11_XMLNS,
 389                                                             prefix);
 390             Element publicKeyElem = DOMUtils.createElement(doc, "PublicKey",
 391                                                            XMLDSIG_11_XMLNS,
 392                                                            prefix);
 393             Object[] args = new Object[] { ecParams };
 394             try {
 395                 String oid = (String) getCurveName.invoke(null, args);
 396                 DOMUtils.setAttribute(namedCurveElem, "URI", "urn:oid:" + oid);
 397             } catch (IllegalAccessException iae) {
 398                 throw new MarshalException(iae);
 399             } catch (InvocationTargetException ite) {
 400                 throw new MarshalException(ite);
 401             }
 402             String qname = (prefix == null || prefix.length() == 0)
 403                        ? "xmlns" : "xmlns:" + prefix;
 404             namedCurveElem.setAttributeNS("http://www.w3.org/2000/xmlns/",
 405                                           qname, XMLDSIG_11_XMLNS);
 406             ecKeyValueElem.appendChild(namedCurveElem);
 407             String encoded = Base64.encode(ecPublicKey);
 408             publicKeyElem.appendChild
 409                 (DOMUtils.getOwnerDocument(publicKeyElem).createTextNode(encoded));
 410             ecKeyValueElem.appendChild(publicKeyElem);
 411             parent.appendChild(ecKeyValueElem);
 412         }
 413 
 414         PublicKey unmarshalKeyValue(Element kvtElem)
 415             throws MarshalException
 416         {
 417             if (eckf == null) {
 418                 try {
 419                     eckf = KeyFactory.getInstance("EC");
 420                 } catch (NoSuchAlgorithmException e) {
 421                     throw new RuntimeException
 422                         ("unable to create EC KeyFactory: " + e.getMessage());
 423                 }
 424             }
 425             try {
 426                 AccessController.doPrivileged(
 427                     new PrivilegedExceptionAction<Void>() {
 428                         public Void run() throws
 429                             ClassNotFoundException, NoSuchMethodException
 430                         {
 431                             getMethods();
 432                             return null;
 433                         }
 434                     }
 435                 );
 436             } catch (PrivilegedActionException pae) {
 437                 throw new MarshalException("ECKeyValue not supported",
 438                                            pae.getException());
 439             }
 440             ECParameterSpec ecParams = null;
 441             Element curElem = DOMUtils.getFirstChildElement(kvtElem);
 442             if (curElem.getLocalName().equals("ECParameters")) {
 443                 throw new UnsupportedOperationException
 444                     ("ECParameters not supported");
 445             } else if (curElem.getLocalName().equals("NamedCurve")) {
 446                 String uri = DOMUtils.getAttributeValue(curElem, "URI");
 447                 // strip off "urn:oid"
 448                 if (uri.startsWith("urn:oid:")) {
 449                     String oid = uri.substring(8);
 450                     try {
 451                         Object[] args = new Object[] { oid };
 452                         ecParams = (ECParameterSpec)
 453                                     getECParameterSpec.invoke(null, args);
 454                     } catch (IllegalAccessException iae) {
 455                         throw new MarshalException(iae);
 456                     } catch (InvocationTargetException ite) {
 457                         throw new MarshalException(ite);
 458                     }
 459                 } else {
 460                     throw new MarshalException("Invalid NamedCurve URI");
 461                 }
 462             } else {
 463                 throw new MarshalException("Invalid ECKeyValue");
 464             }
 465             curElem = DOMUtils.getNextSiblingElement(curElem, "PublicKey");
 466             ECPoint ecPoint = null;
 467             try {
 468                 Object[] args = new Object[] { Base64.decode(curElem),
 469                                                ecParams.getCurve() };
 470                 ecPoint = (ECPoint)decodePoint.invoke(null, args);
 471             } catch (Base64DecodingException bde) {
 472                 throw new MarshalException("Invalid EC PublicKey", bde);
 473             } catch (IllegalAccessException iae) {
 474                 throw new MarshalException(iae);
 475             } catch (InvocationTargetException ite) {
 476                 throw new MarshalException(ite);
 477             }
 478 /*
 479                 ecPoint = sun.security.ec.ECParameters.decodePoint(
 480                     Base64.decode(curElem), ecParams.getCurve());
 481 */
 482             ECPublicKeySpec spec = new ECPublicKeySpec(ecPoint, ecParams);
 483             return generatePublicKey(eckf, spec);
 484         }
 485     }
 486 
 487     static final class Unknown extends DOMKeyValue {
 488         private javax.xml.crypto.dom.DOMStructure externalPublicKey;
 489         Unknown(Element elem) throws MarshalException {
 490             super(elem);
 491         }
 492         PublicKey unmarshalKeyValue(Element kvElem) throws MarshalException {
 493             externalPublicKey = new javax.xml.crypto.dom.DOMStructure(kvElem);
 494             return null;
 495         }
 496         void marshalPublicKey(Node parent, Document doc, String dsPrefix,
 497                               DOMCryptoContext context)
 498             throws MarshalException
 499         {
 500             parent.appendChild(externalPublicKey.getNode());
 501         }
 502     }
 503 }