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.profile; 17 18 import com.ohos.hapsigntool.adapter.LocalizationAdapter; 19 import com.ohos.hapsigntool.error.CustomException; 20 import com.ohos.hapsigntool.error.ERROR; 21 import com.ohos.hapsigntool.error.SignToolErrMsg; 22 import com.ohos.hapsigntool.error.VerifyException; 23 import com.ohos.hapsigntool.profile.model.VerificationResult; 24 import com.ohos.hapsigntool.signer.ISigner; 25 import com.ohos.hapsigntool.signer.SignerFactory; 26 import com.ohos.hapsigntool.utils.LogUtils; 27 import com.ohos.hapsigntool.utils.ValidateUtils; 28 29 import org.bouncycastle.asn1.ASN1EncodableVector; 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.cms.Attribute; 35 import org.bouncycastle.asn1.cms.CMSObjectIdentifiers; 36 import org.bouncycastle.asn1.cms.ContentInfo; 37 import org.bouncycastle.asn1.cms.IssuerAndSerialNumber; 38 import org.bouncycastle.asn1.cms.SignedData; 39 import org.bouncycastle.asn1.cms.SignerIdentifier; 40 import org.bouncycastle.asn1.cms.SignerInfo; 41 import org.bouncycastle.asn1.cms.Time; 42 import org.bouncycastle.asn1.pkcs.PKCSObjectIdentifiers; 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.operator.DefaultDigestAlgorithmIdentifierFinder; 47 import org.bouncycastle.operator.DefaultSignatureAlgorithmIdentifierFinder; 48 import org.bouncycastle.operator.DigestCalculator; 49 import org.bouncycastle.operator.DigestCalculatorProvider; 50 import org.bouncycastle.operator.OperatorCreationException; 51 import org.bouncycastle.operator.jcajce.JcaDigestCalculatorProviderBuilder; 52 53 import java.io.IOException; 54 import java.security.cert.CRLException; 55 import java.security.cert.CertificateEncodingException; 56 import java.security.cert.X509CRL; 57 import java.security.cert.X509Certificate; 58 import java.time.LocalDateTime; 59 import java.time.ZoneId; 60 import java.util.Date; 61 import java.util.List; 62 63 /** 64 * To sign and verify profile. 65 * 66 * @since 2021/12/28 67 */ 68 public final class ProfileSignTool { 69 /** 70 * Empty byte array. 71 */ 72 private static final byte[] NO_BYTE = {}; 73 74 /** 75 * logger 76 */ 77 private static final LogUtils LOGGER = new LogUtils(ProfileSignTool.class); 78 ProfileSignTool()79 private ProfileSignTool() { 80 } 81 82 /** 83 * generateP7b. 84 * 85 * @param adapter local adapter with params 86 * @param content content to sign 87 * @return signed content 88 */ generateP7b(LocalizationAdapter adapter, byte[] content)89 public static byte[] generateP7b(LocalizationAdapter adapter, byte[] content) { 90 ISigner signer = new SignerFactory().getSigner(adapter); 91 byte[] p7b = signProfile(content, signer, adapter.getSignAlg()); 92 VerifyHelper verifyHelper = new VerifyHelper(); 93 VerificationResult verificationResult = null; 94 try { 95 verificationResult = verifyHelper.verify(p7b); 96 } catch (VerifyException e) { 97 CustomException.throwException(ERROR.VERIFY_ERROR, SignToolErrMsg.VERIFY_PROFILE_FAILED 98 .toString("Generate Profile Failed! " + e.getMessage())); 99 } 100 ValidateUtils.throwIfNotMatches(verificationResult.isVerifiedPassed(), 101 ERROR.SIGN_ERROR, SignToolErrMsg.SIGNATURE_FAILED.toString(verificationResult.getMessage())); 102 return p7b; 103 } 104 105 /** 106 * signProfile. 107 * 108 * @param content content to sign 109 * @param signer signer 110 * @param sigAlg sign algorithm 111 * @return signed data 112 */ signProfile(byte[] content, ISigner signer, String sigAlg)113 public static byte[] signProfile(byte[] content, ISigner signer, String sigAlg) { 114 try { 115 AlgorithmIdentifier sigAlgId = (new DefaultSignatureAlgorithmIdentifierFinder()).find(sigAlg); 116 ASN1EncodableVector digestAlgIds = new ASN1EncodableVector(); 117 AlgorithmIdentifier digestAlgId = (new DefaultDigestAlgorithmIdentifierFinder()).find(sigAlgId); 118 digestAlgIds.add(digestAlgId); 119 byte[] digest = getContentDigest(content, digestAlgId); 120 ASN1Set signedAttr = generatePKCS9Attributes(digest); 121 byte[] signature = signer.getSignature(signedAttr.getEncoded("DER"), sigAlg, null); 122 // To validate cert(public key) and private key 123 VerifyHelper.verifySignature(signer.getCertificates().get(0), signature, 124 signedAttr.getEncoded("DER"), sigAlg); 125 SignerIdentifier signerIdentifier = generateSignerIdentifier(signer.getCertificates().get(0)); 126 SignerInfo signerInfo = new SignerInfo(signerIdentifier, digestAlgId, signedAttr, sigAlgId, 127 new DEROctetString(signature), null); 128 ASN1EncodableVector signerInfos = new ASN1EncodableVector(); 129 signerInfos.add(signerInfo); 130 ASN1Set certList = createBerSetFromCerts(signer.getCertificates()); 131 List<X509CRL> crls = signer.getCrls(); 132 ASN1Set crlList = createBerSetFromCrls(crls); 133 ContentInfo encryptInfo = new ContentInfo(CMSObjectIdentifiers.data, new DEROctetString(content)); 134 SignedData sd = new SignedData(new DERSet(digestAlgIds), encryptInfo, certList, crlList, 135 new DERSet(signerInfos)); 136 ContentInfo contentInfo = new ContentInfo(CMSObjectIdentifiers.signedData, sd); 137 return contentInfo.getEncoded("DER"); 138 } catch (OperatorCreationException | IOException | CertificateEncodingException | CRLException e) { 139 LOGGER.debug(e.getMessage(), e); 140 CustomException.throwException(ERROR.SIGN_ERROR, SignToolErrMsg.SIGNATURE_FAILED.toString(e.getMessage())); 141 } 142 return NO_BYTE; 143 } 144 generateSignerIdentifier(X509Certificate certificate)145 private static SignerIdentifier generateSignerIdentifier(X509Certificate certificate) 146 throws CertificateEncodingException { 147 return new SignerIdentifier(new IssuerAndSerialNumber( 148 (new JcaX509CertificateHolder(certificate)).toASN1Structure())); 149 } 150 generatePKCS9Attributes(byte[] digest)151 private static ASN1Set generatePKCS9Attributes(byte[] digest) { 152 ASN1EncodableVector vector = new ASN1EncodableVector(); 153 Attribute signTime = new Attribute(PKCSObjectIdentifiers.pkcs_9_at_signingTime, 154 new DERSet(new Time(Date.from(LocalDateTime.now().atZone(ZoneId.systemDefault()).toInstant())))); 155 Attribute contentType = new Attribute(PKCSObjectIdentifiers.pkcs_9_at_contentType, 156 new DERSet(PKCSObjectIdentifiers.data)); 157 Attribute digestAtt = new Attribute(PKCSObjectIdentifiers.pkcs_9_at_messageDigest, 158 new DERSet(new DEROctetString(digest))); 159 vector.add(signTime); 160 vector.add(contentType); 161 vector.add(digestAtt); 162 return new DERSet(vector); 163 } 164 getContentDigest(byte[] content, AlgorithmIdentifier digestAlgorithmIdentifier)165 private static byte[] getContentDigest(byte[] content, AlgorithmIdentifier digestAlgorithmIdentifier) 166 throws OperatorCreationException, IOException { 167 DigestCalculatorProvider digestCalculatorProvider = (new JcaDigestCalculatorProviderBuilder()).build(); 168 DigestCalculator digestCalculator = digestCalculatorProvider.get(digestAlgorithmIdentifier); 169 digestCalculator.getOutputStream().write(content); 170 return digestCalculator.getDigest(); 171 } 172 createBerSetFromCrls(List<X509CRL> crls)173 private static ASN1Set createBerSetFromCrls(List<X509CRL> crls) throws CRLException { 174 if (crls != null && crls.size() != 0) { 175 ASN1EncodableVector vector = new ASN1EncodableVector(crls.size()); 176 for (X509CRL crl : crls) { 177 vector.add((new JcaX509CRLHolder(crl)).toASN1Structure()); 178 } 179 return new BERSet(vector); 180 } else { 181 return null; 182 } 183 } 184 createBerSetFromCerts(List<X509Certificate> certs)185 private static ASN1Set createBerSetFromCerts(List<X509Certificate> certs) throws CertificateEncodingException { 186 if (certs != null && certs.size() != 0) { 187 ASN1EncodableVector vector = new ASN1EncodableVector(certs.size()); 188 for (X509Certificate cert : certs) { 189 vector.add((new JcaX509CertificateHolder(cert)).toASN1Structure()); 190 } 191 return new BERSet(vector); 192 } else { 193 return null; 194 } 195 } 196 } 197