diff --git a/.idea/SmartCard.iml b/.idea/SmartCard.iml
index b4df20b..c249b7b 100644
--- a/.idea/SmartCard.iml
+++ b/.idea/SmartCard.iml
@@ -2,7 +2,7 @@
-
+
\ No newline at end of file
diff --git a/.idea/misc.xml b/.idea/misc.xml
index 9d5ddc3..3df9af7 100644
--- a/.idea/misc.xml
+++ b/.idea/misc.xml
@@ -3,5 +3,5 @@
-
+
\ No newline at end of file
diff --git a/BuildPackage-PC21-PY308.bat b/BuildPackage-PC21-PY308.bat
new file mode 100644
index 0000000..c0b58b4
--- /dev/null
+++ b/BuildPackage-PC21-PY308.bat
@@ -0,0 +1,9 @@
+@echo off
+rmdir /s /q build
+%1 mshta vbscript:CreateObject("Shell.Application").ShellExecute("cmd.exe","/c %~s0 ::","","runas",1)(window.close)&&exit
+cd /d "%~dp0"
+D:\Venvs\TPMSmartCard-PY308\Scripts\python.exe BuildPackage.py build -p ttkbootstrap
+xcopy Config "build\exe.win-amd64-3.8\Config\"
+xcopy Config\myfont "build\exe.win-amd64-3.8\Config\\myfont\"
+xcopy Tooler "build\exe.win-amd64-3.8\Tooler\"
+xcopy OpenSC "build\exe.win-amd64-3.8\OpenSC\"
\ No newline at end of file
diff --git a/BuildPackage.py b/BuildPackage.py
index 7bcbdf3..040b839 100644
--- a/BuildPackage.py
+++ b/BuildPackage.py
@@ -8,7 +8,7 @@
# TARGET
target = Executable(
script="SmartCardAPP.py",
- # base="Win32GUI",
+ base="Win32GUI",
icon="Config/iconer/icon02.ico",
uac_admin=True
)
diff --git a/Config/button.json b/Config/button.json
index 66a8308..092780a 100644
--- a/Config/button.json
+++ b/Config/button.json
@@ -38,25 +38,25 @@
"name": "cer",
"text": "➕ Import Certificate",
"type": "success",
- "w": 145
+ "w": 155
},
{
"name": "sys",
"text": "\uD83D\uDD12 Show in System",
"type": "info",
- "w": 140
+ "w": 155
},
{
"name": "out",
"text": "\uD83D\uDCBE Save to File",
"type": "info",
- "w": 130
+ "w": 155
},
{
"name": "non",
"text": "❌ Delete Certificate",
"type": "danger",
- "w": 143
+ "w": 155
}
]
}
\ No newline at end of file
diff --git a/Config/frames.json b/Config/frames.json
index cc29d66..778ab37 100644
--- a/Config/frames.json
+++ b/Config/frames.json
@@ -18,9 +18,27 @@
260
],
"cert_main": [
- 590,
- 500,
+ 650,
+ 520,
415,
5
+ ],
+ "cert_info": [
+ 625,
+ 160,
+ 428,
+ 260
+ ],
+ "cert_user": [
+ 308,
+ 95,
+ 428,
+ 420
+ ],
+ "cert_last": [
+ 308,
+ 95,
+ 745,
+ 420
]
}
\ No newline at end of file
diff --git a/Config/global.json b/Config/global.json
new file mode 100644
index 0000000..de989bf
--- /dev/null
+++ b/Config/global.json
@@ -0,0 +1,34 @@
+{
+ "cn": {
+ "card_name": "卡片名称",
+ "card_uuid": "卡片编号",
+ "card_path": "实例路径",
+ "sc_status": "卡片状态",
+ "sc_length": "密码规则",
+ "cert_name": "证书名称",
+ "cert_uuid": "证书编号",
+ "cert_sha1": "证书SHA1",
+ "cert_sign": "签名算法",
+ "cert_open": "生效日期",
+ "cert_stop": "失效日期",
+ "is_ca_cert": "是否为CA",
+ "is_expired": "证书过期",
+ "pub_length": "加密算法",
+ "key_usages": "密钥用途",
+ "key_identy": "密钥标识",
+ "sub_usages": "拓展用途",
+ "sub_identy": "身份标识",
+ "user_name": "通用名称",
+ "user_code": "国家代码",
+ "user_area": "所在地区",
+ "user_city": "所在城市",
+ "user_on_t": "组织名称",
+ "user_ou_t": "部门名称",
+ "last_name": "通用名称",
+ "last_code": "国家代码",
+ "last_area": "所在地区",
+ "last_city": "所在城市",
+ "last_on_t": "组织名称",
+ "last_ou_t": "部门名称"
+ }
+}
\ No newline at end of file
diff --git a/Config/labels.json b/Config/labels.json
index 6ab48e7..60ddc77 100644
--- a/Config/labels.json
+++ b/Config/labels.json
@@ -10,36 +10,106 @@
"..."
]
],
- "card_info": [
- [
- "card_name",
+ "card_info": {
+ "conf": [
43,
- 275,
- 100
+ 275
],
- [
- "card_uuid",
- 43,
- 295,
- 100
+ "data": [
+ {
+ "card_name": 100
+ },
+ {
+ "card_uuid": 200
+ },
+ {
+ "card_path": 200
+ },
+ {
+ "sc_status": 200
+ },
+ {
+ "sc_length": 200
+ }
+ ]
+ },
+ "cert_info": {
+ "conf": [
+ 435,
+ 278
],
- [
- "card_path",
- 43,
- 315,
- 100
+ "data": [
+ {
+ "cert_uuid": 150,
+ "cert_name": 225
+ },
+ {
+ "cert_sha1": 150,
+ "cert_open": 102,
+ "cert_stop": 102
+ },
+ {
+ "is_ca_cert": 43,
+ "is_expired": 42,
+ "pub_length": 60,
+ "cert_sign": 150
+ },
+ {
+ "key_usages": 220
+ },
+ {
+ "sub_usages": 220
+ },
+ {
+ "key_identy": 220
+ },
+ {
+ "sub_identy": 220
+ }
+ ]
+ },
+ "cert_user": {
+ "conf": [
+ 435,
+ 435
],
- [
- "sc_status",
- 43,
- 335,
- 100
+ "data": [
+ {
+ "user_name": 150
+ },
+ {
+ "user_code": 20,
+ "user_area": 40,
+ "user_city": 30
+ },
+ {
+ "user_on_t": 20
+ },
+ {
+ "user_ou_t": 20
+ }
+ ]
+ },
+ "cert_last": {
+ "conf": [
+ 750,
+ 435
],
- [
- "sc_length",
- 43,
- 355,
- 100
+ "data": [
+ {
+ "last_name": 150
+ },
+ {
+ "last_code": 20,
+ "last_area": 40,
+ "last_city": 30
+ },
+ {
+ "last_on_t": 20
+ },
+ {
+ "last_ou_t": 20
+ }
]
- ]
-}
\ No newline at end of file
+ }
+}
diff --git a/Config/myfont/MapleMono-SC-NF-Regular.ttf b/Config/myfont/MapleMono-SC-NF-Regular.ttf
new file mode 100644
index 0000000..3496def
Binary files /dev/null and b/Config/myfont/MapleMono-SC-NF-Regular.ttf differ
diff --git a/Config/myfont/NotoSerifCJKsc-Regular.otf b/Config/myfont/NotoSerifCJKsc-Regular.otf
new file mode 100644
index 0000000..cba8a47
Binary files /dev/null and b/Config/myfont/NotoSerifCJKsc-Regular.otf differ
diff --git a/Config/tables.json b/Config/tables.json
index 8c387da..2f21f9b 100644
--- a/Config/tables.json
+++ b/Config/tables.json
@@ -33,7 +33,7 @@
"GUID"
],
"NAME": [
- 240,
+ 300,
"Cert Name"
],
"LENS": [
diff --git a/Images/20240901-185515.png b/Images/20240901-185515.png
new file mode 100644
index 0000000..6ebe0f3
Binary files /dev/null and b/Images/20240901-185515.png differ
diff --git a/Module/Certificates.py b/Module/Certificates.py
index ecf816f..9ef2e70 100644
--- a/Module/Certificates.py
+++ b/Module/Certificates.py
@@ -1,37 +1,113 @@
-class CertSubjects:
- def __init__(self, in_text=""):
- self.OwnerCountryCode = ""
- self.OwnerCommonNames = ""
- self.OwnerDescription = ""
- self.OwnerMailAddress = ""
- self.OrganizationUnit = ""
- self.OrganizationName = ""
- if len(in_text) > 0:
- self.parseText(in_text)
-
- def parseText(self, in_text):
- in_text = in_text.split(",")
+import OpenSSL.crypto
class CertDataInfo:
+ """
+ {
+ 'C': 'XX',
+ 'O': '******************',
+ 'OU': '******************',
+ 'CN': '******************',
+ 'name': '******************',
+ 'description': '******************',
+ 'businessCategory': '******************',
+ 'jurisdictionC': 'XX'
+ }
+ """
+
def __init__(self,
- cr_uuid, # 证书序列号
- cr_from, # 证书颁发者
- cr_user, # 证书使用者
- cr_open, # 证书生效于
- cr_stop, # 证书失效于
- cr_hash, # 证书的SHA1
- is_a_ca, # 是否CA证书
+ _certs_=None
):
- self.cr_uuid = cr_uuid
- self.cr_owns = cr_from
- self.cr_user = cr_user
- self.cr_open = cr_open
- self.cr_open = cr_open
- self.cr_stop = cr_stop
- self.cr_hash = cr_hash
- self.is_a_ca = is_a_ca
- self.cr_lens = 0 # 密钥长度
-
-SUB = CertSubjects
+ self.CRLsDPoint = None
+ self.AuthAccess = None
+ self.SubsHashID = None
+ self.MainHashID = None
+ self.SubsOwners = None
+ self.SubsUsages = None
+ self.MainUsages = None
+ self.CertPolicy = None
+ self.TextExtend = None
+ self.SerialNums = None
+ self.CertExtend = None
+ self.Algorithms = None
+ self.CommonName = None
+ self.IssuedDate = None
+ self.ExpireDate = None
+
+ self.pub_key_al = None
+ self.pub_origin = None
+ self.pub_length = None
+
+ self.IssuerInfo = None
+ self.OwnersInfo = None
+
+ self.is_expired = None
+ self.is_ca_cert = None
+ self._certs_ = _certs_
+ if _certs_ is not None:
+ self.parseCert()
+
+ @staticmethod
+ def KeysUsages(in_data):
+ # 定义密钥用途的映射
+ key_usage = in_data
+ usages = {
+ 'digitalSignature': 0x80,
+ 'contentCommitment': 0x40,
+ 'keyEncipherment': 0x20,
+ 'dataEncipherment': 0x10,
+ 'keyAgreement': 0x08,
+ 'keyCertSign': 0x04,
+ 'cRLSign': 0x02,
+ 'encipherOnly': 0x01,
+ 'decipherOnly': 0x80, # 注意:这里的值与digitalSignature相同,需要根据实际情况调整
+ }
+ enabled_usages = [name for name, bit in usages.items() if key_usage & bit]
+ return enabled_usages
+
+ def certExtend(self, in_name):
+ if in_name in self.CertExtend:
+ return self.CertExtend[in_name]
+ return b""
+
+ def textExtend(self, in_name):
+ for ext_line in self.TextExtend:
+ ext_line = str(ext_line)
+ if ext_line.find(in_name) == 0:
+ return ext_line[len(in_name) + 1:]
+ return "Unknown"
+
+ def parseCert(self):
+ algorithms_maps = {
+ "6": "RSA"
+ }
+ self.CommonName = self._certs_['common_name'] if 'common_name' in self._certs_ else "Unknown"
+ self.SerialNums = str(self._certs_['serial_number'])[2:] if 'serial_number' in self._certs_ else "Unknown"
+ self.Algorithms = self._certs_['signature_algorithm'] if 'signature_algorithm' in self._certs_ else "Unknown"
+ self.IssuedDate = self._certs_['start_time'] if 'start_time' in self._certs_ else "Unknown"
+ self.ExpireDate = self._certs_['end_time'] if 'end_time' in self._certs_ else "Unknown"
+
+ self.pub_length = str(self._certs_['pubkey_len']) if 'pubkey_len' in self._certs_ else "Unknown"
+ self.pub_origin = str(self._certs_['pubkey']) if 'pubkey' in self._certs_ else "Unknown"
+ self.pub_key_al = str(self._certs_['pubkey_type']) if 'pubkey_type' in self._certs_ else "Unknown"
+ self.pub_key_al = algorithms_maps[self.pub_key_al] if self.pub_key_al in algorithms_maps else self.pub_key_al
+ self.IssuerInfo = self._certs_['issuer_info'] if 'issuer_info' in self._certs_ else "Unknown"
+ self.OwnersInfo = self._certs_['subject_info'] if 'subject_info' in self._certs_ else "Unknown"
+
+ self.CertExtend = self._certs_['extension_data'] if 'extension_data' in self._certs_ else []
+ self.TextExtend = self._certs_['extension_text'] if 'extension_text' in self._certs_ else []
+
+ self.is_expired = self._certs_['has_expired'] if 'has_expired' in self._certs_ else "Unknown"
+ self.is_ca_cert = str(self.certExtend('basicConstraints')).split(":")[-1]
+
+ self.CertPolicy = self.certExtend('certificatePolicies')
+ self.MainUsages = self.certExtend('keyUsage')
+ self.MainHashID = self.certExtend('authorityKeyIdentifier')
+ self.SubsUsages = self.certExtend('extendedKeyUsage')
+ self.SubsOwners = self.certExtend('subjectAltName')
+ self.SubsHashID = self.certExtend('subjectKeyIdentifier')
+ self.AuthAccess = self.certExtend('authorityInfoAccess')
+ self.CRLsDPoint = self.certExtend('crlDistributionPoints')
+
+
CER = CertDataInfo
diff --git a/Module/OpenSCDealer.py b/Module/OpenSCDealer.py
index 8396923..592c6fd 100644
--- a/Module/OpenSCDealer.py
+++ b/Module/OpenSCDealer.py
@@ -17,7 +17,7 @@ def openText(in_comm, in_tool="pkcs15-tool"):
print(results)
return results
- def readCert(self, results):
+ def readCert(self, results, details):
cr_name = results[0].split("[")[1].replace("]", "")
key_alg = results[0].split(" ")[1]
results = [i.split(": ") for i in results[1:11] if len(i)]
@@ -27,7 +27,8 @@ def readCert(self, results):
self.certs[cr_name] = SmartCardCer(
cr_name, results['MD:guid'],
key_alg + " " + results['ModLength'] if 'ModLength' in results else "No Key",
- results['Usage'], None
+ results['Usage'],
+ None if details.find("not found") >= 0 else details
)
print(results)
@@ -51,6 +52,9 @@ def readCard(self, results, card_name):
)
print(results)
+ def readFile(self, in_data):
+ pass
+
def readList(self):
self.cards = {}
self.certs = {}
@@ -73,4 +77,8 @@ def readList(self):
"--reader %d" % int(card_uuid)))
for nums in range(0, len(results)):
if results[nums].find("Private") >= 0:
- self.readCert(results[nums:nums + 11])
+ details = OpenSCDealer.openText(
+ "--read-certificate %d %s" % (
+ int(nums),
+ "--reader %d" % int(card_uuid)))
+ self.readCert(results[nums:nums + 11], "\n".join(details))
diff --git a/Module/SmartCardAPI.py b/Module/SmartCardAPI.py
index e46df19..b4b6f8c 100644
--- a/Module/SmartCardAPI.py
+++ b/Module/SmartCardAPI.py
@@ -1,8 +1,3 @@
-import os
-import time
-from winpty import PtyProcess
-
-
class SmartCardAPI:
def __init__(self, use_opensc=True):
self.cards = {}
diff --git a/Module/SmartCardObj.py b/Module/SmartCardObj.py
index 10323cc..afcb646 100644
--- a/Module/SmartCardObj.py
+++ b/Module/SmartCardObj.py
@@ -1,4 +1,6 @@
-from Module.Certificates import CER, SUB
+import OpenSSL.crypto
+from dateutil import parser
+from Module.Certificates import CER
class SmartCardDev:
@@ -27,10 +29,43 @@ def __init__(self,
sc_devs: str,
sc_exec: str,
sc_keys: str,
- sc_cert: (CER, dict),
+ sc_cert: (CER, dict, str),
):
self.sc_name = sc_name # 智能卡证书名称(Certutil+OpenSC二者通用)
self.sc_devs = sc_devs # 智能卡证书设备(Certutil)/容器ID(OpenSC)
self.sc_exec = sc_exec # 智能卡证书执行(Certutil)/Length(OpenSC)
self.sc_keys = sc_keys # 智能卡密钥哈希(Certutil)/用途ID(OpenSC)
self.sc_cert = sc_cert # 智能卡证书文件(Certutil)/智能卡描述信息
+ self.sc_text = ""
+ if type(self.sc_cert) is str:
+ self.sc_text = sc_cert
+ self.parseCert()
+
+ def parseCert(self):
+ cert_content: OpenSSL.crypto.X509 = OpenSSL.crypto.load_certificate(OpenSSL.crypto.FILETYPE_PEM, self.sc_cert)
+ cert_issuers = cert_content.get_issuer()
+ cert_subject = cert_content.get_subject()
+ cert_info_ls = {
+ "version": cert_content.get_version() + 1,
+ "serial_number": hex(cert_content.get_serial_number()),
+ "signature_algorithm": cert_content.get_signature_algorithm().decode("UTF-8"),
+ "common_name": cert_subject.commonName,
+ "start_time": parser.parse(cert_content.get_notBefore().decode("UTF-8")).strftime('%Y-%m-%d %H:%M'),
+ "end_time": parser.parse(cert_content.get_notAfter().decode("UTF-8")).strftime('%Y-%m-%d %H:%M'),
+ "has_expired": cert_content.has_expired(),
+ "pubkey": OpenSSL.crypto.dump_publickey(OpenSSL.crypto.FILETYPE_PEM, cert_content.get_pubkey()),
+ "pubkey_len": cert_content.get_pubkey().bits(),
+ "pubkey_type": cert_content.get_pubkey().type(),
+ "extension_count": cert_content.get_extension_count(),
+ "issuer_info": {i[0].decode(): i[1].decode() for i in cert_issuers.get_components()},
+ "subject_info": {i[0].decode(): i[1].decode() for i in cert_subject.get_components()},
+ "extension_data": {
+ cert_content.get_extension(i).get_short_name().decode(): cert_content.get_extension(i) \
+ for i in range(cert_content.get_extension_count())
+ },
+ "extension_text": [
+ cert_content.get_extension(i) for i in range(cert_content.get_extension_count())
+ ],
+ }
+ # print(cert_info_ls)
+ self.sc_cert = CER(cert_info_ls)
diff --git a/Module/TPMSmartCard.py b/Module/TPMSmartCard.py
index d89cef3..4e2f6cb 100644
--- a/Module/TPMSmartCard.py
+++ b/Module/TPMSmartCard.py
@@ -1,11 +1,5 @@
-import os
import subprocess
-from sys import stdout
-
-import chardet
import time
-
-from Tools.scripts.fixnotice import process
from winpty import PtyProcess
@@ -87,8 +81,22 @@ def dropCerts(in_name):
# "Get-ChildItem -Path Cert:\ -Recurse | Where-Object {$_.SerialNumber -eq '%s' } | Remove-Item" % ""
return result
- def changePIN(self):
- pass
-
- def resetsPIN(self):
- pass
+ @staticmethod
+ def changePIN(old, new, uid, type="--change-pin"):
+ command = ".\\OpenSC\\pkcs15-tool.exe --reader %s %s\r" % (uid, type)
+ proc = PtyProcess.spawn(command)
+ # proc.write("@echo off && cls\r")
+ # proc.read()
+ # proc.write(command)
+ time.sleep(1)
+ proc.write(old + "\r")
+ proc.write(new + "\r")
+ proc.write(new + "\r")
+ # proc.write("\rexit\r")
+ time.sleep(1)
+ result = proc.read()
+ result = result.split(": ")[-1]
+ if len(result) == 0:
+ result = "OK"
+ print(result)
+ return result
diff --git a/README.html b/README.html
new file mode 100644
index 0000000..c6e99c1
--- /dev/null
+++ b/README.html
@@ -0,0 +1,1589 @@
+
+
+
+
+
+README
+
+
+
TPM虚拟智能卡管理器 TPM Virtual Smart Cards Manager Create & Manage Smart Cards on TPM 创建和管理TPM虚拟智能卡 介绍 / Introduction 本工具用于管理TPM虚拟智能卡,依赖于微软“Tpmvscmgr”模块。可以帮助用户在计算机上安全地存储和管理加密密钥、证书和其他敏感信息。TPM(受信任平台模块)是一个硬件安全模块,通常集成在计算机主板上,提供加密和安全功能。
This tool is used to manage TPM virtual smart cards and relies on the Microsoft "Tpmvscmgr" module. It can help users securely store and manage encryption keys, certificates, and other sensitive information on their computers. TPM (Trusted Platform Module) is a hardware security module typically integrated on a computer motherboard, providing encryption and security functions.
相较于直接存储证书在Windows系统内,使用TPM虚拟智能卡的有点包括:
Compared to directly storing certificates within the Windows system, the advantages of using TPM virtual smart cards include:
安全性高:由于智能卡数据存储在硬件中,因此很难被黑客攻击。
High security: Due to the fact that smart card data is stored in hardware, it is difficult to be attacked by hackers.
便于管理:用户可以轻松地在计算机上创建、删除和管理虚拟智能卡。
Easy to manage: Users can easily create, delete, and manage virtual smart cards on their computers.
易于集成:许多操作系统和应用程序已经支持TPM虚拟智能卡,无需额外的硬件。
Easy to integrate: Many operating systems and applications already support TPM virtual smart cards without the need for additional hardware.
成本效益:与传统的物理智能卡相比,TPM虚拟智能卡可以节省硬件和管理成本。
Cost effectiveness: Compared to traditional physical smart cards, TPM virtual smart cards can save hardware and management costs.
注意:在使用本工具前,您需要提前安装 OpenSC / Notice: Before using this tool, you need to install OpenSC
功能 / Function 虚拟智能卡管理:创建、删除、虚拟智能卡,修改重置密码
证书和密钥管理:导入、删除、证书和密钥,查看数字证书
Virtual smart card management: create, delete, virtual smart cards, modify and reset passwords
Certificate and Key Management: Import, Delete, Certificate and Key, View Digital Certificates
要求 / Requirement 系统:Windows 8 x86/64或者更高
软件:OpenSC Certutil Tpmvscmgr
硬件:TPM硬件 1.2/2.0或更高
System: Windows 8 x86/64 or higher
Software: OpenSC Certuti Tpmvscmgr
Hardware: TPM hardware 1.2/2.0 or higher
参考 / Reference OpenSC/OpenSC: Open source smart card tools and middleware
windows-itpro-docs/windows/security/identity-protection/virtual-smart-cards
+
+
\ No newline at end of file
diff --git a/README.md b/README.md
index 8dc7982..8a08793 100644
--- a/README.md
+++ b/README.md
@@ -26,6 +26,12 @@ Compared to directly storing certificates within the Windows system, the advanta
- Cost effectiveness: Compared to traditional physical smart cards, TPM virtual smart cards can save hardware and management costs.
+![20240901-185515](Images/20240901-185515.png)
+
+
+
+注意:在使用本工具前,您需要提前安装[OpenSC](https://github.com/OpenSC/OpenSC/releases) / Notice: Before using this tool, you need to install [OpenSC](https://github.com/OpenSC/OpenSC/releases)
+
### 功能 / Function
- 虚拟智能卡管理:创建、删除、虚拟智能卡,修改重置密码
diff --git a/SmartCardAPP.py b/SmartCardAPP.py
index 77e79f9..8a74803 100644
--- a/SmartCardAPP.py
+++ b/SmartCardAPP.py
@@ -1,16 +1,15 @@
import json
-import multiprocessing
import os
import random
-import subprocess
-import sys
+import pyglet
+import pyperclip
import threading
import time
-import tkinter as tk
-from tkinter import messagebox, filedialog
-from tkinter import ttk as ttk_old
+from ttkbootstrap import *
import ttkbootstrap as ttk
-from ttkbootstrap.constants import *
+# import tkinter as tk
+from functools import partial
+from tkinter import messagebox, filedialog, font
from Module.SmartCardAPI import SmartCardAPI
from Module.TPMSmartCard import TPMSmartCard
@@ -20,22 +19,35 @@ def __init__(self):
self.root = tk.Tk()
self.conf = dict()
self.read_config()
+ self.lang = "cn"
# 设置主窗口宽度和高度
- self.root.geometry("1020x530")
+ self.root.geometry("1080x540")
self.size = self.get_screens()
self.root.geometry(f"+{self.size[0]}+{self.size[1]}")
- self.root.title("皮卡虚拟智能卡")
+ self.root.title("TPM Smart Card Manager")
self.data = SmartCardAPI()
self.tpms = TPMSmartCard()
+ pyglet.font.add_file("Config/myfont/MapleMono-SC-NF-Regular.ttf")
+ pyglet.font.add_file("Config/myfont/NotoSerifCJKsc-Regular.otf")
self.pick = {
"card": "",
"cert": ""
}
-
+ # FONT_NAME = "Noto Serif CJK SC"
+ FONT_NAME = "MapleMono SC NF"
+ self.t_fonts = {
+ "label_data": font.Font(family=FONT_NAME,
+ size=-12),
+ "label_name": font.Font(family=FONT_NAME,
+ size=-12, weight="bold")
+ }
self.frames = {
"card_main": self.view_frames("card_main"),
"card_info": self.view_frames("card_info"),
"cert_main": self.view_frames("cert_main"),
+ "cert_info": self.view_frames("cert_info"),
+ "cert_user": self.view_frames("cert_user"),
+ "cert_last": self.view_frames("cert_last"),
}
self.tables = {
@@ -50,18 +62,36 @@ def __init__(self):
"card_main": self.view_button("card_main"),
"cert_main": self.view_button("cert_main"),
}
- self.button["card_main"]["del"].config(state=tk.DISABLED)
+
self.labels = {
"card_info": self.view_labels("card_info"),
+ "cert_info": self.view_labels("cert_info"),
+ "cert_user": self.view_labels("cert_user"),
+ "cert_last": self.view_labels("cert_last"),
}
+
self.button["card_main"]["pin"].config(state=tk.DISABLED)
self.button["card_main"]["puk"].config(state=tk.DISABLED)
- self.button["cert_main"]["non"].config(state=tk.DISABLED)
+ self.button["card_main"]["del"].config(state=tk.DISABLED)
+
self.button["cert_main"]["sys"].config(state=tk.DISABLED)
self.button["cert_main"]["out"].config(state=tk.DISABLED)
+ self.button["cert_main"]["non"].config(state=tk.DISABLED)
+
self.load_status()
self.root.mainloop()
+ def la(self, in_name):
+ if "global" not in self.conf:
+ return in_name
+ temp_data = self.conf["global"]
+ if self.lang not in temp_data:
+ return in_name
+ temp_data = temp_data[self.lang]
+ if in_name not in temp_data:
+ return in_name
+ return temp_data[in_name]
+
def get_screens(self):
# 获取窗口的宽度和高度
window_width = self.root.winfo_reqwidth()
@@ -77,7 +107,8 @@ def get_screens(self):
return x, y
def read_config(self):
- for conf_name in ["tables", "frames", "button", "labels"]:
+ for conf_name in ["tables", "frames", "button",
+ "labels", "global"]:
with open("Config/%s.json" % conf_name, "r",
encoding="utf8") as conf_file:
conf_data = conf_file.read()
@@ -85,6 +116,9 @@ def read_config(self):
def load_status(self):
self.data.readInfo()
+ self.button["card_main"]["del"].config(state=tk.DISABLED)
+ self.button["cert_main"]["non"].config(state=tk.DISABLED)
+
self.tables["card_main"][0].delete(*self.tables["card_main"][0].get_children())
self.tables["cert_main"][0].delete(*self.tables["cert_main"][0].get_children())
for card_now in self.data.cards:
@@ -158,9 +192,14 @@ def view_button(self, in_name):
bt_map = {
"add": (self.card_create, None),
"del": (self.data_delete, "card"),
+ "puk": (self.card_change, "puk"),
+ "pin": (self.card_change, "pin"),
"cer": (self.cert_import, None),
- 'non': (self.data_delete, "cert"),
+ "sys": (self.cert_system, None),
+ "out": (self.cert_out_to, None),
+ "non": (self.data_delete, "cert"),
}
+ # bt_fun = {}
for now in bt_all:
bt_fun = bt_map[now['name']] if now['name'] in bt_map else (None, None)
if bt_fun[1] is None:
@@ -168,7 +207,7 @@ def view_button(self, in_name):
command=bt_fun[0])
else:
bta = ttk.Button(self.root, text=now['text'], bootstyle=now['type'],
- command=lambda: bt_fun[0](bt_fun[1]))
+ command=partial(bt_fun[0], bt_fun[1]))
bta.place(x=bt_btx, y=bt_bty, width=now['w'])
bt_btx = bt_btx + now['w'] + 2
bt_dict[now['name']] = bta
@@ -180,12 +219,22 @@ def view_labels(self, in_name):
tb_conf = self.conf['labels']
tb_data = dict()
if in_name in tb_conf:
- for now in tb_conf[in_name]:
- tag = ttk.Label(bootstyle="info", text=now[0] + ": ")
- tag.place(x=now[1], y=now[2])
- inf = ttk.Label(bootstyle="info", text="")
- inf.place(x=now[1] + (len(now[0]) + 2) * 6, y=now[2])
- tb_data[now[0]] = [tag, inf]
+ tb_conf = tb_conf[in_name]
+ tb_ln_x = tb_conf['conf'][0]
+ tb_it_y = tb_conf['conf'][1]
+ for tb_line in tb_conf['data']:
+ tb_it_x = tb_ln_x
+ for tb_item in tb_line:
+ tag = ttk.Label(bootstyle="default", text=self.la(tb_item) + ": "
+ , font=self.t_fonts['label_name'])
+ tag.place(x=tb_it_x, y=tb_it_y)
+ inf = ttk.Label(bootstyle="default", text="", font=self.t_fonts['label_data'])
+ # inf.place(x=tb_it_x + tag.winfo_reqwidth() + 5, y=tb_it_y)
+ inf.place(x=tb_it_x + 60 + 5, y=tb_it_y)
+ tb_data[tb_item] = [tag, inf]
+ # tb_it_x += tag.winfo_reqwidth() + 5 + tb_line[tb_item]
+ tb_it_x += 60 + 5 + tb_line[tb_item]
+ tb_it_y += 20
return tb_data
def card_select(self, event):
@@ -195,6 +244,8 @@ def card_select(self, event):
selected_name = treeview.set(selected_item[0], '#2')
self.pick["card"] = selected_name
self.button["card_main"]["del"].config(state=tk.NORMAL)
+ self.button["card_main"]["pin"].config(state=tk.NORMAL)
+ self.button["card_main"]["puk"].config(state=tk.NORMAL)
if selected_name in self.data.cards:
card_now = self.data.cards[selected_name]
card_map = {
@@ -209,28 +260,166 @@ def card_select(self, event):
self.labels["card_info"][fill_name][1].config(text=card_map[fill_name])
print(selected_name)
+ def cert_out_to(self, in_path=None):
+ if self.pick["cert"] in self.data.certs:
+ now_cert = self.data.certs[self.pick["cert"]]
+ if now_cert.sc_cert is not None:
+ if in_path is None:
+ save_path = filedialog.asksaveasfilename(
+ defaultextension=".crt", # 默认文件扩展名
+ filetypes=[("Cert files", "*.crt"), ("All files", "*.*")], # 文件类型过滤器
+ initialdir="~", # 初始目录,对于Windows系统,你可能想要设置为os.path.expanduser('~')
+ initialfile="%s.crt" % str(now_cert.sc_cert.SerialNums), # 初始文件名
+ title="Save as" # 对话框标题
+ )
+ else:
+ save_path = "%s\\%s.crt" % (in_path, str(now_cert.sc_cert.SerialNums))
+ with open(save_path, "w") as save_file:
+ save_file.write(now_cert.sc_text)
+ if in_path is None:
+ messagebox.showinfo("成功", "证书导出成功:%s\n"
+ "注意:只能导出证书公钥数据,私钥存储在TPM上无法导出" % str(
+ now_cert.sc_cert.CommonName))
+ return save_path
+ else:
+ messagebox.showerror("错误", "证书读取错误")
+ else:
+ messagebox.showerror("错误", "无法找到证书")
+ return None
+
+ def cert_system(self):
+ save_path = self.cert_out_to(in_path=os.getenv('APPDATA'))
+ if save_path is not None and os.path.exists(save_path):
+ os.system("explorer %s" % save_path)
+
def cert_select(self, event):
treeview = event.widget
selected_item = treeview.selection()
+ for fill_name in self.labels["cert_info"]:
+ self.labels["cert_info"][fill_name][1].config(text="")
if len(selected_item) > 0:
selected_name = treeview.set(selected_item[0], '#2')
selected_uuid = treeview.set(selected_item[0], '#1')
self.pick["cert"] = selected_name + " " + selected_uuid
self.button["cert_main"]["non"].config(state=tk.NORMAL)
- # self.button["cert_main"]["sys"].config(state=tk.NORMAL)
- # self.button["cert_main"]["out"].config(state=tk.NORMAL)
- if selected_name in self.data.certs:
- pass
+ self.button["cert_main"]["sys"].config(state=tk.NORMAL)
+ self.button["cert_main"]["out"].config(state=tk.NORMAL)
+ if self.pick["cert"] in self.data.certs:
+ cert_now = self.data.certs[self.pick["cert"]]
+ if cert_now.sc_cert is not None:
+ cert_now = cert_now.sc_cert
+ cert_map = {
+ "cert_name": cert_now.CommonName,
+ "cert_uuid": cert_now.SerialNums,
+ "cert_sign": cert_now.Algorithms,
+ "cert_open": cert_now.IssuedDate,
+ "cert_stop": cert_now.ExpireDate,
+ "is_ca_cert": cert_now.is_ca_cert,
+ "is_expired": cert_now.is_expired,
+ "pub_length": cert_now.pub_key_al + cert_now.pub_length,
+ "key_usages": str(cert_now.MainUsages)[:86] + "..." \
+ if len(str(cert_now.MainUsages)) >= 86 else cert_now.MainUsages,
+ "sub_usages": str(cert_now.SubsUsages)[:86] + "..." \
+ if len(str(cert_now.SubsUsages)) >= 86 else cert_now.SubsUsages,
+ "key_identy": cert_now.MainHashID,
+ "sub_identy": cert_now.SubsHashID,
+
+ "user_name": cert_now.OwnersInfo["CN"] if "CN" in cert_now.OwnersInfo else "",
+ "user_code": cert_now.OwnersInfo["C"] if "C" in cert_now.OwnersInfo else "",
+ "user_area": cert_now.OwnersInfo["``"] if "``" in cert_now.OwnersInfo else "",
+ "user_city": cert_now.OwnersInfo["``"] if "``" in cert_now.OwnersInfo else "",
+ "user_on_t": cert_now.OwnersInfo["O"] if "O" in cert_now.OwnersInfo else "",
+ "user_ou_t": cert_now.OwnersInfo["OU"] if "OU" in cert_now.OwnersInfo else "",
+
+ "last_name": cert_now.IssuerInfo["CN"] if "CN" in cert_now.IssuerInfo else "",
+ "last_code": cert_now.IssuerInfo["C"] if "C" in cert_now.IssuerInfo else "",
+ "last_area": cert_now.IssuerInfo["``"] if "``" in cert_now.IssuerInfo else "",
+ "last_city": cert_now.IssuerInfo["``"] if "``" in cert_now.IssuerInfo else "",
+ "last_on_t": cert_now.IssuerInfo["O"] if "O" in cert_now.IssuerInfo else "",
+ "last_ou_t": cert_now.IssuerInfo["OU"] if "OU" in cert_now.IssuerInfo else "",
+ }
+ for fill_name in cert_map:
+ for label in ["cert_info", "cert_user", "cert_last"]:
+ label_now = self.labels[label]
+ if fill_name in label_now:
+ label_now[fill_name][1].config(text=cert_map[fill_name])
print(selected_name)
+ def card_change(self, in_type="pin"):
+ if not self.pick["card"] or self.pick["card"] not in self.data.cards:
+ return None
+
+ def submit():
+ card_uid = self.pick["card"].split(" ")[-1]
+ pass_key = pass_txt.get()
+ pass_new = next_txt.get()
+ same_new = same_txt.get()
+ if pass_key == "" or len(pass_key) < 4:
+ make.attributes('-topmost', False)
+ messagebox.showwarning("错误", "原密码至少为4位")
+ make.attributes('-topmost', True)
+ elif pass_new == "" or len(pass_new) < 4:
+ make.attributes('-topmost', False)
+ messagebox.showwarning("错误", "新密码至少为4位")
+ make.attributes('-topmost', True)
+ elif pass_new != same_new:
+ make.attributes('-topmost', False)
+ messagebox.showwarning("错误", "两次输入的不匹配")
+ make.attributes('-topmost', True)
+ else:
+ result = TPMSmartCard.changePIN(pass_key, pass_new, card_uid,
+ type="--change-pin" \
+ if in_type == "pin" else "--unblock-pin")
+ messagebox.showinfo("修改/重置密码结果", result)
+ make.destroy()
+
+ make = ttk.Toplevel(self.root)
+ make.geometry("600x200")
+ make.geometry(f"+{self.size[0]}+{self.size[1]}")
+ make.title("%sPIN密码" % "修改" if in_type == "pin" else "重置PIN密码")
+ pass_tag = ttk.Label(make, text="旧的 PIN: " if in_type == "pin" else "卡片 PUK: ",
+ bootstyle="info")
+ pass_tag.grid(column=0, row=1, pady=10, padx=15)
+ pass_txt = ttk.Entry(make, bootstyle="info", width=60, text="")
+ pass_txt.grid(column=1, row=1, pady=10, padx=5)
+ pass_tip = ttk.Label(make, text="❌✅", bootstyle="info")
+ pass_tip.grid(column=2, row=1, pady=10, padx=5)
+
+ next_pin = ttk.Label(make, text="新的 PIN: ", bootstyle="info")
+ next_pin.grid(column=0, row=2, pady=10, padx=15)
+ next_txt = ttk.Entry(make, bootstyle="info", width=60)
+ next_txt.grid(column=1, row=2, pady=10, padx=5)
+ next_tip = ttk.Label(make, text="❌✅", bootstyle="info")
+ next_tip.grid(column=2, row=2, pady=10, padx=5)
+
+ same_pin = ttk.Label(make, text="确认 PIN: ", bootstyle="info")
+ same_pin.grid(column=0, row=3, pady=10, padx=15)
+ same_txt = ttk.Entry(make, bootstyle="info", width=60)
+ same_txt.grid(column=1, row=3, pady=10, padx=5)
+ same_tip = ttk.Label(make, text="❌✅", bootstyle="info")
+ same_tip.grid(column=2, row=3, pady=10, padx=5)
+
+ cancel_button = ttk.Button(make, text="取消", command=make.destroy, bootstyle="danger")
+ cancel_button.grid(column=0, row=4, pady=5, padx=15)
+ submit_button = ttk.Button(make, text="确认", command=submit, bootstyle="info")
+ submit_button.grid(column=2, row=4, pady=5, padx=0)
+
def card_create(self):
def disable():
if puks_var.get():
puks_txt.delete(0, tk.END)
puks_txt.config(state=tk.DISABLED)
+ make.attributes('-topmost', False)
+ messagebox.showinfo("禁用PUK",
+ ("注意:您禁用了PUK码,PIN码将是您唯一访问凭据,请妥善保管\n"
+ "无法使用Admin Key恢复PIN,如果丢失PIN码将无法访问智能卡!\n"))
+ make.attributes('-topmost', True)
else:
puks_txt.config(state=tk.NORMAL)
+ puks_txt.delete(0, tk.END)
+ for i in range(0, 16):
+ puks_txt.insert(0, str(random.randint(0, 9)))
def randoms():
if adks_var.get():
@@ -244,6 +433,7 @@ def randoms():
make = ttk.Toplevel(self.root)
make.geometry("640x240")
make.geometry(f"+{self.size[0]}+{self.size[1]}")
+ make.attributes('-topmost', True)
make.title("创建新的TPM虚拟智能卡")
# 在新窗口中添加输入部件
@@ -268,11 +458,11 @@ def randoms():
puks_txt = ttk.Entry(make, bootstyle="info", width=60)
puks_txt.grid(column=1, row=2, pady=10, padx=5)
puks_var = tk.BooleanVar()
- puks_txt.config(state=tk.DISABLED)
- puks_var.set(True)
- puks_tip = ttk.Checkbutton(make, text="禁用此项", bootstyle="info-round-toggle",
+ puks_tip = ttk.Checkbutton(make, text="禁用 PUK", bootstyle="info-round-toggle",
variable=puks_var, command=disable)
puks_tip.grid(column=2, row=2, pady=10, padx=5)
+ for i in range(0, 16):
+ puks_txt.insert(0, str(random.randint(0, 9)))
adks_tag = ttk.Label(make, text="管理密码: ", bootstyle="info")
adks_tag.grid(column=0, row=3, pady=10, padx=15)
@@ -281,7 +471,11 @@ def randoms():
adks_var = tk.BooleanVar()
adks_tip = ttk.Checkbutton(make, text="随机生成", bootstyle="info-round-toggle",
variable=adks_var, command=randoms)
+ for i in range(0, 48):
+ adks_txt.insert(0, str(random.randint(0, 9)))
+ adks_txt.config(state=tk.DISABLED)
adks_tip.grid(column=2, row=3, pady=10, padx=5)
+ adks_var.set(True)
for i in range(0, 48):
adks_txt.insert(0, str(random.randint(0, 9)))
@@ -318,9 +512,20 @@ def create():
deals_process['value'] = 100
self.load_status()
deals_destroy = True
+ pin = pins_txt.get()
+ puk = puks_txt.get()
+ adk = adks_txt.get()
+ out = "用户 PIN: %s\n用户 PUK: %s\n管理密码: %s" % (
+ pin, puk, adk
+ )
+ # cid = result.find("ROOT")
+ # if cid >= 0:
+ # out = result[cid:cid + 25] + "\n" + out
if deals_destroy:
make.destroy()
- messagebox.showinfo("创建TPM卡片结果", result)
+ messagebox.showinfo("创建结果", result)
+ pyperclip.copy(out)
+ messagebox.showinfo("卡片信息", out + "\n卡片信息已经复制到剪贴板,请妥善保存")
def update():
global deals_destroy
@@ -430,7 +635,9 @@ def submit():
pass_txt.get())
self.load_status()
make.destroy()
- messagebox.showinfo("导入证书结果", result)
+ messagebox.showinfo("导入证书结果",
+ result + "\n注意:证书导入后,私钥无法再通过任何方式从TPM导出或者备份"
+ "\n如需备份,请保留原始的PFX文件以及密码,否则可能会丢失数据")
# 在新窗口中添加输入部件
path_tag = ttk.Label(make, text="证书路径: ", bootstyle="info")
diff --git a/requirements.txt b/requirements.txt
new file mode 100644
index 0000000..2350db4
Binary files /dev/null and b/requirements.txt differ