| /* |
| * Copyright 2005-2010 The Apache Software Foundation. |
| * |
| * Licensed under the Apache License, Version 2.0 (the "License"); |
| * you may not use this file except in compliance with the License. |
| * You may obtain a copy of the License at |
| * |
| * http://www.apache.org/licenses/LICENSE-2.0 |
| * |
| * Unless required by applicable law or agreed to in writing, software |
| * distributed under the License is distributed on an "AS IS" BASIS, |
| * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. |
| * See the License for the specific language governing permissions and |
| * limitations under the License. |
| * |
| */ |
| /* |
| * Copyright 2005 Sun Microsystems, Inc. All rights reserved. |
| */ |
| /* |
| * $Id$ |
| */ |
| package org.jcp.xml.dsig.internal.dom; |
| |
| import javax.xml.crypto.*; |
| import javax.xml.crypto.dom.DOMCryptoContext; |
| import javax.xml.crypto.dsig.*; |
| import javax.xml.crypto.dsig.keyinfo.KeyValue; |
| |
| import java.io.IOException; |
| import java.security.KeyException; |
| import java.security.KeyFactory; |
| import java.security.NoSuchAlgorithmException; |
| import java.security.PublicKey; |
| import java.security.interfaces.DSAParams; |
| import java.security.interfaces.DSAPublicKey; |
| import java.security.interfaces.ECPublicKey; |
| import java.security.interfaces.RSAPublicKey; |
| import java.security.spec.DSAPublicKeySpec; |
| import java.security.spec.ECParameterSpec; |
| import java.security.spec.ECPoint; |
| import java.security.spec.ECPublicKeySpec; |
| import java.security.spec.InvalidKeySpecException; |
| import java.security.spec.KeySpec; |
| import java.security.spec.RSAPublicKeySpec; |
| import org.w3c.dom.Document; |
| import org.w3c.dom.Element; |
| import org.w3c.dom.Node; |
| |
| import org.apache.xml.security.exceptions.Base64DecodingException; |
| import org.apache.xml.security.utils.Base64; |
| |
| /** |
| * DOM-based implementation of KeyValue. |
| * |
| * @author Sean Mullan |
| */ |
| public abstract class DOMKeyValue extends DOMStructure implements KeyValue { |
| |
| private static final String XMLDSIG_11_XMLNS |
| = "http://www.w3.org/2009/xmldsig11#"; |
| private final PublicKey publicKey; |
| |
| public DOMKeyValue(PublicKey key) throws KeyException { |
| if (key == null) { |
| throw new NullPointerException("key cannot be null"); |
| } |
| this.publicKey = key; |
| } |
| |
| /** |
| * Creates a <code>DOMKeyValue</code> from an element. |
| * |
| * @param kvtElem a KeyValue child element |
| */ |
| public DOMKeyValue(Element kvtElem) throws MarshalException { |
| this.publicKey = unmarshalKeyValue(kvtElem); |
| } |
| |
| static KeyValue unmarshal(Element kvElem) throws MarshalException { |
| Element kvtElem = DOMUtils.getFirstChildElement(kvElem); |
| if (kvtElem.getLocalName().equals("DSAKeyValue")) { |
| return new DSA(kvtElem); |
| } else if (kvtElem.getLocalName().equals("RSAKeyValue")) { |
| return new RSA(kvtElem); |
| } else if (kvtElem.getLocalName().equals("ECKeyValue")) { |
| return new EC(kvtElem); |
| } else { |
| return new Unknown(kvtElem); |
| } |
| } |
| |
| public PublicKey getPublicKey() throws KeyException { |
| if (publicKey == null) { |
| throw new KeyException("can't convert KeyValue to PublicKey"); |
| } else { |
| return publicKey; |
| } |
| } |
| |
| public void marshal(Node parent, String dsPrefix, DOMCryptoContext context) |
| throws MarshalException { |
| Document ownerDoc = DOMUtils.getOwnerDocument(parent); |
| |
| // create KeyValue element |
| Element kvElem = DOMUtils.createElement |
| (ownerDoc, "KeyValue", XMLSignature.XMLNS, dsPrefix); |
| marshalPublicKey(kvElem, ownerDoc, dsPrefix, context); |
| |
| parent.appendChild(kvElem); |
| } |
| |
| abstract void marshalPublicKey(Node parent, Document doc, String dsPrefix, |
| DOMCryptoContext context) throws MarshalException; |
| |
| abstract PublicKey unmarshalKeyValue(Element kvtElem) |
| throws MarshalException; |
| |
| private static PublicKey generatePublicKey(KeyFactory kf, KeySpec keyspec) { |
| try { |
| return kf.generatePublic(keyspec); |
| } catch (InvalidKeySpecException e) { |
| //@@@ should dump exception to log |
| return null; |
| } |
| } |
| |
| public boolean equals(Object obj) { |
| if (this == obj) { |
| return true; |
| } |
| if (!(obj instanceof KeyValue)) { |
| return false; |
| } |
| try { |
| KeyValue kv = (KeyValue) obj; |
| if (publicKey == null ) { |
| if (kv.getPublicKey() != null) { |
| return false; |
| } |
| } else if (!publicKey.equals(kv.getPublicKey())) { |
| return false; |
| } |
| } catch (KeyException ke) { |
| // no practical way to determine if the keys are equal |
| return false; |
| } |
| |
| return true; |
| } |
| |
| static final class RSA extends DOMKeyValue { |
| // RSAKeyValue CryptoBinaries |
| private DOMCryptoBinary modulus, exponent; |
| private KeyFactory rsakf; |
| |
| RSA(PublicKey key) throws KeyException { |
| super(key); |
| RSAPublicKey rkey = (RSAPublicKey) key; |
| exponent = new DOMCryptoBinary(rkey.getPublicExponent()); |
| modulus = new DOMCryptoBinary(rkey.getModulus()); |
| } |
| |
| RSA(Element elem) throws MarshalException { |
| super(elem); |
| } |
| |
| void marshalPublicKey(Node parent, Document doc, String dsPrefix, |
| DOMCryptoContext context) throws MarshalException { |
| Element rsaElem = DOMUtils.createElement |
| (doc, "RSAKeyValue", XMLSignature.XMLNS, dsPrefix); |
| Element modulusElem = DOMUtils.createElement |
| (doc, "Modulus", XMLSignature.XMLNS, dsPrefix); |
| Element exponentElem = DOMUtils.createElement |
| (doc, "Exponent", XMLSignature.XMLNS, dsPrefix); |
| modulus.marshal(modulusElem, dsPrefix, context); |
| exponent.marshal(exponentElem, dsPrefix, context); |
| rsaElem.appendChild(modulusElem); |
| rsaElem.appendChild(exponentElem); |
| parent.appendChild(rsaElem); |
| } |
| |
| PublicKey unmarshalKeyValue(Element kvtElem) |
| throws MarshalException { |
| if (rsakf == null) { |
| try { |
| rsakf = KeyFactory.getInstance("RSA"); |
| } catch (NoSuchAlgorithmException e) { |
| throw new RuntimeException |
| ("unable to create RSA KeyFactory: " + e.getMessage()); |
| } |
| } |
| Element modulusElem = DOMUtils.getFirstChildElement(kvtElem); |
| modulus = new DOMCryptoBinary(modulusElem.getFirstChild()); |
| Element exponentElem = DOMUtils.getNextSiblingElement(modulusElem); |
| exponent = new DOMCryptoBinary(exponentElem.getFirstChild()); |
| RSAPublicKeySpec spec = new RSAPublicKeySpec |
| (modulus.getBigNum(), exponent.getBigNum()); |
| return generatePublicKey(rsakf, spec); |
| } |
| } |
| |
| static final class DSA extends DOMKeyValue { |
| // DSAKeyValue CryptoBinaries |
| private DOMCryptoBinary p, q, g, y, j, seed, pgen; |
| private KeyFactory dsakf; |
| |
| DSA(PublicKey key) throws KeyException { |
| super(key); |
| DSAPublicKey dkey = (DSAPublicKey) key; |
| DSAParams params = dkey.getParams(); |
| p = new DOMCryptoBinary(params.getP()); |
| q = new DOMCryptoBinary(params.getQ()); |
| g = new DOMCryptoBinary(params.getG()); |
| y = new DOMCryptoBinary(dkey.getY()); |
| } |
| |
| DSA(Element elem) throws MarshalException { |
| super(elem); |
| } |
| |
| void marshalPublicKey(Node parent, Document doc, String dsPrefix, |
| DOMCryptoContext context) throws MarshalException { |
| Element dsaElem = DOMUtils.createElement |
| (doc, "DSAKeyValue", XMLSignature.XMLNS, dsPrefix); |
| // parameters J, Seed & PgenCounter are not included |
| Element pElem = DOMUtils.createElement |
| (doc, "P", XMLSignature.XMLNS, dsPrefix); |
| Element qElem = DOMUtils.createElement |
| (doc, "Q", XMLSignature.XMLNS, dsPrefix); |
| Element gElem = DOMUtils.createElement |
| (doc, "G", XMLSignature.XMLNS, dsPrefix); |
| Element yElem = DOMUtils.createElement |
| (doc, "Y", XMLSignature.XMLNS, dsPrefix); |
| p.marshal(pElem, dsPrefix, context); |
| q.marshal(qElem, dsPrefix, context); |
| g.marshal(gElem, dsPrefix, context); |
| y.marshal(yElem, dsPrefix, context); |
| dsaElem.appendChild(pElem); |
| dsaElem.appendChild(qElem); |
| dsaElem.appendChild(gElem); |
| dsaElem.appendChild(yElem); |
| parent.appendChild(dsaElem); |
| } |
| |
| PublicKey unmarshalKeyValue(Element kvtElem) |
| throws MarshalException { |
| if (dsakf == null) { |
| try { |
| dsakf = KeyFactory.getInstance("DSA"); |
| } catch (NoSuchAlgorithmException e) { |
| throw new RuntimeException |
| ("unable to create DSA KeyFactory: " + e.getMessage()); |
| } |
| } |
| Element curElem = DOMUtils.getFirstChildElement(kvtElem); |
| // check for P and Q |
| if (curElem.getLocalName().equals("P")) { |
| p = new DOMCryptoBinary(curElem.getFirstChild()); |
| curElem = DOMUtils.getNextSiblingElement(curElem); |
| q = new DOMCryptoBinary(curElem.getFirstChild()); |
| curElem = DOMUtils.getNextSiblingElement(curElem); |
| } |
| if (curElem.getLocalName().equals("G")) { |
| g = new DOMCryptoBinary(curElem.getFirstChild()); |
| curElem = DOMUtils.getNextSiblingElement(curElem); |
| } |
| y = new DOMCryptoBinary(curElem.getFirstChild()); |
| curElem = DOMUtils.getNextSiblingElement(curElem); |
| if (curElem != null && curElem.getLocalName().equals("J")) { |
| j = new DOMCryptoBinary(curElem.getFirstChild()); |
| curElem = DOMUtils.getNextSiblingElement(curElem); |
| } |
| if (curElem != null) { |
| seed = new DOMCryptoBinary(curElem.getFirstChild()); |
| curElem = DOMUtils.getNextSiblingElement(curElem); |
| pgen = new DOMCryptoBinary(curElem.getFirstChild()); |
| } |
| //@@@ do we care about j, pgenCounter or seed? |
| DSAPublicKeySpec spec = new DSAPublicKeySpec |
| (y.getBigNum(), p.getBigNum(), q.getBigNum(), g.getBigNum()); |
| return generatePublicKey(dsakf, spec); |
| } |
| } |
| |
| static final class EC extends DOMKeyValue { |
| // ECKeyValue CryptoBinaries |
| private byte[] ecPublicKey; |
| private KeyFactory eckf; |
| private ECParameterSpec ecParams; |
| |
| EC(PublicKey key) throws KeyException { |
| super(key); |
| ECPublicKey ecKey = (ECPublicKey) key; |
| ECPoint ecPoint = ecKey.getW(); |
| ecParams = ecKey.getParams(); |
| ecPublicKey = sun.security.ec.ECParameters.encodePoint( |
| ecPoint, ecParams.getCurve()); |
| } |
| |
| EC(Element dmElem) throws MarshalException { |
| super(dmElem); |
| } |
| |
| void marshalPublicKey(Node parent, Document doc, String dsPrefix, |
| DOMCryptoContext context) throws MarshalException { |
| String prefix = DOMUtils.getNSPrefix(context, XMLDSIG_11_XMLNS); |
| Element ecKeyValueElem = DOMUtils.createElement |
| (doc, "ECKeyValue", XMLDSIG_11_XMLNS, prefix); |
| Element namedCurveElem = DOMUtils.createElement |
| (doc, "NamedCurve", XMLDSIG_11_XMLNS, prefix); |
| Element publicKeyElem = DOMUtils.createElement |
| (doc, "PublicKey", XMLDSIG_11_XMLNS, prefix); |
| String oid = sun.security.ec.ECParameters.getCurveName(ecParams); |
| DOMUtils.setAttribute(namedCurveElem, "URI", "urn:oid:" + oid); |
| String qname = (prefix == null || prefix.length() == 0) |
| ? "xmlns" : "xmlns:" + prefix; |
| namedCurveElem.setAttributeNS |
| ("http://www.w3.org/2000/xmlns/", qname, XMLDSIG_11_XMLNS); |
| ecKeyValueElem.appendChild(namedCurveElem); |
| String encoded = Base64.encode(ecPublicKey); |
| publicKeyElem.appendChild |
| (DOMUtils.getOwnerDocument(publicKeyElem).createTextNode(encoded)); |
| ecKeyValueElem.appendChild(publicKeyElem); |
| parent.appendChild(ecKeyValueElem); |
| } |
| |
| PublicKey unmarshalKeyValue(Element kvtElem) |
| throws MarshalException { |
| if (eckf == null) { |
| try { |
| eckf = KeyFactory.getInstance("EC"); |
| } catch (NoSuchAlgorithmException e) { |
| throw new RuntimeException |
| ("unable to create EC KeyFactory: " + e.getMessage()); |
| } |
| } |
| ECParameterSpec ecParams = null; |
| Element curElem = DOMUtils.getFirstChildElement(kvtElem); |
| if (curElem.getLocalName().equals("ECParameters")) { |
| throw new UnsupportedOperationException |
| ("ECParameters not supported"); |
| } else if (curElem.getLocalName().equals("NamedCurve")) { |
| String uri = DOMUtils.getAttributeValue(curElem, "URI"); |
| // strip off "urn:oid" |
| if (uri.startsWith("urn:oid:")) { |
| String oid = uri.substring(8); |
| ecParams = sun.security.ec.NamedCurve.getECParameterSpec(oid); |
| } else { |
| throw new MarshalException("Invalid NamedCurve URI"); |
| } |
| } else { |
| throw new MarshalException("Invalid ECKeyValue"); |
| } |
| curElem = DOMUtils.getNextSiblingElement(curElem); |
| ECPoint ecPoint = null; |
| try { |
| ecPoint = sun.security.ec.ECParameters.decodePoint( |
| Base64.decode(curElem), ecParams.getCurve()); |
| } catch (Base64DecodingException bde) { |
| throw new MarshalException("Invalid EC PublicKey", bde); |
| } catch (IOException ioe) { |
| throw new MarshalException("Invalid EC PublicKey", ioe); |
| } |
| ECPublicKeySpec spec = new ECPublicKeySpec(ecPoint, ecParams); |
| return generatePublicKey(eckf, spec); |
| } |
| } |
| |
| static final class Unknown extends DOMKeyValue { |
| private javax.xml.crypto.dom.DOMStructure externalPublicKey; |
| Unknown(Element elem) throws MarshalException { |
| super(elem); |
| } |
| PublicKey unmarshalKeyValue(Element kvElem) throws MarshalException { |
| externalPublicKey = new javax.xml.crypto.dom.DOMStructure(kvElem); |
| return null; |
| } |
| void marshalPublicKey(Node parent, Document doc, String dsPrefix, |
| DOMCryptoContext context) throws MarshalException { |
| parent.appendChild(externalPublicKey.getNode()); |
| } |
| } |
| } |