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