• 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 
86         try {
87             LOGGER.info("try to find plugin by path {}", plugin.getCanonicalPath());
88         } catch (IOException e) {
89             LOGGER.warn("can not find signerPlugin by param signerPlugin = {}", signerPlugin);
90             return Optional.empty();
91         }
92         Optional<URL> url = fileToUrl(plugin);
93         if (!url.isPresent()) {
94             return Optional.empty();
95         }
96         try {
97             ClassLoader classLoader = generateSignerClassLoader(url.get());
98             try (InputStream inputStream = classLoader.getResourceAsStream("signer.properties")) {
99                 if (inputStream == null) {
100                     LOGGER.warn("can not find entry signer.properties in {}", plugin);
101                     return Optional.empty();
102                 }
103                 Properties properties = new Properties();
104                 properties.load(inputStream);
105                 String implClassName = properties.getProperty(ISigner.class.getName());
106                 if (StringUtils.isEmpty(implClassName)) {
107                     LOGGER.warn("can not find {} in signer.properties", ISigner.class.getName());
108                     return Optional.empty();
109                 }
110                 Class<?> implClass = classLoader.loadClass(implClassName);
111                 Constructor<?> constructor = implClass.getConstructor(Map.class);
112                 Object signer = constructor.newInstance(adapter.getOptions());
113                 if (signer instanceof ISigner) {
114                     return Optional.of((ISigner) signer);
115                 }
116             }
117         } catch (IOException | ClassNotFoundException | NoSuchMethodException
118             | InvocationTargetException | InstantiationException | IllegalAccessException e) {
119             LOGGER.warn("load remote signer from {} failed, msg: {}", signerPlugin, e.getMessage());
120         }
121         return Optional.empty();
122     }
123 
fileToUrl(File file)124     private Optional<URL> fileToUrl(File file) {
125         if (!file.exists()) {
126             LOGGER.warn("{} is not exists", file);
127             return Optional.empty();
128         }
129         try {
130             return Optional.of(file.toURI().toURL());
131         } catch (MalformedURLException e) {
132             LOGGER.warn("{} can not convert to valid url, msg: {}", file, e.getMessage());
133         }
134         return Optional.empty();
135     }
136 
getClassLocation()137     private File getClassLocation() {
138         String jarPath = SignerFactory.class.getProtectionDomain().getCodeSource().getLocation().getFile();
139         if (StringUtils.isEmpty(jarPath)) {
140             CustomException.throwException(ERROR.COMMAND_ERROR, "class path is empty");
141         }
142         try {
143             jarPath = URLDecoder.decode(URLEncoder.encode(jarPath, "utf-8"), "utf-8");
144         } catch (UnsupportedEncodingException | IllegalArgumentException e) {
145             LOGGER.warn("decode class location failed, will ignored. msg :{}", e.getMessage());
146         }
147         File jarFile = new File(jarPath);
148         if (!jarFile.exists()) {
149             CustomException.throwException(ERROR.COMMAND_ERROR, "class path" + jarFile + "is not exists");
150         }
151         if (jarFile.isFile()) {
152             return jarFile.getParentFile();
153         }
154         return jarFile;
155     }
156 
generateSignerClassLoader(URL signerClassUrl)157     private static synchronized ClassLoader generateSignerClassLoader(URL signerClassUrl) {
158         ClassLoader classLoader = SIGNER_LOADERS.get(signerClassUrl);
159         if (classLoader == null) {
160             ClassLoader parent = SignerFactory.class.getClassLoader();
161             classLoader = URLClassLoader.newInstance(new URL[]{signerClassUrl}, parent);
162             SIGNER_LOADERS.put(signerClassUrl, classLoader);
163         }
164         return classLoader;
165     }
166 }
167