• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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.google.gson.JsonObject;
19 import com.ohos.hapsigntool.error.CustomException;
20 import com.ohos.hapsigntool.error.ERROR;
21 import com.ohos.hapsigntool.error.VerifyException;
22 import com.ohos.hapsigntool.hap.verify.VerifyUtils;
23 import com.ohos.hapsigntool.profile.model.VerificationResult;
24 import com.ohos.hapsigntool.utils.CertChainUtils;
25 import com.ohos.hapsigntool.utils.CertUtils;
26 import com.ohos.hapsigntool.utils.FileUtils;
27 import com.ohos.hapsigntool.utils.ValidateUtils;
28 import org.apache.logging.log4j.LogManager;
29 import org.apache.logging.log4j.Logger;
30 import org.bouncycastle.asn1.ASN1Encodable;
31 import org.bouncycastle.asn1.ASN1Set;
32 import org.bouncycastle.asn1.cms.Attribute;
33 import org.bouncycastle.asn1.pkcs.PKCSObjectIdentifiers;
34 import org.bouncycastle.asn1.x509.Time;
35 import org.bouncycastle.cert.X509CertificateHolder;
36 import org.bouncycastle.cert.jcajce.JcaX509CertificateConverter;
37 import org.bouncycastle.cms.CMSException;
38 import org.bouncycastle.cms.CMSSignedData;
39 import org.bouncycastle.cms.SignerId;
40 import org.bouncycastle.cms.SignerInformation;
41 import org.bouncycastle.cms.SignerInformationStore;
42 import org.bouncycastle.util.Store;
43 
44 import javax.security.auth.x500.X500Principal;
45 import java.io.IOException;
46 import java.nio.charset.StandardCharsets;
47 import java.security.InvalidKeyException;
48 import java.security.NoSuchAlgorithmException;
49 import java.security.Signature;
50 import java.security.SignatureException;
51 import java.security.cert.CertificateException;
52 import java.security.cert.X509Certificate;
53 import java.time.LocalDateTime;
54 import java.time.ZoneId;
55 import java.util.ArrayList;
56 import java.util.Collection;
57 import java.util.Iterator;
58 import java.util.List;
59 import java.util.Date;
60 
61 /**
62  * Signed provision profile verifier.
63  *
64  * @since 2021/12/28
65  */
66 public class VerifyHelper implements IProvisionVerifier {
67     /**
68      * LOGGER.
69      */
70     private static final Logger LOGGER = LogManager.getLogger(VerifyHelper.class);
71 
72     /**
73      * Signed provision profile verifier.
74      */
VerifyHelper()75     public VerifyHelper() {}
76 
77     /**
78      * Checked signed data with public key.
79      *
80      * @param cert         public key
81      * @param signedData   signed data with private key
82      * @param unsignedData unsigned data
83      * @param algorithm    algorithm
84      */
verifySignature(X509Certificate cert, byte[] signedData, byte[] unsignedData, String algorithm)85     public static void verifySignature(X509Certificate cert, byte[] signedData, byte[] unsignedData, String algorithm) {
86         try {
87             Signature signature = Signature.getInstance(algorithm);
88             signature.initVerify(cert);
89             signature.update(unsignedData);
90             ValidateUtils.throwIfNotMatches(signature.verify(signedData), ERROR.SIGN_ERROR, "Signature not matched!");
91         } catch (InvalidKeyException | SignatureException | NoSuchAlgorithmException exception) {
92             LOGGER.debug(exception.getMessage(), exception);
93             CustomException.throwException(ERROR.SIGN_ERROR, "Failed to verify signature: " + exception.getMessage());
94         }
95     }
96 
97     /**
98      * Convert store collection to list.
99      *
100      * @param certificates certificates from cmsSignedData
101      * @return List<X509Certificate>
102      */
certStoreToCertList(Store<X509CertificateHolder> certificates)103     public static List<X509Certificate> certStoreToCertList(Store<X509CertificateHolder> certificates) {
104         String errorMsg = "Verify failed, not found cert chain";
105         JcaX509CertificateConverter converter = new JcaX509CertificateConverter();
106         ValidateUtils.throwIfMatches(certificates == null, ERROR.VERIFY_ERROR, errorMsg);
107         Collection<X509CertificateHolder> matches = certificates.getMatches(null);
108         ValidateUtils.throwIfMatches(matches == null || !matches.iterator().hasNext(),
109                 ERROR.VERIFY_ERROR, errorMsg);
110 
111         Iterator<X509CertificateHolder> iterator = matches.iterator();
112         List<X509Certificate> certificateList = new ArrayList<>();
113         try {
114             while (iterator.hasNext()) {
115                 X509CertificateHolder next = iterator.next();
116                 certificateList.add(converter.getCertificate(next));
117             }
118         } catch (CertificateException exception) {
119             LOGGER.debug(exception.getMessage(), exception);
120             CustomException.throwException(ERROR.VERIFY_ERROR, errorMsg);
121         }
122         ValidateUtils.throwIfMatches(certificateList.size() == 0, ERROR.VERIFY_ERROR, errorMsg);
123         return certificateList;
124     }
125 
126     /**
127      * verify p7b content.
128      *
129      * @param p7b signed p7b content
130      * @return result
131      * @throws VerifyException verify p7b failed
132      */
133     @Override
verify(byte[] p7b)134     public VerificationResult verify(byte[] p7b) throws VerifyException {
135         VerificationResult result = new VerificationResult();
136 
137         try {
138             CMSSignedData cmsSignedData = this.verifyPkcs(p7b);
139             List<X509Certificate> certificates = certStoreToCertList(cmsSignedData.getCertificates());
140             CertUtils.sortCertificateChain(certificates);
141 
142             SignerInformationStore signerInfos = cmsSignedData.getSignerInfos();
143             Collection<SignerInformation> signers = signerInfos.getSigners();
144 
145             for (SignerInformation signer : signers) {
146                 SignerId sid = signer.getSID();
147                 Date signTime = getSignTime(signer);
148 
149                 X500Principal principal = new X500Principal(sid.getIssuer().getEncoded());
150                 CertChainUtils.verifyCertChain(certificates, principal, sid.getSerialNumber(),
151                         certificates.get(certificates.size() - 1), signTime);
152             }
153 
154             result.setContent(FileUtils.GSON.fromJson(new String((byte[]) (cmsSignedData
155                     .getSignedContent().getContent()), StandardCharsets.UTF_8), JsonObject.class));
156             result.setMessage("OK");
157             result.setVerifiedPassed(true);
158             return result;
159         } catch (CustomException | IOException exception) {
160             LOGGER.debug(exception.getMessage(), exception);
161             result.setMessage(exception.getMessage());
162             result.setVerifiedPassed(false);
163             return result;
164         }
165     }
166 
getSignTime(SignerInformation signer)167     Date getSignTime(SignerInformation signer) {
168         Date signTime = Date.from(LocalDateTime.now().atZone(ZoneId.systemDefault()).toInstant());
169 
170         Attribute attribute = signer.getSignedAttributes().get(PKCSObjectIdentifiers.pkcs_9_at_signingTime);
171 
172         if (attribute == null) {
173             LOGGER.warn("sign information does not include signTime");
174             return signTime;
175         }
176 
177         ASN1Set attrValues = attribute.getAttrValues();
178         if (attrValues.size() == 0) {
179             LOGGER.warn("get sign time false, use local datetime verify profile cert chain");
180             return signTime;
181         }
182 
183         ASN1Encodable objectAt = attrValues.getObjectAt(0);
184         signTime = Time.getInstance(objectAt).getDate();
185 
186         return signTime;
187     }
188 
verifyPkcs(byte[] p7b)189     CMSSignedData verifyPkcs(byte[] p7b) {
190         CMSSignedData cmsSignedData = null;
191         try {
192             cmsSignedData = new CMSSignedData(p7b);
193             boolean verifyResult = VerifyUtils.verifyCmsSignedData(cmsSignedData);
194             ValidateUtils.throwIfNotMatches(verifyResult, ERROR.VERIFY_ERROR,
195                     "Failed to verify BC signatures");
196             return cmsSignedData;
197         } catch (CMSException exception) {
198             LOGGER.debug(exception.getMessage(), exception);
199             CustomException.throwException(ERROR.VERIFY_ERROR, "Failed to verify BC signatures: "
200                     + exception.getMessage());
201         }
202         return cmsSignedData;
203     }
204 }
205