通过Python脚本下载【来此加密的免费HTTPS SSL证书】并自动更新到服务器和阿里云CDN

本文已参与「新人创作礼」活动,一起开启掘金创作之路。

异想之旅:本人原创博客完全手敲,绝对非搬运,全网不可能有重复;本人无团队,仅为技术爱好者进行分享,所有内容不牵扯广告。本人所有文章仅在CSDN、掘金和个人博客(一定是异想之旅域名)发布,除此之外全部是盗文!


本文适用对象:要求证书更新后自动化部署(除了IIS之外的服务器软件)、能接受一定成本(19元)的个人开发者

去年3月网站使用SSL证书先是使用的腾讯云免费证书。这个证书虽然有一年的有效期,但是免费版只支持单域名,对我这样做了许多小微服务站点的开发者就很不友好了。于是我在知乎上找到了来此加密(https://letsencrypt.osfipin.com/)

这是一个基于Let's Encrypt项目的封装,选择它主要是由于它简单、一劳永逸的验证方式和API的支持,方便我们部署到自己的服务器。

如果你的需求是上传到阿里云等云服务商的SSL证书管理,不需要本地部署,可以考虑OHTTPS(https://ohttps.com/),操作更加方便,成本接近。

我们首先注册账号,然后点击左上角的三条横杠,获取积分,充值一定金额保证账户余额达到200积分,再点击积分兑换兑换VIP。紧接着去申请证书,如果你已经可以使用自动验证那么最好,没有的话也没关系,先稍微麻烦一下配几次DNS验证,申请完后可以更改为自动验证。

关于自动重申:如果你有钱去自动重申自然是更好的(也不贵,更新一次一块钱),但是本文接下来的方法如果仅使用自动验证也是可以的,只是需要你每次更新时上网站来点两下按钮罢了。

按照上述操作后应该会看到这样子的界面:

在这里插入图片描述

我们先手动部署一次证书,然后再记住给出的自动验证ID,接着就可以上代码了!

代码很简单,且相比官方给出的脚本可扩展性更强、同时兼容Linux和Windows。需要改的都在开头,该讲到的在注释说明了。

每次过期前13天起,程序会每12小时拉取一次证书,直到证书有效期变长(也就是已经在官网重新申请了的)。

API KEY获取:点击来此加密网页左上角三个杠,API接口,创建一个KEY

##### User Info #####

email = "[email protected]"  # 来此加密注册邮箱
key = "APIKEY"  # 你的API KEY
cids = ["29dwnz"]  # 每个需要更新证书的自动验证ID
crt_files = ["E:/Desktop/fullchain.crt"]  # 需要更新的证书文件的本地路径(顺序对应cid)
crt_key_files = ["E:/Desktop/private.pem"]  # 证书密钥文件(顺序对应cid)
reload_shell = ["nginx -s reload"]  # 更新证书后需执行的命令

##### End User Info #####

import os
import shutil
import time
import requests as rq
import zipfile

# 构建API请求参数
head = {"Authorization": "Bearer %s:%s" % (key, email)}
url = "https://api.osfipin.com/letsencrypt/api/order/down?type=auto&day&id="
# 从下载的文件取fullchain.crt和private.pem
crt_file_name = "fullchain.crt"
crt_key_file_name = "private.pem"


def unzip(file_name):
    # 解压zip文件
    zip_file = zipfile.ZipFile(file_name)
    if os.path.isdir(file_name + "_files"):
        pass
    else:
        os.mkdir(file_name + "_files")
    for names in zip_file.namelist():
        zip_file.extract(names, file_name + "_files/")
    zip_file.close()
    return file_name + "_files"


def ftime():
    # 获取格式化的时间显示
    return time.strftime("%Y-%m-%d %H:%M:%S", time.localtime())


def get_etime(file):
    # 检查crt文件的有效期,返回GMT格式字符串
    return os.popen("openssl x509 -in %s -noout -enddate -checkend 9123200" %
                    file).read().splitlines()[0].split("=")[1]


while True:
    # 遍历所有证书信息
    for i in range(len(cids)):
        # 初始化名称信息
        crt_file = crt_files[i]
        crt_key_file = crt_key_files[i]
        cid = cids[i]

        # 检查当前使用的crt文件到期时间(etime为GMT格式,etimes为时间戳)
        etime = ""
        etimes = 0
        if os.path.isfile(crt_file):
            etime = get_etime(crt_file)
            etimes = time.mktime(time.strptime(etime, "%b %d %H:%M:%S %Y GMT"))

        # 到期13天内则更新
        if etimes - time.time() < 86400 * 13:
            # 下载并保存证书文件(zip格式)
            r = rq.get(url + cid, headers=head)
            zname = r.headers["Content-Disposition"].split("=")[-1]
            with open(zname, "wb") as f:
                f.write(r.content)

            # 解压并更新文件
            fname = unzip(zname)
            shutil.copyfile(fname + "/" + crt_file_name, crt_file)
            shutil.copyfile(fname + "/" + crt_key_file_name, crt_key_file)
            etime = get_etime(crt_file)

            print("%s: 更新成功。更新后有效期至%s。" % (ftime(), etime))

            # 重启服务,并删除下载的临时文件
            for j in reload_shell:
                os.system(j)
            os.remove(zname)
            shutil.rmtree(fname)
        else:
            print("%s: 当前证书有效期至%s, 暂未更新。" % (ftime(), etime))

    # 休眠12小时
    time.sleep(3600 * 12)
复制代码

使用 nohup 或者 screen 挂起脚本运行,即可在每季度仅仅动一下手指的情况下更新证书了!

另外,本人有更新至阿里云CDN的需求,因此也为这个写了一个类似的脚本。腾讯云等可以略改一点后使用

##### User Info #####

# 下方所有列表配置内容的顺序均遵循cids的配置

### 来此加密用户信息 ###
email = ""
key = ""
cids = ["29dwnz", "6d4de1"]

### 本地服务器信息(证书和密钥存储位置)
crt_files = ["yixiangzhilv.com_fullchain.crt", "yxzl.top_fullchain.crt"]
crt_key_files = ["yixiangzhilv.com_private.pem", "yxzl.top_private.pem"]

### 阿里云CDN信息
ACCESS_KEY = ""
ACCESS_TOKEN = ""
domains = [["www.yixiangzhilv.com"], ["login.yxzl.top"]]  # 每个证书所对应的CDN域名

##### End User Info #####

import os
import shutil
import time
import requests as rq
import zipfile
from aliyunsdkcore.client import AcsClient
from aliyunsdkcore.auth.credentials import AccessKeyCredential
from aliyunsdkcdn.request.v20180510.SetDomainServerCertificateRequest import SetDomainServerCertificateRequest

crt_file_name = "fullchain.crt"
crt_key_file_name = "private.pem"
head = {"Authorization": "Bearer %s:%s" % (key, email)}
url = "https://api.osfipin.com/letsencrypt/api/order/down?type=auto&day&id="
credentials = AccessKeyCredential(ACCESS_KEY, ACCESS_TOKEN)
client = AcsClient(region_id="cn-hangzhou", credential=credentials)


def log(l):
    print(l)
    rq.post(
        "https://chat.yixiangzhilv.com/public-cdnssl",  # 可选,使用异想之旅chat获取结果。房间路径建议自定义
        data={
            "send_type": "secret",
            "text": l
        },
        cookies={"send_type": "secret"})


def upload(domain, cert_name, cert, cert_key):
    with open(cert, "r") as f:
        cert_content = f.read()
    with open(cert_key, "r") as f:
        cert_key_content = f.read()
    request = SetDomainServerCertificateRequest()
    request.set_accept_format("json")
    request.set_DomainName(domain)
    request.set_CertName(cert_name)
    request.set_CertType("upload")
    request.set_ServerCertificateStatus("on")
    request.set_ForceSet("1")
    request.set_ServerCertificate(cert_content)
    request.set_PrivateKey(cert_key_content)
    response = client.do_action_with_exception(request)
    print(str(response, encoding='utf-8'))


def unzip(file_name):
    # 解压zip文件到同一路径
    zip_file = zipfile.ZipFile(file_name)
    if os.path.isdir(file_name + "_files"):
        pass
    else:
        os.mkdir(file_name + "_files")
    for names in zip_file.namelist():
        zip_file.extract(names, file_name + "_files/")
    zip_file.close()
    return file_name + "_files"


def ftime():
    # 获取格式化的时间显示
    return time.strftime("%Y-%m-%d %H:%M:%S", time.localtime())


def get_etime(file):
    # 检查crt文件的有效期,返回GMT格式字符串
    return os.popen("openssl x509 -in %s -noout -enddate -checkend 9123200" %
                    file).read().splitlines()[0].split("=")[1]


while True:
    # 遍历所有证书信息
    for i in range(len(cids)):
        # 初始化名称信息
        crt_file = crt_files[i]
        crt_key_file = crt_key_files[i]
        cid = cids[i]

        # 检查当前使用的crt文件到期时间
        etime = ""
        etimes = 0
        if os.path.isfile(crt_file):
            etime = get_etime(crt_file)
            etimes = time.mktime(time.strptime(etime, "%b %d %H:%M:%S %Y GMT"))

        # 到期13天内则更新
        if etimes - time.time() < 86400 * 13:
            r = rq.get(url + cid, headers=head)
            zname = r.headers["Content-Disposition"].split("=")[-1]
            with open(zname, "wb") as f:
                f.write(r.content)

            # 解压并复制文件
            fname = unzip(zname)
            shutil.copyfile(fname + "/" + crt_file_name, crt_file)
            shutil.copyfile(fname + "/" + crt_key_file_name, crt_key_file)
            etime = get_etime(crt_file)
            for j in domains[i]:
                upload(j, cid, crt_file, crt_key_file)

            log("%s: 更新成功%s。更新后有效期至%s。" % (ftime(), cid, etime))

            # 删除下载的临时文件
            os.remove(zname)
            shutil.rmtree(fname)
        else:
            print("%s: 当前证书有效期至%s, 暂未更新。" % (ftime(), etime))

    # 休眠12小时
    time.sleep(3600 * 12)

复制代码

如果有人注册来此加密,还烦请您可以填写我的邀请码获取积分:ND8Y17QD

点个赞再走吧~

猜你喜欢

转载自juejin.im/post/7082606332552937508