Compare commits
14 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
| f64e7b18b0 | |||
| 9c92be13d9 | |||
| 89cb0af79a | |||
| 9cbb6b022c | |||
| 375816df87 | |||
| 2b36de569d | |||
| 21142a6104 | |||
| f3a2f05b1c | |||
| 722d88a017 | |||
| 179400f9ed | |||
| cc2b18ba80 | |||
| fc3801f0f2 | |||
| c2ca546955 | |||
| f711ca8280 |
@ -12,7 +12,7 @@ DB_PORT=3306 # 数据库端口(MySQL默认3306)
|
|||||||
|
|
||||||
# [应用配置]
|
# [应用配置]
|
||||||
APP_HOST=0.0.0.0 # 应用监听地址(0.0.0.0表示允许所有IP访问)
|
APP_HOST=0.0.0.0 # 应用监听地址(0.0.0.0表示允许所有IP访问)
|
||||||
APP_PORT=9875 # 应用监听端口
|
APP_PORT=5000 # 应用监听端口
|
||||||
DEBUG=False # 调试模式(生产环境必须关闭)
|
DEBUG=False # 调试模式(生产环境必须关闭)
|
||||||
APP_DOMAIN=localhost # 应用对外域名(用于邮件链接生成)
|
APP_DOMAIN=localhost # 应用对外域名(用于邮件链接生成)
|
||||||
APP_PROTOCOL=http # 应用协议(http或https)
|
APP_PROTOCOL=http # 应用协议(http或https)
|
||||||
BIN
.gitignore
vendored
BIN
.gitignore
vendored
Binary file not shown.
33
Dockerfile
Normal file
33
Dockerfile
Normal file
@ -0,0 +1,33 @@
|
|||||||
|
# 使用官方 Python 镜像(Debian 系)
|
||||||
|
FROM python:3.8-slim
|
||||||
|
|
||||||
|
# 设置时区为北京时间
|
||||||
|
ENV TZ=Asia/Shanghai
|
||||||
|
RUN ln -snf /usr/share/zoneinfo/$TZ /etc/localtime && echo $TZ > /etc/timezone
|
||||||
|
|
||||||
|
# 安装系统依赖(仅运行时需要的库)
|
||||||
|
RUN apt-get update && \
|
||||||
|
apt-get install -y --no-install-recommends \
|
||||||
|
libjpeg-dev \
|
||||||
|
zlib1g-dev \
|
||||||
|
libtiff5-dev \
|
||||||
|
libfreetype6-dev \
|
||||||
|
liblcms2-dev \
|
||||||
|
libwebp-dev \
|
||||||
|
&& rm -rf /var/lib/apt/lists/*
|
||||||
|
|
||||||
|
# 设置工作目录
|
||||||
|
WORKDIR /app
|
||||||
|
|
||||||
|
# 复制依赖文件并安装
|
||||||
|
COPY requirements.txt .
|
||||||
|
RUN pip install --no-cache-dir -i https://pypi.tuna.tsinghua.edu.cn/simple -r requirements.txt
|
||||||
|
|
||||||
|
# 复制应用代码
|
||||||
|
COPY . .
|
||||||
|
|
||||||
|
# 暴露端口
|
||||||
|
EXPOSE 5000
|
||||||
|
|
||||||
|
# 运行命令
|
||||||
|
CMD ["python", "app.py"]
|
||||||
21
LICENSE
Normal file
21
LICENSE
Normal file
@ -0,0 +1,21 @@
|
|||||||
|
MIT License
|
||||||
|
|
||||||
|
Copyright (c) [2025] [jeazw]
|
||||||
|
|
||||||
|
Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||||
|
of this software and associated documentation files (the "Software"), to deal
|
||||||
|
in the Software without restriction, including without limitation the rights
|
||||||
|
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||||
|
copies of the Software, and to permit persons to whom the Software is
|
||||||
|
furnished to do so, subject to the following conditions:
|
||||||
|
|
||||||
|
The above copyright notice and this permission notice shall be included in all
|
||||||
|
copies or substantial portions of the Software.
|
||||||
|
|
||||||
|
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||||
|
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||||
|
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||||
|
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||||
|
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||||
|
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
||||||
|
SOFTWARE.
|
||||||
131
README.md
Normal file
131
README.md
Normal file
@ -0,0 +1,131 @@
|
|||||||
|
# 自签名证书管理系统
|
||||||
|

|
||||||
|

|
||||||
|
|
||||||
|
<div align="center">
|
||||||
|
<img src="./static/favicon.svg" alt="qilin SSL Logo" width="150">
|
||||||
|
<p>一款易用的自签证书管理系统</p>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
一个基于Flask的Web应用程序,用于管理自签名证书颁发机构(CA)和证书。
|
||||||
|
**全部代码由DeepSeek生成**
|
||||||
|
|
||||||
|
## 功能特性
|
||||||
|
|
||||||
|
- **证书颁发机构管理**
|
||||||
|
- 创建和管理自签名CA
|
||||||
|
- 查看CA详情及相关证书
|
||||||
|
- 生成证书吊销列表(CRL)
|
||||||
|
- 导出CA捆绑包(证书+私钥)
|
||||||
|
|
||||||
|
- **证书管理**
|
||||||
|
- 颁发由您的CA签名的证书
|
||||||
|
- 管理主题备用名称(SAN)
|
||||||
|
- 吊销和续订证书
|
||||||
|
- 支持多种格式导出(PKCS#12, PEM, CRT+KEY)
|
||||||
|
|
||||||
|
- **用户管理**
|
||||||
|
- 带邮箱验证的用户注册
|
||||||
|
- 基于角色的访问控制(管理员/普通用户)
|
||||||
|
- 密码策略强制执行
|
||||||
|
|
||||||
|
## 系统截图
|
||||||
|
<div style="display: flex; justify-content: space-between; margin: 20px 0;">
|
||||||
|
<img src="screenshots/index.png" alt="首页" style="width: 32%; border: 1px solid #ddd; border-radius: 4px;">
|
||||||
|
<img src="screenshots/ca_detail.png" alt="CA详情页" style="width: 32%; border: 1px solid #ddd; border-radius: 4px;">
|
||||||
|
<img src="screenshots/cert_detail.png" alt="证书详情页" style="width: 32%; border: 1px solid #ddd; border-radius: 4px;">
|
||||||
|
</div>
|
||||||
|
|
||||||
|
## demo测试账号
|
||||||
|
https://ssl.liuyan.wang
|
||||||
|
admin/123456
|
||||||
|
|
||||||
|
## 系统要求
|
||||||
|
|
||||||
|
- Python 3.7+
|
||||||
|
- MySQL/MariaDB数据库
|
||||||
|
- OpenSSL
|
||||||
|
- 所需Python包(见`requirements.txt`)
|
||||||
|
|
||||||
|
## 安装指南
|
||||||
|
|
||||||
|
1. 克隆仓库:
|
||||||
|
```bash
|
||||||
|
git clone https://github.com/yourusername/certificate-management-system.git
|
||||||
|
cd certificate-management-system
|
||||||
|
```
|
||||||
|
|
||||||
|
2. 创建并激活虚拟环境:
|
||||||
|
```bash
|
||||||
|
python -m venv venv
|
||||||
|
source venv/bin/activate # Windows系统: venv\Scripts\activate
|
||||||
|
```
|
||||||
|
|
||||||
|
3. 安装依赖:
|
||||||
|
```bash
|
||||||
|
pip install -r requirements.txt
|
||||||
|
```
|
||||||
|
|
||||||
|
4. 基于`.env.example`创建`.env`文件并配置:
|
||||||
|
```ini
|
||||||
|
DB_HOST=localhost # 数据库服务器IP地址或域名
|
||||||
|
DB_PORT=3306 # 数据库端口(MySQL默认3306)
|
||||||
|
DB_NAME=cert_management # 数据库名称
|
||||||
|
DB_USER=root # 数据库用户名
|
||||||
|
DB_PASSWORD=yourpassword # 数据库密码(生产环境建议使用强密码)
|
||||||
|
SECRET_KEY=your-secret-key-here # Flask应用加密密钥(生产环境必须修改,建议使用32位随机字符串)
|
||||||
|
MAIL_SERVER=smtp.example.com # SMTP服务器地址(QQ邮箱为smtp.qq.com)
|
||||||
|
MAIL_PORT=587 # SMTP端口(QQ邮箱SSL端口为465)
|
||||||
|
MAIL_USE_TLS=True # 是否使用SSL加密(QQ邮箱必须开启)
|
||||||
|
MAIL_USERNAME=your-email@example.com # 发件邮箱地址
|
||||||
|
MAIL_PASSWORD=your-email-password # SMTP授权码(非邮箱密码)
|
||||||
|
APP_DOMAIN=localhost # 应用对外域名(用于邮件链接生成)
|
||||||
|
APP_PROTOCOL=https # 应用协议(http或https)
|
||||||
|
```
|
||||||
|
|
||||||
|
## 运行应用
|
||||||
|
|
||||||
|
```bash
|
||||||
|
python app.py
|
||||||
|
```
|
||||||
|
|
||||||
|
默认情况下,应用将在`http://localhost:5000`可用。
|
||||||
|
|
||||||
|
## 使用说明
|
||||||
|
|
||||||
|
1. 注册新账户(若注册开放)或直接登录
|
||||||
|
2. 管理员可:
|
||||||
|
- 创建和管理CA
|
||||||
|
- 颁发证书
|
||||||
|
- 查看系统所有证书
|
||||||
|
3. 普通用户可:
|
||||||
|
- 使用自己创建的CA颁发证书
|
||||||
|
- 管理自己的证书
|
||||||
|
|
||||||
|
## API接口
|
||||||
|
|
||||||
|
- `/`: 仪表盘
|
||||||
|
- `/login`, `/logout`: 认证相关
|
||||||
|
- `/register`: 用户注册
|
||||||
|
- `/cas`: CA管理
|
||||||
|
- `/certificates`: 证书管理
|
||||||
|
- `/download/<filename>`: 文件下载
|
||||||
|
|
||||||
|
## 安全注意事项
|
||||||
|
|
||||||
|
- 请始终在安全环境中运行本系统
|
||||||
|
- 妥善保管您的`.env`文件
|
||||||
|
- 定期备份证书存储和数据库
|
||||||
|
- 生产环境建议使用HTTPS
|
||||||
|
|
||||||
|
## 开源许可
|
||||||
|
|
||||||
|
[MIT License](LICENSE)
|
||||||
|
|
||||||
|
## 贡献指南
|
||||||
|
|
||||||
|
欢迎提交Pull Request。重大改动请先创建Issue讨论。
|
||||||
|
|
||||||
|
## 技术支持
|
||||||
|
|
||||||
|
如有问题请提交GitHub仓库Issue。
|
||||||
43
app.py
43
app.py
@ -198,37 +198,24 @@ def get_db_connection():
|
|||||||
return None
|
return None
|
||||||
|
|
||||||
|
|
||||||
def generate_captcha():
|
|
||||||
# 生成6位随机验证码
|
|
||||||
captcha_code = ''.join(random.choices(string.ascii_uppercase + string.digits, k=6))
|
|
||||||
conn = get_db_connection()
|
|
||||||
if conn:
|
|
||||||
try:
|
|
||||||
cursor = conn.cursor()
|
|
||||||
# 清除旧的验证码
|
|
||||||
cursor.execute("DELETE FROM captcha WHERE created_at < NOW() - INTERVAL 10 MINUTE")
|
|
||||||
# 插入新验证码
|
|
||||||
cursor.execute("INSERT INTO captcha (code) VALUES (%s)", (captcha_code,))
|
|
||||||
conn.commit()
|
|
||||||
return captcha_code
|
|
||||||
except Error as e:
|
|
||||||
print(f"Database error: {e}")
|
|
||||||
return None
|
|
||||||
finally:
|
|
||||||
if conn.is_connected():
|
|
||||||
cursor.close()
|
|
||||||
conn.close()
|
|
||||||
return None
|
|
||||||
|
|
||||||
|
|
||||||
def verify_captcha(user_input):
|
def verify_captcha(user_input):
|
||||||
|
"""验证用户输入的验证码是否正确(只验证最新的4位验证码)"""
|
||||||
conn = get_db_connection()
|
conn = get_db_connection()
|
||||||
if conn:
|
if conn:
|
||||||
try:
|
try:
|
||||||
cursor = conn.cursor()
|
cursor = conn.cursor()
|
||||||
cursor.execute("SELECT code FROM captcha ORDER BY created_at DESC LIMIT 1")
|
# 只查询最新的验证码(确保是4位的)
|
||||||
|
cursor.execute("""
|
||||||
|
SELECT code FROM captcha
|
||||||
|
WHERE LENGTH(code) = 4 -- 只查询4位验证码
|
||||||
|
ORDER BY created_at DESC
|
||||||
|
LIMIT 1
|
||||||
|
""")
|
||||||
result = cursor.fetchone()
|
result = cursor.fetchone()
|
||||||
if result and user_input.upper() == result[0]:
|
if result and user_input.upper() == result[0]:
|
||||||
|
# 验证成功后删除已使用的验证码
|
||||||
|
cursor.execute("DELETE FROM captcha WHERE code = %s", (result[0],))
|
||||||
|
conn.commit()
|
||||||
return True
|
return True
|
||||||
return False
|
return False
|
||||||
except Error as e:
|
except Error as e:
|
||||||
@ -859,9 +846,9 @@ def register():
|
|||||||
conn.close()
|
conn.close()
|
||||||
|
|
||||||
# 生成新验证码
|
# 生成新验证码
|
||||||
captcha_code = generate_captcha()
|
captcha_url = url_for('captcha') # 使用图片验证码
|
||||||
return render_template('register.html',
|
return render_template('register.html',
|
||||||
captcha_code=captcha_code,
|
captcha_url=captcha_url, # 前端改为显示图片验证码
|
||||||
registration_open=current_app.config['REGISTRATION_OPEN'],
|
registration_open=current_app.config['REGISTRATION_OPEN'],
|
||||||
email_required=current_app.config['EMAIL_VERIFICATION_REQUIRED'])
|
email_required=current_app.config['EMAIL_VERIFICATION_REQUIRED'])
|
||||||
|
|
||||||
@ -1081,8 +1068,8 @@ def login():
|
|||||||
cursor.close()
|
cursor.close()
|
||||||
conn.close()
|
conn.close()
|
||||||
|
|
||||||
captcha_code = generate_captcha()
|
captcha_url = url_for('captcha')
|
||||||
return render_template('login.html', captcha_code=captcha_code)
|
return render_template('login.html', captcha_url=captcha_url)
|
||||||
|
|
||||||
|
|
||||||
@app.route('/logout')
|
@app.route('/logout')
|
||||||
|
|||||||
13
docker-compose.yml
Normal file
13
docker-compose.yml
Normal file
@ -0,0 +1,13 @@
|
|||||||
|
version: '3.8'
|
||||||
|
|
||||||
|
services:
|
||||||
|
tiku_bm:
|
||||||
|
image: certmanager:latest
|
||||||
|
container_name: certmanager
|
||||||
|
ports:
|
||||||
|
- "5002:5000"
|
||||||
|
environment:
|
||||||
|
- FLASK_ENV=production
|
||||||
|
volumes:
|
||||||
|
- ./data:/app/data
|
||||||
|
restart: unless-stopped
|
||||||
BIN
screenshots/ca_detail.png
Normal file
BIN
screenshots/ca_detail.png
Normal file
Binary file not shown.
|
After Width: | Height: | Size: 29 KiB |
BIN
screenshots/cert_detail.png
Normal file
BIN
screenshots/cert_detail.png
Normal file
Binary file not shown.
|
After Width: | Height: | Size: 34 KiB |
BIN
screenshots/index.png
Normal file
BIN
screenshots/index.png
Normal file
Binary file not shown.
|
After Width: | Height: | Size: 36 KiB |
@ -138,6 +138,7 @@
|
|||||||
<th>通用名</th>
|
<th>通用名</th>
|
||||||
<th>状态</th>
|
<th>状态</th>
|
||||||
<th>有效期至</th>
|
<th>有效期至</th>
|
||||||
|
<th>创建者</th>
|
||||||
<th>创建时间</th>
|
<th>创建时间</th>
|
||||||
<th class="pe-4">操作</th>
|
<th class="pe-4">操作</th>
|
||||||
</tr>
|
</tr>
|
||||||
@ -167,6 +168,7 @@
|
|||||||
{% endif %}
|
{% endif %}
|
||||||
</td>
|
</td>
|
||||||
<td>{{ cert.expires_at.strftime('%Y-%m-%d') }}</td>
|
<td>{{ cert.expires_at.strftime('%Y-%m-%d') }}</td>
|
||||||
|
<td>{{ get_username(cert.created_by) }}</td>
|
||||||
<td>{{ cert.created_at.strftime('%Y-%m-%d') }}</td>
|
<td>{{ cert.created_at.strftime('%Y-%m-%d') }}</td>
|
||||||
<td class="pe-4">
|
<td class="pe-4">
|
||||||
<div class="btn-group btn-group-sm">
|
<div class="btn-group btn-group-sm">
|
||||||
|
|||||||
@ -34,6 +34,7 @@
|
|||||||
<th>CA机构</th>
|
<th>CA机构</th>
|
||||||
<th>状态</th>
|
<th>状态</th>
|
||||||
<th>有效期至</th>
|
<th>有效期至</th>
|
||||||
|
<th>创建者</th>
|
||||||
<th>创建时间</th>
|
<th>创建时间</th>
|
||||||
<th class="pe-4">操作</th>
|
<th class="pe-4">操作</th>
|
||||||
</tr>
|
</tr>
|
||||||
@ -67,6 +68,7 @@
|
|||||||
{% endif %}
|
{% endif %}
|
||||||
</td>
|
</td>
|
||||||
<td>{{ cert.expires_at.strftime('%Y-%m-%d') }}</td>
|
<td>{{ cert.expires_at.strftime('%Y-%m-%d') }}</td>
|
||||||
|
<td>{{ get_username(cert.created_by) }}</td>
|
||||||
<td>{{ cert.created_at.strftime('%Y-%m-%d') }}</td>
|
<td>{{ cert.created_at.strftime('%Y-%m-%d') }}</td>
|
||||||
<td class="pe-4">
|
<td class="pe-4">
|
||||||
<div class="btn-group btn-group-sm">
|
<div class="btn-group btn-group-sm">
|
||||||
|
|||||||
Loading…
x
Reference in New Issue
Block a user