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