Android Apk签名工具

目前发布APK之前大家都会采用第三方加固方案来实现应用安全,

在Windows下 360有签名工具可以直接签名,腾讯乐固目前取消了签名工具采用云端加固,但是之后需要自己重签名(因为加固之前会先去掉签名)。

在Mac下没有签名工具,这就比较难受了。

一般来说,我们可以通过命令行来签名Apk,打开命令行或者终端 输入如下命令

jarsigner -verbose -keystore apk-release-key.jks -signedjar new-signed.apk app-release-unsigned.apk alikeys
* apk-release-key.jks是jks的文件路径
* alikeys 是生成jks 的别名
* new-signed.apk 就是生成的apk 的名字
* app-release-unsigned.apk , 是你要签名apk 的名字
* 回车后输入密码, 就得到new-signed.apk了

但是这样容易比较麻烦,还容易出错,其实我们还可以使用脚本来完成自动签名。

这里来简单说明一下吧,我是采用python来实现的, Mac与Windows均可使用
本文python环境:python 3.8.2

wecom-temp-b7f3fa55925b223e9c064297858a82fc.png

sign.json 与signTool.py文件需要在同一文件夹下

其中sign.json 是签名文件配置项(在工具中可以快捷选择,也可以不配置)
如下:

[
  {
    "signApk": "签名后apk的文件夹的绝对路径",
    "jks": "jks的绝对路径",
    "alikeys":"别名",
    "keyStorePas":"密钥",
    "alikeysPas":"别名密码"
  },
  //mac下的样式
  {
    "signApk": "/Users/output",
    "jks": "/Users/aaa.jks",
    "alikeys":"alikeys",
    "keyStorePas":"08DAHGHKJHJUGBGYHJJVHVHVNVNV",
    "alikeysPas":"DRFTVG7FYBJBJ9XRCVHFGCHGMIOOQ"
  },
  //Windows下的样式
  {
    "signApk": "C:\\User\\output",
    "jks": "C:\\User\\aaa.jks",
    "alikeys":"alikeys",
    "keyStorePas":"08DAHGHKJHJUGBGYHJJVHVHVNVNV",
    "alikeysPas":"DRFTVG7FYBJBJ9XRCVHFGCHGMIOOQ"
  }

]

这样就配置好了

signTool.py 是签名工具,通过命令行或者终端运行该文件就行了

signTool.py文件如下:

# coding=utf-8
import json
import platform
import time

import tkinter as tk
from tkinter import messagebox,  INSERT, END
import tkinter.filedialog

from tkinter import ttk
import os
import subprocess


