Python 轉 exe 執行檔並進行程式簽章

前言

最近在將 Python 程式轉成 Windows 執行檔給同事時,發現會一直被系統防毒軟體判定為病毒,這造成我們蠻大的困擾。

後來經過查詢發現這跟程式中所使用的 Library 有非常大的關聯性,為了避免這個問題,可以考慮使用 Code Signing 透過可信任憑證授權中心 (CA) 來減少安全性警告,但是一般來說 Code Signing 必需要以「公司名義」向微軟認可的發行商購買,且具有使用期限價格也不斐,對於個人開發者來說是一筆非常龐大的負擔,所幸在台灣有一個自然人憑證 IC 卡,他可以用來作為網路資料交換時,做為身份識別與驗證,而這個「自然人憑證」包含了「數位簽章」及「公開金鑰」,而其中的數位簽章是經過 Windows 所認可的,也就是說我們可以用此數位簽章作為一個 Code Signing,用自己的名字為自己寫的程式作為一個背書,確認自己是檔案的發行者,藉此獲得 Windows 的認可,接下來會說明怎麼使用自然人憑證進行 Code Signing。

Python 打包成執行檔

目前對 Python 代碼打包成執行檔的方式常用的有四種方法(bbFreeze已停止維護,就此忽略它)。

Solution Windows Linux OSX Python3 License One-file mode Zipfile import Eggs pkg_resources support Latest release date
py2exe yes no no yes MIT yes yes no no Nov 17, 2021
pyInstaller yes yes yes yes GPL yes no yes no Nov 10, 2021
cx_Freeze yes yes yes yes PSF no yes yes no Nov 29, 2021
py2app no no yes yes MIT no yes yes yes Sep 27, 2021

資料來源:The Hitchhiker’s Guide to Python, Update the latest release date on 2021/12/2

我個人比較常用的是 PyInstaller,關於各種方法使用比較可以參考 Python 打包 EXE 方法匯總整理, 在這裡我就不多做著墨,大家依照自己的需求做選擇。

PyInstaller 安裝方法

1
2
3
4
5
# 透過 pip 安裝 Pyinstaller
pip install pyinstaller

# 如果安裝失敗,可以直接用以下指令從 Github 進行安裝
pip install https://github.com/pyinstaller/pyinstaller/archive/develop.tar.gz

PyInstaller 打包方式

安裝完成後,這邊先簡單紀錄最常用的指令,想知道進階指令可以到 pyinstaller -h 去查詢。

整個 PyInstaller 簡易指令架構如下,需要注意指令的大小寫

1
pyinstaller [功能指令] [python script 檔案位置] [-n NAME] [-c] [-i]
縮寫指令 完整指令 說明
-F –onefile 將所有檔案資料打包成單一 exe 執行檔
-D –onedir 將所有的東西打包成一個資料夾(包含多個引用資源)其中的含括 exe 執行檔
–specpath N/A 輸出檔案到指定路徑或資料夾
-n Name –name Name 打包的檔名,如果省略該選項,那麼第一個指令碼的主檔名將作為 exe 執行檔的命名
-w –windowed
–noconsole
指定程式執行時不顯示命令列視窗,僅針對 Windows 和 Mac OS X 有效
-c –console
–nowindowed
指定程式執行使用命令列視窗,默認模式
-i 設置執行檔的圖示,需為 .ico 檔,預設為 NONE,其他進階應用請參見 pyinstaller -h

在未指定輸出路徑狀態下,請先確認自己的 Terminal 路徑位置是在哪,預設轉換好的文件會放置在 Terminal 指令路徑下。
如下圖所示,打包好的文件會直接放置在 ~/Documents/02_Program/GitHub/test,這是蠻多第一次使用的人沒有注意到的地方。

PyInstaller 生成檔案

-F 模式

1
pyinstaller -F /Users/Documents/02_Program/GitHub/test/test.py

test.py 會生成檔案如下

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
.
├── build
│   └── test
│   ├── Analysis-00.toc
│   ├── EXE-00.toc
│   ├── PKG-00.toc
│   ├── PYZ-00.pyz
│   ├── PYZ-00.toc
│   ├── Tree-00.toc
│   ├── Tree-01.toc
│   ├── Tree-02.toc
│   ├── base_library.zip
│   ├── test.pkg
│   ├── warn-test.txt
│   └── xref-test.html
├── dist
│   └── test
├── test.py
└── test.spec

4 directories, 2 files
檔案 說明
build 資料夾 編譯過程的產物;後續可以刪除。
dist 資料夾 放置編譯結果,所有編譯完成需要的檔案都在這裡,其中包含我們需要的 exe 執行檔
test.spec 根據使用者針對 Pyinstaller 有使用到的功能,整理成.spec檔;後續可以刪除。

