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.hap.provider; 17 18 import com.google.gson.JsonElement; 19 import com.google.gson.JsonObject; 20 import com.google.gson.JsonParseException; 21 import com.google.gson.JsonParser; 22 import com.ohos.hapsigntool.api.model.Options; 23 import com.ohos.hapsigntool.error.CustomException; 24 import com.ohos.hapsigntool.hap.config.SignerConfig; 25 import com.ohos.hapsigntool.hap.entity.SigningBlock; 26 import com.ohos.hapsigntool.hap.exception.InvalidParamsException; 27 import com.ohos.hapsigntool.hap.exception.MissingParamsException; 28 import com.ohos.hapsigntool.hap.exception.ProfileException; 29 import com.ohos.hapsigntool.hap.exception.SignatureException; 30 import com.ohos.hapsigntool.hap.exception.VerifyCertificateChainException; 31 import com.ohos.hapsigntool.hap.exception.HapFormatException; 32 import com.ohos.hapsigntool.hap.sign.SignBin; 33 import com.ohos.hapsigntool.hap.sign.SignHap; 34 import com.ohos.hapsigntool.hap.sign.SignatureAlgorithm; 35 import com.ohos.hapsigntool.hap.verify.VerifyUtils; 36 import com.ohos.hapsigntool.utils.CertificateUtils; 37 import com.ohos.hapsigntool.utils.DigestUtils; 38 import com.ohos.hapsigntool.utils.EscapeCharacter; 39 import com.ohos.hapsigntool.utils.FileUtils; 40 import com.ohos.hapsigntool.utils.HapUtils; 41 import com.ohos.hapsigntool.utils.ParamConstants; 42 import com.ohos.hapsigntool.utils.ParamProcessUtil; 43 import com.ohos.hapsigntool.utils.StringUtils; 44 import com.ohos.hapsigntool.zip.ByteBufferZipDataInput; 45 import com.ohos.hapsigntool.zip.RandomAccessFileZipDataInput; 46 import com.ohos.hapsigntool.zip.RandomAccessFileZipDataOutput; 47 import com.ohos.hapsigntool.zip.ZipDataInput; 48 import com.ohos.hapsigntool.zip.ZipDataOutput; 49 import com.ohos.hapsigntool.zip.ZipFileInfo; 50 import com.ohos.hapsigntool.zip.ZipUtils; 51 52 import org.apache.logging.log4j.LogManager; 53 import org.apache.logging.log4j.Logger; 54 import org.bouncycastle.cms.CMSException; 55 import org.bouncycastle.cms.CMSSignedData; 56 import org.bouncycastle.jce.provider.BouncyCastleProvider; 57 58 import java.io.File; 59 import java.io.FileOutputStream; 60 import java.io.IOException; 61 import java.io.RandomAccessFile; 62 import java.nio.ByteBuffer; 63 import java.nio.charset.StandardCharsets; 64 import java.nio.file.Files; 65 import java.nio.file.StandardCopyOption; 66 import java.security.InvalidKeyException; 67 import java.security.Security; 68 import java.security.cert.CertificateException; 69 import java.security.cert.X509CRL; 70 import java.security.cert.X509Certificate; 71 import java.util.ArrayList; 72 import java.util.Collections; 73 import java.util.HashMap; 74 import java.util.List; 75 import java.util.Map; 76 import java.util.Set; 77 import java.util.TimeZone; 78 import java.util.Optional; 79 import java.util.jar.JarFile; 80 import java.util.jar.JarOutputStream; 81 82 /** 83 * Sign provider super class 84 * 85 * @since 2021-12-14 86 */ 87 public abstract class SignProvider { 88 private static final Logger LOGGER = LogManager.getLogger(SignProvider.class); 89 private static final List<String> VALID_SIGN_ALG_NAME = new ArrayList<String>(); 90 private static final List<String> PARAMETERS_NEED_ESCAPE = new ArrayList<String>(); 91 private static final long TIMESTAMP = 1230768000000L; 92 private static final int COMPRESSION_MODE = 9; 93 94 static { 95 VALID_SIGN_ALG_NAME.add(ParamConstants.HAP_SIG_ALGORITHM_SHA256_ECDSA); 96 VALID_SIGN_ALG_NAME.add(ParamConstants.HAP_SIG_ALGORITHM_SHA384_ECDSA); 97 VALID_SIGN_ALG_NAME.add(ParamConstants.HAP_SIG_ALGORITHM_SHA512_ECDSA); 98 VALID_SIGN_ALG_NAME.add(ParamConstants.HAP_SIG_ALGORITHM_SHA256_RSA_PSS); 99 VALID_SIGN_ALG_NAME.add(ParamConstants.HAP_SIG_ALGORITHM_SHA384_RSA_PSS); 100 VALID_SIGN_ALG_NAME.add(ParamConstants.HAP_SIG_ALGORITHM_SHA512_RSA_PSS); 101 VALID_SIGN_ALG_NAME.add(ParamConstants.HAP_SIG_ALGORITHM_SHA256_RSA_MGF1); 102 VALID_SIGN_ALG_NAME.add(ParamConstants.HAP_SIG_ALGORITHM_SHA384_RSA_MGF1); 103 VALID_SIGN_ALG_NAME.add(ParamConstants.HAP_SIG_ALGORITHM_SHA512_RSA_MGF1); 104 } 105 106 static { 107 PARAMETERS_NEED_ESCAPE.add(ParamConstants.PARAM_REMOTE_CODE); 108 PARAMETERS_NEED_ESCAPE.add(ParamConstants.PARAM_LOCAL_JKS_KEYSTORE_CODE); 109 PARAMETERS_NEED_ESCAPE.add(ParamConstants.PARAM_LOCAL_JKS_KEYALIAS_CODE); 110 } 111 112 /** 113 * list of hap signature optional blocks 114 */ 115 protected List<SigningBlock> optionalBlocks = new ArrayList<SigningBlock>(); 116 117 /** 118 * parameters only used in signing 119 */ 120 protected Map<String, String> signParams = new HashMap<String, String>(); 121 122 /** 123 * Read data of optional blocks from file user inputted. 124 * 125 * @throws InvalidParamsException Exception occurs when the input is invalid. 126 */ loadOptionalBlocks()127 protected void loadOptionalBlocks() throws InvalidParamsException { 128 String property = signParams.get(ParamConstants.PARAM_BASIC_PROPERTY); 129 loadOptionalBlock(property, HapUtils.HAP_PROPERTY_BLOCK_ID); 130 131 String profile = signParams.get(ParamConstants.PARAM_BASIC_PROFILE); 132 loadOptionalBlock(profile, HapUtils.HAP_PROFILE_BLOCK_ID); 133 134 String proofOfRotation = signParams.get(ParamConstants.PARAM_BASIC_PROOF); 135 loadOptionalBlock(proofOfRotation, HapUtils.HAP_PROOF_OF_ROTATION_BLOCK_ID); 136 } 137 loadOptionalBlock(String file, int type)138 private void loadOptionalBlock(String file, int type) throws InvalidParamsException { 139 if (!checkStringIsNotNullAndEmity(file)) { 140 return; 141 } 142 if (!checkFile(file)) { 143 LOGGER.error("check file failed"); 144 throw new InvalidParamsException("Invalid file: " + file + ", filetype: " + type); 145 } 146 try { 147 byte[] optionalBlockBytes = HapUtils.readFileToByte(file); 148 if (optionalBlockBytes == null || optionalBlockBytes.length <= 0) { 149 LOGGER.warn("Optional block is null!"); 150 return; 151 } 152 optionalBlocks.add(new SigningBlock(type, optionalBlockBytes)); 153 } catch (IOException e) { 154 LOGGER.error("read file error", e); 155 throw new InvalidParamsException("Invalid file: " + file + " is not readable. filetype: " + type); 156 } 157 } 158 159 /** 160 * check if the input path is a file 161 * 162 * @param filePath input file path 163 * @return true, if path is a file and can be read 164 */ checkFile(String filePath)165 private boolean checkFile(String filePath) { 166 if (!(checkStringIsNotNullAndEmity(filePath))) { 167 LOGGER.error("fileName is null"); 168 return false; 169 } 170 File file = new File(filePath); 171 if (!file.canRead() || !file.isFile()) { 172 LOGGER.error(filePath + " not exist or can not read!"); 173 return false; 174 } 175 return true; 176 } 177 checkStringIsNotNullAndEmity(String str)178 private boolean checkStringIsNotNullAndEmity(String str) { 179 return !(str == null || "".equals(str)); 180 } 181 182 /** 183 * Get certificate chain used to sign. 184 * 185 * @return list of x509 certificates. 186 */ getPublicCerts()187 private List<X509Certificate> getPublicCerts() { 188 String publicCertsFile = signParams.get(ParamConstants.PARAM_LOCAL_PUBLIC_CERT); 189 if (StringUtils.isEmpty(publicCertsFile)) { 190 return Collections.emptyList(); 191 } 192 return getCertificateChainFromFile(publicCertsFile); 193 } 194 195 /** 196 * get certificate revocation list used to sign 197 * 198 * @return certificate revocation list 199 */ getCrl()200 public Optional<X509CRL> getCrl() { 201 return Optional.empty(); 202 } 203 204 /** 205 * Create SignerConfig by certificate chain and certificate revocation list. 206 * 207 * @param certificates certificate chain 208 * @param crl certificate revocation list 209 * @param options options 210 * @return Object of SignerConfig 211 * @throws InvalidKeyException on error when the key is invalid. 212 */ createSignerConfigs(List<X509Certificate> certificates, Optional<X509CRL> crl, Options options)213 public SignerConfig createSignerConfigs(List<X509Certificate> certificates, Optional<X509CRL> crl, Options options) 214 throws InvalidKeyException { 215 SignerConfig signerConfig = new SignerConfig(); 216 signerConfig.fillParameters(this.signParams); 217 signerConfig.setCertificates(certificates); 218 signerConfig.setOptions(options); 219 220 List<SignatureAlgorithm> signatureAlgorithms = new ArrayList<SignatureAlgorithm>(); 221 signatureAlgorithms.add( 222 ParamProcessUtil.getSignatureAlgorithm(this.signParams.get(ParamConstants.PARAM_BASIC_SIGANTURE_ALG))); 223 signerConfig.setSignatureAlgorithms(signatureAlgorithms); 224 225 if (!crl.equals(Optional.empty())) { 226 signerConfig.setX509CRLs(Collections.singletonList(crl.get())); 227 } 228 return signerConfig; 229 } 230 231 /** 232 * sign bin file 233 * 234 * @param options parameters used to sign bin file 235 * @return true, if sign successfully. 236 */ signBin(Options options)237 public boolean signBin(Options options) { 238 Security.addProvider(new BouncyCastleProvider()); 239 List<X509Certificate> publicCert = null; 240 SignerConfig signerConfig; 241 try { 242 publicCert = getX509Certificates(options); 243 244 // Get x509 CRL 245 Optional<X509CRL> crl = getCrl(); 246 247 // Create signer configs, which contains public cert and crl info. 248 signerConfig = createSignerConfigs(publicCert, crl, options); 249 } catch (InvalidKeyException | InvalidParamsException | MissingParamsException | ProfileException e) { 250 LOGGER.error("create signer configs failed.", e); 251 printErrorLogWithoutStack(e); 252 return false; 253 } 254 255 /* 6. make signed file into output file. */ 256 if (!SignBin.sign(signerConfig, signParams)) { 257 LOGGER.error("hap-sign-tool: error: Sign bin internal failed."); 258 return false; 259 } 260 LOGGER.info("Sign success"); 261 return true; 262 } 263 264 /** 265 * sign hap file 266 * 267 * @param options parameters used to sign hap file 268 * @return true, if sign successfully 269 */ sign(Options options)270 public boolean sign(Options options) { 271 Security.addProvider(new BouncyCastleProvider()); 272 List<X509Certificate> publicCerts = null; 273 File output = null; 274 File tmpOutput = null; 275 boolean isRet = false; 276 boolean isPathOverlap = false; 277 try { 278 publicCerts = getX509Certificates(options); 279 checkCompatibleVersion(); 280 File input = new File(signParams.get(ParamConstants.PARAM_BASIC_INPUT_FILE)); 281 output = new File(signParams.get(ParamConstants.PARAM_BASIC_OUTPUT_FILE)); 282 if (input.getCanonicalPath().equals(output.getCanonicalPath())) { 283 tmpOutput = File.createTempFile("signedHap", ".hap"); 284 isPathOverlap = true; 285 } else { 286 tmpOutput = output; 287 } 288 // copy file and Alignment 289 int alignment = Integer.parseInt(signParams.get(ParamConstants.PARAM_BASIC_ALIGNMENT)); 290 copyFileAndAlignment(input, tmpOutput, alignment); 291 // generate sign block and output signedHap 292 try (RandomAccessFile outputHap = new RandomAccessFile(tmpOutput, "rw")) { 293 ZipDataInput outputHapIn = new RandomAccessFileZipDataInput(outputHap); 294 ZipFileInfo zipInfo = ZipUtils.findZipInfo(outputHapIn); 295 long centralDirectoryOffset = zipInfo.getCentralDirectoryOffset(); 296 ZipDataInput beforeCentralDir = outputHapIn.slice(0, centralDirectoryOffset); 297 ByteBuffer centralDirBuffer = 298 outputHapIn.createByteBuffer(centralDirectoryOffset, zipInfo.getCentralDirectorySize()); 299 ZipDataInput centralDirectory = new ByteBufferZipDataInput(centralDirBuffer); 300 301 ByteBuffer eocdBuffer = zipInfo.getEocd(); 302 ZipDataInput eocd = new ByteBufferZipDataInput(eocdBuffer); 303 304 Optional<X509CRL> crl = getCrl(); 305 SignerConfig signerConfig = createSignerConfigs(publicCerts, crl, options); 306 signerConfig.setCompatibleVersion(Integer.parseInt( 307 signParams.get(ParamConstants.PARAM_BASIC_COMPATIBLE_VERSION))); 308 ZipDataInput[] contents = {beforeCentralDir, centralDirectory, eocd}; 309 byte[] signingBlock = SignHap.sign(contents, signerConfig, optionalBlocks); 310 long newCentralDirectoryOffset = centralDirectoryOffset + signingBlock.length; 311 ZipUtils.setCentralDirectoryOffset(eocdBuffer, newCentralDirectoryOffset); 312 LOGGER.info("Generate signing block success, begin write it to output file"); 313 314 outputSignedFile(outputHap, centralDirectoryOffset, signingBlock, centralDirectory, eocdBuffer); 315 isRet = true; 316 } 317 } catch (IOException | InvalidKeyException | HapFormatException | MissingParamsException 318 | InvalidParamsException | ProfileException | NumberFormatException | CustomException e) { 319 printErrorLogWithoutStack(e); 320 } catch (SignatureException e) { 321 printErrorLog(e); 322 } 323 return doAfterSign(isRet, isPathOverlap, tmpOutput, output); 324 } 325 326 /** 327 * Load certificate chain from input parameters 328 * 329 * @param options parameters used to sign hap file 330 * @return list of type x509certificate 331 * @throws MissingParamsException Exception occurs when the required parameters are not entered. 332 * @throws InvalidParamsException Exception occurs when the required parameters are invalid. 333 * @throws ProfileException Exception occurs when profile is invalid. 334 */ getX509Certificates(Options options)335 private List<X509Certificate> getX509Certificates(Options options) throws MissingParamsException, 336 InvalidParamsException, ProfileException { 337 List<X509Certificate> publicCerts; 338 // 1. check the parameters 339 checkParams(options); 340 // 2. get x509 verify certificate 341 publicCerts = getPublicCerts(); 342 // 3. load optionalBlocks 343 loadOptionalBlocks(); 344 checkProfileValid(publicCerts); 345 return publicCerts; 346 } 347 outputSignedFile(RandomAccessFile outputHap, long centralDirectoryOffset, byte[] signingBlock, ZipDataInput centralDirectory, ByteBuffer eocdBuffer)348 private void outputSignedFile(RandomAccessFile outputHap, long centralDirectoryOffset, 349 byte[] signingBlock, ZipDataInput centralDirectory, ByteBuffer eocdBuffer) throws IOException { 350 ZipDataOutput outputHapOut = new RandomAccessFileZipDataOutput(outputHap, centralDirectoryOffset); 351 outputHapOut.write(signingBlock, 0, signingBlock.length); 352 centralDirectory.copyTo(0, centralDirectory.size(), outputHapOut); 353 outputHapOut.write(eocdBuffer); 354 } 355 doAfterSign(boolean isSuccess, boolean pathOverlap, File tmpOutput, File output)356 private boolean doAfterSign(boolean isSuccess, boolean pathOverlap, File tmpOutput, File output) { 357 boolean isRet = isSuccess; 358 if (isRet && pathOverlap) { 359 try { 360 Files.move(tmpOutput.toPath(), output.toPath(), StandardCopyOption.REPLACE_EXISTING); 361 } catch (IOException e) { 362 printErrorLog(e); 363 isRet = false; 364 } 365 } 366 367 if (isRet) { 368 LOGGER.info("Sign Hap success!"); 369 } else { 370 FileUtils.deleteFile(tmpOutput); 371 } 372 return isRet; 373 } 374 printErrorLog(Exception exception)375 private void printErrorLog(Exception exception) { 376 if (exception != null) { 377 LOGGER.error("hap-sign-tool: error: {}", exception.getMessage(), exception); 378 } 379 } 380 printErrorLogWithoutStack(Exception exception)381 private void printErrorLogWithoutStack(Exception exception) { 382 if (exception != null) { 383 LOGGER.error("hap-sign-tool: error: {}", exception.getMessage()); 384 } 385 } 386 387 /** 388 * Copy file and alignment 389 * 390 * @param input file input 391 * @param tmpOutput file tmpOutput 392 * @param alignment alignment 393 * @throws IOException io error 394 */ copyFileAndAlignment(File input, File tmpOutput, int alignment)395 private void copyFileAndAlignment(File input, File tmpOutput, int alignment) throws IOException { 396 try (JarFile inputJar = new JarFile(input, false); 397 FileOutputStream outputFile = new FileOutputStream(tmpOutput); 398 JarOutputStream outputJar = new JarOutputStream(outputFile)) { 399 long timestamp = TIMESTAMP; 400 timestamp -= TimeZone.getDefault().getOffset(timestamp); 401 outputJar.setLevel(COMPRESSION_MODE); 402 List<String> entryNames = SignHap.getEntryNamesFromHap(inputJar); 403 SignHap.copyFiles(entryNames, inputJar, outputJar, timestamp, alignment); 404 } 405 } 406 407 /** 408 * check signature algorithm 409 * 410 * @throws InvalidParamsException Exception occurs when the inputted sign algorithm is invalid. 411 */ checkSignatureAlg()412 private void checkSignatureAlg() throws InvalidParamsException { 413 String signAlg = signParams.get(ParamConstants.PARAM_BASIC_SIGANTURE_ALG).trim(); 414 for (String validAlg : VALID_SIGN_ALG_NAME) { 415 if (validAlg.equalsIgnoreCase(signAlg)) { 416 return; 417 } 418 } 419 LOGGER.error("Unsupported signature algorithm :" + signAlg); 420 throw new InvalidParamsException("Invalid parameter: Sign Alg"); 421 } 422 423 /** 424 * check alignment 425 */ checkSignAlignment()426 protected void checkSignAlignment() { 427 if (!signParams.containsKey(ParamConstants.PARAM_BASIC_ALIGNMENT)) { 428 signParams.put(ParamConstants.PARAM_BASIC_ALIGNMENT, ParamConstants.ALIGNMENT); 429 } 430 } 431 432 /** 433 * Get CN value of developer certificate from profile. 434 * 435 * @param buildInfoObject json obect of buildInfo in profile. 436 * @return Object of development-certificate. 437 */ getDevelopmentCertificate(JsonObject buildInfoObject)438 private X509Certificate getDevelopmentCertificate(JsonObject buildInfoObject) { 439 final String developmentCertElememt = "development-certificate"; 440 String developmentCertificate = buildInfoObject.get(developmentCertElememt).getAsString(); 441 return DigestUtils.decodeBase64ToX509Certifate(developmentCertificate); 442 } 443 444 /** 445 * Get CN value of release certificate from profile. 446 * 447 * @param buildInfoObject json obect of buildInfo in profile. 448 * @return Object of distribution-certificate. 449 */ getReleaseCertificate(JsonObject buildInfoObject)450 private X509Certificate getReleaseCertificate(JsonObject buildInfoObject) { 451 final String distributeCertElememt = "distribution-certificate"; 452 String distributeCertificate = buildInfoObject.get(distributeCertElememt).getAsString(); 453 return DigestUtils.decodeBase64ToX509Certifate(distributeCertificate); 454 } 455 getCertificateCN(X509Certificate cert)456 private String getCertificateCN(X509Certificate cert) { 457 if (cert == null) { 458 return ""; 459 } 460 String valueOfDN = cert.getSubjectDN().toString(); 461 valueOfDN = valueOfDN.replace("\"", ""); 462 String[] arrayDN = valueOfDN.split(","); 463 for (String element : arrayDN) { 464 if (element.trim().startsWith("CN=")) { 465 return element.split("=")[1]; 466 } 467 } 468 return ""; 469 } 470 findProfileFromOptionalBlocks()471 private byte[] findProfileFromOptionalBlocks() { 472 byte[] profile = new byte[0]; 473 for (SigningBlock optionalBlock : optionalBlocks) { 474 if (optionalBlock.getType() == HapUtils.HAP_PROFILE_BLOCK_ID) { 475 profile = optionalBlock.getValue(); 476 } 477 } 478 return profile; 479 } 480 481 /** 482 * Check profile is valid. A valid profile must include type and 483 * certificate which has a non-empty value of DN. 484 * 485 * @param inputCerts certificates inputted by user. 486 * @throws ProfileException Exception occurs when profile is invalid. 487 */ checkProfileValid(List<X509Certificate> inputCerts)488 private void checkProfileValid(List<X509Certificate> inputCerts) throws ProfileException { 489 try { 490 byte[] profile = findProfileFromOptionalBlocks(); 491 boolean isProfileWithoutSign = ParamConstants.ProfileSignFlag.UNSIGNED_PROFILE.getSignFlag().equals( 492 signParams.get(ParamConstants.PARAM_BASIC_PROFILE_SIGNED)); 493 String content; 494 if (!isProfileWithoutSign) { 495 CMSSignedData cmsSignedData = new CMSSignedData(profile); 496 boolean isVerify = VerifyUtils.verifyCmsSignedData(cmsSignedData); 497 if (!isVerify) { 498 throw new ProfileException("Verify profile pkcs7 failed! Profile is invalid."); 499 } 500 Object contentObj = cmsSignedData.getSignedContent().getContent(); 501 if (!(contentObj instanceof byte[])) { 502 throw new ProfileException("Check profile failed, signed profile content is not byte array!"); 503 } 504 content = new String((byte[]) contentObj, StandardCharsets.UTF_8); 505 } else { 506 content = new String(profile, StandardCharsets.UTF_8); 507 } 508 JsonElement parser = JsonParser.parseString(content); 509 JsonObject profileJson = parser.getAsJsonObject(); 510 checkProfileInfo(profileJson, inputCerts); 511 } catch (CMSException e) { 512 throw new ProfileException("Verify profile pkcs7 failed! Profile is invalid.", e); 513 } catch (JsonParseException e) { 514 throw new ProfileException("Invalid parameter: profile content is not a JSON.", e); 515 } 516 } 517 checkProfileInfo(JsonObject profileJson, List<X509Certificate> inputCerts)518 private void checkProfileInfo(JsonObject profileJson, List<X509Certificate> inputCerts) throws ProfileException { 519 String profileTypeKey = "type"; 520 String profileType = profileJson.get(profileTypeKey).getAsString(); 521 if (profileType == null || profileType.length() == 0) { 522 throw new ProfileException("Get profile type error!"); 523 } 524 String buildInfoMember = "bundle-info"; 525 JsonObject buildInfoObject = profileJson.getAsJsonObject(buildInfoMember); 526 X509Certificate certInProfile; 527 if (profileType.equalsIgnoreCase("release")) { 528 certInProfile = getReleaseCertificate(buildInfoObject); 529 } else if (profileType.equalsIgnoreCase("debug")) { 530 certInProfile = getDevelopmentCertificate(buildInfoObject); 531 } else { 532 throw new ProfileException("Unsupported profile type!"); 533 } 534 if (!inputCerts.isEmpty() && !checkInputCertMatchWithProfile(inputCerts.get(0), certInProfile)) { 535 throw new ProfileException("input certificates do not match with profile!"); 536 } 537 String cn = getCertificateCN(certInProfile); 538 LOGGER.info("certificate in profile: {}", cn); 539 if (cn.isEmpty()) { 540 throw new ProfileException("Common name of certificate is empty!"); 541 } 542 } 543 544 /** 545 * check whether certificate inputted by user is matched with the certificate in profile. 546 * 547 * @param inputCert certificates inputted by user. 548 * @param certInProfile the certificate in profile. 549 * @return true, if it is match. 550 */ checkInputCertMatchWithProfile(X509Certificate inputCert, X509Certificate certInProfile)551 protected boolean checkInputCertMatchWithProfile(X509Certificate inputCert, X509Certificate certInProfile) { 552 return true; 553 } 554 555 /** 556 * Check input parameters is valid. And put valid parameters into signParams. 557 * 558 * @param options parameters inputted by user. 559 * @throws MissingParamsException Exception occurs when the required parameters are not entered. 560 * @throws InvalidParamsException Exception occurs when the required parameters are invalid. 561 */ checkParams(Options options)562 public void checkParams(Options options) throws MissingParamsException, InvalidParamsException { 563 String[] paramFileds = { 564 ParamConstants.PARAM_BASIC_ALIGNMENT, 565 ParamConstants.PARAM_BASIC_SIGANTURE_ALG, 566 ParamConstants.PARAM_BASIC_INPUT_FILE, 567 ParamConstants.PARAM_BASIC_OUTPUT_FILE, 568 ParamConstants.PARAM_BASIC_PRIVATE_KEY, 569 ParamConstants.PARAM_BASIC_PROFILE, 570 ParamConstants.PARAM_BASIC_PROOF, 571 ParamConstants.PARAM_BASIC_PROPERTY, 572 ParamConstants.PARAM_REMOTE_SERVER, 573 ParamConstants.PARAM_BASIC_PROFILE_SIGNED, 574 ParamConstants.PARAM_LOCAL_PUBLIC_CERT, 575 ParamConstants.PARAM_BASIC_COMPATIBLE_VERSION 576 }; 577 Set<String> paramSet = ParamProcessUtil.initParamField(paramFileds); 578 579 for (String paramKey : options.keySet()) { 580 if (paramSet.contains(paramKey)) { 581 signParams.put(paramKey, getParamValue(paramKey, options.getString(paramKey))); 582 } 583 } 584 if (!signParams.containsKey(ParamConstants.PARAM_BASIC_PROFILE_SIGNED)) { 585 signParams.put(ParamConstants.PARAM_BASIC_PROFILE_SIGNED, "1"); 586 } 587 checkSignatureAlg(); 588 checkSignAlignment(); 589 } 590 591 /** 592 * Check compatible version, if param do not have compatible version default 9. 593 * 594 * @throws InvalidParamsException invalid param 595 * @throws MissingParamsException missing param 596 */ checkCompatibleVersion()597 protected void checkCompatibleVersion() throws InvalidParamsException, MissingParamsException { 598 if (!signParams.containsKey(ParamConstants.PARAM_BASIC_COMPATIBLE_VERSION)) { 599 signParams.put(ParamConstants.PARAM_BASIC_COMPATIBLE_VERSION, "9"); 600 return; 601 } 602 String compatibleApiVersionVal = signParams.get(ParamConstants.PARAM_BASIC_COMPATIBLE_VERSION); 603 try { 604 int compatibleApiVersion = Integer.parseInt(compatibleApiVersionVal); 605 } catch (NumberFormatException e) { 606 throw new InvalidParamsException("Invalid parameter: " + ParamConstants.PARAM_BASIC_COMPATIBLE_VERSION); 607 } 608 } 609 610 /** 611 * Get parameters from inputted strings. This function unescape some escaped parameters and return it. 612 * 613 * @param paramName the name of parameter 614 * @param paramValue the value of parameter 615 * @return parameter value in the correct form. 616 */ getParamValue(String paramName, String paramValue)617 protected String getParamValue(String paramName, String paramValue) { 618 for (String name : PARAMETERS_NEED_ESCAPE) { 619 if (name.equals(paramName)) { 620 return EscapeCharacter.unescape(paramValue); 621 } 622 } 623 return paramValue; 624 } 625 getCertificateChainFromFile(String certChianFile)626 private List<X509Certificate> getCertificateChainFromFile(String certChianFile) { 627 try { 628 return CertificateUtils.getCertListFromFile(certChianFile); 629 } catch (CertificateException e) { 630 LOGGER.error("File content is not certificates! " + e.getMessage()); 631 } catch (IOException e) { 632 LOGGER.error("Certificate file exception: " + e.getMessage()); 633 } catch (VerifyCertificateChainException e) { 634 LOGGER.error(e.getMessage()); 635 } 636 return Collections.emptyList(); 637 } 638 } 639