/* * 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 intermediateCerts; private final X509Certificate signerCert; private final byte[] signature; private SigXml( List 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 parseIntermediateCerts(Element rootNode) throws CertParsingException { List contents = CertUtils.getXmlNodeContents( CertUtils.MUST_EXIST_UNENFORCED, rootNode, INTERMEDIATE_CERT_LIST_TAG, INTERMEDIATE_CERT_ITEM_TAG); List 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 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 contents = CertUtils.getXmlNodeContents( CertUtils.MUST_EXIST_EXACTLY_ONE, rootNode, SIGNATURE_NODE_TAG); return CertUtils.decodeBase64(contents.get(0)); } }