blob: e75be8574254e7625a93597be030cb159ec54ebe [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 java.security.cert.X509Certificate;
import java.util.ArrayList;
import java.util.Collections;
import java.util.Date;
import java.util.List;
import org.w3c.dom.Element;
/**
* Parses and holds the XML file containing the signature of the XML file containing the list of THM
* public-key certificates.
*/
public final class SigXml {
private static final String INTERMEDIATE_CERT_LIST_TAG = "intermediates";
private static final String INTERMEDIATE_CERT_ITEM_TAG = "cert";
private static final String SIGNER_CERT_NODE_TAG = "certificate";
private static final String SIGNATURE_NODE_TAG = "value";
private final List<X509Certificate> intermediateCerts;
private final X509Certificate signerCert;
private final byte[] signature;
private SigXml(
List<X509Certificate> intermediateCerts, X509Certificate signerCert, byte[] signature) {
this.intermediateCerts = intermediateCerts;
this.signerCert = signerCert;
this.signature = signature;
}
/**
* Verifies the signature contained in this XML file against a trusted root certificate and the
* binary content of another file. The signer's public-key certificate and possible intermediate
* CA certificates are included in this XML file, and will be validated against the trusted root
* certificate.
*
* @param trustedRoot the trusted root certificate
* @param signedFileBytes the original file content that has been signed
* @throws CertValidationException if the signature verification fails, or the signer's
* certificate contained in this XML file cannot be validated
* based on the trusted root certificate
*/
public void verifyFileSignature(X509Certificate trustedRoot, byte[] signedFileBytes)
throws CertValidationException {
verifyFileSignature(trustedRoot, signedFileBytes, /*validationDate=*/ null);
}
@VisibleForTesting
void verifyFileSignature(
X509Certificate trustedRoot, byte[] signedFileBytes, @Nullable Date validationDate)
throws CertValidationException {
CertUtils.validateCert(validationDate, trustedRoot, intermediateCerts, signerCert);
CertUtils.verifyRsaSha256Signature(signerCert.getPublicKey(), signature, signedFileBytes);
}
/**
* Parses a byte array as the content of the XML file containing the signature and related
* certificates.
*
* @param bytes the bytes of the XML file
* @return a {@code SigXml} instance that contains the parsing result
* @throws CertParsingException if any parsing error occurs
*/
public static SigXml parse(byte[] bytes) throws CertParsingException {
Element rootNode = CertUtils.getXmlRootNode(bytes);
return new SigXml(
parseIntermediateCerts(rootNode), parseSignerCert(rootNode),
parseFileSignature(rootNode));
}
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 X509Certificate parseSignerCert(Element rootNode) throws CertParsingException {
List<String> contents =
CertUtils.getXmlNodeContents(
CertUtils.MUST_EXIST_EXACTLY_ONE, rootNode, SIGNER_CERT_NODE_TAG);
return CertUtils.decodeCert(CertUtils.decodeBase64(contents.get(0)));
}
private static byte[] parseFileSignature(Element rootNode) throws CertParsingException {
List<String> contents =
CertUtils.getXmlNodeContents(
CertUtils.MUST_EXIST_EXACTLY_ONE, rootNode, SIGNATURE_NODE_TAG);
return CertUtils.decodeBase64(contents.get(0));
}
}