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.signer; 17 18 import com.ohos.hapsigntool.adapter.LocalizationAdapter; 19 import com.ohos.hapsigntool.error.CustomException; 20 import com.ohos.hapsigntool.error.ERROR; 21 import com.ohos.hapsigntool.error.SignToolErrMsg; 22 import com.ohos.hapsigntool.utils.LogUtils; 23 import com.ohos.hapsigntool.utils.StringUtils; 24 25 import java.io.File; 26 import java.io.IOException; 27 import java.io.InputStream; 28 import java.io.UnsupportedEncodingException; 29 import java.lang.reflect.Constructor; 30 import java.lang.reflect.InvocationTargetException; 31 import java.net.MalformedURLException; 32 import java.net.URL; 33 import java.net.URLClassLoader; 34 import java.net.URLDecoder; 35 import java.net.URLEncoder; 36 import java.security.KeyPair; 37 import java.util.HashMap; 38 import java.util.Map; 39 import java.util.Optional; 40 import java.util.Properties; 41 42 /** 43 * Factory pattern to create signer. 44 * 45 * @since 2021/12/28 46 */ 47 public class SignerFactory { 48 private static final LogUtils LOGGER = new LogUtils(SignerFactory.class); 49 50 private static final Map<URL, ClassLoader> SIGNER_LOADERS = new HashMap<>(); 51 52 /** 53 * Create a signer. 54 * 55 * @param adapter Params adapter 56 * @return Local signer or remote signer 57 */ getSigner(LocalizationAdapter adapter)58 public ISigner getSigner(LocalizationAdapter adapter) { 59 if (adapter.isRemoteSigner()) { 60 Optional<ISigner> remoteSigner = loadRemoteSigner(adapter); 61 if (remoteSigner.isPresent()) { 62 return remoteSigner.get(); 63 } 64 LOGGER.warn("load remote signer failed, use default implementation"); 65 return new RemoteSigner(adapter.getOptions()); 66 } 67 KeyPair keyPair = adapter.getAliasKey(false); 68 adapter.releasePwd(); 69 return new LocalSigner(keyPair.getPrivate(), adapter.getSignCertChain()); 70 } 71 loadRemoteSigner(LocalizationAdapter adapter)72 private Optional<ISigner> loadRemoteSigner(LocalizationAdapter adapter) { 73 String signerPlugin = adapter.getOptions().getString("signerPlugin"); 74 if (StringUtils.isEmpty(signerPlugin)) { 75 LOGGER.warn("lost parameter signerPlugin"); 76 return Optional.empty(); 77 } 78 79 File plugin = new File(signerPlugin); 80 if (!plugin.exists()) { 81 File classLocation = getClassLocation(); 82 plugin = new File(classLocation, signerPlugin); 83 } 84 if (!plugin.exists() || !plugin.isFile()) { 85 LOGGER.warn("can not find signerPlugin or not a file by param signerPlugin = {}", signerPlugin); 86 return Optional.empty(); 87 } 88 Optional<URL> url = fileToUrl(plugin); 89 if (!url.isPresent()) { 90 return Optional.empty(); 91 } 92 try { 93 ClassLoader classLoader = generateSignerClassLoader(url.get()); 94 try (InputStream inputStream = classLoader.getResourceAsStream("signer.properties")) { 95 if (inputStream == null) { 96 LOGGER.warn("can not find entry signer.properties in {}", plugin); 97 return Optional.empty(); 98 } 99 Properties properties = new Properties(); 100 properties.load(inputStream); 101 String implClassName = properties.getProperty(ISigner.class.getName()); 102 if (StringUtils.isEmpty(implClassName)) { 103 LOGGER.warn("can not find {} in signer.properties", ISigner.class.getName()); 104 return Optional.empty(); 105 } 106 Class<?> implClass = classLoader.loadClass(implClassName); 107 Constructor<?> constructor = implClass.getConstructor(Map.class); 108 Object signer = constructor.newInstance(adapter.getOptions()); 109 if (signer instanceof ISigner) { 110 return Optional.of((ISigner) signer); 111 } 112 } 113 } catch (IOException | ClassNotFoundException | NoSuchMethodException 114 | InvocationTargetException | InstantiationException | IllegalAccessException e) { 115 LOGGER.warn("load remote signer from {} failed, msg: {}", signerPlugin, e.getMessage()); 116 } 117 return Optional.empty(); 118 } 119 fileToUrl(File file)120 private Optional<URL> fileToUrl(File file) { 121 if (!file.exists()) { 122 LOGGER.warn("{} is not exists", file); 123 return Optional.empty(); 124 } 125 try { 126 return Optional.of(file.toURI().toURL()); 127 } catch (MalformedURLException e) { 128 LOGGER.warn("{} can not convert to valid url, msg: {}", file, e.getMessage()); 129 } 130 return Optional.empty(); 131 } 132 getClassLocation()133 private File getClassLocation() { 134 String jarPath = SignerFactory.class.getProtectionDomain().getCodeSource().getLocation().getFile(); 135 if (StringUtils.isEmpty(jarPath)) { 136 CustomException.throwException(ERROR.COMMAND_ERROR, SignToolErrMsg.LOAD_REMOTE_PLUGIN_FAILED 137 .toString("Class path is empty")); 138 } 139 try { 140 jarPath = URLDecoder.decode(URLEncoder.encode(jarPath, "utf-8"), "utf-8"); 141 } catch (UnsupportedEncodingException | IllegalArgumentException e) { 142 LOGGER.warn("decode class location failed, will ignored. msg :{}", e.getMessage()); 143 } 144 File jarFile = new File(jarPath); 145 if (!jarFile.exists()) { 146 CustomException.throwException(ERROR.COMMAND_ERROR, SignToolErrMsg.LOAD_REMOTE_PLUGIN_FAILED 147 .toString("class path" + jarFile + "is not exists")); 148 } 149 if (jarFile.isFile()) { 150 return jarFile.getParentFile(); 151 } 152 return jarFile; 153 } 154 generateSignerClassLoader(URL signerClassUrl)155 private static synchronized ClassLoader generateSignerClassLoader(URL signerClassUrl) { 156 ClassLoader classLoader = SIGNER_LOADERS.get(signerClassUrl); 157 if (classLoader == null) { 158 ClassLoader parent = SignerFactory.class.getClassLoader(); 159 classLoader = URLClassLoader.newInstance(new URL[]{signerClassUrl}, parent); 160 SIGNER_LOADERS.put(signerClassUrl, classLoader); 161 } 162 return classLoader; 163 } 164 } 165