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 private static final LogUtils LOGGER = new LogUtils(BcSignedDataGenerator.class); 78 79 private static final SignatureAlgorithmIdentifierFinder SIGN_ALG_ID_FINDER 80 = new DefaultSignatureAlgorithmIdentifierFinder(); 81 82 private static final DigestAlgorithmIdentifierFinder DIGEST_ALG_ID_FINDER 83 = new DefaultDigestAlgorithmIdentifierFinder(); 84 85 private String ownerID; 86 setOwnerID(String ownerID)87 public void setOwnerID(String ownerID) { 88 this.ownerID = ownerID; 89 } 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( 95 CodeSignErrMsg.CODE_SIGN_INTERNAL_ERROR.toString("The content to be signed is empty")); 96 } 97 Pair<DERSet, DERSet> pairDigestAndSignInfo = getSignInfo(content, signConfig); 98 // Unsupported certificate revocation, SignedData's _crls is null 99 SignedData signedData = new SignedData(new ASN1Integer(1), pairDigestAndSignInfo.getFirst(), 100 new ContentInfo(PKCSObjectIdentifiers.data, null), createBerSetFromLst(signConfig.getCertificates()), 101 createBerSetFromLst(null), pairDigestAndSignInfo.getSecond()); 102 return encodingUnsignedData(content, signedData); 103 } 104 getSignInfo(byte[] content, SignerConfig signConfig)105 private Pair<DERSet, DERSet> getSignInfo(byte[] content, SignerConfig signConfig) throws CodeSignException { 106 ASN1EncodableVector signInfoVector = new ASN1EncodableVector(); 107 ASN1EncodableVector digestVector = new ASN1EncodableVector(); 108 for (SignatureAlgorithm signAlgorithm : signConfig.getSignatureAlgorithms()) { 109 SignerInfo signInfo = createSignInfo(signAlgorithm, content, signConfig); 110 signInfoVector.add(signInfo); 111 digestVector.add(signInfo.getDigestAlgorithm()); 112 LOGGER.info("Create a sign info successfully."); 113 } 114 return Pair.create(new DERSet(digestVector), new DERSet(signInfoVector)); 115 } 116 createSignInfo(SignatureAlgorithm signAlgorithm, byte[] unsignedDataDigest, SignerConfig signConfig)117 private SignerInfo createSignInfo(SignatureAlgorithm signAlgorithm, byte[] unsignedDataDigest, 118 SignerConfig signConfig) throws CodeSignException { 119 ContentDigestAlgorithm hashAlgorithm = signAlgorithm.getContentDigestAlgorithm(); 120 byte[] digest = computeDigest(unsignedDataDigest, hashAlgorithm.name()); 121 ASN1Set authed = getPKCS9Attributes(digest); 122 byte[] codeAuthed = getEncoded(authed); 123 Pair<String, ? extends AlgorithmParameterSpec> signPair = signAlgorithm.getSignatureAlgAndParams(); 124 byte[] signBytes = signConfig.getSigner().getSignature(codeAuthed, signPair.getFirst(), signPair.getSecond()); 125 if (signBytes == null) { 126 throw new CodeSignException(CodeSignErrMsg.CODE_SIGN_INTERNAL_ERROR.toString("Get signature failed")); 127 } 128 if (signConfig.getCertificates().isEmpty()) { 129 throw new CodeSignException( 130 CodeSignErrMsg.CERTIFICATES_CONFIGURE_ERROR.toString("No certificate is configured for sign")); 131 } 132 X509Certificate cert = signConfig.getCertificates().get(0); 133 if (!verifySignFromServer(cert.getPublicKey(), signBytes, signPair, codeAuthed)) { 134 throw new CodeSignException( 135 CodeSignErrMsg.CODE_SIGN_INTERNAL_ERROR.toString("Verify signed result failed")); 136 } 137 JcaX509CertificateHolder certificateHolder = getJcaX509CertificateHolder(cert); 138 return new SignerInfo(new ASN1Integer(1), 139 new IssuerAndSerialNumber(certificateHolder.getIssuer(), certificateHolder.getSerialNumber()), 140 DIGEST_ALG_ID_FINDER.find(hashAlgorithm.getDigestAlgorithm()), authed, 141 SIGN_ALG_ID_FINDER.find(signPair.getFirst()), new DEROctetString(signBytes), null); 142 } 143 computeDigest(byte[] unsignedDataDigest, String algorithm)144 private byte[] computeDigest(byte[] unsignedDataDigest, String algorithm) throws CodeSignException { 145 byte[] digest; 146 try { 147 digest = DigestUtils.computeDigest(unsignedDataDigest, algorithm); 148 } catch (NoSuchAlgorithmException e) { 149 throw new CodeSignException(CodeSignErrMsg.ALGORITHM_NOT_SUPPORT_ERROR.toString(algorithm), e); 150 } 151 return digest; 152 } 153 getEncoded(ASN1Set authed)154 private byte[] getEncoded(ASN1Set authed) throws CodeSignException { 155 byte[] codeAuthed; 156 try { 157 codeAuthed = authed.getEncoded(); 158 } catch (IOException e) { 159 throw new CodeSignException(CodeSignErrMsg.CODE_SIGN_INTERNAL_ERROR.toString("Encode data error"), e); 160 } 161 return codeAuthed; 162 } 163 getJcaX509CRLHolder(X509CRL crl)164 private JcaX509CRLHolder getJcaX509CRLHolder(X509CRL crl) throws CodeSignException { 165 JcaX509CRLHolder crlHolder; 166 try { 167 crlHolder = new JcaX509CRLHolder(crl); 168 } catch (CRLException e) { 169 throw new CodeSignException(CodeSignErrMsg.CODE_SIGN_INTERNAL_ERROR.toString("Create crl failed"), e); 170 } 171 return crlHolder; 172 } 173 getJcaX509CertificateHolder(X509Certificate cert)174 private JcaX509CertificateHolder getJcaX509CertificateHolder(X509Certificate cert) throws CodeSignException { 175 JcaX509CertificateHolder certificateHolder; 176 try { 177 certificateHolder = new JcaX509CertificateHolder(cert); 178 } catch (CertificateEncodingException e) { 179 throw new CodeSignException(CodeSignErrMsg.CODE_SIGN_INTERNAL_ERROR.toString("Create sign info failed"), e); 180 } 181 return certificateHolder; 182 } 183 getPKCS9Attributes(byte[] digest)184 private ASN1Set getPKCS9Attributes(byte[] digest) { 185 ASN1EncodableVector table = new ASN1EncodableVector(); 186 Attribute signingTimeAttr = new Attribute(PKCSObjectIdentifiers.pkcs_9_at_signingTime, 187 new DERSet(new Time(new Date()))); 188 Attribute contentTypeAttr = new Attribute(PKCSObjectIdentifiers.pkcs_9_at_contentType, 189 new DERSet(PKCSObjectIdentifiers.data)); 190 Attribute messageDigestAttr = new Attribute(PKCSObjectIdentifiers.pkcs_9_at_messageDigest, 191 new DERSet(new DEROctetString(digest))); 192 table.add(signingTimeAttr); 193 table.add(contentTypeAttr); 194 table.add(messageDigestAttr); 195 if (ownerID != null) { 196 Attribute ownerIDAttr = new Attribute(new ASN1ObjectIdentifier(SIGNER_OID), 197 new DERSet(new DERUTF8String(ownerID))); 198 table.add(ownerIDAttr); 199 } 200 return new DERSet(table); 201 } 202 verifySignFromServer(PublicKey publicKey, byte[] signBytes, Pair<String, ? extends AlgorithmParameterSpec> signPair, byte[] authed)203 private boolean verifySignFromServer(PublicKey publicKey, byte[] signBytes, 204 Pair<String, ? extends AlgorithmParameterSpec> signPair, byte[] authed) throws CodeSignException { 205 try { 206 Signature signature = Signature.getInstance(signPair.getFirst()); 207 signature.initVerify(publicKey); 208 if (signPair.getSecond() != null) { 209 signature.setParameter(signPair.getSecond()); 210 } 211 signature.update(authed); 212 if (!signature.verify(signBytes)) { 213 throw new CodeSignException(CodeSignErrMsg.SIGNATURE_VERIFY_FAILED_ERROR.toString()); 214 } 215 return true; 216 } catch (InvalidKeyException | SignatureException e) { 217 LOGGER.error("The generated signature could not be verified " + " using the public key in the certificate", 218 e); 219 } catch (NoSuchAlgorithmException e) { 220 LOGGER.error("The generated signature " + signPair.getFirst() 221 + " could not be verified using the public key in the certificate", e); 222 } catch (InvalidAlgorithmParameterException e) { 223 LOGGER.error("The generated signature " + signPair.getSecond() 224 + " could not be verified using the public key in the certificate", e); 225 } 226 return false; 227 } 228 createBerSetFromLst(List<?> lists)229 private ASN1Set createBerSetFromLst(List<?> lists) throws CodeSignException { 230 if (lists == null || lists.size() == 0) { 231 return null; 232 } 233 ASN1EncodableVector vector = new ASN1EncodableVector(); 234 for (Object obj : lists) { 235 if (obj instanceof X509CRL) { 236 vector.add(getJcaX509CRLHolder((X509CRL) obj).toASN1Structure()); 237 } else if (obj instanceof X509Certificate) { 238 vector.add(getJcaX509CertificateHolder((X509Certificate) obj).toASN1Structure()); 239 } 240 } 241 return new BERSet(vector); 242 } 243 encodingUnsignedData(byte[] unsignedDataDigest, SignedData signedData)244 private byte[] encodingUnsignedData(byte[] unsignedDataDigest, SignedData signedData) throws CodeSignException { 245 byte[] signResult; 246 try { 247 ContentInfo contentInfo = new ContentInfo(PKCSObjectIdentifiers.signedData, signedData); 248 signResult = contentInfo.getEncoded(ASN1Encoding.DER); 249 } catch (IOException e) { 250 throw new CodeSignException(CodeSignErrMsg.CODE_SIGN_INTERNAL_ERROR.toString("Encode data error"), e); 251 } 252 verifySignResult(unsignedDataDigest, signResult); 253 return signResult; 254 } 255 verifySignResult(byte[] unsignedDataDigest, byte[] signResult)256 private void verifySignResult(byte[] unsignedDataDigest, byte[] signResult) throws CodeSignException { 257 boolean result = false; 258 try { 259 result = CmsUtils.verifySignDataWithUnsignedDataDigest(unsignedDataDigest, signResult); 260 } catch (CMSException e) { 261 throw new CodeSignException( 262 CodeSignErrMsg.CODE_SIGN_INTERNAL_ERROR.toString("PKCS cms data verify failed"), e); 263 } 264 if (!result) { 265 throw new CodeSignException( 266 CodeSignErrMsg.CODE_SIGN_INTERNAL_ERROR.toString("PKCS cms data did not verify")); 267 } 268 } 269 } 270