• 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.hap.verify.VerifyUtils;
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.asn1.ASN1Encodable;
30 import org.bouncycastle.asn1.ASN1Set;
31 import org.bouncycastle.asn1.cms.Attribute;
32 import org.bouncycastle.asn1.pkcs.PKCSObjectIdentifiers;
33 import org.bouncycastle.asn1.x509.Time;
34 import org.bouncycastle.cert.X509CertificateHolder;
35 import org.bouncycastle.cert.jcajce.JcaX509CertificateConverter;
36 import org.bouncycastle.cms.CMSException;
37 import org.bouncycastle.cms.CMSSignedData;
38 import org.bouncycastle.cms.SignerId;
39 import org.bouncycastle.cms.SignerInformation;
40 import org.bouncycastle.cms.SignerInformationStore;
41 import org.bouncycastle.util.Store;
42 
43 import javax.security.auth.x500.X500Principal;
44 import java.io.IOException;
45 import java.nio.charset.StandardCharsets;
46 import java.security.InvalidKeyException;
47 import java.security.NoSuchAlgorithmException;
48 import java.security.Signature;
49 import java.security.SignatureException;
50 import java.security.cert.CertificateException;
51 import java.security.cert.X509Certificate;
52 import java.time.LocalDateTime;
53 import java.time.ZoneId;
54 import java.util.ArrayList;
55 import java.util.Collection;
56 import java.util.Iterator;
57 import java.util.List;
58 import java.util.Date;
59 
60 /**
61  * Signed provision profile verifier.
62  *
63  * @since 2021/12/28
64  */
65 public class VerifyHelper implements IProvisionVerifier {
66     /**
67      * LOGGER.
68      */
69     private static final Logger LOGGER = LogManager.getLogger(VerifyHelper.class);
70 
71     /**
72      * Signed provision profile verifier.
73      */
VerifyHelper()74     public VerifyHelper() {
75         // Empty constructor
76     }
77 
78     /**
79      * Checked signed data with public key.
80      *
81      * @param cert         public key
82      * @param signedData   signed data with private key
83      * @param unsignedData unsigned data
84      * @param algorithm    algorithm
85      */
verifySignature(X509Certificate cert, byte[] signedData, byte[] unsignedData, String algorithm)86     public static void verifySignature(X509Certificate cert, byte[] signedData, byte[] unsignedData, String algorithm) {
87         try {
88             Signature signature = Signature.getInstance(algorithm);
89             signature.initVerify(cert);
90             signature.update(unsignedData);
91             ValidateUtils.throwIfNotMatches(signature.verify(signedData), ERROR.SIGN_ERROR, "Signature not matched!");
92         } catch (InvalidKeyException | SignatureException | NoSuchAlgorithmException exception) {
93             LOGGER.debug(exception.getMessage(), exception);
94             CustomException.throwException(ERROR.SIGN_ERROR, "Failed to verify signature: " + exception.getMessage());
95         }
96     }
97 
98     /**
99      * Convert store collection to list.
100      *
101      * @param certificates certificates from cmsSignedData
102      * @return List<X509Certificate>
103      */
certStoreToCertList(Store<X509CertificateHolder> certificates)104     public static List<X509Certificate> certStoreToCertList(Store<X509CertificateHolder> certificates) {
105         String errorMsg = "Verify failed, not found cert chain";
106         JcaX509CertificateConverter converter = new JcaX509CertificateConverter();
107         ValidateUtils.throwIfMatches(certificates == null, ERROR.VERIFY_ERROR, errorMsg);
108         Collection<X509CertificateHolder> matches = certificates.getMatches(null);
109         ValidateUtils.throwIfMatches(matches == null || !matches.iterator().hasNext(),
110                 ERROR.VERIFY_ERROR, errorMsg);
111 
112         Iterator<X509CertificateHolder> iterator = matches.iterator();
113         List<X509Certificate> certificateList = new ArrayList<>();
114         try {
115             while (iterator.hasNext()) {
116                 X509CertificateHolder next = iterator.next();
117                 certificateList.add(converter.getCertificate(next));
118             }
119         } catch (CertificateException exception) {
120             LOGGER.debug(exception.getMessage(), exception);
121             CustomException.throwException(ERROR.VERIFY_ERROR, errorMsg);
122         }
123         ValidateUtils.throwIfMatches(certificateList.size() == 0, ERROR.VERIFY_ERROR, errorMsg);
124         return certificateList;
125     }
126 
127     /**
128      * verify p7b content.
129      *
130      * @param p7b signed p7b content
131      * @return result
132      */
133     @Override
verify(byte[] p7b)134     public VerificationResult verify(byte[] p7b) {
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