-D 模式

1
pyinstaller -D /Users/Documents/02_Program/GitHub/test/test.py

test.py 會生成檔案如下

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
.
├── build
│   └── test
│   ├── Analysis-00.toc
│   ├── COLLECT-00.toc
│   ├── EXE-00.toc
│   ├── PKG-00.toc
│   ├── ...
│   ├── ...
│   ├── base_library.zip
│   ├── test
│   ├── test.pkg
│   ├── warn-test.txt
│   └── xref-test.html
├── dist
│   └── test
│   ├── 31
│   ├── 32
│   ├── 33
│   ├── ...
│   ├── ...
│   ├── ...
│   ├── test.exe
│   ├── tk
│   ├── tkConfig.sh
│   ├── tornado
│   ├── typed_ast
│   ├── wheel-0.37.0-py3.9.egg-info
│   ├── zmq
│   ├── zope
│   ├── zope.event-4.5.0-py3.8.egg-info
│   └── zope.interface-5.4.0.dist-info
├── test.py
└── test.spec

1163 directories, 8752 files

由上述產生的檔案清單進行比較,可以看出兩者之間的差異。

自然人憑證簽章事前準備

接下來我們需要針對編譯完成的「.exe執行檔進行簽章」,在使用自然人憑證簽章前,需要先準備以下幾個工具。

  1. 自然人憑證 IC 卡
    請至戶政事務所辦理(不包含各地民政局(處))。
  2. IC 卡讀卡機
    因為自然人憑證不像一般的 Code Signing 可以轉匯出 PFX 憑證,所以每次執行時都需要使用讀卡機用 IC 卡簽署。
  3. 安裝自然人憑證「ICOS 卡片管理工具」
    請至內政部憑證管理中心下載,此為自然人憑證 PIN 碼驗證簽章與加解密功能檢測程式。
  4. 安裝 Visual Studio
    需要使用 Visual Studio 內含的 Signtool 命令列工具,請至 Microsoft 下載。
    如果不像安裝 Visual Studio,也可以上網搜尋網友分離出來的 Signtool 進行使用。

進行程式碼簽章作業

  1. 在執行簽章作業前,請先確認工作列是否已經執行了「ICOS 卡片管理工具」。

  2. 開啟 Visual Studio 的 「Developer PowerShell for VS」,這裡要特別注意,在沒有設置 PATH 狀況下使用 PowerShellWindows Terminal 會造成 Signtool 執行失敗,為了簡化設置流程,建議直接使用 Visual Studio 的 「Developer PowerShell for VS」即可。

  3. 接下來我們可以直接執行簽章作業,可以參考下列範例指令。

    1
    signtool sign /a /t http://timestamp.sectigo.com /v C:\Users\Desktop\test\test.exe

    語法對照

    1
    signtool [command] [options] [file_name | ...]

    以下稍微說明這些指令的意義,需要注意指令的大小寫,進階指令可以至 Microsoft 查詢。

    引數 說明
    sign 數位簽署檔案。 數位簽章可以防止檔案遭到篡改,而且可讓使用者根據簽署憑證確認簽署者。
    /a 自動選取最佳的簽署憑證。 簽署工具會找到滿足所有指定條件的所有有效憑證,並且選取有效時間最長的一個。 如果沒有這個選項,簽署工具只需要找出一個有效的簽署憑證。
    /t URL 指定時間戳記伺服器的 URL。 如果沒有這個選項,簽署的檔案就不會加上時間戳記。 如果加上時間戳記失敗,便會產生警告。範例中的 http://timestamp.sectigo.com,是時間戳記伺服器的 URL。
    /v 不論命令執行成功或失敗,都顯示詳細資訊輸出,並顯示警告訊息。
    /u 指定為加入的目錄檔自動產生一個唯一的名稱。 必要時,目錄檔會重新命名,以避免與現有的目錄檔發生名稱衝突。 如果沒有指定這個選項,簽署工具會覆寫具有與所要加入之目錄相同名稱的所有現有目錄。
  4. 執行指令後,會跳出輸入自然人憑證 Pin 碼視窗,請輸入自然人憑證 IC 卡的 PIN 碼。

  5. 當程式執行完後,會顯示以下狀態,來顯示程式簽署的狀況,Number of files successfully Signed 代表完成簽署的文件數量。

    1
    2
    3
    Number of files successfully Signed: 1
    Number of warnings: 0
    Number of errors: 0
  6. 當簽署完後,可以直接檔案的「內容」去查看簽署的狀況。

以上就完成整個簽章流程,這個程式就將會是以你的名字作為背書,這樣應該就可解決讓防毒軟體造成誤判的問題呢!~