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, 2018, Oracle and/or its affiliates. All rights reserved. 25 */ 26 /* 27 * $Id: DOMX509Data.java 1789702 2017-03-31 15:15:04Z coheigea $ 28 */ 29 package org.jcp.xml.dsig.internal.dom; 30 31 import java.io.ByteArrayInputStream; 32 import java.io.IOException; 33 import java.security.cert.*; 34 import java.util.*; 35 36 import javax.xml.crypto.*; 37 import javax.xml.crypto.dom.DOMCryptoContext; 38 import javax.xml.crypto.dsig.*; 39 import javax.xml.crypto.dsig.keyinfo.X509Data; 40 import javax.xml.crypto.dsig.keyinfo.X509IssuerSerial; 41 import javax.security.auth.x500.X500Principal; 42 43 import org.w3c.dom.Document; 44 import org.w3c.dom.Element; 45 import org.w3c.dom.Node; 46 47 import com.sun.org.apache.xml.internal.security.utils.XMLUtils; 48 49 /** 50 * DOM-based implementation of X509Data. 51 * 52 */ 53 //@@@ check for illegal combinations of data violating MUSTs in W3c spec 54 public final class DOMX509Data extends DOMStructure implements X509Data { 55 56 private final List<Object> content; 57 private CertificateFactory cf; 58 59 /** 60 * Creates a DOMX509Data. 61 * 62 * @param content a list of one or more X.509 data types. Valid types are 63 * {@link String} (subject names), {@code byte[]} (subject key ids), 64 * {@link java.security.cert.X509Certificate}, {@link X509CRL}, 65 * or {@link javax.xml.dsig.XMLStructure} 66 * objects or elements from an external namespace). The list is 67 * defensively copied to protect against subsequent modification. 68 * @throws NullPointerException if {@code content} is {@code null} 69 * @throws IllegalArgumentException if {@code content} is empty 70 * @throws ClassCastException if {@code content} contains any entries 71 * that are not of one of the valid types mentioned above 72 */ DOMX509Data(List<?> content)73 public DOMX509Data(List<?> content) { 74 if (content == null) { 75 throw new NullPointerException("content cannot be null"); 76 } 77 List<Object> contentCopy = new ArrayList<>(content); 78 if (contentCopy.isEmpty()) { 79 throw new IllegalArgumentException("content cannot be empty"); 80 } 81 for (int i = 0, size = contentCopy.size(); i < size; i++) { 82 Object x509Type = contentCopy.get(i); 83 if (x509Type instanceof String) { 84 new X500Principal((String)x509Type); 85 } else if (!(x509Type instanceof byte[]) && 86 !(x509Type instanceof X509Certificate) && 87 !(x509Type instanceof X509CRL) && 88 !(x509Type instanceof XMLStructure)) { 89 throw new ClassCastException 90 ("content["+i+"] is not a valid X509Data type"); 91 } 92 } 93 this.content = Collections.unmodifiableList(contentCopy); 94 } 95 96 /** 97 * Creates a {@code DOMX509Data} from an element. 98 * 99 * @param xdElem an X509Data element 100 * @throws MarshalException if there is an error while unmarshalling 101 */ DOMX509Data(Element xdElem)102 public DOMX509Data(Element xdElem) throws MarshalException { 103 // get all children nodes 104 List<Object> newContent = new ArrayList<>(); 105 Node firstChild = xdElem.getFirstChild(); 106 while (firstChild != null) { 107 if (firstChild.getNodeType() == Node.ELEMENT_NODE) { 108 Element childElem = (Element)firstChild; 109 String localName = childElem.getLocalName(); 110 String namespace = childElem.getNamespaceURI(); 111 if ("X509Certificate".equals(localName) && XMLSignature.XMLNS.equals(namespace)) { 112 newContent.add(unmarshalX509Certificate(childElem)); 113 } else if ("X509IssuerSerial".equals(localName) && XMLSignature.XMLNS.equals(namespace)) { 114 newContent.add(new DOMX509IssuerSerial(childElem)); 115 } else if ("X509SubjectName".equals(localName) && XMLSignature.XMLNS.equals(namespace)) { 116 newContent.add(childElem.getFirstChild().getNodeValue()); 117 } else if ("X509SKI".equals(localName) && XMLSignature.XMLNS.equals(namespace)) { 118 String content = XMLUtils.getFullTextChildrenFromElement(childElem); 119 newContent.add(XMLUtils.decode(content)); 120 } else if ("X509CRL".equals(localName) && XMLSignature.XMLNS.equals(namespace)) { 121 newContent.add(unmarshalX509CRL(childElem)); 122 } else { 123 newContent.add(new javax.xml.crypto.dom.DOMStructure(childElem)); 124 } 125 } 126 firstChild = firstChild.getNextSibling(); 127 } 128 this.content = Collections.unmodifiableList(newContent); 129 } 130 getContent()131 public List<Object> getContent() { 132 return content; 133 } 134 marshal(Node parent, String dsPrefix, DOMCryptoContext context)135 public void marshal(Node parent, String dsPrefix, DOMCryptoContext context) 136 throws MarshalException 137 { 138 Document ownerDoc = DOMUtils.getOwnerDocument(parent); 139 Element xdElem = DOMUtils.createElement(ownerDoc, "X509Data", 140 XMLSignature.XMLNS, dsPrefix); 141 142 // append children and preserve order 143 for (int i = 0, size = content.size(); i < size; i++) { 144 Object object = content.get(i); 145 if (object instanceof X509Certificate) { 146 marshalCert((X509Certificate)object,xdElem,ownerDoc,dsPrefix); 147 } else if (object instanceof XMLStructure) { 148 if (object instanceof X509IssuerSerial) { 149 ((DOMX509IssuerSerial)object).marshal 150 (xdElem, dsPrefix, context); 151 } else { 152 javax.xml.crypto.dom.DOMStructure domContent = 153 (javax.xml.crypto.dom.DOMStructure)object; 154 DOMUtils.appendChild(xdElem, domContent.getNode()); 155 } 156 } else if (object instanceof byte[]) { 157 marshalSKI((byte[])object, xdElem, ownerDoc, dsPrefix); 158 } else if (object instanceof String) { 159 marshalSubjectName((String)object, xdElem, ownerDoc,dsPrefix); 160 } else if (object instanceof X509CRL) { 161 marshalCRL((X509CRL)object, xdElem, ownerDoc, dsPrefix); 162 } 163 } 164 165 parent.appendChild(xdElem); 166 } 167 marshalSKI(byte[] skid, Node parent, Document doc, String dsPrefix)168 private void marshalSKI(byte[] skid, Node parent, Document doc, 169 String dsPrefix) 170 { 171 Element skidElem = DOMUtils.createElement(doc, "X509SKI", 172 XMLSignature.XMLNS, dsPrefix); 173 skidElem.appendChild(doc.createTextNode(XMLUtils.encodeToString(skid))); 174 parent.appendChild(skidElem); 175 } 176 marshalSubjectName(String name, Node parent, Document doc, String dsPrefix)177 private void marshalSubjectName(String name, Node parent, Document doc, 178 String dsPrefix) 179 { 180 Element snElem = DOMUtils.createElement(doc, "X509SubjectName", 181 XMLSignature.XMLNS, dsPrefix); 182 snElem.appendChild(doc.createTextNode(name)); 183 parent.appendChild(snElem); 184 } 185 marshalCert(X509Certificate cert, Node parent, Document doc, String dsPrefix)186 private void marshalCert(X509Certificate cert, Node parent, Document doc, 187 String dsPrefix) 188 throws MarshalException 189 { 190 Element certElem = DOMUtils.createElement(doc, "X509Certificate", 191 XMLSignature.XMLNS, dsPrefix); 192 try { 193 certElem.appendChild(doc.createTextNode 194 (XMLUtils.encodeToString(cert.getEncoded()))); 195 } catch (CertificateEncodingException e) { 196 throw new MarshalException("Error encoding X509Certificate", e); 197 } 198 parent.appendChild(certElem); 199 } 200 marshalCRL(X509CRL crl, Node parent, Document doc, String dsPrefix)201 private void marshalCRL(X509CRL crl, Node parent, Document doc, 202 String dsPrefix) 203 throws MarshalException 204 { 205 Element crlElem = DOMUtils.createElement(doc, "X509CRL", 206 XMLSignature.XMLNS, dsPrefix); 207 try { 208 crlElem.appendChild(doc.createTextNode 209 (XMLUtils.encodeToString(crl.getEncoded()))); 210 } catch (CRLException e) { 211 throw new MarshalException("Error encoding X509CRL", e); 212 } 213 parent.appendChild(crlElem); 214 } 215 unmarshalX509Certificate(Element elem)216 private X509Certificate unmarshalX509Certificate(Element elem) 217 throws MarshalException 218 { 219 try (ByteArrayInputStream bs = unmarshalBase64Binary(elem)) { 220 return (X509Certificate)cf.generateCertificate(bs); 221 } catch (CertificateException e) { 222 throw new MarshalException("Cannot create X509Certificate", e); 223 } catch (IOException e) { 224 throw new MarshalException("Error closing stream", e); 225 } 226 } 227 unmarshalX509CRL(Element elem)228 private X509CRL unmarshalX509CRL(Element elem) throws MarshalException { 229 try (ByteArrayInputStream bs = unmarshalBase64Binary(elem)) { 230 return (X509CRL)cf.generateCRL(bs); 231 } catch (CRLException e) { 232 throw new MarshalException("Cannot create X509CRL", e); 233 } catch (IOException e) { 234 throw new MarshalException("Error closing stream", e); 235 } 236 } 237 unmarshalBase64Binary(Element elem)238 private ByteArrayInputStream unmarshalBase64Binary(Element elem) 239 throws MarshalException { 240 try { 241 if (cf == null) { 242 cf = CertificateFactory.getInstance("X.509"); 243 } 244 String content = XMLUtils.getFullTextChildrenFromElement(elem); 245 return new ByteArrayInputStream(XMLUtils.decode(content)); 246 } catch (CertificateException e) { 247 throw new MarshalException("Cannot create CertificateFactory", e); 248 } 249 } 250 251 @Override equals(Object o)252 public boolean equals(Object o) { 253 if (this == o) { 254 return true; 255 } 256 257 if (!(o instanceof X509Data)) { 258 return false; 259 } 260 X509Data oxd = (X509Data)o; 261 262 List<?> ocontent = oxd.getContent(); 263 int size = content.size(); 264 if (size != ocontent.size()) { 265 return false; 266 } 267 268 for (int i = 0; i < size; i++) { 269 Object x = content.get(i); 270 Object ox = ocontent.get(i); 271 if (x instanceof byte[]) { 272 if (!(ox instanceof byte[]) || 273 !Arrays.equals((byte[])x, (byte[])ox)) { 274 return false; 275 } 276 } else { 277 if (!(x.equals(ox))) { 278 return false; 279 } 280 } 281 } 282 283 return true; 284 } 285 286 @Override hashCode()287 public int hashCode() { 288 int result = 17; 289 result = 31 * result + content.hashCode(); 290 291 return result; 292 } 293 } 294