0N/A/*
4604N/A * Copyright (c) 2005, 2012, Oracle and/or its affiliates. All rights reserved.
0N/A * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
0N/A *
0N/A * This code is free software; you can redistribute it and/or modify it
0N/A * under the terms of the GNU General Public License version 2 only, as
0N/A * published by the Free Software Foundation.
0N/A *
0N/A * This code is distributed in the hope that it will be useful, but WITHOUT
0N/A * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
0N/A * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
0N/A * version 2 for more details (a copy is included in the LICENSE file that
0N/A * accompanied this code).
0N/A *
0N/A * You should have received a copy of the GNU General Public License version
0N/A * 2 along with this work; if not, write to the Free Software Foundation,
0N/A * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
0N/A *
2362N/A * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
2362N/A * or visit www.oracle.com if you need additional information or have any
2362N/A * questions.
0N/A */
0N/A
0N/Aimport java.io.*;
4604N/Aimport java.security.Key;
4604N/Aimport java.security.KeyException;
4604N/Aimport java.security.PublicKey;
0N/Aimport java.security.cert.*;
0N/Aimport java.util.*;
0N/Aimport javax.crypto.SecretKey;
0N/Aimport javax.xml.crypto.*;
0N/Aimport javax.xml.crypto.dsig.*;
0N/Aimport javax.xml.crypto.dom.*;
0N/Aimport javax.xml.crypto.dsig.keyinfo.*;
0N/Aimport javax.xml.parsers.DocumentBuilderFactory;
0N/Aimport javax.xml.parsers.DocumentBuilder;
0N/Aimport org.w3c.dom.Document;
0N/Aimport org.w3c.dom.Node;
0N/Aimport org.w3c.dom.Element;
0N/Aimport org.w3c.dom.traversal.*;
0N/Aimport sun.security.util.DerValue;
0N/Aimport sun.security.x509.X500Name;
0N/A
0N/A/**
0N/A * This is a class which supplies several KeySelector implementations
0N/A */
0N/Aclass KeySelectors {
0N/A
0N/A /**
0N/A * KeySelector which would always return the secret key specified in its
0N/A * constructor.
0N/A */
0N/A static class SecretKeySelector extends KeySelector {
0N/A private SecretKey key;
0N/A SecretKeySelector(byte[] bytes) {
0N/A key = wrapBytes(bytes);
0N/A }
0N/A SecretKeySelector(SecretKey key) {
0N/A this.key = key;
0N/A }
0N/A
0N/A public KeySelectorResult select(KeyInfo ki,
0N/A KeySelector.Purpose purpose,
0N/A AlgorithmMethod method,
0N/A XMLCryptoContext context)
0N/A throws KeySelectorException {
0N/A return new SimpleKSResult(key);
0N/A }
0N/A
0N/A private SecretKey wrapBytes(final byte[] bytes) {
0N/A return new SecretKey() {
0N/A public String getFormat() {
0N/A return "RAW";
0N/A }
0N/A
0N/A public String getAlgorithm() {
0N/A return "Secret key";
0N/A }
0N/A
0N/A public byte[] getEncoded() {
4604N/A return bytes.clone();
0N/A }
0N/A };
0N/A }
0N/A }
0N/A
0N/A /**
0N/A * KeySelector which would retrieve the X509Certificate out of the
0N/A * KeyInfo element and return the public key.
0N/A * NOTE: If there is an X509CRL in the KeyInfo element, then revoked
0N/A * certificate will be ignored.
0N/A */
0N/A static class RawX509KeySelector extends KeySelector {
0N/A
0N/A public KeySelectorResult select(KeyInfo keyInfo,
0N/A KeySelector.Purpose purpose,
0N/A AlgorithmMethod method,
0N/A XMLCryptoContext context)
0N/A throws KeySelectorException {
0N/A if (keyInfo == null) {
0N/A throw new KeySelectorException("Null KeyInfo object!");
0N/A }
0N/A // search for X509Data in keyinfo
0N/A Iterator iter = keyInfo.getContent().iterator();
0N/A while (iter.hasNext()) {
0N/A XMLStructure kiType = (XMLStructure) iter.next();
0N/A if (kiType instanceof X509Data) {
0N/A X509Data xd = (X509Data) kiType;
0N/A Object[] entries = xd.getContent().toArray();
0N/A X509CRL crl = null;
0N/A // Looking for CRL before finding certificates
0N/A for (int i = 0; (i<entries.length&&crl != null); i++) {
0N/A if (entries[i] instanceof X509CRL) {
0N/A crl = (X509CRL) entries[i];
0N/A }
0N/A }
0N/A Iterator xi = xd.getContent().iterator();
0N/A boolean hasCRL = false;
0N/A while (xi.hasNext()) {
0N/A Object o = xi.next();
0N/A // skip non-X509Certificate entries
0N/A if (o instanceof X509Certificate) {
0N/A if ((purpose != KeySelector.Purpose.VERIFY) &&
0N/A (crl != null) &&
0N/A crl.isRevoked((X509Certificate)o)) {
0N/A continue;
0N/A } else {
0N/A return new SimpleKSResult
0N/A (((X509Certificate)o).getPublicKey());
0N/A }
0N/A }
0N/A }
0N/A }
0N/A }
0N/A throw new KeySelectorException("No X509Certificate found!");
0N/A }
0N/A }
0N/A
0N/A /**
0N/A * KeySelector which would retrieve the public key out of the
0N/A * KeyValue element and return it.
0N/A * NOTE: If the key algorithm doesn't match signature algorithm,
0N/A * then the public key will be ignored.
0N/A */
0N/A static class KeyValueKeySelector extends KeySelector {
0N/A public KeySelectorResult select(KeyInfo keyInfo,
0N/A KeySelector.Purpose purpose,
0N/A AlgorithmMethod method,
0N/A XMLCryptoContext context)
0N/A throws KeySelectorException {
0N/A if (keyInfo == null) {
0N/A throw new KeySelectorException("Null KeyInfo object!");
0N/A }
0N/A SignatureMethod sm = (SignatureMethod) method;
0N/A List list = keyInfo.getContent();
0N/A
0N/A for (int i = 0; i < list.size(); i++) {
0N/A XMLStructure xmlStructure = (XMLStructure) list.get(i);
0N/A if (xmlStructure instanceof KeyValue) {
0N/A PublicKey pk = null;
0N/A try {
0N/A pk = ((KeyValue)xmlStructure).getPublicKey();
0N/A } catch (KeyException ke) {
0N/A throw new KeySelectorException(ke);
0N/A }
0N/A // make sure algorithm is compatible with method
0N/A if (algEquals(sm.getAlgorithm(), pk.getAlgorithm())) {
0N/A return new SimpleKSResult(pk);
0N/A }
0N/A }
0N/A }
0N/A throw new KeySelectorException("No KeyValue element found!");
0N/A }
0N/A
0N/A //@@@FIXME: this should also work for key types other than DSA/RSA
0N/A static boolean algEquals(String algURI, String algName) {
0N/A if (algName.equalsIgnoreCase("DSA") &&
0N/A algURI.equals(SignatureMethod.DSA_SHA1)) {
0N/A return true;
0N/A } else if (algName.equalsIgnoreCase("RSA") &&
0N/A (algURI.equals(SignatureMethod.RSA_SHA1) ||
0N/A algURI.equals
0N/A ("http://www.w3.org/2001/04/xmldsig-more#rsa-sha256") ||
0N/A algURI.equals
0N/A ("http://www.w3.org/2001/04/xmldsig-more#rsa-sha384") ||
0N/A algURI.equals
0N/A ("http://www.w3.org/2001/04/xmldsig-more#rsa-sha512"))) {
0N/A return true;
0N/A } else {
0N/A return false;
0N/A }
0N/A }
0N/A }
0N/A
0N/A /**
0N/A * KeySelector which would perform special lookup as documented
0N/A * by the ie/baltimore/merlin-examples testcases and return the
0N/A * matching public key.
0N/A */
0N/A static class CollectionKeySelector extends KeySelector {
4604N/A private CertificateFactory cf;
0N/A private File certDir;
4604N/A private Vector<X509Certificate> certs;
0N/A private static final int MATCH_SUBJECT = 0;
0N/A private static final int MATCH_ISSUER = 1;
0N/A private static final int MATCH_SERIAL = 2;
0N/A private static final int MATCH_SUBJECT_KEY_ID = 3;
0N/A private static final int MATCH_CERTIFICATE = 4;
0N/A
0N/A CollectionKeySelector(File dir) {
0N/A certDir = dir;
0N/A try {
4604N/A cf = CertificateFactory.getInstance("X509");
0N/A } catch (CertificateException ex) {
0N/A // not going to happen
0N/A }
4604N/A certs = new Vector<X509Certificate>();
0N/A File[] files = new File(certDir, "certs").listFiles();
0N/A for (int i = 0; i < files.length; i++) {
4604N/A try (FileInputStream fis = new FileInputStream(files[i])) {
4604N/A certs.add((X509Certificate)cf.generateCertificate(fis));
0N/A } catch (Exception ex) { }
0N/A }
0N/A }
0N/A
4604N/A Vector<X509Certificate> match(int matchType, Object value,
4604N/A Vector<X509Certificate> pool) {
4604N/A Vector<X509Certificate> matchResult = new Vector<>();
0N/A for (int j=0; j < pool.size(); j++) {
4604N/A X509Certificate c = pool.get(j);
0N/A switch (matchType) {
0N/A case MATCH_SUBJECT:
0N/A try {
0N/A if (c.getSubjectDN().equals(new X500Name((String)value))) {
0N/A matchResult.add(c);
0N/A }
0N/A } catch (IOException ioe) { }
0N/A break;
0N/A case MATCH_ISSUER:
0N/A try {
0N/A if (c.getIssuerDN().equals(new X500Name((String)value))) {
0N/A matchResult.add(c);
0N/A }
0N/A } catch (IOException ioe) { }
0N/A break;
0N/A case MATCH_SERIAL:
0N/A if (c.getSerialNumber().equals(value)) {
0N/A matchResult.add(c);
0N/A }
0N/A
0N/A break;
0N/A case MATCH_SUBJECT_KEY_ID:
0N/A byte[] extension = c.getExtensionValue("2.5.29.14");
0N/A if (extension != null) {
0N/A try {
0N/A DerValue derValue = new DerValue(extension);
0N/A DerValue derValue2 = new DerValue(derValue.getOctetString());
0N/A byte[] extVal = derValue2.getOctetString();
0N/A
0N/A if (Arrays.equals(extVal, (byte[]) value)) {
0N/A matchResult.add(c);
0N/A }
0N/A } catch (IOException ex) { }
0N/A }
0N/A break;
0N/A case MATCH_CERTIFICATE:
0N/A if (c.equals(value)) {
0N/A matchResult.add(c);
0N/A }
0N/A break;
0N/A }
0N/A }
0N/A return matchResult;
0N/A }
0N/A
0N/A public KeySelectorResult select(KeyInfo keyInfo,
0N/A KeySelector.Purpose purpose,
0N/A AlgorithmMethod method,
0N/A XMLCryptoContext context)
0N/A throws KeySelectorException {
0N/A if (keyInfo == null) {
0N/A throw new KeySelectorException("Null KeyInfo object!");
0N/A }
0N/A Iterator iter = keyInfo.getContent().iterator();
0N/A while (iter.hasNext()) {
0N/A XMLStructure xmlStructure = (XMLStructure) iter.next();
0N/A try {
0N/A if (xmlStructure instanceof KeyName) {
0N/A String name = ((KeyName)xmlStructure).getName();
0N/A PublicKey pk = null;
4604N/A File certFile = new File(new File(certDir, "certs"),
4604N/A name.toLowerCase() + ".crt");
4604N/A try (FileInputStream fis = new FileInputStream(certFile)) {
0N/A // Lookup the public key using the key name 'Xxx',
0N/A // i.e. the public key is in "certs/xxx.crt".
0N/A X509Certificate cert = (X509Certificate)
4604N/A cf.generateCertificate(fis);
0N/A pk = cert.getPublicKey();
0N/A } catch (FileNotFoundException e) {
0N/A // assume KeyName contains subject DN and search
0N/A // collection of certs for match
4604N/A Vector<X509Certificate> result =
0N/A match(MATCH_SUBJECT, name, certs);
0N/A int numOfMatches = (result==null? 0:result.size());
0N/A if (numOfMatches != 1) {
0N/A throw new KeySelectorException
0N/A ((numOfMatches==0?"No":"More than one") +
0N/A " match found");
0N/A }
4604N/A pk = result.get(0).getPublicKey();
0N/A }
0N/A return new SimpleKSResult(pk);
0N/A } else if (xmlStructure instanceof RetrievalMethod) {
0N/A // Lookup the public key using the retrievel method.
0N/A // NOTE: only X509Certificate type is supported.
0N/A RetrievalMethod rm = (RetrievalMethod) xmlStructure;
0N/A String type = rm.getType();
0N/A if (type.equals(X509Data.RAW_X509_CERTIFICATE_TYPE)) {
0N/A String uri = rm.getURI();
4604N/A try (FileInputStream fis =
4604N/A new FileInputStream(new File(certDir, uri))) {
4604N/A X509Certificate cert = (X509Certificate)
4604N/A cf.generateCertificate(fis);
4604N/A return new SimpleKSResult(cert.getPublicKey());
4604N/A }
0N/A } else {
0N/A throw new KeySelectorException
0N/A ("Unsupported RetrievalMethod type");
0N/A }
0N/A } else if (xmlStructure instanceof X509Data) {
0N/A List content = ((X509Data)xmlStructure).getContent();
0N/A int size = content.size();
4604N/A Vector<X509Certificate> result = null;
0N/A // Lookup the public key using the information
0N/A // specified in X509Data element, i.e. searching
0N/A // over the collection of certificate files under
0N/A // "certs" subdirectory and return those match.
0N/A for (int k = 0; k<size; k++) {
0N/A Object obj = content.get(k);
0N/A if (obj instanceof String) {
0N/A result = match(MATCH_SUBJECT, obj, certs);
0N/A } else if (obj instanceof byte[]) {
0N/A result = match(MATCH_SUBJECT_KEY_ID, obj,
0N/A certs);
0N/A } else if (obj instanceof X509Certificate) {
0N/A result = match(MATCH_CERTIFICATE, obj, certs);
0N/A } else if (obj instanceof X509IssuerSerial) {
0N/A X509IssuerSerial is = (X509IssuerSerial) obj;
0N/A result = match(MATCH_SERIAL,
0N/A is.getSerialNumber(), certs);
0N/A result = match(MATCH_ISSUER,
0N/A is.getIssuerName(), result);
0N/A } else {
0N/A throw new KeySelectorException("Unsupported X509Data: " + obj);
0N/A }
0N/A }
0N/A int numOfMatches = (result==null? 0:result.size());
0N/A if (numOfMatches != 1) {
0N/A throw new KeySelectorException
0N/A ((numOfMatches==0?"No":"More than one") +
0N/A " match found");
0N/A }
4604N/A return new SimpleKSResult(result.get(0).getPublicKey());
0N/A }
0N/A } catch (Exception ex) {
0N/A throw new KeySelectorException(ex);
0N/A }
0N/A }
0N/A throw new KeySelectorException("No matching key found!");
0N/A }
0N/A }
0N/A
0N/A static class ByteUtil {
0N/A
0N/A private static String mapping = "0123456789ABCDEF";
0N/A private static int numBytesPerRow = 6;
0N/A
0N/A private static String getHex(byte value) {
0N/A int low = value & 0x0f;
0N/A int high = ((value >> 4) & 0x0f);
0N/A char[] res = new char[2];
0N/A res[0] = mapping.charAt(high);
0N/A res[1] = mapping.charAt(low);
0N/A return new String(res);
0N/A }
0N/A
0N/A static String dumpArray(byte[] in) {
0N/A int numDumped = 0;
0N/A StringBuffer buf = new StringBuffer(512);
0N/A buf.append("{");
0N/A for (int i=0;i<(in.length/numBytesPerRow); i++) {
0N/A for (int j=0; j<(numBytesPerRow); j++) {
0N/A buf.append("(byte)0x" + getHex(in[i*numBytesPerRow+j]) +
0N/A ", ");
0N/A }
0N/A numDumped += numBytesPerRow;
0N/A }
0N/A while (numDumped < in.length) {
0N/A buf.append("(byte)0x" + getHex(in[numDumped]) + " ");
0N/A numDumped += 1;
0N/A }
0N/A buf.append("}");
0N/A return buf.toString();
0N/A }
0N/A }
0N/A}
0N/A
0N/Aclass SimpleKSResult implements KeySelectorResult {
0N/A private final Key key;
0N/A
0N/A SimpleKSResult(Key key) { this.key = key; }
0N/A
0N/A public Key getKey() { return key; }
0N/A}