readChannelList(Path channelList) throws IOException {
101 | return Files.readAllLines(channelList, StandardCharsets.UTF_8)
102 | .stream()
103 | .map(String::trim)
104 | .filter(s -> !s.startsWith("#"))
105 | .map(s -> s.split("#")[0])
106 | .map(String::trim)
107 | .filter(s -> !s.isEmpty())
108 | .collect(Collectors.toList());
109 | }
110 |
111 | public void build(String channel, Path out) throws IOException {
112 | out = out.toAbsolutePath();
113 | Path parent = out.getParent();
114 | Files.createDirectories(parent);
115 | Path tmp = Files.createTempFile(parent, "tmp", ".apk");
116 | Files.deleteIfExists(out);
117 | try {
118 | try (AxmlFastZipOut zout = new AxmlFastZipOut(tmp.toFile());) {
119 | zout.initByAndroidManifestContent(axml);
120 | zout.copyPart(in, entries);
121 | updateUM(axml, channel);
122 | ByteBuffer bb = ByteBuffer.wrap(axml.toByteArray());
123 | Source am = Source.newRawEntry("AndroidManifest.xml", bb);
124 | zout.copyPart(am, am.entries());
125 | zout.copyEnd();
126 | }
127 |
128 | ApkSigns.sign(tmp, out, this.key, false);
129 | try {
130 | ChannelWriter.put(out.toFile(), channel);
131 | } catch (SignatureNotFoundException e) {
132 | throw new IOException(e);
133 | }
134 | } finally {
135 | Files.deleteIfExists(tmp);
136 | }
137 | }
138 |
139 | @Override
140 | public void close() throws IOException {
141 | in.close();
142 | }
143 | }
144 |
--------------------------------------------------------------------------------
/signer/src/main/java/dx/channel/FixSha1WithDsaProvider.java:
--------------------------------------------------------------------------------
1 | /**
2 | * dx-signer
3 | *
4 | * Copyright 2022 北京顶象技术有限公司
5 | *
6 | * Licensed under the Apache License, Version 2.0 (the "License");
7 | * you may not use this file except in compliance with the License.
8 | * You may obtain a copy of the License at
9 | *
10 | * http://www.apache.org/licenses/LICENSE-2.0
11 | *
12 | * Unless required by applicable law or agreed to in writing, software
13 | * distributed under the License is distributed on an "AS IS" BASIS,
14 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
15 | * See the License for the specific language governing permissions and
16 | * limitations under the License.
17 | */
18 | package dx.channel;
19 |
20 | import org.slf4j.Logger;
21 | import org.slf4j.LoggerFactory;
22 |
23 | import java.nio.ByteBuffer;
24 | import java.security.*;
25 |
26 | /**
27 | * JDK-8184341 : Release Note: New defaults for DSA keys in jarsigner and keytool
28 | * https://bugs.java.com/bugdatabase/view_bug.do?bug_id=8184341
29 | *
30 | * 高版本java(8u151+)中DSA限制了最小的长度,而Android低版本兼容要求使用sha1算法,
31 | * apksigner为了兼容,选择使用Sha1WithDSA签名算法, 进而产生崩溃
32 | *
33 | *
34 | * Caused by: java.security.InvalidKeyException: The security strength of SHA-1 digest algorithm is not sufficient for this key size
35 | * at java.base/sun.security.provider.DSA.checkKey(DSA.java:124)
36 | * at java.base/sun.security.provider.DSA.engineInitSign(DSA.java:156)
37 | * ...
38 | * at java.base/java.security.Signature.initSign(Signature.java:636)
39 | * at com.android.apksig.internal.apk.v1.V1SchemeSigner.generateSignatureBlock(V1SchemeSigner.java:511)
40 | *
41 | */
42 | public class FixSha1WithDsaProvider extends Provider {
43 | private static final Logger log = LoggerFactory.getLogger(FixSha1WithDsaProvider.class);
44 | public FixSha1WithDsaProvider() {
45 | super("fix-sha1-with-dsa-provider", 1.0, "null");
46 | // 使用BC的实现
47 | // put("Signature.SHA1WITHDSA", org.bouncycastle.jcajce.provider.asymmetric.dsa.DSASigner.stdDSA.class.getName());
48 |
49 | // 使用RawDSA实现
50 | put("Signature.SHA1WITHDSA", MySignatureSpi.class.getName());
51 | }
52 |
53 | public static class MySignatureSpi extends SignatureSpi {
54 |
55 | private static boolean logged = false;
56 | public MySignatureSpi() throws NoSuchAlgorithmException {
57 | this.sha1 = MessageDigest.getInstance("SHA-1");
58 | this.rawDSA = Signature.getInstance("RawDSA");
59 | if (!logged) {
60 | logged = true;
61 | log.info("Sha1WithDSA patch enabled");
62 | }
63 | }
64 |
65 | private final MessageDigest sha1;
66 | private final Signature rawDSA;
67 |
68 | @Override
69 | protected void engineInitVerify(PublicKey publicKey) throws InvalidKeyException {
70 | rawDSA.initVerify(publicKey);
71 | sha1.reset();
72 | }
73 |
74 | @Override
75 | protected void engineInitSign(PrivateKey privateKey) throws InvalidKeyException {
76 | rawDSA.initSign(privateKey);
77 | sha1.reset();
78 | }
79 |
80 | protected void engineUpdate(byte b) {
81 | sha1.update(b);
82 | }
83 |
84 | protected void engineUpdate(byte[] data, int off, int len) {
85 | sha1.update(data, off, len);
86 | }
87 |
88 | protected void engineUpdate(ByteBuffer b) {
89 | sha1.update(b);
90 | }
91 |
92 | @Override
93 | protected byte[] engineSign() throws SignatureException {
94 | byte[] data = sha1.digest();
95 | rawDSA.update(data);
96 | return rawDSA.sign();
97 | }
98 |
99 | @Override
100 | protected boolean engineVerify(byte[] sigBytes) throws SignatureException {
101 | byte[] data = sha1.digest();
102 | rawDSA.update(data);
103 | return rawDSA.verify(sigBytes);
104 | }
105 |
106 | @Override
107 | protected void engineSetParameter(String param, Object value) throws InvalidParameterException {
108 | throw new InvalidParameterException("No parameter accepted");
109 | }
110 |
111 | @Override
112 | protected Object engineGetParameter(String param) throws InvalidParameterException {
113 | return null;
114 | }
115 | }
116 | }
117 |
--------------------------------------------------------------------------------
/signer/src/main/java/dx/signer/CommandLine.java:
--------------------------------------------------------------------------------
1 | /**
2 | * dx-signer
3 | *
4 | * Copyright 2022 北京顶象技术有限公司
5 | *
6 | * Licensed under the Apache License, Version 2.0 (the "License");
7 | * you may not use this file except in compliance with the License.
8 | * You may obtain a copy of the License at
9 | *
10 | * http://www.apache.org/licenses/LICENSE-2.0
11 | *
12 | * Unless required by applicable law or agreed to in writing, software
13 | * distributed under the License is distributed on an "AS IS" BASIS,
14 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
15 | * See the License for the specific language governing permissions and
16 | * limitations under the License.
17 | */
18 | package dx.signer;
19 |
20 | import org.slf4j.Logger;
21 | import org.slf4j.LoggerFactory;
22 | import org.slf4j.impl.SimpleLogger;
23 |
24 | import java.io.BufferedReader;
25 | import java.io.IOException;
26 | import java.nio.charset.StandardCharsets;
27 | import java.nio.file.Files;
28 | import java.nio.file.Path;
29 | import java.nio.file.Paths;
30 | import java.util.Properties;
31 |
32 | public class CommandLine {
33 | public static void main(String... args) throws IOException {
34 | System.setProperty(SimpleLogger.SHOW_LOG_NAME_KEY, "false");
35 | System.setProperty(SimpleLogger.SHOW_THREAD_NAME_KEY, "false");
36 |
37 | Logger log = LoggerFactory.getLogger(CommandLine.class);
38 |
39 |
40 | Properties p = new Properties();
41 | try {
42 | if (args.length < 1 || !args[0].equals("sign")) {
43 | throw new RuntimeException("参数解析失败");
44 | }
45 | for (int i = 1; i < args.length; i += 2) {
46 | String key = args[i];
47 | String value = args[i + 1];
48 | if ("--config".equals(key)) {
49 | Properties p2;
50 | try {
51 | p2 = load(Paths.get(value));
52 | } catch (IOException e) {
53 | throw new RuntimeException("文件" + value + "解析失败", e);
54 | }
55 | p.putAll(p2);
56 | } else {
57 | if (!key.startsWith("--")) {
58 | throw new RuntimeException("参数解析失败");
59 | }
60 | if (key.equals("--in")) {
61 | p.setProperty("in-filename", "");
62 | }
63 | p.setProperty(key.substring(2), value);
64 | }
65 | }
66 | for (String k : new String[]{"in", "out", "ks"}) {
67 | String v = p.getProperty(k, "");
68 | if (v == null || v.length() == 0) {
69 | throw new RuntimeException("请指定参数" + k);
70 | }
71 | }
72 | } catch (Exception e) {
73 | e.printStackTrace();
74 | System.err.println("USAGE: java -jar dx-signer.apk sign [--option value]*");
75 | System.err.println(" option:");
76 | System.err.println(" --config 配置文件");
77 | System.err.println(" --in 输入文件apk、aab");
78 | System.err.println(" --out 输出文件、文件夹");
79 | System.err.println(" --ks Keystore位置");
80 | System.err.println(" --ks-pass Keystore密码");
81 | System.err.println(" --ks-key-alias");
82 | System.err.println(" --key-pass");
83 | System.err.println(" --channel-list 渠道清单");
84 | System.exit(3);
85 | }
86 | Path input = Paths.get(p.getProperty("in"));
87 | Path ks = Paths.get(p.getProperty("ks"));
88 | String ksPass = p.getProperty("ks-pass", "");
89 |
90 | String ksKeyAlias = p.getProperty("ks-key-alias", "");
91 | String keyPass = p.getProperty("key-pass", "");
92 | if (p.getProperty("channel-list", "").length() > 0) {
93 | Path out = detectOutDir(p.getProperty("out"));
94 |
95 | int result = SignWorker.signChannelApk(input, p.getProperty("in-filename", ""),
96 | out,
97 | Paths.get(p.getProperty("channel-list")),
98 | ks, ksPass, ksKeyAlias, keyPass);
99 |
100 | if (result != 0) {
101 | log.error("多渠道失败");
102 | System.exit(2);
103 | }
104 | } else {
105 | Path out = Paths.get(p.getProperty("out"));
106 | int result = SignWorker.signApk(input, out, ks,
107 | ksPass, ksKeyAlias, keyPass);
108 |
109 | if (result != 0) {
110 | log.error("签名失败");
111 | System.exit(2);
112 | }
113 | }
114 | }
115 |
116 | static Properties load(Path configFile) throws IOException {
117 | Properties p = new Properties();
118 | try (BufferedReader r = Files.newBufferedReader(configFile, StandardCharsets.UTF_8)) {
119 | p.load(r);
120 | }
121 | return p;
122 | }
123 |
124 | static Path detectOutDir(String out) {
125 | Path path = Paths.get(out);
126 | return (out.endsWith("/") || Files.isDirectory(path)) ? path : path.toAbsolutePath().getParent();
127 | }
128 | }
129 |
--------------------------------------------------------------------------------
/signer/src/main/java/dx/signer/FixApk.java:
--------------------------------------------------------------------------------
1 | /**
2 | * dx-signer
3 | *
4 | * Copyright 2022 北京顶象技术有限公司
5 | *
6 | * Licensed under the Apache License, Version 2.0 (the "License");
7 | * you may not use this file except in compliance with the License.
8 | * You may obtain a copy of the License at
9 | *
10 | * http://www.apache.org/licenses/LICENSE-2.0
11 | *
12 | * Unless required by applicable law or agreed to in writing, software
13 | * distributed under the License is distributed on an "AS IS" BASIS,
14 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
15 | * See the License for the specific language governing permissions and
16 | * limitations under the License.
17 | */
18 | package dx.signer;
19 |
20 | import dx.channel.ApkSigns;
21 |
22 | import java.io.File;
23 | import java.io.IOException;
24 |
25 | public class FixApk {
26 | public static void main(String... args) throws IOException {
27 | ApkSigns.zipAlign(new File(args[0]).toPath(), new File(args[1]).toPath(), false);
28 | }
29 | }
30 |
--------------------------------------------------------------------------------
/signer/src/main/java/dx/signer/JTextAreaOutputStream.java:
--------------------------------------------------------------------------------
1 | /**
2 | * dx-signer
3 | *
4 | * Copyright 2022 北京顶象技术有限公司
5 | *
6 | * Licensed under the Apache License, Version 2.0 (the "License");
7 | * you may not use this file except in compliance with the License.
8 | * You may obtain a copy of the License at
9 | *
10 | * http://www.apache.org/licenses/LICENSE-2.0
11 | *
12 | * Unless required by applicable law or agreed to in writing, software
13 | * distributed under the License is distributed on an "AS IS" BASIS,
14 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
15 | * See the License for the specific language governing permissions and
16 | * limitations under the License.
17 | */
18 | package dx.signer;
19 |
20 | import javax.swing.*;
21 | import java.io.IOException;
22 | import java.io.OutputStream;
23 | import java.io.PrintStream;
24 | import java.lang.ref.WeakReference;
25 |
26 | public class JTextAreaOutputStream extends OutputStream {
27 | static private WeakReference mDestination = new WeakReference<>(null);
28 | PrintStream orgOut;
29 |
30 | static JTextAreaOutputStream hjOut = new JTextAreaOutputStream();
31 | static JTextAreaOutputStream hjErr = new JTextAreaOutputStream();
32 |
33 | private JTextAreaOutputStream() {
34 | }
35 |
36 | public synchronized static void hijack(JTextArea destination) {
37 | if (hjOut.orgOut == null) {
38 | hjOut.orgOut = System.out;
39 | }
40 | if (hjErr.orgOut == null) {
41 | hjErr.orgOut = System.err;
42 | }
43 | mDestination = new WeakReference<>(destination);
44 |
45 | System.setOut(new PrintStream(hjOut, true));
46 | System.setErr(new PrintStream(hjErr, true));
47 | }
48 |
49 | @Override
50 | public void write(byte[] buffer, int offset, int length) throws IOException {
51 | final String text = new String(buffer, offset, length);
52 | orgOut.write(buffer, offset, length);
53 | JTextArea t = mDestination.get();
54 | if (t != null) {
55 | SwingUtilities.invokeLater(() -> t.append(text));
56 | }
57 | }
58 |
59 | @Override
60 | public void write(int b) throws IOException {
61 | write(new byte[]{(byte) b}, 0, 1);
62 | }
63 | }
64 |
65 |
--------------------------------------------------------------------------------
/signer/src/main/java/dx/signer/SignWorker.java:
--------------------------------------------------------------------------------
1 | /**
2 | * dx-signer
3 | *
4 | * Copyright 2022 北京顶象技术有限公司
5 | *
6 | * Licensed under the Apache License, Version 2.0 (the "License");
7 | * you may not use this file except in compliance with the License.
8 | * You may obtain a copy of the License at
9 | *
10 | * http://www.apache.org/licenses/LICENSE-2.0
11 | *
12 | * Unless required by applicable law or agreed to in writing, software
13 | * distributed under the License is distributed on an "AS IS" BASIS,
14 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
15 | * See the License for the specific language governing permissions and
16 | * limitations under the License.
17 | */
18 | package dx.signer;
19 |
20 |
21 | import dx.channel.ApkSigns;
22 | import dx.channel.ChannelBuilder;
23 | import org.slf4j.Logger;
24 | import org.slf4j.LoggerFactory;
25 |
26 | import java.io.IOException;
27 | import java.nio.file.Files;
28 | import java.nio.file.Path;
29 | import java.security.KeyStore;
30 | import java.util.List;
31 | import java.util.zip.DataFormatException;
32 |
33 | public class SignWorker {
34 | private static final Logger log = LoggerFactory.getLogger(SignWorker.class);
35 |
36 | private static void sign(Path inApk, Path ksPath, String ksPass, String keyAlias, String keyPass, Path outApk) throws Throwable {
37 | KeyStore.PrivateKeyEntry key = ApkSigns.loadKey(ksPath, ksPass, keyAlias, keyPass);
38 | ApkSigns.sign(inApk, outApk, key, inApk.getFileName().toString().endsWith("aab"));
39 | }
40 |
41 | public static int signApk(Path apkUnsigned, Path apkOut, Path ksPath, String ksPass, String keyAlias, String keyPass) {
42 | Path tmp = null;
43 | String suffix = apkUnsigned.getFileName().toString().endsWith("aab") ? "aab" : "apk";
44 | try {
45 | Path p = apkOut.toAbsolutePath().getParent();
46 | if (!Files.exists(p)) {
47 | Files.createDirectories(p);
48 | }
49 | tmp = Files.createTempFile(p, "tmpsigner", "." + suffix);
50 | } catch (IOException e) {
51 | e.printStackTrace(System.err);
52 | return 2;
53 | }
54 |
55 | try {
56 | log.info("{}", "> 签名中, 请稍等 ...");
57 |
58 | log.info("{}", ">> 清理原有签名, 对齐 ...");
59 | ApkSigns.zipAlign(apkUnsigned, tmp, false);
60 | log.info("{}", "<< 完成");
61 |
62 | log.info("{}", ">> 签名 ...");
63 | sign(tmp, ksPath, ksPass, keyAlias, keyPass, apkOut);
64 | log.info("{}", "<< 完成");
65 |
66 | log.info("{}", "< 签名结束, 结果: 完成");
67 | log.info(" 输出APK: {}", apkOut);
68 | } catch (Throwable e) {
69 | log.info("签名结束, 结果: 失败", e);
70 | return -1;
71 | } finally {
72 | if (tmp != null) {
73 | try {
74 | Files.deleteIfExists(tmp);
75 | } catch (IOException ignore) {
76 | }
77 | }
78 | }
79 | return 0;
80 | }
81 |
82 | public static int signChannelApk(Path input, String inputFileName, Path outDir,
83 | Path channelListFile,
84 | Path ksPath,
85 | String ksPass,
86 | String keyAlias,
87 | String keyPass) throws IOException {
88 |
89 | if (inputFileName == null || inputFileName.trim().length() == 0) {
90 | inputFileName = input.getFileName().toString();
91 | }
92 | if (inputFileName.endsWith(".aab")) {
93 | throw new RuntimeException("only .apk supported");
94 | }
95 |
96 | List channelList = ChannelBuilder.readChannelList(channelListFile);
97 | log.info("读取到{}个渠道", channelList.size());
98 |
99 | KeyStore.PrivateKeyEntry key = ApkSigns.loadKey(ksPath, ksPass, keyAlias, keyPass);
100 | try (ChannelBuilder cb = new ChannelBuilder(input, key)) {
101 | log.info("已加载模板: {}", input);
102 | int dot = inputFileName.lastIndexOf('.');
103 | String apkName = dot > 0 ? inputFileName.substring(0, dot) : inputFileName;
104 | if (apkName.startsWith("dx_unsigned_")) {
105 | apkName = apkName.substring("dx_unsigned_".length());
106 | }
107 | for (String channel : channelList) {
108 | String safeName = String.format("SIGNED_%s-%s.apk", apkName, channel)
109 | .replace('/', '_')
110 | .replace('\\', '_')
111 | .replace(' ', '_');
112 | Path outPath = outDir.resolve(safeName);
113 | log.info("正在输出渠道: {}", channel);
114 | try {
115 | cb.build(channel, outPath);
116 | log.info("已经生成: {}", outPath);
117 | }catch (Throwable e) {
118 | log.error("多渠道失败", e);
119 | return 1;
120 | }
121 | }
122 | log.info("多渠道完成: {}", outDir);
123 | }
124 |
125 | return 0;
126 | }
127 |
128 | }
129 |
--------------------------------------------------------------------------------
/signer/src/main/java/dx/signer/UX.form:
--------------------------------------------------------------------------------
1 |
2 |
280 |
--------------------------------------------------------------------------------
/signer/src/main/java/dx/signer/UX.java:
--------------------------------------------------------------------------------
1 | /**
2 | * dx-signer
3 | *
4 | * Copyright 2022 北京顶象技术有限公司
5 | *
6 | * Licensed under the Apache License, Version 2.0 (the "License");
7 | * you may not use this file except in compliance with the License.
8 | * You may obtain a copy of the License at
9 | *
10 | * http://www.apache.org/licenses/LICENSE-2.0
11 | *
12 | * Unless required by applicable law or agreed to in writing, software
13 | * distributed under the License is distributed on an "AS IS" BASIS,
14 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
15 | * See the License for the specific language governing permissions and
16 | * limitations under the License.
17 | */
18 | package dx.signer;
19 |
20 | import com.intellij.uiDesigner.core.GridConstraints;
21 | import com.intellij.uiDesigner.core.GridLayoutManager;
22 | import dx.channel.ApkSigns;
23 | import org.slf4j.impl.SimpleLogger;
24 |
25 | import javax.swing.*;
26 | import javax.swing.event.PopupMenuEvent;
27 | import javax.swing.event.PopupMenuListener;
28 | import javax.swing.filechooser.FileFilter;
29 | import javax.swing.text.DefaultCaret;
30 | import java.awt.*;
31 | import java.io.*;
32 | import java.nio.charset.StandardCharsets;
33 | import java.nio.file.Files;
34 | import java.nio.file.Path;
35 | import java.nio.file.Paths;
36 | import java.security.KeyStore;
37 | import java.util.*;
38 | import java.util.concurrent.ExecutorService;
39 | import java.util.concurrent.Executors;
40 |
41 | public class UX {
42 | ExecutorService es = Executors.newSingleThreadExecutor();
43 | private JButton inBtn;
44 | private JTextField inPathTF;
45 | private JTabbedPane tabbedPane1;
46 | private JTextField ksPathTF;
47 | private JButton ksBtn;
48 | private JTextField outPathTF;
49 | private JButton signBtn;
50 | private JTextArea loggingTA;
51 | private JCheckBox 保存密码CheckBox;
52 | private JComboBox keyAliasCB;
53 | private JPasswordField keyPassPF;
54 | private JPasswordField ksPassPF;
55 | public JPanel top;
56 | private JProgressBar progressBar1;
57 | private JTextField channelPathTF;
58 | private JButton channelBtn;
59 | private JCheckBox v1SigningEnabledCheckBox;
60 | private JCheckBox v2SigningEnabledCheckBox;
61 |
62 | private boolean readOnly = false;
63 | private String inputFileName = "";
64 |
65 | public static void main(String[] args) throws IOException {
66 |
67 | System.setProperty(SimpleLogger.SHOW_LOG_NAME_KEY, "false");
68 | System.setProperty(SimpleLogger.SHOW_THREAD_NAME_KEY, "false");
69 |
70 | if (args.length >= 1 && args[0].equals("sign")) {
71 | CommandLine.main(args);
72 | return;
73 | }
74 |
75 | JFrame frame = new JFrame("Apk签名&多渠道工具");
76 | frame.setContentPane(new UX().top);
77 | frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
78 | frame.pack();
79 |
80 | // make the frame half the height and width
81 | Dimension screenSize = Toolkit.getDefaultToolkit().getScreenSize();
82 | int width1 = screenSize.width / 2;
83 | int height1 = screenSize.height / 2;
84 |
85 |
86 | width1 = 900;
87 | if (height1 < 600) {
88 | height1 = 600;
89 | }
90 |
91 | frame.setSize(width1, height1);
92 |
93 | // here's the part where i center the jframe on screen
94 | frame.setLocationRelativeTo(null);
95 |
96 | frame.setVisible(true);
97 | }
98 |
99 |
100 | public UX() {
101 | JFileChooser fileChooser = new JFileChooser();
102 | fileChooser.setCurrentDirectory(new File("."));
103 | fileChooser.setFileSelectionMode(JFileChooser.FILES_ONLY);
104 | fileChooser.setMultiSelectionEnabled(false);
105 |
106 | inBtn.addActionListener(e -> {
107 | fileChooser.setFileFilter(new FileFilter() {
108 | @Override
109 | public boolean accept(File f) {
110 | String s = f.getName().toLowerCase();
111 | return f.isDirectory() || s.endsWith(".apk") || s.endsWith(".aab");
112 | }
113 |
114 | @Override
115 | public String getDescription() {
116 | return "*.apk,*.aab";
117 | }
118 | });
119 | int result = fileChooser.showOpenDialog(inBtn);
120 | if (result == JFileChooser.APPROVE_OPTION) {
121 | File file = fileChooser.getSelectedFile();
122 | setInput(file);
123 |
124 | }
125 | });
126 | ksBtn.addActionListener(e -> {
127 | fileChooser.setFileFilter(new FileFilter() {
128 | @Override
129 | public boolean accept(File f) {
130 | String s = f.getName().toLowerCase();
131 | return f.isDirectory() || s.endsWith(".ks") || s.endsWith(".keystore") || s.endsWith(".p12") || s.endsWith(".pfx") || s.endsWith(".jks");
132 | }
133 |
134 | @Override
135 | public String getDescription() {
136 | return "*.ks, *.keystore, *.p12, *.pfx, *.jks";
137 | }
138 | });
139 | int result = fileChooser.showOpenDialog(ksBtn);
140 | if (result == JFileChooser.APPROVE_OPTION) {
141 | File file = fileChooser.getSelectedFile();
142 | ksPathTF.setText(file.getAbsolutePath());
143 | }
144 | });
145 |
146 | channelBtn.addActionListener(e -> {
147 | fileChooser.setFileFilter(new FileFilter() {
148 | @Override
149 | public boolean accept(File f) {
150 | String s = f.getName().toLowerCase();
151 | return f.isDirectory() || (f.isFile() && s.endsWith(".txt"));
152 | }
153 |
154 | @Override
155 | public String getDescription() {
156 | return "*.txt";
157 | }
158 | });
159 | int result = fileChooser.showOpenDialog(channelBtn);
160 | if (result == JFileChooser.APPROVE_OPTION) {
161 | File file = fileChooser.getSelectedFile();
162 | channelPathTF.setText(file.getAbsolutePath());
163 | }
164 | });
165 |
166 | signBtn.addActionListener(e -> {
167 | String channelPath = channelPathTF.getText();
168 |
169 | String out = outPathTF.getText();
170 |
171 | if (channelPath != null && channelPath.length() > 0) {
172 | Path apkDir = CommandLine.detectOutDir(out);
173 | if (Files.exists(apkDir)) {
174 | if (JOptionPane.YES_OPTION != JOptionPane.showConfirmDialog(UX.this.top, "多渠道输出APK目录已经存在,是否覆盖:\n" + apkDir, "输出APK已经存在,是否覆盖", JOptionPane.OK_CANCEL_OPTION)) {
175 | return;
176 | }
177 | }
178 | } else {
179 | if (JOptionPane.YES_OPTION != JOptionPane.showConfirmDialog(UX.this.top, "输出APK已经存在,是否覆盖:\n" + out, "输出APK已经存在,是否覆盖", JOptionPane.OK_CANCEL_OPTION)) {
180 | return;
181 | }
182 | }
183 |
184 | signBtn.setEnabled(false);
185 |
186 | String in = inPathTF.getText();
187 |
188 | String ksPass;
189 | String keyPass;
190 | try {
191 | ksPass = new String(ksPassPF.getPassword());
192 | } catch (NullPointerException ignore) {
193 | ksPass = "";
194 | }
195 | try {
196 | keyPass = new String(UX.this.keyPassPF.getPassword());
197 | } catch (NullPointerException ignore) {
198 | keyPass = null;
199 | }
200 | String keyAlias = (String) UX.this.keyAliasCB.getSelectedItem();
201 | Properties mConfig = new Properties();
202 | String ksPath0 = ksPathTF.getText();
203 | mConfig.put("ks", ksPath0);
204 | mConfig.put("in", in);
205 | mConfig.put("ks-key-alias", keyAlias);
206 | mConfig.put("in-filename", this.inputFileName);
207 | mConfig.put("out", this.outPathTF.getText());
208 | mConfig.put("channel-list", this.channelPathTF.getText());
209 |
210 | if (保存密码CheckBox.isSelected()) {
211 | mConfig.put("ks-pass", ksPass);
212 | mConfig.put("key-pass", keyPass);
213 | }
214 |
215 | if (!readOnly) {
216 | try {
217 | Path configFile = getConfigPath();
218 | try (BufferedWriter r = Files.newBufferedWriter(configFile, StandardCharsets.UTF_8)) {
219 | mConfig.store(r, "#");
220 | }
221 | } catch (IOException ignore) {
222 | }
223 | }
224 |
225 | loggingTA.setText("");
226 |
227 | String finalKsPass = ksPass;
228 | String finalKeyPass = keyPass;
229 | String pbOrg = progressBar1.getString();
230 | progressBar1.setString("签名中...");
231 | progressBar1.setStringPainted(true);
232 | progressBar1.setIndeterminate(true);
233 |
234 | Path ksPath = Paths.get(ksPath0);
235 | Path input = Paths.get(in);
236 |
237 | es.submit(() -> {
238 | try {
239 | int result;
240 |
241 | if (channelPath != null && channelPath.length() > 0) {
242 | Path apkDir = CommandLine.detectOutDir(out);
243 | result = SignWorker.signChannelApk(input, inputFileName,
244 | apkDir,
245 | Paths.get(channelPath),
246 | ksPath, finalKsPass, keyAlias, finalKeyPass);
247 | progressBar1.setIndeterminate(false);
248 | progressBar1.setString(pbOrg);
249 | if (result == 0) {
250 | JOptionPane.showMessageDialog(UX.this.top, "多渠道成功, 输出APK文件夹\n" + apkDir);
251 | } else {
252 | JOptionPane.showMessageDialog(UX.this.top, "多渠道失败");
253 | }
254 | } else {
255 | result = SignWorker.signApk(input, Paths.get(out), ksPath,
256 | finalKsPass, keyAlias, finalKeyPass);
257 | progressBar1.setIndeterminate(false);
258 | progressBar1.setString(pbOrg);
259 | if (result == 0) {
260 | JOptionPane.showMessageDialog(UX.this.top, "签名成功, 输出APK\n" + out);
261 | } else {
262 | JOptionPane.showMessageDialog(UX.this.top, "签名失败");
263 | }
264 | }
265 |
266 | } catch (Exception ex) {
267 | ex.printStackTrace();
268 | }
269 |
270 | signBtn.setEnabled(true);
271 | });
272 |
273 | });
274 |
275 | keyAliasCB.removeAllItems();
276 | keyAliasCB.addItem("{{auto}}");
277 | keyAliasCB.setSelectedItem("{{auto}}");
278 | keyAliasCB.addPopupMenuListener(new PopupMenuListener() {
279 | @Override
280 | public void popupMenuWillBecomeVisible(PopupMenuEvent e) {
281 | KeyStore keyStore = null;
282 | try {
283 | byte[] d = Files.readAllBytes(Paths.get(ksPathTF.getText()));
284 | Set keyList = new HashSet<>();
285 | keyList.add(new String(ksPassPF.getPassword()));
286 | keyStore = ApkSigns.loadKeyStore(d, keyList);
287 | } catch (Exception ignore) {
288 | keyStore = null;
289 | }
290 |
291 | if (keyStore != null) {
292 | keyAliasCB.removeAllItems();
293 | keyAliasCB.addItem("{{auto}}");
294 | try {
295 | Enumeration aliases = keyStore.aliases();
296 | while (aliases.hasMoreElements()) {
297 | String alias = aliases.nextElement();
298 | keyAliasCB.addItem(alias);
299 | }
300 | } catch (Exception ignore) {
301 | }
302 | keyAliasCB.setSelectedItem("{{auto}}");
303 | }
304 | }
305 |
306 | @Override
307 | public void popupMenuWillBecomeInvisible(PopupMenuEvent e) {
308 |
309 | }
310 |
311 | @Override
312 | public void popupMenuCanceled(PopupMenuEvent e) {
313 |
314 | }
315 | });
316 |
317 |
318 | DefaultCaret caret = (DefaultCaret) loggingTA.getCaret();
319 | caret.setUpdatePolicy(DefaultCaret.ALWAYS_UPDATE);
320 |
321 | JTextAreaOutputStream.hijack(loggingTA);
322 |
323 | try {
324 | Path configFile = getConfigPath();
325 | Properties initConfig = CommandLine.load(configFile);
326 |
327 | readOnly = "true".equals(initConfig.getProperty("config-read-only", ""));
328 | ksPathTF.setText(initConfig.getProperty("ks", ""));
329 |
330 | String inPath = initConfig.getProperty("in", "");
331 | if (inPath.length() > 0) {
332 | setInput(new File(inPath), initConfig.getProperty("in-filename", ""));
333 | }
334 | String outPath = initConfig.getProperty("out", "");
335 | if (outPath.length() > 0) {
336 | outPathTF.setText(outPath);
337 | }
338 | ksPassPF.setText(initConfig.getProperty("ks-pass", ""));
339 | keyPassPF.setText(initConfig.getProperty("key-pass", ""));
340 | channelPathTF.setText(initConfig.getProperty("channel-list", ""));
341 |
342 | String s = initConfig.getProperty("ks-key-alias", "{{auto}}");
343 | if (!s.equals("{{auto}}") && s.length() != 0) {
344 | keyAliasCB.addItem(s);
345 | keyAliasCB.setSelectedItem(s);
346 | }
347 |
348 | } catch (IOException ignore) {
349 | }
350 |
351 | if (readOnly) {
352 | 保存密码CheckBox.setEnabled(false);
353 | 保存密码CheckBox.setSelected(false);
354 | channelBtn.setEnabled(false);
355 | channelPathTF.setEnabled(false);
356 |
357 | inBtn.setEnabled(false);
358 | inPathTF.setEnabled(false);
359 |
360 | if (ksPathTF.getText().length() > 0) {
361 | ksBtn.setEnabled(false);
362 | ksPathTF.setEnabled(false);
363 | keyAliasCB.setEnabled(false);
364 | ksPassPF.setEnabled(false);
365 | keyPassPF.setEnabled(false);
366 | }
367 | outPathTF.setEnabled(false);
368 | }
369 | }
370 |
371 | private void setInput(File file) {
372 | setInput(file, null);
373 | }
374 |
375 | private void setInput(File file, String name) {
376 | if (name == null || name.length() == 0) {
377 | name = file.getName();
378 | }
379 | this.inputFileName = name;
380 | inPathTF.setText(file.getAbsolutePath());
381 |
382 | String fileName = inputFileName;
383 | if (fileName.startsWith("dx_unsigned")) {
384 | fileName = "SIGNED" + fileName.substring("dx_unsigned".length());
385 | } else {
386 | fileName = "SIGNED-" + fileName;
387 | }
388 | File out = new File(file.getParent(), fileName);
389 | outPathTF.setText(out.toString());
390 | }
391 |
392 | private static Path getConfigPath() {
393 | Path HOME = Paths.get(".");
394 | Path configDir = HOME.resolve("etc");
395 | if (!Files.exists(configDir)) {
396 | try {
397 | Files.createDirectories(configDir);
398 | } catch (IOException ignore) {
399 |
400 | }
401 | }
402 |
403 | return configDir.resolve("cfg.properties");
404 | }
405 |
406 | {
407 | // GUI initializer generated by IntelliJ IDEA GUI Designer
408 | // >>> IMPORTANT!! <<<
409 | // DO NOT EDIT OR ADD ANY CODE HERE!
410 | $$$setupUI$$$();
411 | }
412 |
413 | /**
414 | * Method generated by IntelliJ IDEA GUI Designer
415 | * >>> IMPORTANT!! <<<
416 | * DO NOT edit this method OR call it in your code!
417 | *
418 | * @noinspection ALL
419 | */
420 | private void $$$setupUI$$$() {
421 | top = new JPanel();
422 | top.setLayout(new GridLayoutManager(3, 1, new Insets(5, 5, 5, 5), -1, -1));
423 | tabbedPane1 = new JTabbedPane();
424 | top.add(tabbedPane1, new GridConstraints(0, 0, 1, 1, GridConstraints.ANCHOR_CENTER, GridConstraints.FILL_BOTH, GridConstraints.SIZEPOLICY_CAN_SHRINK | GridConstraints.SIZEPOLICY_CAN_GROW, GridConstraints.SIZEPOLICY_CAN_SHRINK | GridConstraints.SIZEPOLICY_CAN_GROW, null, new Dimension(200, 200), null, 0, false));
425 | final JPanel panel1 = new JPanel();
426 | panel1.setLayout(new GridLayoutManager(6, 3, new Insets(0, 0, 0, 0), -1, -1));
427 | tabbedPane1.addTab("Apk签名 & 多渠道", panel1);
428 | inPathTF = new JTextField();
429 | inPathTF.setEditable(false);
430 | inPathTF.setText("");
431 | panel1.add(inPathTF, new GridConstraints(0, 1, 1, 1, GridConstraints.ANCHOR_WEST, GridConstraints.FILL_HORIZONTAL, GridConstraints.SIZEPOLICY_WANT_GROW, GridConstraints.SIZEPOLICY_FIXED, null, new Dimension(150, -1), null, 0, false));
432 | ksPathTF = new JTextField();
433 | panel1.add(ksPathTF, new GridConstraints(1, 1, 1, 1, GridConstraints.ANCHOR_WEST, GridConstraints.FILL_HORIZONTAL, GridConstraints.SIZEPOLICY_WANT_GROW, GridConstraints.SIZEPOLICY_FIXED, null, new Dimension(150, -1), null, 0, false));
434 | final JLabel label1 = new JLabel();
435 | label1.setText("输入apk/aab");
436 | panel1.add(label1, new GridConstraints(0, 0, 1, 1, GridConstraints.ANCHOR_EAST, GridConstraints.FILL_NONE, GridConstraints.SIZEPOLICY_FIXED, GridConstraints.SIZEPOLICY_FIXED, null, null, null, 0, false));
437 | inBtn = new JButton();
438 | inBtn.setText("1.选择输入APK");
439 | panel1.add(inBtn, new GridConstraints(0, 2, 1, 1, GridConstraints.ANCHOR_CENTER, GridConstraints.FILL_NONE, GridConstraints.SIZEPOLICY_CAN_SHRINK | GridConstraints.SIZEPOLICY_CAN_GROW, GridConstraints.SIZEPOLICY_FIXED, null, null, null, 0, false));
440 | final JLabel label2 = new JLabel();
441 | label2.setText("KeyStore");
442 | panel1.add(label2, new GridConstraints(1, 0, 1, 1, GridConstraints.ANCHOR_EAST, GridConstraints.FILL_NONE, GridConstraints.SIZEPOLICY_FIXED, GridConstraints.SIZEPOLICY_FIXED, null, null, null, 0, false));
443 | ksBtn = new JButton();
444 | ksBtn.setText("2.选择KeyStore");
445 | panel1.add(ksBtn, new GridConstraints(1, 2, 1, 1, GridConstraints.ANCHOR_CENTER, GridConstraints.FILL_NONE, GridConstraints.SIZEPOLICY_CAN_SHRINK | GridConstraints.SIZEPOLICY_CAN_GROW, GridConstraints.SIZEPOLICY_FIXED, null, null, null, 0, false));
446 | final JLabel label3 = new JLabel();
447 | label3.setText("KeyStore密码");
448 | panel1.add(label3, new GridConstraints(2, 0, 1, 1, GridConstraints.ANCHOR_EAST, GridConstraints.FILL_NONE, GridConstraints.SIZEPOLICY_FIXED, GridConstraints.SIZEPOLICY_FIXED, null, null, null, 0, false));
449 | final JLabel label4 = new JLabel();
450 | label4.setText("3.输入KeyStore密码");
451 | panel1.add(label4, new GridConstraints(2, 2, 1, 1, GridConstraints.ANCHOR_CENTER, GridConstraints.FILL_NONE, GridConstraints.SIZEPOLICY_FIXED, GridConstraints.SIZEPOLICY_FIXED, null, null, null, 0, false));
452 | outPathTF = new JTextField();
453 | panel1.add(outPathTF, new GridConstraints(4, 1, 1, 1, GridConstraints.ANCHOR_WEST, GridConstraints.FILL_HORIZONTAL, GridConstraints.SIZEPOLICY_WANT_GROW, GridConstraints.SIZEPOLICY_FIXED, null, new Dimension(150, -1), null, 0, false));
454 | final JLabel label5 = new JLabel();
455 | label5.setText("输出apk/aab");
456 | panel1.add(label5, new GridConstraints(4, 0, 1, 1, GridConstraints.ANCHOR_EAST, GridConstraints.FILL_NONE, GridConstraints.SIZEPOLICY_FIXED, GridConstraints.SIZEPOLICY_FIXED, null, null, null, 0, false));
457 | 保存密码CheckBox = new JCheckBox();
458 | 保存密码CheckBox.setSelected(true);
459 | 保存密码CheckBox.setText("保存密码");
460 | panel1.add(保存密码CheckBox, new GridConstraints(3, 1, 1, 1, GridConstraints.ANCHOR_WEST, GridConstraints.FILL_NONE, GridConstraints.SIZEPOLICY_CAN_SHRINK | GridConstraints.SIZEPOLICY_CAN_GROW, GridConstraints.SIZEPOLICY_FIXED, null, null, null, 0, false));
461 | ksPassPF = new JPasswordField();
462 | panel1.add(ksPassPF, new GridConstraints(2, 1, 1, 1, GridConstraints.ANCHOR_WEST, GridConstraints.FILL_HORIZONTAL, GridConstraints.SIZEPOLICY_WANT_GROW, GridConstraints.SIZEPOLICY_FIXED, null, new Dimension(150, -1), null, 0, false));
463 | channelPathTF = new JTextField();
464 | panel1.add(channelPathTF, new GridConstraints(5, 1, 1, 1, GridConstraints.ANCHOR_WEST, GridConstraints.FILL_HORIZONTAL, GridConstraints.SIZEPOLICY_WANT_GROW, GridConstraints.SIZEPOLICY_FIXED, null, new Dimension(150, -1), null, 0, false));
465 | final JLabel label6 = new JLabel();
466 | label6.setText("渠道清单[可选]");
467 | panel1.add(label6, new GridConstraints(5, 0, 1, 1, GridConstraints.ANCHOR_WEST, GridConstraints.FILL_NONE, GridConstraints.SIZEPOLICY_FIXED, GridConstraints.SIZEPOLICY_FIXED, null, null, null, 0, false));
468 | channelBtn = new JButton();
469 | channelBtn.setText("选择渠道清单");
470 | panel1.add(channelBtn, new GridConstraints(5, 2, 1, 1, GridConstraints.ANCHOR_CENTER, GridConstraints.FILL_HORIZONTAL, GridConstraints.SIZEPOLICY_CAN_SHRINK | GridConstraints.SIZEPOLICY_CAN_GROW, GridConstraints.SIZEPOLICY_FIXED, null, null, null, 0, false));
471 | final JPanel panel2 = new JPanel();
472 | panel2.setLayout(new GridLayoutManager(4, 4, new Insets(0, 0, 0, 0), -1, -1));
473 | tabbedPane1.addTab("高级", panel2);
474 | keyAliasCB = new JComboBox();
475 | panel2.add(keyAliasCB, new GridConstraints(1, 1, 1, 3, GridConstraints.ANCHOR_WEST, GridConstraints.FILL_HORIZONTAL, GridConstraints.SIZEPOLICY_CAN_GROW, GridConstraints.SIZEPOLICY_FIXED, null, null, null, 0, false));
476 | final JLabel label7 = new JLabel();
477 | label7.setText("KeyAlias");
478 | panel2.add(label7, new GridConstraints(1, 0, 1, 1, GridConstraints.ANCHOR_WEST, GridConstraints.FILL_NONE, GridConstraints.SIZEPOLICY_FIXED, GridConstraints.SIZEPOLICY_FIXED, null, null, null, 0, false));
479 | keyPassPF = new JPasswordField();
480 | panel2.add(keyPassPF, new GridConstraints(2, 1, 1, 3, GridConstraints.ANCHOR_WEST, GridConstraints.FILL_HORIZONTAL, GridConstraints.SIZEPOLICY_WANT_GROW, GridConstraints.SIZEPOLICY_FIXED, null, new Dimension(150, -1), null, 0, false));
481 | final JLabel label8 = new JLabel();
482 | label8.setText("证书密码");
483 | panel2.add(label8, new GridConstraints(2, 0, 1, 1, GridConstraints.ANCHOR_WEST, GridConstraints.FILL_NONE, GridConstraints.SIZEPOLICY_FIXED, GridConstraints.SIZEPOLICY_FIXED, null, null, null, 0, false));
484 | final JLabel label9 = new JLabel();
485 | label9.setText("如果您的Keystore包含多个证书,或者您的证书密码与Keystore密码不同, 请设置下列参数");
486 | panel2.add(label9, new GridConstraints(0, 1, 1, 2, GridConstraints.ANCHOR_WEST, GridConstraints.FILL_NONE, GridConstraints.SIZEPOLICY_FIXED, GridConstraints.SIZEPOLICY_FIXED, null, null, null, 0, false));
487 | v2SigningEnabledCheckBox = new JCheckBox();
488 | v2SigningEnabledCheckBox.setEnabled(false);
489 | v2SigningEnabledCheckBox.setSelected(true);
490 | v2SigningEnabledCheckBox.setText("--v2-signing-enabled");
491 | panel2.add(v2SigningEnabledCheckBox, new GridConstraints(3, 2, 1, 1, GridConstraints.ANCHOR_WEST, GridConstraints.FILL_NONE, GridConstraints.SIZEPOLICY_CAN_SHRINK | GridConstraints.SIZEPOLICY_CAN_GROW, GridConstraints.SIZEPOLICY_FIXED, null, null, null, 0, false));
492 | v1SigningEnabledCheckBox = new JCheckBox();
493 | v1SigningEnabledCheckBox.setEnabled(false);
494 | v1SigningEnabledCheckBox.setSelected(true);
495 | v1SigningEnabledCheckBox.setText("--v1-signing-enabled");
496 | panel2.add(v1SigningEnabledCheckBox, new GridConstraints(3, 1, 1, 1, GridConstraints.ANCHOR_WEST, GridConstraints.FILL_NONE, GridConstraints.SIZEPOLICY_CAN_SHRINK | GridConstraints.SIZEPOLICY_CAN_GROW, GridConstraints.SIZEPOLICY_FIXED, null, null, null, 0, false));
497 | final JPanel panel3 = new JPanel();
498 | panel3.setLayout(new GridLayoutManager(1, 2, new Insets(0, 0, 0, 0), -1, -1));
499 | top.add(panel3, new GridConstraints(1, 0, 1, 1, GridConstraints.ANCHOR_CENTER, GridConstraints.FILL_BOTH, GridConstraints.SIZEPOLICY_CAN_SHRINK | GridConstraints.SIZEPOLICY_CAN_GROW, GridConstraints.SIZEPOLICY_CAN_SHRINK | GridConstraints.SIZEPOLICY_CAN_GROW, null, null, null, 0, false));
500 | signBtn = new JButton();
501 | signBtn.setText(" 4.签名 ");
502 | panel3.add(signBtn, new GridConstraints(0, 1, 1, 1, GridConstraints.ANCHOR_CENTER, GridConstraints.FILL_NONE, GridConstraints.SIZEPOLICY_CAN_SHRINK | GridConstraints.SIZEPOLICY_CAN_GROW, GridConstraints.SIZEPOLICY_FIXED, null, null, null, 0, false));
503 | progressBar1 = new JProgressBar();
504 | progressBar1.setString("点击\"4.签名\"按钮开始 >>>>");
505 | progressBar1.setStringPainted(true);
506 | panel3.add(progressBar1, new GridConstraints(0, 0, 1, 1, GridConstraints.ANCHOR_CENTER, GridConstraints.FILL_HORIZONTAL, GridConstraints.SIZEPOLICY_WANT_GROW, GridConstraints.SIZEPOLICY_FIXED, null, null, null, 0, false));
507 | final JScrollPane scrollPane1 = new JScrollPane();
508 | top.add(scrollPane1, new GridConstraints(2, 0, 1, 1, GridConstraints.ANCHOR_CENTER, GridConstraints.FILL_BOTH, GridConstraints.SIZEPOLICY_CAN_SHRINK | GridConstraints.SIZEPOLICY_WANT_GROW, GridConstraints.SIZEPOLICY_CAN_SHRINK | GridConstraints.SIZEPOLICY_WANT_GROW, null, null, null, 0, false));
509 | loggingTA = new JTextArea();
510 | loggingTA.setDoubleBuffered(true);
511 | loggingTA.setEditable(true);
512 | loggingTA.setInheritsPopupMenu(true);
513 | loggingTA.setLineWrap(true);
514 | loggingTA.setText(" 点击“4.签名”按钮开始签名...");
515 | scrollPane1.setViewportView(loggingTA);
516 | label1.setLabelFor(inPathTF);
517 | label2.setLabelFor(ksPathTF);
518 | label3.setLabelFor(ksPassPF);
519 | label4.setLabelFor(ksPassPF);
520 | label5.setLabelFor(outPathTF);
521 | label7.setLabelFor(keyAliasCB);
522 | label8.setLabelFor(keyPassPF);
523 | }
524 |
525 | /**
526 | * @noinspection ALL
527 | */
528 | public JComponent $$$getRootComponent$$$() {
529 | return top;
530 | }
531 |
532 | }
533 |
--------------------------------------------------------------------------------
/signer/src/main/java/fake/security/Signature.java:
--------------------------------------------------------------------------------
1 | /**
2 | * dx-signer
3 | *
4 | * Copyright 2022 北京顶象技术有限公司
5 | *
6 | * Licensed under the Apache License, Version 2.0 (the "License");
7 | * you may not use this file except in compliance with the License.
8 | * You may obtain a copy of the License at
9 | *
10 | * http://www.apache.org/licenses/LICENSE-2.0
11 | *
12 | * Unless required by applicable law or agreed to in writing, software
13 | * distributed under the License is distributed on an "AS IS" BASIS,
14 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
15 | * See the License for the specific language governing permissions and
16 | * limitations under the License.
17 | */
18 | package fake.security;
19 |
20 | import dx.channel.FixSha1WithDsaProvider;
21 |
22 | import java.security.NoSuchAlgorithmException;
23 | import java.security.NoSuchProviderException;
24 | import java.security.Provider;
25 |
26 | public class Signature {
27 | private static final Provider fix = new FixSha1WithDsaProvider();
28 |
29 | public static java.security.Signature getInstance(String algorithm)
30 | throws NoSuchAlgorithmException {
31 | if ("SHA1WITHDSA".equalsIgnoreCase(algorithm)) {
32 | return java.security.Signature.getInstance(algorithm, fix);
33 | }
34 | return java.security.Signature.getInstance(algorithm);
35 | }
36 |
37 | public static java.security.Signature getInstance(String algorithm, String provider)
38 | throws NoSuchAlgorithmException, NoSuchProviderException {
39 | if ("SHA1WITHDSA".equalsIgnoreCase(algorithm)) {
40 | return java.security.Signature.getInstance(algorithm, fix);
41 | }
42 | return java.security.Signature.getInstance(algorithm, provider);
43 | }
44 |
45 | public static java.security.Signature getInstance(String algorithm, Provider provider)
46 | throws NoSuchAlgorithmException {
47 | if ("SHA1WITHDSA".equalsIgnoreCase(algorithm)) {
48 | return java.security.Signature.getInstance(algorithm, fix);
49 | }
50 | return java.security.Signature.getInstance(algorithm, provider);
51 | }
52 | }
53 |
--------------------------------------------------------------------------------
/signer/src/main/resources/dx/signer/LICENSE.txt:
--------------------------------------------------------------------------------
1 | dx-signer
2 |
3 | Copyright 2022 北京顶象技术有限公司
4 |
5 | Licensed under the Apache License, Version 2.0 (the "License");
6 | you may not use this file except in compliance with the License.
7 | You may obtain a copy of the License at
8 |
9 | http://www.apache.org/licenses/LICENSE-2.0
10 |
11 | Unless required by applicable law or agreed to in writing, software
12 | distributed under the License is distributed on an "AS IS" BASIS,
13 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
14 | See the License for the specific language governing permissions and
15 | limitations under the License.
--------------------------------------------------------------------------------