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