Skip to main content

ssl

创建根证书(如果没有现成的根证书):

openssl genpkey -algorithm RSA -out ca.key -pkeyopt rsa_keygen_bits:2048

-algorithm RSA:指定生成的是RSA算法的密钥。
-out ca.key:生成的私钥输出到ca.key文件。
-pkeyopt rsa_keygen_bits:2048:指定密钥长度为2048位。

openssl req -x509 -new -nodes -key ca.key -sha256 -days 3650 -out ca.crt -subj "/C=US/ST=California/L=San Francisco/O=Arick CA/OU=IT/CN=Arick Root CA"

-x509:要求生成的是自签名证书。
-new:生成一个新的证书签名请求(CSR)。
-nodes:表示私钥文件不使用密码保护。
-key ca.key:使用刚刚生成的私钥文件签署证书。
-sha256:指定使用SHA-256签名哈希算法。
-days 3650:证书的有效期为3650天(约10年)。
-out ca.crt:将证书保存到ca.crt文件。
-subj:直接在命令行中指定证书信息。

这会生成一个有效期 10 年的根证书 (ca.crt) 和对应私钥 (ca.key)。

生成域名证书的私钥和证书请求文件 (CSR)

  1. 创建域名证书私钥:

openssl genrsa -out domain.key 2048


2. 创建证书签名请求 (CSR):

openssl req -new -key domain.key -out domain.csr -subj "/C=US/ST=California/L=San Francisco/O=Example Inc/OU=IT/CN=arick.top"


CN=arick.top 是域名。如果有多个域名可以使用 Subject Alternative Name,在下面添加 SAN。

### 用根证书签发 Nginx 域名证书

使用根证书 (ca.crt) 和私钥 (ca.key) 签发域名证书:


1. 创建一个域名证书扩展配置文件 (domain.ext):

