blob: ff22a8dc934f1f06d786e03947d4648f65b6186f [file] [log] [blame]
/*
* Copyright (C) 2017 The Android Open Source Project
*
* 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.
*/
package com.android.server.locksettings.recoverablekeystore.certificate;
import android.annotation.Nullable;
import com.android.internal.annotations.VisibleForTesting;
import org.w3c.dom.Element;
import java.security.SecureRandom;
import java.security.cert.CertPath;
import java.security.cert.X509Certificate;
import java.util.ArrayList;
import java.util.Collections;
import java.util.Date;
import java.util.List;
/**
* Parses and holds the XML file containing the list of THM public-key certificates and related
* metadata.
*/
public final class CertXml {
private static final String METADATA_NODE_TAG = "metadata";
private static final String METADATA_SERIAL_NODE_TAG = "serial";
private static final String ENDPOINT_CERT_LIST_TAG = "endpoints";
private static final String ENDPOINT_CERT_ITEM_TAG = "cert";
private static final String INTERMEDIATE_CERT_LIST_TAG = "intermediates";
private static final String INTERMEDIATE_CERT_ITEM_TAG = "cert";
private final long serial;
private final List<X509Certificate> intermediateCerts;
private final List<X509Certificate> endpointCerts;
private CertXml(
long serial,
List<X509Certificate> intermediateCerts,
List<X509Certificate> endpointCerts) {
this.serial = serial;
this.intermediateCerts = intermediateCerts;
this.endpointCerts = endpointCerts;
}
/** Gets the serial number of the XML file containing public-key certificates. */
public long getSerial() {
return serial;
}
@VisibleForTesting
List<X509Certificate> getAllIntermediateCerts() {
return intermediateCerts;
}
@VisibleForTesting
List<X509Certificate> getAllEndpointCerts() {
return endpointCerts;
}
/**
* Chooses a random endpoint certificate from the XML file, validates the chosen certificate,
* and returns the certificate path including the chosen certificate if it is valid.
*
* @param trustedRoot the trusted root certificate
* @return the certificate path including the chosen certificate if the certificate is valid
* @throws CertValidationException if the chosen certificate cannot be validated based on the
* trusted root certificate
*/
public CertPath getRandomEndpointCert(X509Certificate trustedRoot)
throws CertValidationException {
return getEndpointCert(
new SecureRandom().nextInt(this.endpointCerts.size()),
/*validationDate=*/ null,
trustedRoot);
}
@VisibleForTesting
CertPath getEndpointCert(
int index, @Nullable Date validationDate, X509Certificate trustedRoot)
throws CertValidationException {
X509Certificate chosenCert = endpointCerts.get(index);
return CertUtils.validateCert(validationDate, trustedRoot, intermediateCerts, chosenCert);
}
/**
* Parses a byte array as the content of the XML file containing a list of endpoint
* certificates.
*
* @param bytes the bytes of the XML file
* @return a {@code CertXml} instance that contains the parsing result
* @throws CertParsingException if any parsing error occurs
*/
public static CertXml parse(byte[] bytes) throws CertParsingException {
Element rootNode = CertUtils.getXmlRootNode(bytes);
return new CertXml(
parseSerial(rootNode),
parseIntermediateCerts(rootNode),
parseEndpointCerts(rootNode));
}
private static long parseSerial(Element rootNode) throws CertParsingException {
List<String> contents =
CertUtils.getXmlNodeContents(
CertUtils.MUST_EXIST_EXACTLY_ONE,
rootNode,
METADATA_NODE_TAG,
METADATA_SERIAL_NODE_TAG);
return Long.parseLong(contents.get(0));
}
private static List<X509Certificate> parseIntermediateCerts(Element rootNode)
throws CertParsingException {
List<String> contents =
CertUtils.getXmlNodeContents(
CertUtils.MUST_EXIST_UNENFORCED,
rootNode,
INTERMEDIATE_CERT_LIST_TAG,
INTERMEDIATE_CERT_ITEM_TAG);
List<X509Certificate> res = new ArrayList<>();
for (String content : contents) {
res.add(CertUtils.decodeCert(CertUtils.decodeBase64(content)));
}
return Collections.unmodifiableList(res);
}
private static List<X509Certificate> parseEndpointCerts(Element rootNode)
throws CertParsingException {
List<String> contents =
CertUtils.getXmlNodeContents(
CertUtils.MUST_EXIST_AT_LEAST_ONE,
rootNode,
ENDPOINT_CERT_LIST_TAG,
ENDPOINT_CERT_ITEM_TAG);
List<X509Certificate> res = new ArrayList<>();
for (String content : contents) {
res.add(CertUtils.decodeCert(CertUtils.decodeBase64(content)));
}
return Collections.unmodifiableList(res);
}
}