• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 /*
2  * Copyright (c) 2023-2023 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.codesigning.sign;
17 
18 import com.ohos.hapsigntool.codesigning.exception.CodeSignErrMsg;
19 import com.ohos.hapsigntool.codesigning.exception.CodeSignException;
20 import com.ohos.hapsigntool.codesigning.utils.CmsUtils;
21 import com.ohos.hapsigntool.codesigning.utils.DigestUtils;
22 import com.ohos.hapsigntool.entity.Pair;
23 import com.ohos.hapsigntool.entity.ContentDigestAlgorithm;
24 import com.ohos.hapsigntool.entity.SignatureAlgorithm;
25 import com.ohos.hapsigntool.hap.config.SignerConfig;
26 import com.ohos.hapsigntool.utils.LogUtils;
27 import org.bouncycastle.asn1.ASN1EncodableVector;
28 import org.bouncycastle.asn1.ASN1Encoding;
29 import org.bouncycastle.asn1.ASN1Integer;
30 import org.bouncycastle.asn1.ASN1Set;
31 import org.bouncycastle.asn1.BERSet;
32 import org.bouncycastle.asn1.DEROctetString;
33 import org.bouncycastle.asn1.DERSet;
34 import org.bouncycastle.asn1.ASN1ObjectIdentifier;
35 import org.bouncycastle.asn1.DERUTF8String;
36 import org.bouncycastle.asn1.cms.Time;
37 import org.bouncycastle.asn1.pkcs.Attribute;
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.cert.jcajce.JcaX509CRLHolder;
44 import org.bouncycastle.cert.jcajce.JcaX509CertificateHolder;
45 import org.bouncycastle.cms.CMSException;
46 import org.bouncycastle.operator.DefaultDigestAlgorithmIdentifierFinder;
47 import org.bouncycastle.operator.DefaultSignatureAlgorithmIdentifierFinder;
48 import org.bouncycastle.operator.DigestAlgorithmIdentifierFinder;
49 import org.bouncycastle.operator.SignatureAlgorithmIdentifierFinder;
50 
51 import java.io.IOException;
52 import java.security.InvalidAlgorithmParameterException;
53 import java.security.InvalidKeyException;
54 import java.security.NoSuchAlgorithmException;
55 import java.security.PublicKey;
56 import java.security.Signature;
57 import java.security.SignatureException;
58 import java.security.cert.CRLException;
59 import java.security.cert.CertificateEncodingException;
60 import java.security.cert.X509CRL;
61 import java.security.cert.X509Certificate;
62 import java.security.spec.AlgorithmParameterSpec;
63 import java.util.Date;
64 import java.util.List;
65 
66 /**
67  * BC implementation
68  *
69  * @since 2023/06/05
70  */
71 public class BcSignedDataGenerator implements SignedDataGenerator {
72     /**
73      * OID of the signer identity
74      */
75     public static final String SIGNER_OID = "1.3.6.1.4.1.2011.2.376.1.4.1";
76 
77     /**
78      * PLUGIN ID of the signer identity
79      */
80     public static final String SIGNER_PLUGIN_ID = "1.3.6.1.4.1.2011.2.376.1.4.2";
81 
82     private static final LogUtils LOGGER = new LogUtils(BcSignedDataGenerator.class);
83 
84     private static final SignatureAlgorithmIdentifierFinder SIGN_ALG_ID_FINDER
85         = new DefaultSignatureAlgorithmIdentifierFinder();
86 
87     private static final DigestAlgorithmIdentifierFinder DIGEST_ALG_ID_FINDER
88         = new DefaultDigestAlgorithmIdentifierFinder();
89 
90     private String ownerID;
91 
92     private String pluginId;
93 
setOwnerID(String ownerID)94     public void setOwnerID(String ownerID) {
95         this.ownerID = ownerID;
96     }
97 
setPluginId(String pluginId)98     public void setPluginId(String pluginId) {
99         this.pluginId = pluginId;
100     }
101 
102     @Override
generateSignedData(byte[] content, SignerConfig signConfig)103     public byte[] generateSignedData(byte[] content, SignerConfig signConfig) throws CodeSignException {
104         if (content == null) {
105             throw new CodeSignException(
106                 CodeSignErrMsg.CODE_SIGN_INTERNAL_ERROR.toString("The content to be signed is empty"));
107         }
108         Pair<DERSet, DERSet> pairDigestAndSignInfo = getSignInfo(content, signConfig);
109         // Unsupported certificate revocation, SignedData's _crls is null
110         SignedData signedData = new SignedData(new ASN1Integer(1), pairDigestAndSignInfo.getFirst(),
111             new ContentInfo(PKCSObjectIdentifiers.data, null), createBerSetFromLst(signConfig.getCertificates()),
112             createBerSetFromLst(null), pairDigestAndSignInfo.getSecond());
113         return encodingUnsignedData(content, signedData);
114     }
115 
getSignInfo(byte[] content, SignerConfig signConfig)116     private Pair<DERSet, DERSet> getSignInfo(byte[] content, SignerConfig signConfig) throws CodeSignException {
117         ASN1EncodableVector signInfoVector = new ASN1EncodableVector();
118         ASN1EncodableVector digestVector = new ASN1EncodableVector();
119         for (SignatureAlgorithm signAlgorithm : signConfig.getSignatureAlgorithms()) {
120             SignerInfo signInfo = createSignInfo(signAlgorithm, content, signConfig);
121             signInfoVector.add(signInfo);
122             digestVector.add(signInfo.getDigestAlgorithm());
123             LOGGER.info("Create a sign info successfully.");
124         }
125         return Pair.create(new DERSet(digestVector), new DERSet(signInfoVector));
126     }
127 
createSignInfo(SignatureAlgorithm signAlgorithm, byte[] unsignedDataDigest, SignerConfig signConfig)128     private SignerInfo createSignInfo(SignatureAlgorithm signAlgorithm, byte[] unsignedDataDigest,
129         SignerConfig signConfig) throws CodeSignException {
130         ContentDigestAlgorithm hashAlgorithm = signAlgorithm.getContentDigestAlgorithm();
131         byte[] digest = computeDigest(unsignedDataDigest, hashAlgorithm.name());
132         ASN1Set authed = getPKCS9Attributes(digest);
133         byte[] codeAuthed = getEncoded(authed);
134         Pair<String, ? extends AlgorithmParameterSpec> signPair = signAlgorithm.getSignatureAlgAndParams();
135         byte[] signBytes = signConfig.getSigner().getSignature(codeAuthed, signPair.getFirst(), signPair.getSecond());
136         if (signBytes == null) {
137             throw new CodeSignException(CodeSignErrMsg.CODE_SIGN_INTERNAL_ERROR.toString("Get signature failed"));
138         }
139         if (signConfig.getCertificates().isEmpty()) {
140             throw new CodeSignException(
141                 CodeSignErrMsg.CERTIFICATES_CONFIGURE_ERROR.toString("No certificate is configured for sign"));
142         }
143         X509Certificate cert = signConfig.getCertificates().get(0);
144         if (!verifySignFromServer(cert.getPublicKey(), signBytes, signPair, codeAuthed)) {
145             throw new CodeSignException(
146                 CodeSignErrMsg.CODE_SIGN_INTERNAL_ERROR.toString("Verify signed result failed"));
147         }
148         JcaX509CertificateHolder certificateHolder = getJcaX509CertificateHolder(cert);
149         return new SignerInfo(new ASN1Integer(1),
150             new IssuerAndSerialNumber(certificateHolder.getIssuer(), certificateHolder.getSerialNumber()),
151             DIGEST_ALG_ID_FINDER.find(hashAlgorithm.getDigestAlgorithm()), authed,
152             SIGN_ALG_ID_FINDER.find(signPair.getFirst()), new DEROctetString(signBytes), null);
153     }
154 
computeDigest(byte[] unsignedDataDigest, String algorithm)155     private byte[] computeDigest(byte[] unsignedDataDigest, String algorithm) throws CodeSignException {
156         byte[] digest;
157         try {
158             digest = DigestUtils.computeDigest(unsignedDataDigest, algorithm);
159         } catch (NoSuchAlgorithmException e) {
160             throw new CodeSignException(CodeSignErrMsg.ALGORITHM_NOT_SUPPORT_ERROR.toString(algorithm), e);
161         }
162         return digest;
163     }
164 
getEncoded(ASN1Set authed)165     private byte[] getEncoded(ASN1Set authed) throws CodeSignException {
166         byte[] codeAuthed;
167         try {
168             codeAuthed = authed.getEncoded();
169         } catch (IOException e) {
170             throw new CodeSignException(CodeSignErrMsg.CODE_SIGN_INTERNAL_ERROR.toString("Encode data error"), e);
171         }
172         return codeAuthed;
173     }
174 
getJcaX509CRLHolder(X509CRL crl)175     private JcaX509CRLHolder getJcaX509CRLHolder(X509CRL crl) throws CodeSignException {
176         JcaX509CRLHolder crlHolder;
177         try {
178             crlHolder = new JcaX509CRLHolder(crl);
179         } catch (CRLException e) {
180             throw new CodeSignException(CodeSignErrMsg.CODE_SIGN_INTERNAL_ERROR.toString("Create crl failed"), e);
181         }
182         return crlHolder;
183     }
184 
getJcaX509CertificateHolder(X509Certificate cert)185     private JcaX509CertificateHolder getJcaX509CertificateHolder(X509Certificate cert) throws CodeSignException {
186         JcaX509CertificateHolder certificateHolder;
187         try {
188             certificateHolder = new JcaX509CertificateHolder(cert);
189         } catch (CertificateEncodingException e) {
190             throw new CodeSignException(CodeSignErrMsg.CODE_SIGN_INTERNAL_ERROR.toString("Create sign info failed"), e);
191         }
192         return certificateHolder;
193     }
194 
getPKCS9Attributes(byte[] digest)195     private ASN1Set getPKCS9Attributes(byte[] digest) {
196         ASN1EncodableVector table = new ASN1EncodableVector();
197         Attribute signingTimeAttr = new Attribute(PKCSObjectIdentifiers.pkcs_9_at_signingTime,
198             new DERSet(new Time(new Date())));
199         Attribute contentTypeAttr = new Attribute(PKCSObjectIdentifiers.pkcs_9_at_contentType,
200             new DERSet(PKCSObjectIdentifiers.data));
201         Attribute messageDigestAttr = new Attribute(PKCSObjectIdentifiers.pkcs_9_at_messageDigest,
202             new DERSet(new DEROctetString(digest)));
203         table.add(signingTimeAttr);
204         table.add(contentTypeAttr);
205         table.add(messageDigestAttr);
206         if (ownerID != null) {
207             Attribute ownerIDAttr = new Attribute(new ASN1ObjectIdentifier(SIGNER_OID),
208                 new DERSet(new DERUTF8String(ownerID)));
209             table.add(ownerIDAttr);
210         }
211         if (pluginId != null) {
212             Attribute pluginIDAttr = new Attribute(new ASN1ObjectIdentifier(SIGNER_PLUGIN_ID),
213                 new DERSet(new DERUTF8String(pluginId)));
214             table.add(pluginIDAttr);
215         }
216         return new DERSet(table);
217     }
218 
verifySignFromServer(PublicKey publicKey, byte[] signBytes, Pair<String, ? extends AlgorithmParameterSpec> signPair, byte[] authed)219     private boolean verifySignFromServer(PublicKey publicKey, byte[] signBytes,
220         Pair<String, ? extends AlgorithmParameterSpec> signPair, byte[] authed) throws CodeSignException {
221         try {
222             Signature signature = Signature.getInstance(signPair.getFirst());
223             signature.initVerify(publicKey);
224             if (signPair.getSecond() != null) {
225                 signature.setParameter(signPair.getSecond());
226             }
227             signature.update(authed);
228             if (!signature.verify(signBytes)) {
229                 throw new CodeSignException(CodeSignErrMsg.SIGNATURE_VERIFY_FAILED_ERROR.toString());
230             }
231             return true;
232         } catch (InvalidKeyException | SignatureException e) {
233             LOGGER.error("The generated signature could not be verified " + " using the public key in the certificate",
234                 e);
235         } catch (NoSuchAlgorithmException e) {
236             LOGGER.error("The generated signature " + signPair.getFirst()
237                 + " could not be verified using the public key in the certificate", e);
238         } catch (InvalidAlgorithmParameterException e) {
239             LOGGER.error("The generated signature " + signPair.getSecond()
240                 + " could not be verified using the public key in the certificate", e);
241         }
242         return false;
243     }
244 
createBerSetFromLst(List<?> lists)245     private ASN1Set createBerSetFromLst(List<?> lists) throws CodeSignException {
246         if (lists == null || lists.size() == 0) {
247             return null;
248         }
249         ASN1EncodableVector vector = new ASN1EncodableVector();
250         for (Object obj : lists) {
251             if (obj instanceof X509CRL) {
252                 vector.add(getJcaX509CRLHolder((X509CRL) obj).toASN1Structure());
253             } else if (obj instanceof X509Certificate) {
254                 vector.add(getJcaX509CertificateHolder((X509Certificate) obj).toASN1Structure());
255             }
256         }
257         return new BERSet(vector);
258     }
259 
encodingUnsignedData(byte[] unsignedDataDigest, SignedData signedData)260     private byte[] encodingUnsignedData(byte[] unsignedDataDigest, SignedData signedData) throws CodeSignException {
261         byte[] signResult;
262         try {
263             ContentInfo contentInfo = new ContentInfo(PKCSObjectIdentifiers.signedData, signedData);
264             signResult = contentInfo.getEncoded(ASN1Encoding.DER);
265         } catch (IOException e) {
266             throw new CodeSignException(CodeSignErrMsg.CODE_SIGN_INTERNAL_ERROR.toString("Encode data error"), e);
267         }
268         verifySignResult(unsignedDataDigest, signResult);
269         return signResult;
270     }
271 
verifySignResult(byte[] unsignedDataDigest, byte[] signResult)272     private void verifySignResult(byte[] unsignedDataDigest, byte[] signResult) throws CodeSignException {
273         boolean result = false;
274         try {
275             result = CmsUtils.verifySignDataWithUnsignedDataDigest(unsignedDataDigest, signResult);
276         } catch (CMSException e) {
277             throw new CodeSignException(
278                 CodeSignErrMsg.CODE_SIGN_INTERNAL_ERROR.toString("PKCS cms data verify failed"), e);
279         }
280         if (!result) {
281             throw new CodeSignException(
282                 CodeSignErrMsg.CODE_SIGN_INTERNAL_ERROR.toString("PKCS cms data did not verify"));
283         }
284     }
285 }
286