Files
aaPanel/class/ssl_info.py
T
Jack ed55fa708d Update to 7.7.0
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
2024-07-19 11:25:10 +08:00

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: