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