• 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         // Empty constructor
77     }
78 
79     /**
80      * Checked signed data with public key.
81      *
82      * @param cert         public key
83      * @param signedData   signed data with private key
84      * @param unsignedData unsigned data
85      * @param algorithm    algorithm
86      */
verifySignature(X509Certificate cert, byte[] signedData, byte[] unsignedData, String algorithm)87     public static void verifySignature(X509Certificate cert, byte[] signedData, byte[] unsignedData, String algorithm) {
88         try {
89             Signature signature = Signature.getInstance(algorithm);
90             signature.initVerify(cert);
91             signature.update(unsignedData);
92             ValidateUtils.throwIfNotMatches(signature.verify(signedData), ERROR.SIGN_ERROR, "Signature not matched!");
93         } catch (InvalidKeyException | SignatureException | NoSuchAlgorithmException exception) {
94             LOGGER.debug(exception.getMessage(), exception);
95             CustomException.throwException(ERROR.SIGN_ERROR, "Failed to verify signature: " + exception.getMessage());
96         }
97     }
98 
99     /**
100      * Convert store collection to list.
101      *
102      * @param certificates certificates from cmsSignedData
103      * @return List<X509Certificate>
104      */
certStoreToCertList(Store<X509CertificateHolder> certificates)105     public static List<X509Certificate> certStoreToCertList(Store<X509CertificateHolder> certificates) {
106         String errorMsg = "Verify failed, not found cert chain";
107         JcaX509CertificateConverter converter = new JcaX509CertificateConverter();
108         ValidateUtils.throwIfMatches(certificates == null, ERROR.VERIFY_ERROR, errorMsg);
109         Collection<X509CertificateHolder> matches = certificates.getMatches(null);
110         ValidateUtils.throwIfMatches(matches == null || !matches.iterator().hasNext(),
111                 ERROR.VERIFY_ERROR, errorMsg);
112 
113         Iterator<X509CertificateHolder> iterator = matches.iterator();
114         List<X509Certificate> certificateList = new ArrayList<>();
115         try {
116             while (iterator.hasNext()) {
117                 X509CertificateHolder next = iterator.next();
118                 certificateList.add(converter.getCertificate(next));
119             }
120         } catch (CertificateException exception) {
121             LOGGER.debug(exception.getMessage(), exception);
122             CustomException.throwException(ERROR.VERIFY_ERROR, errorMsg);
123         }
124         ValidateUtils.throwIfMatches(certificateList.size() == 0, ERROR.VERIFY_ERROR, errorMsg);
125         return certificateList;
126     }
127 
128     /**
129      * verify p7b content.
130      *
131      * @param p7b signed p7b content
132      * @return result
133      * @throws VerifyException verify p7b failed
134      */
135     @Override
verify(byte[] p7b)136     public VerificationResult verify(byte[] p7b) throws VerifyException {
137         VerificationResult result = new VerificationResult();
138 
139         try {
140             CMSSignedData cmsSignedData = this.verifyPkcs(p7b);
141             List<X509Certificate> certificates = certStoreToCertList(cmsSignedData.getCertificates());
142             CertUtils.sortCertificateChain(certificates);
143 
144             SignerInformationStore signerInfos = cmsSignedData.getSignerInfos();
145             Collection<SignerInformation> signers = signerInfos.getSigners();
146 
147             for (SignerInformation signer : signers) {
148                 SignerId sid = signer.getSID();
149                 Date signTime = getSignTime(signer);
150 
151                 X500Principal principal = new X500Principal(sid.getIssuer().getEncoded());
152                 CertChainUtils.verifyCertChain(certificates, principal, sid.getSerialNumber(),
153                         certificates.get(certificates.size() - 1), signTime);
154             }
155 
156             result.setContent(FileUtils.GSON.fromJson(new String((byte[]) (cmsSignedData
157                     .getSignedContent().getContent()), StandardCharsets.UTF_8), JsonObject.class));
158             result.setMessage("OK");
159             result.setVerifiedPassed(true);
160             return result;
161         } catch (CustomException | IOException exception) {
162             LOGGER.debug(exception.getMessage(), exception);
163             result.setMessage(exception.getMessage());
164             result.setVerifiedPassed(false);
165             return result;
166         }
167     }
168 
getSignTime(SignerInformation signer)169     Date getSignTime(SignerInformation signer) {
170         Date signTime = Date.from(LocalDateTime.now().atZone(ZoneId.systemDefault()).toInstant());
171 
172         Attribute attribute = signer.getSignedAttributes().get(PKCSObjectIdentifiers.pkcs_9_at_signingTime);
173 
174         if (attribute == null) {
175             LOGGER.warn("sign information does not include signTime");
176             return signTime;
177         }
178 
179         ASN1Set attrValues = attribute.getAttrValues();
180         if (attrValues.size() == 0) {
181             LOGGER.warn("get sign time false, use local datetime verify profile cert chain");
182             return signTime;
183         }
184 
185         ASN1Encodable objectAt = attrValues.getObjectAt(0);
186         signTime = Time.getInstance(objectAt).getDate();
187 
188         return signTime;
189     }
190 
verifyPkcs(byte[] p7b)191     CMSSignedData verifyPkcs(byte[] p7b) {
192         CMSSignedData cmsSignedData = null;
193         try {
194             cmsSignedData = new CMSSignedData(p7b);
195             boolean verifyResult = VerifyUtils.verifyCmsSignedData(cmsSignedData);
196             ValidateUtils.throwIfNotMatches(verifyResult, ERROR.VERIFY_ERROR,
197                     "Failed to verify BC signatures");
198             return cmsSignedData;
199         } catch (CMSException exception) {
200             LOGGER.debug(exception.getMessage(), exception);
201             CustomException.throwException(ERROR.VERIFY_ERROR, "Failed to verify BC signatures: "
202                     + exception.getMessage());
203         }
204         return cmsSignedData;
205     }
206 }
207