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 2d7591a8bf603dde97e0c30472d52c907a24ad65..88422bc682c38067aa04de42b7f95190b08b84d3 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 @@ -76,6 +83,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 +188,11 @@ 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 81282dad28f14a500a176c5d5197a74fbb3d913e..863039464e67783d9faa254374d55be9a7d7498e 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; @@ -103,13 +104,15 @@ 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 elf * @return byte array of code sign block * @throws CodeSignException code signing exception * @throws IOException io error * @throws FsVerityDigestException computing FsVerity digest error + * @throws ProfileException profile of elf is invalid */ - public byte[] getElfCodeSignBlock(File input, long offset, String inForm) - throws CodeSignException, FsVerityDigestException, IOException { + public byte[] getElfCodeSignBlock(File input, long offset, String inForm, String profileContent) + throws CodeSignException, FsVerityDigestException, IOException, ProfileException { if (!SUPPORT_BIN_FILE_FORM.contains(inForm)) { throw new CodeSignException("file's format is unsupported"); } @@ -120,7 +123,9 @@ public class CodeSigning { FsVerityGenerator fsVerityGenerator = new FsVerityGenerator(); fsVerityGenerator.generateFsVerityDigest(inputStream, fileSize, fsvTreeOffset); byte[] fsVerityDigest = fsVerityGenerator.getFsVerityDigest(); - byte[] signature = generateSignature(fsVerityDigest); + // ownerID should be DEBUG_LIB_ID while signing ELF + String ownerID = (profileContent == null) ? "DEBUG_LIB_ID" : HapUtils.getAppIdentifier(profileContent); + byte[] signature = generateSignature(fsVerityDigest, ownerID); // add fs-verify info FsVerityDescriptor.Builder fsdbuilder = new FsVerityDescriptor.Builder().setFileSize(fileSize) .setHashAlgorithm(FsVerityGenerator.getFsVerityHashAlgorithm()) @@ -148,17 +153,19 @@ 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); + return getElfCodeSignBlock(input, offset, inForm, profileContent); } if (!SUPPORT_FILE_FORM.contains(inForm)) { throw new CodeSignException("file's format is unsupported"); @@ -175,9 +182,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 +194,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 +246,8 @@ 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 +263,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 +309,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 +325,8 @@ 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 +340,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 +367,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 119a82d4d228ce4ef2407d30454400c7c9954346..9fbf0553407e6a8a13fd84e190b577f5bcbdbb78 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,11 +28,18 @@ 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; @@ -43,6 +50,7 @@ import java.util.Arrays; import java.util.Enumeration; import java.util.List; import java.util.Locale; +import java.util.Collection; import java.util.jar.JarEntry; import java.util.jar.JarFile; @@ -65,6 +73,42 @@ 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 (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 +154,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 +185,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 +200,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 d3c9ab4f57926de83038c014a43404c786791a0c..6d92964a6acc31f27f53044e463949cd98b616b5 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,10 @@ 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"; + + private static final int MAX_APP_ID_LEN = 32; // max app-identifier in profile + static { HAP_CONFIG_FILES.add(HAP_FA_CONFIG_JSON_FILE); HAP_CONFIG_FILES.add(HAP_STAGE_MODULE_JSON_FILE); @@ -102,4 +113,71 @@ public class HapUtils { // default to compress native libs return true; } + + /** + * get app-id from profile + * + * @param profileContent the content of profile + * @return string value of app-id + * @throws ProfileException profile is invalid + */ + 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"); + } + } + + /** + * parse app-id and profileType from profile + * + * @param profileContent the content of profile + * @return Pair value of app-id and profileType + * @throws ProfileException profile is invalid + */ + 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()) { + throw new ProfileException("value of app-identifier is not string"); + } + ownerID = ownerIDElement.getAsString(); + if (ownerID.isEmpty() || ownerID.length() > MAX_APP_ID_LEN) { + throw new ProfileException("app-id length in profile is invalid"); + } + + } + } 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 4e22f8145df2efc97f4d47fdb6cad06d4d79849b..3eef01faec58a4db54ebd4db5401e3a86fd5d8f3 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 @@ -125,6 +125,8 @@ public abstract class SignProvider { */ protected Map signParams = new HashMap(); + private String profileContent; + /** * Read data of optional blocks from file user inputted. * @@ -295,6 +297,9 @@ public abstract class SignProvider { return false; } + if (profileContent != null) { + signParams.put(ParamConstants.PARAM_PROFILE_JSON_CONTENT, profileContent); + } /* 6. make signed file into output file. */ if (!SignElf.sign(signerConfig, signParams)) { LOGGER.error("hap-sign-tool: error: Sign elf internal failed."); @@ -377,10 +382,11 @@ public abstract class SignProvider { * @throws CodeSignException code sign on error * @throws IOException IO error * @throws HapFormatException hap format on error + * @throws ProfileException profile of app is invalid */ 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 +394,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 +597,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 +607,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 811f123979a0a54e032a06aa8187381b7675e999..17212f672b42b35553c4634eb91809b24087d148 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,15 @@ 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; + String profileContent = signParams.get(ParamConstants.PARAM_PROFILE_JSON_CONTENT); byte[] codesignData = codeSigning.getCodeSignBlock(new File(inputFile), offset, - signParams.get(ParamConstants.PARAM_IN_FORM)); + signParams.get(ParamConstants.PARAM_IN_FORM), profileContent); 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 e5d893bc150d4d3bf20574f681374778e3eb32a9..fdc8f45a9d4f4c2c697a9fb9521e1ed85d6b4911 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; @@ -81,6 +84,23 @@ public class VerifyHap { this.isPrintCert = isPrintCert; } + 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); + } + } + + /** * Check whether parameters are valid * @@ -279,6 +299,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; } @@ -309,9 +332,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); @@ -322,7 +346,6 @@ public class VerifyHap { return false; } ByteBuffer byteBuffer = ByteBuffer.wrap(propertyBlockArray); - String suffix = fileNameArray[fileNameArray.length - 1]; ByteBuffer header = HapUtils.reverseSliceBuffer(byteBuffer, 0, ZIP_HEAD_OF_SUBSIGNING_BLOCK_LENGTH); int blockOffset = header.getInt(); int blockLength = header.getInt(); @@ -332,7 +355,11 @@ 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); + String suffix = fileNameArray[fileNameArray.length - 1]; + boolean isCodeSign = VerifyCodeSignature.verifyHap(outputFile, blockOffset, blockLength, + suffix, profileContent); if (!isCodeSign) { LOGGER.error("Verify Hap has no code sign data error!"); return false; diff --git a/hapsigntool/hap_sign_tool_lib/src/main/java/com/ohos/hapsigntool/utils/ParamConstants.java b/hapsigntool/hap_sign_tool_lib/src/main/java/com/ohos/hapsigntool/utils/ParamConstants.java index bb6ff785d0e99461281394a00f61992a735ec8ad..c2c37a4d86ab92f9efbf246753c254023420782c 100644 --- a/hapsigntool/hap_sign_tool_lib/src/main/java/com/ohos/hapsigntool/utils/ParamConstants.java +++ b/hapsigntool/hap_sign_tool_lib/src/main/java/com/ohos/hapsigntool/utils/ParamConstants.java @@ -136,6 +136,11 @@ public class ParamConstants { */ public static final String PARAM_BASIC_PROFILE = "profileFile"; + /** + * json type content of Hap-file's capability profile + */ + public static final String PARAM_PROFILE_JSON_CONTENT = "profileContent"; + /** * Hap-file's proof-of-rotation */