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