Path: blob/master/test/jdk/javax/xml/crypto/dsig/KeySelectors.java
41152 views
/*1* Copyright (c) 2005, 2018, Oracle and/or its affiliates. All rights reserved.2* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.3*4* This code is free software; you can redistribute it and/or modify it5* under the terms of the GNU General Public License version 2 only, as6* published by the Free Software Foundation.7*8* This code is distributed in the hope that it will be useful, but WITHOUT9* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or10* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License11* version 2 for more details (a copy is included in the LICENSE file that12* accompanied this code).13*14* You should have received a copy of the GNU General Public License version15* 2 along with this work; if not, write to the Free Software Foundation,16* Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.17*18* Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA19* or visit www.oracle.com if you need additional information or have any20* questions.21*/2223import java.io.*;24import java.security.Key;25import java.security.KeyException;26import java.security.PublicKey;27import java.security.cert.*;28import java.util.*;29import javax.crypto.SecretKey;30import javax.xml.crypto.*;31import javax.xml.crypto.dsig.*;32import javax.xml.crypto.dsig.keyinfo.*;33import sun.security.util.DerValue;34import sun.security.x509.X500Name;3536/**37* This is a class which supplies several KeySelector implementations38*/39class KeySelectors {4041/**42* KeySelector which would always return the secret key specified in its43* constructor.44*/45static class SecretKeySelector extends KeySelector {46private SecretKey key;47SecretKeySelector(byte[] bytes) {48key = wrapBytes(bytes);49}50SecretKeySelector(SecretKey key) {51this.key = key;52}5354public KeySelectorResult select(KeyInfo ki,55KeySelector.Purpose purpose,56AlgorithmMethod method,57XMLCryptoContext context)58throws KeySelectorException {59return new SimpleKSResult(key);60}6162private SecretKey wrapBytes(final byte[] bytes) {63return new SecretKey() {64public String getFormat() {65return "RAW";66}6768public String getAlgorithm() {69return "Secret key";70}7172public byte[] getEncoded() {73return bytes.clone();74}75};76}77}7879/**80* KeySelector which would retrieve the X509Certificate out of the81* KeyInfo element and return the public key.82* NOTE: If there is an X509CRL in the KeyInfo element, then revoked83* certificate will be ignored.84*/85static class RawX509KeySelector extends KeySelector {8687public KeySelectorResult select(KeyInfo keyInfo,88KeySelector.Purpose purpose,89AlgorithmMethod method,90XMLCryptoContext context)91throws KeySelectorException {92if (keyInfo == null) {93throw new KeySelectorException("Null KeyInfo object!");94}95// search for X509Data in keyinfo96for (XMLStructure kiType : keyInfo.getContent()) {97if (kiType instanceof X509Data) {98X509Data xd = (X509Data) kiType;99Object[] entries = xd.getContent().toArray();100X509CRL crl = null;101// Looking for CRL before finding certificates102for (int i = 0; (i<entries.length&&crl != null); i++) {103if (entries[i] instanceof X509CRL) {104crl = (X509CRL) entries[i];105}106}107boolean hasCRL = false;108for (Object o : xd.getContent()) {109// skip non-X509Certificate entries110if (o instanceof X509Certificate) {111if ((purpose != KeySelector.Purpose.VERIFY) &&112(crl != null) &&113crl.isRevoked((X509Certificate)o)) {114continue;115} else {116return new SimpleKSResult117(((X509Certificate)o).getPublicKey());118}119}120}121}122}123throw new KeySelectorException("No X509Certificate found!");124}125}126127/**128* KeySelector which would retrieve the public key out of the129* KeyValue element and return it.130* NOTE: If the key algorithm doesn't match signature algorithm,131* then the public key will be ignored.132*/133static class KeyValueKeySelector extends KeySelector {134public KeySelectorResult select(KeyInfo keyInfo,135KeySelector.Purpose purpose,136AlgorithmMethod method,137XMLCryptoContext context)138throws KeySelectorException {139if (keyInfo == null) {140throw new KeySelectorException("Null KeyInfo object!");141}142SignatureMethod sm = (SignatureMethod) method;143144for (XMLStructure xmlStructure : keyInfo.getContent()) {145if (xmlStructure instanceof KeyValue) {146PublicKey pk = null;147try {148pk = ((KeyValue)xmlStructure).getPublicKey();149} catch (KeyException ke) {150throw new KeySelectorException(ke);151}152// make sure algorithm is compatible with method153if (algEquals(sm.getAlgorithm(), pk.getAlgorithm())) {154return new SimpleKSResult(pk);155}156}157}158throw new KeySelectorException("No KeyValue element found!");159}160161static boolean algEquals(String algURI, String algName) {162algName = algName.toUpperCase(Locale.ROOT);163return algName.equals("DSA") && algURI.contains("#dsa-")164|| algName.equals("RSA")165&& (algURI.contains("#rsa-") || algURI.contains("-rsa-MGF1"))166|| algName.equals("EC") && algURI.contains("#ecdsa-");167}168}169170/**171* KeySelector which would perform special lookup as documented172* by the ie/baltimore/merlin-examples testcases and return the173* matching public key.174*/175static class CollectionKeySelector extends KeySelector {176private CertificateFactory cf;177private File certDir;178private Vector<X509Certificate> certs;179private static final int MATCH_SUBJECT = 0;180private static final int MATCH_ISSUER = 1;181private static final int MATCH_SERIAL = 2;182private static final int MATCH_SUBJECT_KEY_ID = 3;183private static final int MATCH_CERTIFICATE = 4;184185CollectionKeySelector(File dir) {186certDir = dir;187try {188cf = CertificateFactory.getInstance("X509");189} catch (CertificateException ex) {190// not going to happen191}192certs = new Vector<X509Certificate>();193File[] files = new File(certDir, "certs").listFiles();194for (int i = 0; i < files.length; i++) {195try (FileInputStream fis = new FileInputStream(files[i])) {196certs.add((X509Certificate)cf.generateCertificate(fis));197} catch (Exception ex) { }198}199}200201Vector<X509Certificate> match(int matchType, Object value,202Vector<X509Certificate> pool) {203Vector<X509Certificate> matchResult = new Vector<>();204for (int j=0; j < pool.size(); j++) {205X509Certificate c = pool.get(j);206switch (matchType) {207case MATCH_SUBJECT:208try {209if (c.getSubjectDN().equals(new X500Name((String)value))) {210matchResult.add(c);211}212} catch (IOException ioe) { }213break;214case MATCH_ISSUER:215try {216if (c.getIssuerDN().equals(new X500Name((String)value))) {217matchResult.add(c);218}219} catch (IOException ioe) { }220break;221case MATCH_SERIAL:222if (c.getSerialNumber().equals(value)) {223matchResult.add(c);224}225226break;227case MATCH_SUBJECT_KEY_ID:228byte[] extension = c.getExtensionValue("2.5.29.14");229if (extension != null) {230try {231DerValue derValue = new DerValue(extension);232DerValue derValue2 = new DerValue(derValue.getOctetString());233byte[] extVal = derValue2.getOctetString();234235if (Arrays.equals(extVal, (byte[]) value)) {236matchResult.add(c);237}238} catch (IOException ex) { }239}240break;241case MATCH_CERTIFICATE:242if (c.equals(value)) {243matchResult.add(c);244}245break;246}247}248return matchResult;249}250251public KeySelectorResult select(KeyInfo keyInfo,252KeySelector.Purpose purpose,253AlgorithmMethod method,254XMLCryptoContext context)255throws KeySelectorException {256if (keyInfo == null) {257throw new KeySelectorException("Null KeyInfo object!");258}259for (XMLStructure xmlStructure : keyInfo.getContent()) {260try {261if (xmlStructure instanceof KeyName) {262String name = ((KeyName)xmlStructure).getName();263PublicKey pk = null;264File certFile = new File(new File(certDir, "certs"),265name.toLowerCase() + ".crt");266try (FileInputStream fis = new FileInputStream(certFile)) {267// Lookup the public key using the key name 'Xxx',268// i.e. the public key is in "certs/xxx.crt".269X509Certificate cert = (X509Certificate)270cf.generateCertificate(fis);271pk = cert.getPublicKey();272} catch (FileNotFoundException e) {273// assume KeyName contains subject DN and search274// collection of certs for match275Vector<X509Certificate> result =276match(MATCH_SUBJECT, name, certs);277int numOfMatches = (result==null? 0:result.size());278if (numOfMatches != 1) {279throw new KeySelectorException280((numOfMatches==0?"No":"More than one") +281" match found");282}283pk = result.get(0).getPublicKey();284}285return new SimpleKSResult(pk);286} else if (xmlStructure instanceof RetrievalMethod) {287// Lookup the public key using the retrievel method.288// NOTE: only X509Certificate type is supported.289RetrievalMethod rm = (RetrievalMethod) xmlStructure;290String type = rm.getType();291if (type.equals(X509Data.RAW_X509_CERTIFICATE_TYPE)) {292String uri = rm.getURI();293try (FileInputStream fis =294new FileInputStream(new File(certDir, uri))) {295X509Certificate cert = (X509Certificate)296cf.generateCertificate(fis);297return new SimpleKSResult(cert.getPublicKey());298}299} else {300throw new KeySelectorException301("Unsupported RetrievalMethod type");302}303} else if (xmlStructure instanceof X509Data) {304List content = ((X509Data)xmlStructure).getContent();305Vector<X509Certificate> result = null;306// Lookup the public key using the information307// specified in X509Data element, i.e. searching308// over the collection of certificate files under309// "certs" subdirectory and return those match.310for (Object obj : content) {311if (obj instanceof String) {312result = match(MATCH_SUBJECT, obj, certs);313} else if (obj instanceof byte[]) {314result = match(MATCH_SUBJECT_KEY_ID, obj,315certs);316} else if (obj instanceof X509Certificate) {317result = match(MATCH_CERTIFICATE, obj, certs);318} else if (obj instanceof X509IssuerSerial) {319X509IssuerSerial is = (X509IssuerSerial) obj;320result = match(MATCH_SERIAL,321is.getSerialNumber(), certs);322result = match(MATCH_ISSUER,323is.getIssuerName(), result);324} else {325throw new KeySelectorException("Unsupported X509Data: " + obj);326}327}328int numOfMatches = (result==null? 0:result.size());329if (numOfMatches != 1) {330throw new KeySelectorException331((numOfMatches==0?"No":"More than one") +332" match found");333}334return new SimpleKSResult(result.get(0).getPublicKey());335}336} catch (Exception ex) {337throw new KeySelectorException(ex);338}339}340throw new KeySelectorException("No matching key found!");341}342}343344static class ByteUtil {345346private static String mapping = "0123456789ABCDEF";347private static int numBytesPerRow = 6;348349private static String getHex(byte value) {350int low = value & 0x0f;351int high = ((value >> 4) & 0x0f);352char[] res = new char[2];353res[0] = mapping.charAt(high);354res[1] = mapping.charAt(low);355return new String(res);356}357358static String dumpArray(byte[] in) {359int numDumped = 0;360StringBuffer buf = new StringBuffer(512);361buf.append("{");362for (int i=0;i<(in.length/numBytesPerRow); i++) {363for (int j=0; j<(numBytesPerRow); j++) {364buf.append("(byte)0x" + getHex(in[i*numBytesPerRow+j]) +365", ");366}367numDumped += numBytesPerRow;368}369while (numDumped < in.length) {370buf.append("(byte)0x" + getHex(in[numDumped]) + " ");371numDumped += 1;372}373buf.append("}");374return buf.toString();375}376}377}378379class SimpleKSResult implements KeySelectorResult {380private final Key key;381382SimpleKSResult(Key key) { this.key = key; }383384public Key getKey() { return key; }385}386387388