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