```sh
cat > domain.ext <<EOF
authorityKeyIdentifier=keyid,issuer
basicConstraints=CA:FALSE
keyUsage = digitalSignature, nonRepudiation, keyEncipherment, dataEncipherment
subjectAltName = @alt_names

[alt_names]
DNS.1 = arick.top
DNS.2 = www.arick.top
DNS.3 = *.arick.top
EOF

如果需要其他子域名(如 www.example.com),可以在 alt_names 中继续添加 DNS 项。

  1. 签发域名证书:

    openssl x509 -req -in domain.csr -CA ca.crt -CAkey ca.key -CAcreateserial -out domain.crt -days 365 -sha256 -extfile domain.ext

    domain.crt 是生成的域名证书。

    domain.key 是生成的域名证书key。

    -days 365 设置证书有效期为一年。

p12 转pem

转换所有内容(私钥+证书)
openssl pkcs12 -in certificate.p12 -out certificate.pem -nodes

分别提取:
仅私钥:
openssl pkcs12 -in certificate.p12 -nocerts -out private-key.pem -nodes
仅证书:
openssl pkcs12 -in certificate.p12 -nokeys -out certificate.pem

报错
# 使用-legacy选项
openssl pkcs12 -in mitmproxy-ca.p12 -nocerts -out private-key.pem -legacy

# 如果上面不工作,尝试设置provider
openssl pkcs12 -in mitmproxy-ca.p12 -nocerts -out private-key.pem -provider legacy -provider default

Base64 转文件(surge 配置 转 p12文件 )

<!DOCTYPE html>
<html lang="zh">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>Base64文件解码器</title>
<style>
body {
font-family: 'Segoe UI', Tahoma, Geneva, Verdana, sans-serif;
max-width: 800px;
margin: 0 auto;
padding: 20px;
background: linear-gradient(135deg, #667eea 0%, #764ba2 100%);
min-height: 100vh;
color: #333;
}

.container {
background: rgba(255, 255, 255, 0.95);
border-radius: 15px;
padding: 30px;
box-shadow: 0 20px 40px rgba(0,0,0,0.1);
backdrop-filter: blur(10px);
}

h1 {
text-align: center;
color: #4a5568;
margin-bottom: 30px;
font-size: 2em;
}

.input-group {
margin-bottom: 20px;
}

label {
display: block;
margin-bottom: 8px;
font-weight: 600;
color: #4a5568;
}

textarea {
width: 100%;
min-height: 200px;
padding: 12px;
border: 2px solid #e2e8f0;
border-radius: 8px;
font-family: 'Courier New', monospace;
font-size: 12px;
resize: vertical;
transition: border-color 0.3s ease;
}

textarea:focus {
outline: none;
border-color: #667eea;
box-shadow: 0 0 0 3px rgba(102, 126, 234, 0.1);
}

input[type="text"] {
width: 100%;
padding: 12px;
border: 2px solid #e2e8f0;
border-radius: 8px;
font-size: 14px;
transition: border-color 0.3s ease;
}

input[type="text"]:focus {
outline: none;
border-color: #667eea;
box-shadow: 0 0 0 3px rgba(102, 126, 234, 0.1);
}

button {
background: linear-gradient(135deg, #667eea 0%, #764ba2 100%);
color: white;
border: none;
padding: 12px 30px;
border-radius: 8px;
cursor: pointer;
font-size: 16px;
font-weight: 600;
transition: transform 0.2s ease, box-shadow 0.2s ease;
margin-right: 10px;
margin-bottom: 10px;
}

button:hover {
transform: translateY(-2px);
box-shadow: 0 5px 15px rgba(102, 126, 234, 0.4);
}

button:active {
transform: translateY(0);
}

.button-group {
text-align: center;
margin: 20px 0;
}

.info {
background: #e6fffa;
border: 1px solid #81e6d9;
border-radius: 8px;
padding: 15px;
margin: 20px 0;
font-size: 14px;
color: #234e52;
}

.error {
background: #fed7d7;
border: 1px solid #fc8181;
border-radius: 8px;
padding: 15px;
margin: 20px 0;
color: #742a2a;
display: none;
}

.success {
background: #f0fff4;
border: 1px solid #9ae6b4;
border-radius: 8px;
padding: 15px;
margin: 20px 0;
color: #22543d;
display: none;
}
</style>
</head>
<body>
<div class="container">
<h1>🔓 Base64文件解码器</h1>

<div class="info">
<strong>使用说明:</strong><br>
1. 将base64编码的数据粘贴到下面的文本框中<br>
2. 输入要保存的文件名(包含扩展名)<br>
3. 点击"解码并下载"按钮即可下载还原的文件
</div>

<div class="input-group">
<label for="base64Input">Base64编码数据:</label>
<textarea id="base64Input" placeholder="请粘贴您的base64编码数据..."></textarea>
</div>

<div class="input-group">
<label for="filename">文件名:</label>
<input type="text" id="filename" placeholder="例如: certificate.p12, image.jpg, document.pdf" value="">
</div>

<div class="button-group">
<button onclick="decodeAndDownload()">🔽 解码并下载</button>
<button onclick="clearAll()">🗑️ 清空</button>
</div>

<div id="error" class="error"></div>
<div id="success" class="success"></div>
</div>

<script>
function decodeAndDownload() {
const base64Input = document.getElementById('base64Input').value.trim();
const filename = document.getElementById('filename').value.trim();
const errorDiv = document.getElementById('error');
const successDiv = document.getElementById('success');

// 隐藏之前的消息
errorDiv.style.display = 'none';
successDiv.style.display = 'none';

// 验证输入
if (!base64Input) {
showError('请输入base64编码数据');
return;
}

if (!filename) {
showError('请输入文件名');
return;
}

try {
// 清理base64数据(移除可能的空格、换行符等)
const cleanBase64 = base64Input.replace(/\s/g, '');

// 验证base64格式
if (!/^[A-Za-z0-9+/]*={0,2}$/.test(cleanBase64)) {
showError('无效的base64格式');
return;
}

// 解码base64
const binaryString = atob(cleanBase64);
const bytes = new Uint8Array(binaryString.length);

for (let i = 0; i < binaryString.length; i++) {
bytes[i] = binaryString.charCodeAt(i);
}

// 创建Blob对象
const blob = new Blob([bytes]);

// 创建下载链接
const url = URL.createObjectURL(blob);
const a = document.createElement('a');
a.href = url;
a.download = filename;
document.body.appendChild(a);
a.click();
document.body.removeChild(a);
URL.revokeObjectURL(url);

showSuccess(`文件 "${filename}" 已成功解码并开始下载!`);

} catch (error) {
showError('解码失败:' + error.message);
}
}

function clearAll() {
document.getElementById('base64Input').value = '';
document.getElementById('filename').value = '';
document.getElementById('error').style.display = 'none';
document.getElementById('success').style.display = 'none';
}

function showError(message) {
const errorDiv = document.getElementById('error');
errorDiv.textContent = message;
errorDiv.style.display = 'block';
}

function showSuccess(message) {
const successDiv = document.getElementById('success');
successDiv.textContent = message;
successDiv.style.display = 'block';
}

// 自动检测可能的文件类型
document.getElementById('base64Input').addEventListener('input', function() {
const base64 = this.value.trim();
const filenameInput = document.getElementById('filename');

if (base64 && !filenameInput.value) {
// 尝试检测文件类型
try {
const cleanBase64 = base64.replace(/\s/g, '');
const binaryString = atob(cleanBase64.substring(0, 100)); // 只解码前100个字符来检测

// 检测常见的文件签名
if (binaryString.startsWith('\x30\x82')) {
filenameInput.value = 'certificate.p12';
} else if (binaryString.startsWith('\xFF\xD8\xFF')) {
filenameInput.value = 'image.jpg';
} else if (binaryString.startsWith('\x89PNG')) {
filenameInput.value = 'image.png';
} else if (binaryString.startsWith('GIF')) {
filenameInput.value = 'image.gif';
} else if (binaryString.startsWith('%PDF')) {
filenameInput.value = 'document.pdf';
} else if (binaryString.startsWith('PK')) {
filenameInput.value = 'archive.zip';
}
} catch (e) {
// 忽略检测错误
}
}
});
</script>
</body>
</html>