时间戳签名
时间戳签名的主要用途是提供一个文件或数据在某个特定时间点存在的无可争议的证明
假设我编写了一篇文章,并将其进行时间戳签名。在未来,有人盗取了这篇文章,我就可以提供时间戳签名来证明我最初拥有这篇文章,从而维护自己的知识产权
免费的时间戳服务器(TSA, Time-Stamp Authority)有很多,最好选一个有足够公信力比较靠谱的大厂
这里选择globalsign,老牌大厂,公信力足够高,TSA服务器地址为http://rfc3161timestamp.globalsign.com/advanced
签名步骤为:创建时间戳请求文件 -> 发送到TSA服务器进行签名 -> 保管好签名证书和源文件,并在未来必要时进行验证
假设文件为example.txt,以下使用openssl进行演示
创建时间戳请求文件(TSQ),openssl的默认哈希算法是sha256
1
| openssl ts -query -data example.txt -cert -no_nonce -out example.tsq
|
发送到TSA服务器,签名时间戳
1
| curl -H "Content-Type: application/timestamp-query" --data-binary @example.tsq http://rfc3161timestamp.globalsign.com/advanced -o example.tsr
|
example.tsr就是签名完的时间戳证书,现在把example.tsr文件和example.txt只读备份保存好,tsq文件就可以删掉了
1 2 3 4
| #查看tsr证书内容 openssl ts -reply -in example.tsr -text #验证签名 openssl ts -verify -in example.tsr -data example.txt -CAfile r6.pem
|
显示Verification: OK
就是验证成功
r6.pem
是globalsign的公钥,可以从以下地址获取https://support.globalsign.com/ca-certificates/root-certificates/globalsign-root-certificates
注意一个小坑:pem格式是base64编码,不要直接点击下载Download Certificate
,点击View in Base64
把base64复制下来到自己创建的r6.pem文件中
python自动化
用openssl要输入很多指令,太麻烦,所以可以用python自动化实现
asn1crypto库可以创建和解析asn.1结构
以下代码计算文件的sha256并创建tsq
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19
| from asn1crypto import tsp, core
def sha256_file(file_path:str) -> bytes: sha256_hash = hashlib.sha256() with open(file_path, "rb") as f: for byte_block in iter(lambda: f.read(50 * 1024 * 1024), b""): sha256_hash.update(byte_block) return sha256_hash.digest()
def get_file_tsq(file_path:str, set_nonce:bool = False, require_cert:bool=False) -> bytes: return tsp.TimeStampReq({ 'version': 1, 'message_imprint': tsp.MessageImprint({ 'hash_algorithm': {'algorithm': 'sha256'}, 'hashed_message': sha256_file(file_path) }), 'nonce': core.Integer(random.getrandbits(64)) if set_nonce else None, 'cert_req': require_cert }).dump()
|
发送到TSA服务器签名后保存到tsr文件中
1 2 3 4 5 6 7 8
| def requests_tsa(tsa:str, tsq:bytes) -> bytes: response = requests.post(tsa, headers={"Content-Type":"application/timestamp-query"}, data=tsq) response.raise_for_status() return response.content
file = 'example.txt' with open(file + ".tsr","wb") as f: f.write(requests_tsa('http://rfc3161timestamp.globalsign.com/advanced', get_file_tsq(file)))
|
完整的命令行工具,详见 https://github.com/Brassinolide/pyts