• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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