class signApk():
    window = tk.Tk()
    window.title("签名工具")
    window.rowconfigure(1, weight=1)
    window.columnconfigure(0, weight=1)

    setFrame = tk.LabelFrame(window, text="签名配置")
    setFrame.columnconfigure(2, weight=1)

    # signFrame = tk.LabelFrame(window, text="signFrame")
    # signFrame.rowconfigure(0, weight=1)
    # signFrame.columnconfigure(0,weight=1)

    outputFrame = tk.LabelFrame(window, text="输出栏")
    outputFrame.rowconfigure(0, weight=1)
    outputFrame.columnconfigure(0, weight=1)

    logcat = tk.Text(outputFrame)
    logcat.grid(row=0, sticky=tk.NSEW)

    def __init__(self):
        self.initializeUI()

    def initializeUI(self):

        #获取固定的配置项
        try:
            basedir = os.path.dirname(__file__)
            update_path = os.path.join(basedir, "sign.json")
            with open(update_path) as fp:
                json_data = json.load(fp)
        except:
            json_data = []
            pass


        x = 0
        tk.Label(self.setFrame, text='未签名apk:').grid(row=x, column=0, sticky='w')
        noSignApk = tk.StringVar()
        tk.Entry(self.setFrame, textvariable=noSignApk).grid(row=x, column=1, sticky=tk.EW, columnspan=2)

        def selectFileAPK():
            t = tk.filedialog.askopenfilename()
            noSignApk.set(t)
            print("select")
            print(t)
            pass

        btn1 = ttk.Button(self.setFrame, text="选择", command=selectFileAPK)
        btn1.grid(row=x, column=3)
        x = x + 1
        signApk = tk.StringVar()  #
        tk.Label(self.setFrame, text='签名后apk路径:').grid(row=x, column=0, sticky='w')
        comSianApk = ttk.Combobox(self.setFrame, textvariable=signApk)
        comSignApkList = []
        for aks in json_data:
            comSignApkList.append(aks['signApk'])
        comSianApk['values'] = comSignApkList  # 给下拉菜单设定值
        comSianApk.grid(row=x, column=1, sticky=tk.EW, columnspan=3)

        x = x + 1
        jks = tk.StringVar()
        tk.Label(self.setFrame, text='jks路径:').grid(row=x, column=0, sticky='w')
        com = ttk.Combobox(self.setFrame, textvariable=jks)
        comList = []
        for aks in json_data:
            comList.append(aks['jks'])
        com['values'] = comList  # 给下拉菜单设定值
        com.grid(row=x, column=1, sticky=tk.EW, columnspan=3)

        x = x + 1
        alikeys = tk.StringVar()  #
        tk.Label(self.setFrame, text='别名:').grid(row=x, column=0, sticky='w')
        tk.Entry(self.setFrame, textvariable=alikeys).grid(row=x, column=1, sticky=tk.EW, columnspan=3)

        x = x + 1
        keyStorePas = tk.StringVar()  #
        tk.Label(self.setFrame, text='密钥密码:').grid(row=x, column=0, sticky='w')
        tk.Entry(self.setFrame, textvariable=keyStorePas).grid(row=x, column=1, sticky=tk.EW, columnspan=3)

        x = x + 1
        alikeysPas = tk.StringVar()  #
        tk.Label(self.setFrame, text='别名密码:').grid(row=x, column=0, sticky='w')
        tk.Entry(self.setFrame, textvariable=alikeysPas).grid(row=x, column=1, sticky=tk.EW, columnspan=3)

        def showMsg(*args):
            for aks in json_data:
                if aks['jks'] == com.get():
                    alikeys.set(aks['alikeys'])
                    keyStorePas.set(aks['keyStorePas'])
                    alikeysPas.set(aks['alikeysPas'])

        com.bind('<<ComboboxSelected>>', showMsg)

        x = x + 1
        tk.Label(self.setFrame, text='').grid(row=x, column=4, sticky=tk.EW, columnspan=1)
        x = x + 1
        # 按钮
        tk.Button(self.setFrame, text='签名',
                  command=lambda: self.check(noSignApk, signApk, jks, alikeys, alikeysPas, keyStorePas)).grid(row=x,
                                                                                                              column=0,
                                                                                                              sticky=tk.EW,
                                                                                                              columnspan=4)
        x = x + 1
        tk.Label(self.setFrame, text='').grid(row=x, column=4, sticky=tk.EW, columnspan=1)

        # 输出栏

        w = 370
        h = 700
        sw = self.window.winfo_screenwidth()

        sh = self.window.winfo_screenheight()

        x = (sw - w) / 2
        y = (sh - h) / 2
        self.window.geometry("%dx%d+%d+%d" % (w, h, x, y))

        self.setFrame.grid(row=0, sticky=tk.EW)
        # self.signFrame.grid(row=1, sticky=tk.EW)
        self.outputFrame.grid(row=1, sticky=tk.NSEW)

        self.window.mainloop()
        pass

    def check(self, noSignApk, signApk, jks, alikeys, alikeysPas, keyStorePas):
        self.logcat.delete(INSERT,END)
        errMessage = ""
        if len(noSignApk.get()) == 0:
            errMessage = errMessage + "未签名apk为空"
        elif len(signApk.get()) == 0:
            errMessage = errMessage + "签名apk地址为空"
        elif len(alikeys.get()) == 0:
            errMessage = errMessage + "别名为空"
        elif len(alikeysPas.get()) == 0:
            errMessage = errMessage + "密钥为空"
        elif len(keyStorePas.get()) == 0:
            errMessage = errMessage + "别名密码为空"
        if errMessage != "":
            messagebox.showinfo('tip', errMessage)
        else:
            self.logcat.insert("insert", time.strftime('%Y-%m-%d %H:%M:%S'))
            self.logcat.insert("insert", '\n\n----------------开始签名----------------\n\n')
            self.logcat.insert("insert", '未签名apk路径:'+noSignApk.get()+'\n')
            self.logcat.insert("insert", '签名后apk地址路径:'+signApk.get()+'\n')
            self.logcat.insert("insert", '别名:'+alikeys.get()+'\n')
            self.logcat.insert("insert", '密钥:'+alikeysPas.get()+'\n')
            self.logcat.insert("insert", '别名密码:'+keyStorePas.get()+'\n')
            self.logcat.insert("insert", "校验通过\n")
            self.sign(noSignApk.get(), signApk.get(), jks.get(), alikeys.get(), alikeysPas.get(), keyStorePas.get())
        pass

    def getSystemInfo(self):
        # 获取系统命令行头
        operating = platform.system()
        if operating == 'Windows':
            return "cmd.exe /C "
        elif operating == 'Darwin':
            #mac
            return ""
        else:
            return ""
        pass
    def getSystemCode(self):
        #获取系统编码格式
        operating = platform.system()
        if operating == 'Windows':
            return 'gbk'
        elif operating == 'Darwin':
            #mac
            return 'utf8'
        else:
            return 'utf8'
        pass

    def sign(self, noSignApk, signApk, jks, alikeys, alikeysPas, keyStorePas):
        try:

            # file name (with extension)
            src_apk_file_name = os.path.basename(noSignApk)
            # 分割文件名与后缀
            temp_list = os.path.splitext(src_apk_file_name)
            # name without extension
            src_apk_name = temp_list[0]
            # 后缀名,包含.   例如: ".apk "
            src_apk_extension = temp_list[1]
            # 创建生成目录
            output_dir = signApk + '/output/'
            # 目录不存在则创建
            if not os.path.exists(output_dir):
                os.mkdir(output_dir)
            # 目标文件路径
            target_apk = output_dir + src_apk_name + src_apk_extension
            # 签名后的文件路径
            signer_apk = output_dir + src_apk_name + '_signer' + src_apk_extension
            # 拼装签名命令
            signer_str = self.getSystemInfo()+'apksigner sign --ks ' + jks + ' --ks-pass pass:' + alikeysPas + ' --ks-key-alias ' + alikeys + ' --key-pass pass:' + keyStorePas + ' --out ' + signer_apk + ' ' + noSignApk

            cmd = 'jarsigner' + ' -verbose' + ' -keystore ' + jks + '  -keypass ' + alikeysPas + '  -storepass ' + keyStorePas + ' -signedjar ' + signer_apk + ' ' + noSignApk + ' ' + alikeys

            self.logcat.insert("insert", cmd + '\n')
            self.logcat.insert("insert", "\n开始签名\n\n")
            self.logcat.update()
            print(cmd)
            code=self.run_shell(cmd)
            if code==0:
                verify_str = 'jarsigner verify -v --print-certs ' + signer_apk
                self.run_shell(verify_str)
                pass

        except Exception as e:
            messagebox.showerror('tip', "签名错误:" + repr(e))
            print('Exception:\t', repr(e))

        pass

    def run_shell(self, shell):
        cmd = subprocess.Popen(shell, stdin=subprocess.PIPE, stderr=subprocess.PIPE,
                               stdout=subprocess.PIPE, shell=True)
        output, errno = cmd.communicate()
        self.logcat.insert("insert", output.decode(self.getSystemCode(),'ignore'))
        self.logcat.update()
        return cmd.returncode

    pass


if __name__ == '__main__':
    signApk()

运行如下:


wecom-temp-9f98e80ed1d1d5503c818f21d110cf3d.png

配置好相关文件之后,点击签名就行了

开始签名

   正在添加: META-INF/MANIFEST.MF
   正在添加: META-INF/SUMANSOU.SF
   ...

好了,完成了一个简单而又实用的工具

最后编辑于 :
©著作权归作者所有,转载或内容合作请联系作者
【社区内容提示】社区部分内容疑似由AI辅助生成,浏览时请结合常识与多方信息审慎甄别。
平台声明:文章内容(如有图片或视频亦包括在内)由作者上传并发布,文章内容仅代表作者本人观点,简书系信息发布平台,仅提供信息存储服务。

相关阅读更多精彩内容

友情链接更多精彩内容