From d2c751a36de314e6b216b4d60039909c8872d5f5 Mon Sep 17 00:00:00 2001 From: ligongshao Date: Sat, 11 Nov 2023 10:05:40 +0800 Subject: [PATCH] support add app-id in signature Signed-off-by: ligongshao --- .../sign/BcSignedDataGenerator.java | 15 +++++ .../codesigning/sign/CodeSigning.java | 36 +++++++---- .../codesigning/sign/VerifyCodeSignature.java | 58 +++++++++++++++--- .../codesigning/utils/HapUtils.java | 60 +++++++++++++++++++ .../hap/provider/SignProvider.java | 13 ++-- .../ohos/hapsigntool/hap/sign/SignElf.java | 7 ++- .../hapsigntool/hap/verify/VerifyHap.java | 30 +++++++++- 7 files changed, 188 insertions(+), 31 deletions(-) diff --git a/hapsigntool/hap_sign_tool_lib/src/main/java/com/ohos/hapsigntool/codesigning/sign/BcSignedDataGenerator.java b/hapsigntool/hap_sign_tool_lib/src/main/java/com/ohos/hapsigntool/codesigning/sign/BcSignedDataGenerator.java index 2d7591a8..1d9f0733 100644 --- a/hapsigntool/hap_sign_tool_lib/src/main/java/com/ohos/hapsigntool/codesigning/sign/BcSignedDataGenerator.java +++ b/hapsigntool/hap_sign_tool_lib/src/main/java/com/ohos/hapsigntool/codesigning/sign/BcSignedDataGenerator.java @@ -32,6 +32,8 @@ import org.bouncycastle.asn1.ASN1Set; import org.bouncycastle.asn1.BERSet; import org.bouncycastle.asn1.DEROctetString; import org.bouncycastle.asn1.DERSet; +import org.bouncycastle.asn1.ASN1ObjectIdentifier; +import org.bouncycastle.asn1.DERUTF8String; import org.bouncycastle.asn1.cms.Time; import org.bouncycastle.asn1.pkcs.Attribute; import org.bouncycastle.asn1.pkcs.ContentInfo; @@ -68,6 +70,11 @@ import java.util.List; * @since 2023/06/05 */ public class BcSignedDataGenerator implements SignedDataGenerator { + /** + * OID of the signer identity + */ + public static final String SIGNER_OID = "1.3.6.1.4.1.2011.2.376.1.4.1"; + private static final Logger LOGGER = LogManager.getLogger(BcSignedDataGenerator.class); private static final SignatureAlgorithmIdentifierFinder SIGN_ALG_ID_FINDER @@ -75,7 +82,11 @@ public class BcSignedDataGenerator implements SignedDataGenerator { private static final DigestAlgorithmIdentifierFinder DIGEST_ALG_ID_FINDER = new DefaultDigestAlgorithmIdentifierFinder(); + private String ownerID; + public void setOwnerID(String ownerID) { + this.ownerID = ownerID; + } @Override public byte[] generateSignedData(byte[] content, SignerConfig signConfig) throws CodeSignException { if (content == null) { @@ -176,6 +187,10 @@ public class BcSignedDataGenerator implements SignedDataGenerator { table.add(signingTimeAttr); table.add(contentTypeAttr); table.add(messageDigestAttr); + if(ownerID != null) { + Attribute ownerIDAttr = new Attribute(new ASN1ObjectIdentifier(SIGNER_OID), new DERSet(new DERUTF8String(ownerID))); + table.add(ownerIDAttr); + } return new DERSet(table); } diff --git a/hapsigntool/hap_sign_tool_lib/src/main/java/com/ohos/hapsigntool/codesigning/sign/CodeSigning.java b/hapsigntool/hap_sign_tool_lib/src/main/java/com/ohos/hapsigntool/codesigning/sign/CodeSigning.java index 81282dad..066cbf35 100644 --- a/hapsigntool/hap_sign_tool_lib/src/main/java/com/ohos/hapsigntool/codesigning/sign/CodeSigning.java +++ b/hapsigntool/hap_sign_tool_lib/src/main/java/com/ohos/hapsigntool/codesigning/sign/CodeSigning.java @@ -30,6 +30,7 @@ import com.ohos.hapsigntool.codesigning.utils.HapUtils; import com.ohos.hapsigntool.hap.config.SignerConfig; import com.ohos.hapsigntool.hap.entity.Pair; import com.ohos.hapsigntool.hap.exception.HapFormatException; +import com.ohos.hapsigntool.hap.exception.ProfileException; import com.ohos.hapsigntool.signer.LocalSigner; import com.ohos.hapsigntool.zip.RandomAccessFileZipDataInput; import com.ohos.hapsigntool.zip.ZipDataInput; @@ -120,7 +121,7 @@ public class CodeSigning { FsVerityGenerator fsVerityGenerator = new FsVerityGenerator(); fsVerityGenerator.generateFsVerityDigest(inputStream, fileSize, fsvTreeOffset); byte[] fsVerityDigest = fsVerityGenerator.getFsVerityDigest(); - byte[] signature = generateSignature(fsVerityDigest); + byte[] signature = generateSignature(fsVerityDigest, null); // add fs-verify info FsVerityDescriptor.Builder fsdbuilder = new FsVerityDescriptor.Builder().setFileSize(fileSize) .setHashAlgorithm(FsVerityGenerator.getFsVerityHashAlgorithm()) @@ -148,14 +149,16 @@ public class CodeSigning { * @param input file to sign * @param offset position of codesign block based on start of the file * @param inForm file's format + * @param profileContent profile of the hap * @return byte array of code sign block * @throws CodeSignException code signing exception * @throws IOException io error * @throws HapFormatException hap format invalid * @throws FsVerityDigestException computing FsVerity digest error + * @throws ProfileException profile of the hap error */ - public byte[] getCodeSignBlock(File input, long offset, String inForm) - throws CodeSignException, IOException, HapFormatException, FsVerityDigestException { + public byte[] getCodeSignBlock(File input, long offset, String inForm, String profileContent) + throws CodeSignException, IOException, HapFormatException, FsVerityDigestException, ProfileException { LOGGER.info("Start to sign code."); if (SUPPORT_BIN_FILE_FORM.contains(inForm)) { return getElfCodeSignBlock(input, offset, inForm); @@ -175,9 +178,11 @@ public class CodeSigning { this.codeSignBlock.setFsVerityInfoSegment(fsVerityInfoSegment); LOGGER.debug("Sign hap."); + String ownerID = HapUtils.getAppIdentifier(profileContent); + try (FileInputStream inputStream = new FileInputStream(input)) { Pair hapSignInfoAndMerkleTreeBytesPair = signFile(inputStream, dataSize, true, - fsvTreeOffset); + fsvTreeOffset, ownerID); // update hap segment in CodeSignBlock this.codeSignBlock.getHapInfoSegment().setSignInfo(hapSignInfoAndMerkleTreeBytesPair.getFirst()); // Insert merkle tree bytes into code sign block @@ -185,7 +190,7 @@ public class CodeSigning { hapSignInfoAndMerkleTreeBytesPair.getSecond()); } // update native lib info segment in CodeSignBlock - signNativeLibs(input); + signNativeLibs(input, ownerID); // last update codeSignBlock before generating its byte array representation updateCodeSignBlock(this.codeSignBlock); @@ -237,7 +242,7 @@ public class CodeSigning { } } - private void signNativeLibs(File input) throws IOException, FsVerityDigestException, CodeSignException { + private void signNativeLibs(File input, String ownerID) throws IOException, FsVerityDigestException, CodeSignException { // 'an' libs are always signed extractedNativeLibSuffixs.add(NATIVE_LIB_AN_SUFFIX); if (HapUtils.checkCompressNativeLibs(input)) { @@ -253,7 +258,7 @@ public class CodeSigning { LOGGER.info("No native libs."); return; } - List> nativeLibInfoList = signFilesFromJar(entryNames, inputJar); + List> nativeLibInfoList = signFilesFromJar(entryNames, inputJar, ownerID); // update SoInfoSegment in CodeSignBlock this.codeSignBlock.getSoInfoSegment().setSoInfoList(nativeLibInfoList); } @@ -299,12 +304,13 @@ public class CodeSigning { * * @param entryNames list of entries which need to be signed * @param hap input hap + * @param ownerID app-id in signature to identify * @return sign info and merkle tree of each file * @throws IOException io error * @throws FsVerityDigestException computing FsVerity digest error * @throws CodeSignException sign error */ - private List> signFilesFromJar(List entryNames, JarFile hap) + private List> signFilesFromJar(List entryNames, JarFile hap, String ownerID) throws IOException, FsVerityDigestException, CodeSignException { List> nativeLibInfoList = new ArrayList<>(); for (String name : entryNames) { @@ -314,7 +320,7 @@ public class CodeSigning { long fileSize = inEntry.getSize(); // We don't store merkle tree in code signing of native libs // Therefore, the second value of pair returned is ignored - Pair pairSignInfoAndMerkleTreeBytes = signFile(inputStream, fileSize, false, 0); + Pair pairSignInfoAndMerkleTreeBytes = signFile(inputStream, fileSize, false, 0, ownerID); nativeLibInfoList.add(Pair.create(name, pairSignInfoAndMerkleTreeBytes.getFirst())); } } @@ -328,16 +334,17 @@ public class CodeSigning { * @param fileSize size of the file * @param storeTree whether to store merkle tree in signed info * @param fsvTreeOffset merkle tree raw bytes offset based on the start of file + * @param ownerID app-id in signature to identify * @return pair of signature and tree * @throws FsVerityDigestException computing FsVerity Digest error * @throws CodeSignException signing error */ public Pair signFile(InputStream inputStream, long fileSize, boolean storeTree, - long fsvTreeOffset) throws FsVerityDigestException, CodeSignException { + long fsvTreeOffset, String ownerID) throws FsVerityDigestException, CodeSignException { FsVerityGenerator fsVerityGenerator = new FsVerityGenerator(); fsVerityGenerator.generateFsVerityDigest(inputStream, fileSize, fsvTreeOffset); byte[] fsVerityDigest = fsVerityGenerator.getFsVerityDigest(); - byte[] signature = generateSignature(fsVerityDigest); + byte[] signature = generateSignature(fsVerityDigest, ownerID); int flags = 0; if (storeTree) { flags = SignInfo.FLAG_MERKLE_TREE_INCLUDED; @@ -354,14 +361,17 @@ public class CodeSigning { return Pair.create(signInfo, fsVerityGenerator.getTreeBytes()); } - private byte[] generateSignature(byte[] signedData) throws CodeSignException { + private byte[] generateSignature(byte[] signedData, String ownerID) throws CodeSignException { // signConfig is created by SignerFactory if ((signConfig.getSigner() instanceof LocalSigner)) { if (signConfig.getCertificates().isEmpty()) { throw new CodeSignException("No certificates configured for sign"); } } - return SignedDataGenerator.BC.generateSignedData(signedData, signConfig); + + BcSignedDataGenerator bcSignedDataGenerator = new BcSignedDataGenerator(); + bcSignedDataGenerator.setOwnerID(ownerID); + return bcSignedDataGenerator.generateSignedData(signedData, signConfig); } /** diff --git a/hapsigntool/hap_sign_tool_lib/src/main/java/com/ohos/hapsigntool/codesigning/sign/VerifyCodeSignature.java b/hapsigntool/hap_sign_tool_lib/src/main/java/com/ohos/hapsigntool/codesigning/sign/VerifyCodeSignature.java index 119a82d4..742267c7 100644 --- a/hapsigntool/hap_sign_tool_lib/src/main/java/com/ohos/hapsigntool/codesigning/sign/VerifyCodeSignature.java +++ b/hapsigntool/hap_sign_tool_lib/src/main/java/com/ohos/hapsigntool/codesigning/sign/VerifyCodeSignature.java @@ -28,21 +28,24 @@ import com.ohos.hapsigntool.codesigning.exception.FsVerityDigestException; import com.ohos.hapsigntool.codesigning.exception.VerifyCodeSignException; import com.ohos.hapsigntool.codesigning.fsverity.FsVerityGenerator; import com.ohos.hapsigntool.codesigning.utils.CmsUtils; +import com.ohos.hapsigntool.codesigning.utils.HapUtils; import com.ohos.hapsigntool.hap.entity.Pair; +import com.ohos.hapsigntool.hap.exception.ProfileException; import org.apache.logging.log4j.LogManager; import org.apache.logging.log4j.Logger; +import org.bouncycastle.asn1.ASN1ObjectIdentifier; +import org.bouncycastle.asn1.cms.Attribute; +import org.bouncycastle.asn1.cms.AttributeTable; import org.bouncycastle.cms.CMSException; +import org.bouncycastle.cms.CMSSignedData; +import org.bouncycastle.cms.SignerInformation; import java.io.File; import java.io.FileInputStream; import java.io.IOException; import java.io.InputStream; -import java.util.ArrayList; -import java.util.Arrays; -import java.util.Enumeration; -import java.util.List; -import java.util.Locale; +import java.util.*; import java.util.jar.JarEntry; import java.util.jar.JarFile; @@ -65,6 +68,41 @@ public class VerifyCodeSignature { EXTRACTED_NATIVE_LIB_SUFFIXS.add(NATIVE_LIB_SO_SUFFIX); } + private static void checkOwnerID(byte[] signature, String profileOwnerID, String profileType) throws CMSException, VerifyCodeSignException { + String ownerID = profileOwnerID; + // if profileType is debug, check the app-id in signature, should be null or DEBUG_LIB_ID + if ("debug".equals(profileType)) { + ownerID = "DEBUG_LIB_ID"; + } + + CMSSignedData cmsSignedData = new CMSSignedData(signature); + Collection signers = cmsSignedData.getSignerInfos().getSigners(); + Collection results = null; + for (SignerInformation signer : signers) { + AttributeTable attrTable = signer.getSignedAttributes(); + Attribute attr = attrTable.get(new ASN1ObjectIdentifier(BcSignedDataGenerator.SIGNER_OID)); + // if app-id is null, if profileType is debug, it's ok. if profileType is release and ownerID is not null, throw exception. + if (attr == null) { + if ("debug".equals(profileType)) { + continue; + } + if (ownerID == null) { + continue; + } else { + throw new VerifyCodeSignException("app-identifier is not in the signature"); + } + } + if (ownerID == null) { + throw new VerifyCodeSignException("app-identifier in profile is null, but is not null in signature"); + } + // if app-id in signature exists, it should be equal to the app-id in profile. + String resultOwnerID = attr.getAttrValues().getObjectAt(0).toString(); + if (!ownerID.equals(resultOwnerID)) { + throw new VerifyCodeSignException("app-identifier in signature is invalid"); + } + } + } + /** * Verify a signed elf's signature * @@ -110,18 +148,22 @@ public class VerifyCodeSignature { * @param offset start position of code sign block based on the start of the hap file * @param length byte size of code sign block * @param fileFormat hap or hqf or hsp, etc. + * @param profileContent profile of the hap * @return true if signature verify succeed and false otherwise * @throws IOException If an input or output exception occurred * @throws VerifyCodeSignException parsing result invalid * @throws FsVerityDigestException if fs-verity digest generation failed * @throws CMSException if signature verify failed + * @throws ProfileException profile of the hap failed */ - public static boolean verifyHap(File file, long offset, long length, String fileFormat) - throws IOException, VerifyCodeSignException, FsVerityDigestException, CMSException { + public static boolean verifyHap(File file, long offset, long length, String fileFormat, String profileContent) + throws IOException, VerifyCodeSignException, FsVerityDigestException, CMSException, ProfileException { if (!CodeSigning.SUPPORT_FILE_FORM.contains(fileFormat)) { LOGGER.info("Not hap or hsp file, skip code signing verify"); return true; } + Pair pairResult = HapUtils.parseAppIdentifier(profileContent); + CodeSignBlock csb = generateCodeSignBlock(file, offset, length); // 2) verify hap try (FileInputStream hap = new FileInputStream(file)) { @@ -137,6 +179,7 @@ public class VerifyCodeSignature { // temporary: merkle tree offset set to zero, change to merkleTreeOffset verifySingleFile(hap, dataSize, signature, mte.getMerkleTreeOffset(), csb.getOneMerkleTreeByFileName(CodeSigning.HAP_SIGNATURE_ENTRY_NAME)); + checkOwnerID(signature, pairResult.getFirst(), pairResult.getSecond()); } // 3) verify native libs try (JarFile inputJar = new JarFile(file, false)) { @@ -151,6 +194,7 @@ public class VerifyCodeSignature { InputStream entryInputStream = inputJar.getInputStream(entry); // temporary merkleTreeOffset 0 verifySingleFile(entryInputStream, entry.getSize(), entrySig, 0, null); + checkOwnerID(entrySig, pairResult.getFirst(), pairResult.getSecond()); } } return true; diff --git a/hapsigntool/hap_sign_tool_lib/src/main/java/com/ohos/hapsigntool/codesigning/utils/HapUtils.java b/hapsigntool/hap_sign_tool_lib/src/main/java/com/ohos/hapsigntool/codesigning/utils/HapUtils.java index d3c9ab4f..5d4412c3 100644 --- a/hapsigntool/hap_sign_tool_lib/src/main/java/com/ohos/hapsigntool/codesigning/utils/HapUtils.java +++ b/hapsigntool/hap_sign_tool_lib/src/main/java/com/ohos/hapsigntool/codesigning/utils/HapUtils.java @@ -18,6 +18,11 @@ package com.ohos.hapsigntool.codesigning.utils; import com.google.gson.JsonElement; import com.google.gson.JsonObject; import com.google.gson.JsonParser; +import com.google.gson.JsonSyntaxException; +import com.ohos.hapsigntool.hap.entity.Pair; +import com.ohos.hapsigntool.hap.exception.ProfileException; +import org.apache.logging.log4j.LogManager; +import org.apache.logging.log4j.Logger; import java.io.File; import java.io.IOException; @@ -37,6 +42,8 @@ import java.util.jar.JarFile; * @since 2023/06/05 */ public class HapUtils { + private static final Logger LOGGER = LogManager.getLogger(HapUtils.class); + private static final String COMPRESS_NATIVE_LIBS_OPTION = "compressNativeLibs"; private static final List HAP_CONFIG_FILES = new ArrayList<>(); @@ -45,6 +52,8 @@ public class HapUtils { private static final String HAP_STAGE_MODULE_JSON_FILE = "module.json"; + private static final String HAP_DEBUG_OWNER_ID = "DEBUG_LIB_ID"; + static { HAP_CONFIG_FILES.add(HAP_FA_CONFIG_JSON_FILE); HAP_CONFIG_FILES.add(HAP_STAGE_MODULE_JSON_FILE); @@ -102,4 +111,55 @@ public class HapUtils { // default to compress native libs return true; } + + + public static String getAppIdentifier(String profileContent) throws ProfileException { + Pair resultPair = parseAppIdentifier(profileContent); + String ownerID = resultPair.getFirst(); + String profileType = resultPair.getSecond(); + if ("debug".equals(profileType)) { + return HAP_DEBUG_OWNER_ID; + } else if ("release".equals(profileType)) { + return ownerID; + } else { + throw new ProfileException("unsupported profile type"); + } + } + + public static Pair parseAppIdentifier(String profileContent) throws ProfileException { + String ownerID = null; + String profileType = null; + try { + JsonElement parser = JsonParser.parseString(profileContent); + JsonObject profileJson = parser.getAsJsonObject(); + String profileTypeKey = "type"; + if (!profileJson.has(profileTypeKey)) { + throw new ProfileException("profile has no type key"); + } + + profileType = profileJson.get(profileTypeKey).getAsString(); + if (profileType == null || profileType.length() == 0) { + throw new ProfileException("Get profile type error"); + } + + String appIdentifier = "app-identifier"; + String buildInfoMember = "bundle-info"; + JsonObject buildInfoObject = profileJson.getAsJsonObject(buildInfoMember); + if (buildInfoObject == null) { + throw new ProfileException("can not find bundle-info"); + } + if (buildInfoObject.has(appIdentifier)) { + JsonElement ownerIDElement = buildInfoObject.get(appIdentifier); + if (ownerIDElement.getAsJsonPrimitive().isString()) { + ownerID = ownerIDElement.getAsString(); + } else { + LOGGER.error("value of app-identifier is not string"); + } + } + } catch (JsonSyntaxException | UnsupportedOperationException e) { + LOGGER.error(e.getMessage()); + throw new ProfileException("profile json is invalid"); + } + return Pair.create(ownerID, profileType); + } } diff --git a/hapsigntool/hap_sign_tool_lib/src/main/java/com/ohos/hapsigntool/hap/provider/SignProvider.java b/hapsigntool/hap_sign_tool_lib/src/main/java/com/ohos/hapsigntool/hap/provider/SignProvider.java index 4e22f814..f90c70fd 100644 --- a/hapsigntool/hap_sign_tool_lib/src/main/java/com/ohos/hapsigntool/hap/provider/SignProvider.java +++ b/hapsigntool/hap_sign_tool_lib/src/main/java/com/ohos/hapsigntool/hap/provider/SignProvider.java @@ -115,6 +115,8 @@ public abstract class SignProvider { PARAMETERS_NEED_ESCAPE.add(ParamConstants.PARAM_LOCAL_JKS_KEYALIAS_CODE); } + private String profileContent; + /** * list of hap signature optional blocks */ @@ -380,7 +382,7 @@ public abstract class SignProvider { */ private void appendCodeSignBlock(SignerConfig signerConfig, File tmpOutput, String suffix, long centralDirectoryOffset) - throws FsVerityDigestException, CodeSignException, IOException, HapFormatException { + throws FsVerityDigestException, CodeSignException, IOException, HapFormatException, ProfileException { if (signParams.get(ParamConstants.PARAM_SIGN_CODE) .equals(ParamConstants.SignCodeFlag.ENABLE_SIGN_CODE.getSignCodeFlag())) { // 4 means hap format occupy 4 byte storage location,2 means optional blocks reserve 2 storage location @@ -388,7 +390,7 @@ public abstract class SignProvider { (centralDirectoryOffset + ((4 + 4 + 4) * (optionalBlocks.size() + 2) + (4 + 4 + 4))); // create CodeSigning Object CodeSigning codeSigning = new CodeSigning(signerConfig); - byte[] codeSignArray = codeSigning.getCodeSignBlock(tmpOutput, codeSignOffset, suffix); + byte[] codeSignArray = codeSigning.getCodeSignBlock(tmpOutput, codeSignOffset, suffix, profileContent); ByteBuffer result = ByteBuffer.allocate(codeSignArray.length + (4 + 4 + 4)); result.order(ByteOrder.LITTLE_ENDIAN); result.putInt(HapUtils.HAP_CODE_SIGN_BLOCK_ID); // type @@ -591,7 +593,6 @@ public abstract class SignProvider { byte[] profile = findProfileFromOptionalBlocks(); boolean isProfileWithoutSign = ParamConstants.ProfileSignFlag.DISABLE_SIGN_CODE.getSignFlag().equals( signParams.get(ParamConstants.PARAM_BASIC_PROFILE_SIGNED)); - String content; if (!isProfileWithoutSign) { CMSSignedData cmsSignedData = new CMSSignedData(profile); boolean isVerify = VerifyUtils.verifyCmsSignedData(cmsSignedData); @@ -602,11 +603,11 @@ public abstract class SignProvider { if (!(contentObj instanceof byte[])) { throw new ProfileException("Check profile failed, signed profile content is not byte array!"); } - content = new String((byte[]) contentObj, StandardCharsets.UTF_8); + profileContent = new String((byte[]) contentObj, StandardCharsets.UTF_8); } else { - content = new String(profile, StandardCharsets.UTF_8); + profileContent = new String(profile, StandardCharsets.UTF_8); } - JsonElement parser = JsonParser.parseString(content); + JsonElement parser = JsonParser.parseString(profileContent); JsonObject profileJson = parser.getAsJsonObject(); checkProfileInfo(profileJson, inputCerts); } catch (CMSException e) { diff --git a/hapsigntool/hap_sign_tool_lib/src/main/java/com/ohos/hapsigntool/hap/sign/SignElf.java b/hapsigntool/hap_sign_tool_lib/src/main/java/com/ohos/hapsigntool/hap/sign/SignElf.java index 811f1239..64d811e1 100644 --- a/hapsigntool/hap_sign_tool_lib/src/main/java/com/ohos/hapsigntool/hap/sign/SignElf.java +++ b/hapsigntool/hap_sign_tool_lib/src/main/java/com/ohos/hapsigntool/hap/sign/SignElf.java @@ -25,6 +25,7 @@ import com.ohos.hapsigntool.hap.entity.SignBlockData; import com.ohos.hapsigntool.hap.entity.SignatureBlockTags; import com.ohos.hapsigntool.hap.entity.SignatureBlockTypes; import com.ohos.hapsigntool.hap.exception.HapFormatException; +import com.ohos.hapsigntool.hap.exception.ProfileException; import com.ohos.hapsigntool.utils.FileUtils; import com.ohos.hapsigntool.utils.ParamConstants; import com.ohos.hapsigntool.utils.ParamProcessUtil; @@ -120,7 +121,7 @@ public class SignElf { } catch (IOException e) { LOGGER.error("writeBlockDataToFile failed.", e); return false; - } catch (FsVerityDigestException | CodeSignException | HapFormatException e) { + } catch (FsVerityDigestException | CodeSignException | HapFormatException | ProfileException e) { LOGGER.error("codesign failed.", e); return false; } @@ -196,14 +197,14 @@ public class SignElf { private static SignBlockData generateCodeSignByte(SignerConfig signerConfig, Map signParams, String inputFile, int blockNum, long binFileLen) throws IOException, - FsVerityDigestException, CodeSignException, HapFormatException { + FsVerityDigestException, CodeSignException, HapFormatException, ProfileException { if (CODESIGN_OFF.equals(signParams.get(ParamConstants.PARAM_SIGN_CODE))) { return null; } CodeSigning codeSigning = new CodeSigning(signerConfig); long offset = binFileLen + (long) HwBlockHead.getElfBlockLen() * blockNum; byte[] codesignData = codeSigning.getCodeSignBlock(new File(inputFile), offset, - signParams.get(ParamConstants.PARAM_IN_FORM)); + signParams.get(ParamConstants.PARAM_IN_FORM), null); return new SignBlockData(codesignData, CODESIGN_BLOCK_TYPE); } diff --git a/hapsigntool/hap_sign_tool_lib/src/main/java/com/ohos/hapsigntool/hap/verify/VerifyHap.java b/hapsigntool/hap_sign_tool_lib/src/main/java/com/ohos/hapsigntool/hap/verify/VerifyHap.java index 9bfa3f83..eda2341d 100644 --- a/hapsigntool/hap_sign_tool_lib/src/main/java/com/ohos/hapsigntool/hap/verify/VerifyHap.java +++ b/hapsigntool/hap_sign_tool_lib/src/main/java/com/ohos/hapsigntool/hap/verify/VerifyHap.java @@ -22,6 +22,7 @@ import com.ohos.hapsigntool.codesigning.sign.VerifyCodeSignature; import com.ohos.hapsigntool.hap.entity.Pair; import com.ohos.hapsigntool.hap.entity.SigningBlock; import com.ohos.hapsigntool.hap.exception.HapFormatException; +import com.ohos.hapsigntool.hap.exception.ProfileException; import com.ohos.hapsigntool.hap.exception.SignatureNotFoundException; import com.ohos.hapsigntool.utils.FileUtils; import com.ohos.hapsigntool.utils.HapUtils; @@ -36,6 +37,7 @@ import com.ohos.hapsigntool.zip.ZipUtils; import org.apache.logging.log4j.LogManager; import org.apache.logging.log4j.Logger; import org.bouncycastle.cms.CMSException; +import org.bouncycastle.cms.CMSSignedData; import org.bouncycastle.jce.provider.BouncyCastleProvider; import org.bouncycastle.openssl.jcajce.JcaPEMWriter; import org.bouncycastle.util.Arrays; @@ -48,6 +50,7 @@ import java.io.OutputStream; import java.io.RandomAccessFile; import java.nio.ByteBuffer; import java.nio.ByteOrder; +import java.nio.charset.StandardCharsets; import java.security.Security; import java.security.cert.X509Certificate; import java.util.ArrayList; @@ -70,6 +73,23 @@ public class VerifyHap { static { Security.addProvider(new BouncyCastleProvider()); } + + private static String getProfileContent(byte[] profile) throws ProfileException { + try { + CMSSignedData cmsSignedData = new CMSSignedData(profile); + if (!VerifyUtils.verifyCmsSignedData(cmsSignedData)) { + throw new ProfileException("Verify profile pkcs7 failed! Profile is invalid"); + } + Object contentObj = cmsSignedData.getSignedContent().getContent(); + if(!(contentObj instanceof byte[])) { + throw new ProfileException("Check profile failed, signed profile content is not byte array!"); + } + return new String((byte[]) contentObj, StandardCharsets.UTF_8); + } catch (CMSException e) { + return new String(profile, StandardCharsets.UTF_8); + } + } + private final boolean isPrintCert; public VerifyHap() { this(true); @@ -277,6 +297,9 @@ public class VerifyHap { } catch (CMSException e) { LOGGER.error("Verify Hap failed, code signature verify failed.", e); result = new VerifyResult(false, VerifyResult.RET_SIGNATURE_ERROR, e.getMessage()); + } catch (ProfileException e) { + LOGGER.error("Verify Hap failed, parse app-identifier from profile failed, profile is invalid", e); + return new VerifyResult(false, VerifyResult.RET_CODE_SIGN_BLOCK_ERROR, e.getMessage()); } return result; } @@ -307,9 +330,10 @@ public class VerifyHap { * @throws IOException IO error * @throws VerifyCodeSignException verify code sign on error * @throws CMSException cms on error + * @throws ProfileException profile of the hap error */ private boolean checkCodeSign(String hapFilePath, List optionalBlocks) - throws FsVerityDigestException, IOException, VerifyCodeSignException, CMSException { + throws FsVerityDigestException, IOException, VerifyCodeSignException, CMSException, ProfileException { Map map = optionalBlocks.stream() .collect(Collectors.toMap(SigningBlock::getType, SigningBlock::getValue)); byte[] propertyBlockArray = map.get(HapUtils.HAP_PROPERTY_BLOCK_ID); @@ -330,7 +354,9 @@ public class VerifyHap { return false; } File outputFile = new File(hapFilePath); - boolean isCodeSign = VerifyCodeSignature.verifyHap(outputFile, blockOffset, blockLength, suffix); + byte[] profileArray = map.get(HapUtils.HAP_PROFILE_BLOCK_ID); + String profileContent = getProfileContent(profileArray); + boolean isCodeSign = VerifyCodeSignature.verifyHap(outputFile, blockOffset, blockLength, suffix, profileContent); if (!isCodeSign) { LOGGER.error("Verify Hap has no code sign data error!"); return false; -- Gitee