• 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.hap.sign;
17 
18 import com.ohos.hapsigntool.entity.ContentDigestAlgorithm;
19 import com.ohos.hapsigntool.entity.SignatureAlgorithm;
20 import com.ohos.hapsigntool.hap.config.SignerConfig;
21 import com.ohos.hapsigntool.entity.Pair;
22 import com.ohos.hapsigntool.error.SignatureException;
23 import com.ohos.hapsigntool.hap.verify.VerifyUtils;
24 
25 import org.apache.logging.log4j.LogManager;
26 import org.apache.logging.log4j.Logger;
27 import org.bouncycastle.asn1.ASN1EncodableVector;
28 import org.bouncycastle.asn1.ASN1Encoding;
29 import org.bouncycastle.asn1.ASN1Integer;
30 import org.bouncycastle.asn1.ASN1ObjectIdentifier;
31 import org.bouncycastle.asn1.ASN1Set;
32 import org.bouncycastle.asn1.BERSet;
33 import org.bouncycastle.asn1.DEROctetString;
34 import org.bouncycastle.asn1.DERSet;
35 import org.bouncycastle.asn1.cms.Attribute;
36 import org.bouncycastle.asn1.cms.AttributeTable;
37 import org.bouncycastle.asn1.cms.Time;
38 import org.bouncycastle.asn1.pkcs.ContentInfo;
39 import org.bouncycastle.asn1.pkcs.IssuerAndSerialNumber;
40 import org.bouncycastle.asn1.pkcs.PKCSObjectIdentifiers;
41 import org.bouncycastle.asn1.pkcs.SignedData;
42 import org.bouncycastle.asn1.pkcs.SignerInfo;
43 import org.bouncycastle.asn1.x509.AlgorithmIdentifier;
44 import org.bouncycastle.cert.jcajce.JcaX509CRLHolder;
45 import org.bouncycastle.cert.jcajce.JcaX509CertificateHolder;
46 import org.bouncycastle.cms.CMSException;
47 import org.bouncycastle.cms.CMSSignedData;
48 import org.bouncycastle.operator.DefaultDigestAlgorithmIdentifierFinder;
49 import org.bouncycastle.operator.DefaultSignatureAlgorithmIdentifierFinder;
50 import org.bouncycastle.operator.DigestAlgorithmIdentifierFinder;
51 import org.bouncycastle.operator.SignatureAlgorithmIdentifierFinder;
52 
53 import java.io.IOException;
54 import java.security.InvalidAlgorithmParameterException;
55 import java.security.InvalidKeyException;
56 import java.security.MessageDigest;
57 import java.security.NoSuchAlgorithmException;
58 import java.security.PublicKey;
59 import java.security.Signature;
60 import java.security.cert.CRLException;
61 import java.security.cert.CertificateEncodingException;
62 import java.security.cert.X509CRL;
63 import java.security.cert.X509Certificate;
64 import java.security.spec.AlgorithmParameterSpec;
65 import java.util.Date;
66 import java.util.Hashtable;
67 import java.util.List;
68 
69 /**
70  * BC implementation
71  *
72  * @since 2021/12/21
73  */
74 public class BcPkcs7Generator implements Pkcs7Generator {
75     private static final Logger LOGGER = LogManager.getLogger(BcPkcs7Generator.class);
76     private static final SignatureAlgorithmIdentifierFinder SIGN_ALG_FINDER =
77         new DefaultSignatureAlgorithmIdentifierFinder();
78     private static final DigestAlgorithmIdentifierFinder DIGEST_ALG_FINDER =
79         new DefaultDigestAlgorithmIdentifierFinder();
80 
81     private static final String SIGNATURE_VERIFY_FAILED = "Signature did not verify";
82 
83 
84     @Override
generateSignedData(byte[] content, SignerConfig signerConfig)85     public byte[] generateSignedData(byte[] content, SignerConfig signerConfig) throws SignatureException {
86         if (content == null) {
87             throw new SignatureException("unsigned data is null");
88         }
89         ASN1EncodableVector signerInfoLst = new ASN1EncodableVector();
90         ASN1EncodableVector algorithmIdLst = new ASN1EncodableVector();
91         for (SignatureAlgorithm signatureAlgorithm : signerConfig.getSignatureAlgorithms()) {
92             try {
93                 SignerInfo signerInfo = getSignerInfo(signatureAlgorithm, content, signerConfig);
94                     algorithmIdLst.add(signerInfo.getDigestAlgorithm());
95                     signerInfoLst.add(signerInfo);
96                 LOGGER.info("Add sign data in sign info list success.");
97             } catch (NoSuchAlgorithmException e) {
98                 throw new SignatureException(
99                         "Invalid algorithm: " + signatureAlgorithm.getContentDigestAlgorithm().name(), e);
100             } catch (IOException e) {
101                 throw new SignatureException("sign IOException" + e.getMessage(), e);
102             }
103         }
104         return packagePKCS7(signerConfig, new DERSet(signerInfoLst), new DERSet(algorithmIdLst), content);
105     }
106 
packagePKCS7( SignerConfig signerConfig, ASN1Set signerInfoLst, ASN1Set algorithmIdLst, byte[] unsignedHapDigest)107     private byte[] packagePKCS7(
108             SignerConfig signerConfig,
109             ASN1Set signerInfoLst,
110             ASN1Set algorithmIdLst,
111             byte[] unsignedHapDigest)
112             throws SignatureException {
113         ContentInfo contentInfo = new ContentInfo(PKCSObjectIdentifiers.data, new DEROctetString(unsignedHapDigest));
114         ASN1Set certs = null;
115         ASN1Set crls = null;
116         byte[] signBlock;
117         try {
118             if (checkListNotNullOrEmty(signerConfig.getCertificates())) {
119                 certs = createBerSetFromCerts(signerConfig.getCertificates());
120             }
121             if (checkListNotNullOrEmty(signerConfig.getX509CRLs())) {
122                 crls = createBerSetFromCrls(signerConfig.getX509CRLs());
123             }
124             SignedData signedData = new SignedData(
125                 new ASN1Integer(1), algorithmIdLst, contentInfo, certs, crls, signerInfoLst);
126             ContentInfo pkcs7 = new ContentInfo(PKCSObjectIdentifiers.signedData, signedData);
127             signBlock = pkcs7.getEncoded(ASN1Encoding.DER);
128         } catch (CertificateEncodingException | CRLException | IOException e) {
129             throw new SignatureException("Packaging PKCS cms data failed!", e);
130         }
131         boolean verifyResult = false;
132         try {
133             CMSSignedData cmsSignedData = new CMSSignedData(signBlock);
134             verifyResult = VerifyUtils.verifyCmsSignedData(cmsSignedData);
135         } catch (CMSException e) {
136             throw new SignatureException("PKCS cms data verify failed", e);
137         }
138         if (!verifyResult) {
139             throw new SignatureException("PKCS cms data did not verify");
140         }
141         return signBlock;
142     }
143 
getSignerInfo( SignatureAlgorithm signatureAlgorithm, byte[] unsignedHapDigest, SignerConfig signerConfig)144     private SignerInfo getSignerInfo(
145             SignatureAlgorithm signatureAlgorithm, byte[] unsignedHapDigest, SignerConfig signerConfig)
146             throws SignatureException, IOException, NoSuchAlgorithmException {
147         Pair<String, ? extends AlgorithmParameterSpec> signatureParams = signatureAlgorithm.getSignatureAlgAndParams();
148         ContentDigestAlgorithm contentDigestAlg = signatureAlgorithm.getContentDigestAlgorithm();
149         String jcaSignatureAlg = signatureParams.getFirst();
150         MessageDigest md = MessageDigest.getInstance(contentDigestAlg.name());
151         byte[] digest = md.digest(unsignedHapDigest);
152         ASN1Set authed = generatePKCS9Attributes(digest);
153 
154         // Get sign data content from sign server
155         byte[] signatureBytes = signerConfig.getSigner().getSignature(
156             authed.getEncoded(), jcaSignatureAlg, signatureParams.getSecond());
157         if (signatureBytes == null) {
158             throw new SignatureException("Generate signature bytes error");
159         }
160         if (!checkListNotNullOrEmty(signerConfig.getCertificates())) {
161             throw new SignatureException("No certificates configured for signer");
162         }
163 
164         Pair<String, ? extends AlgorithmParameterSpec> obj = signatureAlgorithm.getSignatureAlgAndParams();
165         Pair<String, AlgorithmParameterSpec> signAlgPair = Pair.create(obj.getFirst(), obj.getSecond());
166         if (!verifySignatureFromServer(signerConfig, signatureBytes, signAlgPair, authed)) {
167             throw new SignatureException(SIGNATURE_VERIFY_FAILED);
168         }
169         return createSignerInfo(signerConfig, signatureAlgorithm, authed, signatureBytes);
170     }
171 
createSignerInfo( SignerConfig signerConfig, SignatureAlgorithm signatureAlgorithm, ASN1Set authed, byte[] signedHapDigest)172     private SignerInfo createSignerInfo(
173             SignerConfig signerConfig,
174             SignatureAlgorithm signatureAlgorithm,
175             ASN1Set authed,
176             byte[] signedHapDigest)
177             throws SignatureException {
178         String digestAlgorithm = signatureAlgorithm.getContentDigestAlgorithm().getDigestAlgorithm();
179         String signAlg = signatureAlgorithm.getSignatureAlgAndParams().getFirst();
180         try {
181             JcaX509CertificateHolder certificateHolder =
182                 new JcaX509CertificateHolder(signerConfig.getCertificates().get(0));
183             AlgorithmIdentifier digestAlgId = DIGEST_ALG_FINDER.find(digestAlgorithm);
184             AlgorithmIdentifier signAlgId = SIGN_ALG_FINDER.find(signAlg);
185             IssuerAndSerialNumber issuerAndSerialNumber =
186                 new IssuerAndSerialNumber(certificateHolder.getIssuer(), certificateHolder.getSerialNumber());
187             return new SignerInfo(new ASN1Integer(1), issuerAndSerialNumber, digestAlgId,
188                     authed, signAlgId, new DEROctetString(signedHapDigest), null);
189         } catch (CertificateEncodingException e) {
190             throw new SignatureException("Generate signer info error", e);
191         }
192     }
193 
generatePKCS9Attributes(byte[] digest)194     private ASN1Set generatePKCS9Attributes(byte[] digest) {
195         Hashtable<ASN1ObjectIdentifier, Attribute> tab = new Hashtable<>();
196         Attribute signTime = new Attribute(PKCSObjectIdentifiers.pkcs_9_at_signingTime,
197             new DERSet(new Time(new Date())));
198         Attribute contentType = new Attribute(PKCSObjectIdentifiers.pkcs_9_at_contentType,
199             new DERSet(PKCSObjectIdentifiers.data));
200         Attribute digestAtt = new Attribute(PKCSObjectIdentifiers.pkcs_9_at_messageDigest,
201             new DERSet(new DEROctetString(digest)));
202         tab.put(signTime.getAttrType(), signTime);
203         tab.put(contentType.getAttrType(), contentType);
204         tab.put(digestAtt.getAttrType(), digestAtt);
205         return new DERSet(new AttributeTable(tab).toASN1EncodableVector());
206     }
207 
createBerSetFromCrls(List<X509CRL> crls)208     private ASN1Set createBerSetFromCrls(List<X509CRL> crls) throws CRLException {
209         if (crls == null || crls.size() == 0) {
210             return null;
211         }
212         ASN1EncodableVector vector = new ASN1EncodableVector();
213         for (X509CRL crl : crls) {
214             vector.add(new JcaX509CRLHolder(crl).toASN1Structure());
215         }
216         return new BERSet(vector);
217     }
218 
createBerSetFromCerts(List<X509Certificate> certs)219     private ASN1Set createBerSetFromCerts(List<X509Certificate> certs) throws CertificateEncodingException {
220         if (certs == null || certs.size() == 0) {
221             return null;
222         }
223         ASN1EncodableVector vector = new ASN1EncodableVector();
224 
225         for (X509Certificate cert : certs) {
226             vector.add(new JcaX509CertificateHolder(cert).toASN1Structure());
227         }
228         return new BERSet(vector);
229     }
230 
checkListNotNullOrEmty(List<?> lists)231     private boolean checkListNotNullOrEmty(List<?> lists) {
232         return (lists != null) && (lists.size() > 0);
233     }
234 
verifySignatureFromServer( SignerConfig signerConfig, byte[] signatureBytes, Pair<String, AlgorithmParameterSpec> signAlgPair, ASN1Set authed)235     private boolean verifySignatureFromServer(
236             SignerConfig signerConfig,
237             byte[] signatureBytes,
238             Pair<String, AlgorithmParameterSpec> signAlgPair,
239             ASN1Set authed)
240             throws SignatureException {
241         try {
242             PublicKey publicKey = signerConfig.getCertificates().get(0).getPublicKey();
243             Signature signature = Signature.getInstance(signAlgPair.getFirst());
244             signature.initVerify(publicKey);
245             if (signAlgPair.getSecond() != null) {
246                 signature.setParameter(signAlgPair.getSecond());
247             }
248             signature.update(authed.getEncoded());
249             if (signatureBytes == null) {
250                 LOGGER.error("signatureBytes is null");
251                 throw new SignatureException(SIGNATURE_VERIFY_FAILED);
252             }
253             if (!signature.verify(signatureBytes)) {
254                 throw new SignatureException(SIGNATURE_VERIFY_FAILED);
255             }
256             return true;
257         } catch (InvalidKeyException | java.security.SignatureException e) {
258             LOGGER.error("Failed to verify generated signature using public key from certificate", e);
259         } catch (NoSuchAlgorithmException e) {
260             LOGGER.error("Failed to verify generated " + signAlgPair.getFirst()
261                     + " signature using public key from certificate", e);
262         } catch (InvalidAlgorithmParameterException e) {
263             LOGGER.error("Failed to verify generated " + signAlgPair.getSecond()
264                     + " signature using public key from certificate", e);
265         } catch (IOException e) {
266             LOGGER.error("PKCS9 Attributes encode failed.", e);
267         }
268         return false;
269     }
270 }
271