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