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 }