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