1 /* 2 * Copyright (c) 2021-2022 Huawei Device Co., Ltd. 3 * Licensed under the Apache License, Version 2.0 (the "License"); 4 * you may not use this file except in compliance with the License. 5 * You may obtain a copy of the License at 6 * 7 * http://www.apache.org/licenses/LICENSE-2.0 8 * 9 * Unless required by applicable law or agreed to in writing, software 10 * distributed under the License is distributed on an "AS IS" BASIS, 11 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 * See the License for the specific language governing permissions and 13 * limitations under the License. 14 */ 15 16 package com.ohos.hapsigntool.profile; 17 18 import com.ohos.hapsigntool.error.CustomException; 19 import com.ohos.hapsigntool.error.ERROR; 20 import com.ohos.hapsigntool.hap.verify.VerifyUtils; 21 import com.ohos.hapsigntool.profile.model.Provision; 22 import com.ohos.hapsigntool.profile.model.VerificationResult; 23 import com.ohos.hapsigntool.utils.CertChainUtils; 24 import com.ohos.hapsigntool.utils.CertUtils; 25 import com.ohos.hapsigntool.utils.FileUtils; 26 import com.ohos.hapsigntool.utils.ValidateUtils; 27 import org.apache.logging.log4j.LogManager; 28 import org.apache.logging.log4j.Logger; 29 import org.bouncycastle.cert.X509CertificateHolder; 30 import org.bouncycastle.cert.jcajce.JcaX509CertificateConverter; 31 import org.bouncycastle.cms.CMSException; 32 import org.bouncycastle.cms.CMSSignedData; 33 import org.bouncycastle.cms.SignerId; 34 import org.bouncycastle.cms.SignerInformation; 35 import org.bouncycastle.cms.SignerInformationStore; 36 import org.bouncycastle.util.Store; 37 38 import javax.security.auth.x500.X500Principal; 39 import java.io.IOException; 40 import java.nio.charset.StandardCharsets; 41 import java.security.InvalidKeyException; 42 import java.security.NoSuchAlgorithmException; 43 import java.security.Signature; 44 import java.security.SignatureException; 45 import java.security.cert.CertificateException; 46 import java.security.cert.X509Certificate; 47 import java.util.ArrayList; 48 import java.util.Collection; 49 import java.util.Iterator; 50 import java.util.List; 51 52 /** 53 * Signed provision profile verifier. 54 * 55 * @since 2021/12/28 56 */ 57 public class VerifyHelper implements IProvisionVerifier { 58 /** 59 * LOGGER. 60 */ 61 private static final Logger LOGGER = LogManager.getLogger(VerifyHelper.class); 62 63 /** 64 * Signed provision profile verifier. 65 */ VerifyHelper()66 public VerifyHelper() { 67 // Empty constructor 68 } 69 70 /** 71 * Checked signed data with public key. 72 * 73 * @param cert public key 74 * @param signedData signed data with private key 75 * @param unsignedData unsigned data 76 * @param algorithm algorithm 77 */ verifySignature(X509Certificate cert, byte[] signedData, byte[] unsignedData, String algorithm)78 public static void verifySignature(X509Certificate cert, byte[] signedData, byte[] unsignedData, String algorithm) { 79 try { 80 Signature signature = Signature.getInstance(algorithm); 81 signature.initVerify(cert); 82 signature.update(unsignedData); 83 ValidateUtils.throwIfNotMatches(signature.verify(signedData), ERROR.SIGN_ERROR, "Signature not matched!"); 84 } catch (InvalidKeyException | SignatureException | NoSuchAlgorithmException exception) { 85 LOGGER.debug(exception.getMessage(), exception); 86 CustomException.throwException(ERROR.SIGN_ERROR, "Failed to verify signature: " + exception.getMessage()); 87 } 88 } 89 90 /** 91 * Convert store collection to list. 92 * 93 * @param certificates certificates from cmsSignedData 94 * @return List<X509Certificate> 95 */ certStoreToCertList(Store<X509CertificateHolder> certificates)96 public static List<X509Certificate> certStoreToCertList(Store<X509CertificateHolder> certificates) { 97 String errorMsg = "Verify failed, not found cert chain"; 98 JcaX509CertificateConverter converter = new JcaX509CertificateConverter(); 99 ValidateUtils.throwIfMatches(certificates == null, ERROR.VERIFY_ERROR, errorMsg); 100 Collection<X509CertificateHolder> matches = certificates.getMatches(null); 101 ValidateUtils.throwIfMatches(matches == null || !matches.iterator().hasNext(), 102 ERROR.VERIFY_ERROR, errorMsg); 103 104 Iterator<X509CertificateHolder> iterator = matches.iterator(); 105 List<X509Certificate> certificateList = new ArrayList<>(); 106 try { 107 while (iterator.hasNext()) { 108 X509CertificateHolder next = iterator.next(); 109 certificateList.add(converter.getCertificate(next)); 110 } 111 } catch (CertificateException exception) { 112 LOGGER.debug(exception.getMessage(), exception); 113 CustomException.throwException(ERROR.VERIFY_ERROR, errorMsg); 114 } 115 ValidateUtils.throwIfMatches(certificateList.size() == 0, ERROR.VERIFY_ERROR, errorMsg); 116 return certificateList; 117 } 118 119 /** 120 * verify p7b content. 121 * 122 * @param p7b signed p7b content 123 * @return result 124 */ 125 @Override verify(byte[] p7b)126 public VerificationResult verify(byte[] p7b) { 127 VerificationResult result = new VerificationResult(); 128 129 try { 130 CMSSignedData cmsSignedData = this.verifyPkcs(p7b); 131 List<X509Certificate> certificates = certStoreToCertList(cmsSignedData.getCertificates()); 132 CertUtils.sortCertificateChain(certificates); 133 134 SignerInformationStore signerInfos = cmsSignedData.getSignerInfos(); 135 Collection<SignerInformation> signers = signerInfos.getSigners(); 136 137 for (SignerInformation signer : signers) { 138 SignerId sid = signer.getSID(); 139 X500Principal principal = new X500Principal(sid.getIssuer().getEncoded()); 140 CertChainUtils.verifyCertChain(certificates, principal, sid.getSerialNumber(), 141 certificates.get(certificates.size() - 1)); 142 } 143 144 result.setContent(FileUtils.GSON.fromJson(new String((byte[]) (cmsSignedData 145 .getSignedContent().getContent()), StandardCharsets.UTF_8), Provision.class)); 146 result.setMessage("OK"); 147 result.setVerifiedPassed(true); 148 return result; 149 } catch (CustomException | IOException exception) { 150 LOGGER.debug(exception.getMessage(), exception); 151 result.setMessage(exception.getMessage()); 152 result.setVerifiedPassed(false); 153 return result; 154 } 155 } 156 verifyPkcs(byte[] p7b)157 CMSSignedData verifyPkcs(byte[] p7b) { 158 CMSSignedData cmsSignedData = null; 159 try { 160 cmsSignedData = new CMSSignedData(p7b); 161 boolean verifyResult = VerifyUtils.verifyCmsSignedData(cmsSignedData); 162 ValidateUtils.throwIfNotMatches(verifyResult, ERROR.VERIFY_ERROR, 163 "Failed to verify BC signatures"); 164 return cmsSignedData; 165 } catch (CMSException exception) { 166 LOGGER.debug(exception.getMessage(), exception); 167 CustomException.throwException(ERROR.VERIFY_ERROR, "Failed to verify BC signatures: " 168 + exception.getMessage()); 169 } 170 return cmsSignedData; 171 } 172 } 173