mirror of
https://github.com/aaPanel/aaPanel.git
synced 2026-08-26 01:22:25 +02:00
Since version 7.7.0, we recommend yours update python to 3.12. [+] Using nginx technology to load static files improves access speed [+] Refactor homepage, website, FTP, and database using vue3 [+] Table loading changed to skeleton screen [+] Add Website statistics-v2 professional plug-in [+] Add Home page - top 5 resource occupancy [+] Add protection for Files management (requires Tamper-proof for Enterprise 3.7) [+] Website, FTP, Databases page add program status [+] Add FTP log analysis (only supports Centos) [+] Add password-free login to phpMyAdmin [+] Add Proxy Project in Website (Supported when web service uses Nginx) [+] Add WP Toolkit (Pro version only) [+] Redesigned Docker module [+] Add WP Toolkit Protection [+] Add WP Toolkit Backup and Restore [+] Add WP Toolkit Migrated [+] Add WP Toolkit Clone site (supports new domain and subdomain) [+] Add WP Toolkit Create site from backup of other panel [+] Add WP Toolkit support for Cron automatic backup (only save Local disk) [+] Add WP Toolkit operation log [+] Add Integrity check for WP Toolkit [+] Add WP Toolkit plug-in management and themes management [*] Optimize phpMyAdmin formula access method [*] Optimize Home page PHP display problem [*] Optimize jump to the login interface after the login expires [*] Optimize automatic renewal of SSL at some times [*] Optimize Let's Encrypt to increase application success rate [-] Fix Logs Audit cannot be opened [-] Fix apache URL rewrite issue [-] Fix phpmyadmin installation problem [-] Fix the problem that some servers cannot install software [-] Fix upload file error [-] Fix left menu hiding problem [-] Fix aaPanel Mobile QR code display problem [-] Fix problem that third-party plug-ins are not displayed in the App Store [-] Fix issue where the menu bar is blank when opening new tabs [-] Fixed panel not being accessible in some cases [-] Fix the issue where Curl warning caused the inability to apply for SSL [-] Fix Quota issues for Website, FTP, Databases [-] Fix file interface display problem on mobile terminal
221 lines
7.5 KiB
Python
221 lines
7.5 KiB
Python
|
|
import os,sys,re,time,subprocess
|
|
|
|
panelPath = '/www/server/panel/'
|
|
os.chdir(panelPath)
|
|
|
|
sys.path.insert(0, panelPath + "class/")
|
|
import public
|
|
from datetime import date, datetime
|
|
|
|
is_openssl = True
|
|
try:
|
|
import OpenSSL
|
|
except:
|
|
is_openssl = False
|
|
|
|
|
|
|
|
class ssl_info:
|
|
|
|
def __init__(self) -> None:
|
|
pass
|
|
|
|
|
|
def create_key(self,bits=2048):
|
|
"""
|
|
@name 创建RSA密钥
|
|
@param bits 密钥长度
|
|
"""
|
|
if is_openssl:
|
|
|
|
key = OpenSSL.crypto.PKey()
|
|
key.generate_key(OpenSSL.crypto.TYPE_RSA, bits)
|
|
private_key = OpenSSL.crypto.dump_privatekey(OpenSSL.crypto.FILETYPE_PEM, key)
|
|
return private_key
|
|
else:
|
|
tmp_pk_file = "/tmp/private_key_{}.pem".format(int(time.time()))
|
|
cmd = ["openssl", "genpkey", "-algorithm", "RSA", "-out", tmp_pk_file, "-pkeyopt", f"rsa_keygen_bits:{bits}"]
|
|
subprocess.run(cmd, check=True)
|
|
with open(tmp_pk_file, "r") as f:
|
|
private_key = f.read()
|
|
try:
|
|
os.remove("private_key.pem")
|
|
except:
|
|
pass
|
|
return private_key
|
|
|
|
def load_ssl_info_by_data(self, pem_data: str):
|
|
if not isinstance(pem_data, (str, bytes)):
|
|
return None
|
|
|
|
if is_openssl:
|
|
return self.__get_cert_info(pem_data)
|
|
|
|
# 使用命令行解析
|
|
pem_file = "/tmp/fullchain_{}.pem".format(int(time.time()))
|
|
public.writeFile(pem_file, pem_data)
|
|
res = public.ExecShell("openssl x509 -in {} -noout -text".format(pem_file))[0]
|
|
try:
|
|
result = {}
|
|
issuer_match = re.search(r"Issuer: (.*)", res)
|
|
if issuer_match:
|
|
data = {}
|
|
issuer = issuer_match.group(1)
|
|
for key, val in re.findall(r"(\w+\s*)=([^,]+)", issuer):
|
|
data[key.strip()] = val
|
|
|
|
if "CN" in data:
|
|
result["issuer"] = data['CN']
|
|
if "O" in data:
|
|
s = data['O'].encode().decode('unicode_escape')
|
|
result["issuer"] = bytes(s, 'latin1').decode('utf-8')
|
|
|
|
validity_match = re.search(r"Not After\s*:\s*(.*)", res)
|
|
if validity_match:
|
|
not_after = validity_match.group(1)
|
|
dt_after = datetime.strptime(not_after, "%b %d %H:%M:%S %Y %Z")
|
|
result['notAfter'] = dt_after.strftime("%Y-%m-%d %H:%M:%S")
|
|
result['endtime'] = (dt_after - datetime.now()).days
|
|
else:
|
|
result['endtime'] = 0
|
|
|
|
validity_match = re.search(r"Not Before\s*:\s*(.*)", res)
|
|
if validity_match:
|
|
not_befoer = validity_match.group(1)
|
|
dt_befoer = datetime.strptime(not_befoer, "%b %d %H:%M:%S %Y %Z")
|
|
result['notBefore'] = dt_befoer.strftime("%Y-%m-%d %H:%M:%S")
|
|
|
|
subject_match = re.search(r"Subject: (.*)", res)
|
|
if subject_match:
|
|
subject = subject_match.group(1)
|
|
for key, val in re.findall(r"(\w+\s*)=([^,]+)", subject):
|
|
if key.strip() == 'CN':
|
|
s = val.encode().decode('unicode_escape')
|
|
result["subject"] = bytes(s, 'latin1').decode('utf-8')
|
|
# 取可选名称
|
|
result['dns'] = []
|
|
dns_match = re.findall(r"DNS:([^\s,]+)", res)
|
|
for dns in dns_match:
|
|
result['dns'].append(dns)
|
|
except:
|
|
result = None
|
|
|
|
if os.path.exists(pem_file):
|
|
os.remove(pem_file)
|
|
return result
|
|
|
|
def load_ssl_info(self,pem_file):
|
|
"""
|
|
@name 获取证书详情
|
|
"""
|
|
if not os.path.exists(pem_file):
|
|
return None
|
|
|
|
pem_data = public.readFile(pem_file)
|
|
if not pem_data:
|
|
return None
|
|
return self.load_ssl_info_by_data(pem_data)
|
|
|
|
def __get_cert_info(self,pem_data):
|
|
"""
|
|
@name 通过python的openssl模块获取证书信息
|
|
@param pem_data 证书内容
|
|
"""
|
|
result = {}
|
|
try:
|
|
x509 = OpenSSL.crypto.load_certificate(OpenSSL.crypto.FILETYPE_PEM, pem_data)
|
|
except: # 证书格式可能是错的,就没有办法读取证书内容
|
|
return None
|
|
|
|
issuer = x509.get_issuer()
|
|
result['issuer'] = ''
|
|
if hasattr(issuer, 'CN'):
|
|
result['issuer'] = issuer.CN
|
|
if not result['issuer']:
|
|
is_key = [b'0', '0']
|
|
issue_comp = issuer.get_components()
|
|
if len(issue_comp) == 1:
|
|
is_key = [b'CN', 'CN']
|
|
for iss in issue_comp:
|
|
if iss[0] in is_key:
|
|
result['issuer'] = iss[1].decode()
|
|
break
|
|
if not result['issuer']:
|
|
if hasattr(issuer, 'O'):
|
|
result['issuer'] = issuer.O
|
|
# 取到期时间
|
|
result['notAfter'] = self.strf_date(
|
|
bytes.decode(x509.get_notAfter())[:-1])
|
|
# 取申请时间
|
|
result['notBefore'] = self.strf_date(
|
|
bytes.decode(x509.get_notBefore())[:-1])
|
|
# 取可选名称
|
|
result['dns'] = []
|
|
for i in range(x509.get_extension_count()):
|
|
s_name = x509.get_extension(i)
|
|
if s_name.get_short_name() in [b'subjectAltName', 'subjectAltName']:
|
|
s_dns = str(s_name).split(',')
|
|
for d in s_dns:
|
|
result['dns'].append(d.split(':')[1])
|
|
subject = x509.get_subject().get_components()
|
|
# 取主要认证名称
|
|
if len(subject) == 1:
|
|
result['subject'] = subject[0][1].decode()
|
|
else:
|
|
if not result['dns']:
|
|
for sub in subject:
|
|
if sub[0] == b'CN':
|
|
result['subject'] = sub[1].decode()
|
|
break
|
|
if 'subject' in result:
|
|
result['dns'].append(result['subject'])
|
|
else:
|
|
result['subject'] = result['dns'][0]
|
|
result['endtime'] = int(int(time.mktime(time.strptime(result['notAfter'], "%Y-%m-%d")) - time.time()) / 86400)
|
|
return result
|
|
|
|
# 转换时间
|
|
def strf_date(self, sdate):
|
|
return time.strftime('%Y-%m-%d', time.strptime(sdate, '%Y%m%d%H%M%S'))
|
|
|
|
#转换时间
|
|
def strfToTime(self,sdate):
|
|
import time
|
|
return time.strftime('%Y-%m-%d',time.strptime(sdate,'%b %d %H:%M:%S %Y %Z'))
|
|
|
|
|
|
def dump_pkcs12_new(self, key_pem=None, cert_pem=None, ca_pem=None, friendly_name=""):
|
|
from cryptography.hazmat.backends import default_backend
|
|
from cryptography.hazmat.primitives import serialization
|
|
from cryptography.hazmat.primitives.serialization.pkcs12 import serialize_key_and_certificates
|
|
from cryptography.x509 import load_pem_x509_certificate
|
|
|
|
private_key = serialization.load_pem_private_key(
|
|
key_pem.encode(),
|
|
password=None, # 如果私钥有密码,请在此处提供密码
|
|
backend=default_backend()
|
|
)
|
|
|
|
cert = load_pem_x509_certificate((cert_pem + ca_pem).encode(), default_backend())
|
|
|
|
# 将证书和私钥组合成PKCS12格式的文件
|
|
p12 = serialize_key_and_certificates(
|
|
name=friendly_name.encode() if friendly_name else None,
|
|
key=private_key,
|
|
cert=cert,
|
|
encryption_algorithm=serialization.NoEncryption(),
|
|
cas=[load_pem_x509_certificate(ca_pem.encode(), default_backend())]
|
|
)
|
|
return p12
|
|
|
|
#class ssl:
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|