WebFrontend
2020-03-02
早期只有一种浏览器存储方式,就是万维网早期,由网景公司设计,加入了 HTTP 1.0 的 Cookie。
HTTP5 的时代,一次性加入了三种 API,分别是 Web Storage,IndexedDB,Web SQL。
- Cookie:通过小型文本文件,在浏览器端存储一些字符类型 key-value 数据,支持设置一些属性。
- 有单个 Cookie 的大小限制,也有 Cookie 总数限制。这个因浏览器不同而不同(大小尽可能控制在 4KB 以内)。
- 每一次 HTTP 调用都会自动带上,发送给服务器端。
- Web Storage:可以存储大量字符类型的 key-value 数据。
- localStorage:没有时间限制。
- sessionStorage:会话结束时自动清除。
- IndexedDB:NoSQL 数据库,可以存储大量的结构化数据。API 相对复杂一丢丢
- 功能强大,甚至支持事务和索引。
- 异步 API。
- Web SQL:基于 SQL 的浏览器端数据库,后来被废弃。
Cookie
Set-Cookie: name=value; expires=Mon, 21 Oct 2019 07:28:00 GMT; path=/; domain=.example.com; secure; HttpOnly
expires
过期时间,GMT 格式。如果不设置该属性,则 Cookie 的生命周期为当前会话,即关闭浏览器后 Cookie 就会被删除。
path
路径,表示该 Cookie 归属于哪个路径。默认为当前页面的路径。
domain
域名,表示该 Cookie 归属于哪个域名。默认为当前页面的域名。
secure
只能通过 HTTPS 协议传输,不能通过 HTTP 协议传输。
HttpOnly
只在网络传输时使用,不能通过 JavaScript 访问。
Web Storage
localStorage.setItem("key", "value");
var value = localStorage.getItem("key");
sessionStorage.setItem("key", "value");
var value = sessionStorage.getItem("key");
setItem(key, value)
存储数据
getItem(key)
读取数据
removeItem(key)
删除数据
clear()
清空数据 (删除所有的键值对)
key(index)
获取键名 (根据索引获取对应的键名)
IndexedDB
没有研究过。
// 打开数据库
var request = indexedDB.open("myDatabase", 1);
// 创建对象仓库
request.onupgradeneeded = function (event) {
var db = event.target.result;
var objectStore = db.createObjectStore("users", { keyPath: "id" });
objectStore.createIndex("name", "name", { unique: false });
objectStore.createIndex("age", "age", { unique: false });
};
// 存储数据
request.onsuccess = function (event) {
var db = event.target.result;
var transaction = db.transaction(["users"], "readwrite");
var objectStore = transaction.objectStore("users");
var user = { id: 1, name: "张三", age: 20 };
var request = objectStore.add(user);
request.onsuccess = function (event) {
console.log("数据存储成功");
};
};
// 读取数据
request.onsuccess = function (event) {
var db = event.target.result;
var transaction = db.transaction(["users"], "readonly");
var objectStore = transaction.objectStore("users");
var index = objectStore.index("name");
var request = index.get("张三");
request.onsuccess = function (event) {
var user = event.target.result;
console.log(user);
};
};
个人
2020-03-01
与老婆签订的不平等条约,应她要求公开 😭
Git
2020-02-24
https://mp.weixin.qq.com/s/67qBDteTmHROeMwOBUeyaw
如何通过 Git 和 Husky 添加提交钩子并实现代码任务自动化
钩子 |
时机 |
用途 |
pre-commit |
提交之前 |
代码检查 |
prepare-commit-msg |
提交信息生成之前 |
生成提交信息 |
commit-msg |
提交信息保存之前 |
检验提交信息 |
post-commit |
提交之后 |
通知,自动测试,CI 等 |
pre-push |
push 之前 |
代码检查,测试,编译打包 |
applypatch-msg |
生成补丁时 |
验证补丁信息 |
fsmonitor-watchman |
文件系统监视器发现变化时 |
触发版本控制操作 |
pre-applypatch |
应用补丁之前 |
验证补丁信息 |
pre-merge-commit |
合并之前 |
检查将要合并的分支是否符合要求 |
pre-rebase |
rebase 操作之前 |
- |
push-to-checkout |
- |
- |
pre-receive |
接受提交之前 |
代码检查,校验权限 |
post-receive |
接受提交之后 |
通知,自动测试,CI 等 |
update |
更新操作之前(分支、Tag) |
提供从旧版本到新版本的改动列表供用户审核 |
post-update |
更新操作之后(分支、Tag) |
通知,自动测试,CI 等 |
示例
pre-receive
#!/usr/bin/env python
"""
每个人都只能提交代码到 username-date-branchName
username 是 git 用户名
date 是 mmdd 日期
branch 是分支描述,支持小写字母、数字、横杠,2 到 16 个字符
"""
import re
import subprocess
import sys
# 获取提交者的用户名
author = subprocess.check_output(['git', 'config', 'user.name']).decode().strip()
# 获取提交的分支名称
branch = subprocess.check_output(['git', 'rev-parse', '--abbrev-ref', 'HEAD']).decode().strip()
# 定义分支名称的正则表达式
branch_pattern = r'^%s-\d{4}-[a-z0-9\-]{2,}$' % (author,)
# 检查分支名称是否符合正则表达式
if not re.match(branch_pattern, branch):
print('Error: Branch name "{}" does not match the required pattern "{}"'.format(branch, branch_pattern), file=sys.stderr)
sys.exit(1)
# 解析日期并检查其是否合法
try:
_, date_str, _ = branch.split('-')
month, day = int(date_str[:2]), int(date_str[2:])
if month < 1 or month > 12 or day < 1 or day > 31:
raise ValueError
except ValueError:
print('Error: Invalid date format in branch name "{}"'.format(branch), file=sys.stderr)
sys.exit(1)
Email 信息安全
2020-02-17
:) 本文正在编辑中,暂时不提供浏览...
Email 信息安全
2020-02-11
简介
SMTP 会话中,发行方会通过 MAIL FROM
命令告诉收信方,这封邮件是谁发出的。
但是,由于邮件系统的设计上没有考虑如何对垃圾邮件进行有效防范,收信方需要一个机制来判断这封邮件的来源是否可靠。因此,人们提出了 SPF 方案:
- 每个邮件服务都需要为自己的发信域名在 DNS 中配置一下 SPF 记录(TXT 类型),记录值是出信 IP。
- 收信方拿到
MAIL FROM
地址之后,使用发信方 IP 地址和发信域 DNS 中记录的 IP 地址做一个比对。如果匹配,说明来源正确,否则,这封邮件可以被视为垃圾邮件,直接返回投递失败(硬退)。
该方案 2006 年提交到 Network Working Group 成为草案,然后 2014 年成为建议标准。
注意:SPF 只是验证了邮件确实是从发信域所在网络投递出来的,但是不能防止邮件伪造和欺诈。应该结合其他技术,如 DKIM(DomainKeys Identified Mail)和 DMARC(Domain-based Message Authentication, Reporting, and Conformance),一起增强电子邮件的安全性。
SPF 记录示例
dig +short txt qq.com
"v=spf1 include:spf.mail.qq.com -all"
dig +short txt spf.mail.qq.com
"v=spf1 include:qq-a.mail.qq.com include:qq-b.mail.qq.com include:qq-c.mail.qq.com include:biz-a.mail.qq.com include:biz-b.mail.qq.com include:biz-c.mail.qq.com include:biz-d.mail.qq.com -all"
dig +short txt qq-{a..c}.mail.qq.com biz-{a..d}.mail.qq.com
"v=spf1 ip4:101.226.139.0/25 ip4:101.91.43.0/25 ip4:101.91.44.128/25 ip4:112.64.237.128/25 ip4:116.128.173.0/25 ip4:121.51.40.128/25 ip4:121.51.6.0/25 ip4:162.62.52.214 ip4:162.62.55.67 ip4:162.62.57.0/24 ip4:162.62.58.211 ip4:162.62.58.216 -all"
"v=spf1 ip4:162.62.58.69 ip4:162.62.63.194 ip4:180.163.24.128/25 ip4:183.2.187.0/25 ip4:203.205.221.128/25 ip4:203.205.251.0/25 ip4:210.51.43.0/25 ip4:58.246.222.128/25 ip4:58.250.143.128/25 ip4:61.241.55.128/25 -all"
"v=spf1 ip4:113.108.92.0/25 ip4:121.14.77.0/25 ip4:81.69.217.16/28 ip4:54.164.151.162 -all"
"v=spf1 ip4:114.132.122.39 ip4:114.132.123.192 ip4:114.132.124.171 ip4:114.132.125.233 ip4:114.132.197.227 ip4:114.132.224.180 ip4:114.132.233.22 ip4:114.132.58.0/24 ip4:43.155.65.254 ip4:114.132.62.0/24 ip4:106.55.200.77 -all"
"v=spf1 ip4:114.132.63.24 ip4:114.132.64.0/26 ip4:114.132.65.219 ip4:43.155.67.158 ip4:114.132.67.179 ip4:114.132.73.137 ip4:114.132.74.132 ip4:43.154.209.5 ip4:43.154.197.177 ip4:43.154.155.102 ip4:43.155.80.173 ip4:43.154.221.58 -all"
"v=spf1 ip4:54.204.34.129 ip4:54.204.34.130 ip4:54.243.244.52 ip4:52.205.10.60 ip4:35.173.142.173 ip4:54.207.22.56 ip4:54.207.19.206 ip4:54.254.200.92 ip4:54.254.200.128 ip4:54.92.39.34 ip4:54.206.16.166 ip4:54.206.34.216 ip4:114.132.75.215 -all"
"v=spf1 ip4:52.59.177.22 ip4:18.194.254.142 ip4:18.132.163.193 ip4:18.169.211.239 ip4:13.245.186.79 ip4:13.245.218.24 ip4:15.184.224.54 ip4:15.184.82.18 ip4:114.132.76.87 ip4:114.132.77.159 ip4:114.132.78.196 ip4:114.132.79.153 ip4:43.154.54.12 -all"
语法
-
"v=spf1":指定 SPF 记录的版本,目前只有 SPFv1 版本。
-
机制(Mechanisms):SPF 使用机制来指定哪些服务器被授权发送邮件。常用的机制有:
-
"all":定义了所有情况下的默认结果。可以是 "+all"(通过验证)或 "-all"(不通过验证)。例如,"-all" 表示除非匹配其他通过机制,否则所有情况都不通过验证,即严格拒绝伪造邮件。
- "ip4" 和 "ip6":指定允许发送邮件的 IPv4 和 IPv6 地址。例如:"ip4:192.0.2.1" 表示允许来自 192.0.2.1 的服务器发送邮件。
- "a" 和 "mx":允许域名的 A 记录和 MX 记录指定的主机发送邮件。
-
"include":允许引用其他域名的 SPF 记录。例如:"include:example.com" 表示允许按照 example.com 的 SPF 记录来验证。
-
修饰符(Modifiers):修饰符可以在机制之后指定,用于调整验证的结果。常用的修饰符有:
-
"+":显示通过验证。
- "-":显示不通过验证,相当于 "-all"。
- "~":软失败,邮件可能被接受,但会标记为不可信。
- "?":中性结果,邮件可能被接受,但不影响验证的结果。
SPF 记录示例:
v=spf1 ip4:192.0.2.1 a mx ~all
以上示例表示允许 IP 地址为 192.0.2.1 的服务器、A 记录和 MX 记录指定的主机发送邮件,但验证结果为软失败,邮件可能会被接受,但标记为不可信。
DNS SPF 类型
IANA 设计了一种专门的 DNS 类型用来记录 SPF 信息,但是采用率非常低。
可以忽略,继续使用 TXT 类型来保存 SPF 信息。
参考资料与拓展阅读
Email 信息安全
2020-02-10
DomainKeys Identified Mail,域名密钥识别邮件
作用是使用非对称加密(公钥 + 私钥)对邮件内容进行签名,防止伪造和篡改。
历史
- 2004 年,雅虎 DomainKeys 和思科 Identified Internet Mail 合并为 DKIM。
- 2007 年 2 月,DKIM 被列入互联网工程工作小组(IETF)的标准提案(Proposed Standard),并于同年 5 月成为正式标准(Standards Track)。
示例
DKIM-Signature: v=1; a=rsa-sha256; d=example.net; s=brisbane;
c=relaxed/simple; q=dns/txt; i=foo@eng.example.net;
t=1117574938; x=1118006938; l=200;
h=from:to:subject:date:keywords:keywords;
z=From:foo@eng.example.net|To:joe@example.com|
Subject:demo=20run|Date:July=205,=202005=203:44:08=20PM=20-0700;
bh=MTIzNDU2Nzg5MDEyMzQ1Njc4OTAxMjM0NTY3ODkwMTI=;
b=dzdVyOfAKCdLXdJOc9G2q8LoXSlEniSbav+yuU4zGeeruD00lszZ
VoG4ZHRNiYzR
Tag |
Required |
Meanning |
v |
✓ |
version |
a |
✓ |
signing algorithm |
d |
✓ |
Signing Domain Identifier (SDID) |
s |
✓ |
selector |
c |
- |
canonicalization algorithm(s) for header and body |
q |
- |
default query method |
i |
- |
Agent or User Identifier (AUID) |
t |
recommended |
signature timestamp |
x |
recommended |
expire time |
l |
- |
body length |
h |
✓ |
header fields - list of those that have been signed |
z |
- |
header fields - copy of selected header fields and values |
bh |
✓ |
body hash |
b |
✓ |
signature of headers and body |
示例 2
邮件中的 DKIM-Signature 头:
DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/simple; d=mail.instagram.com;
s=s1024-2013-q3; t=1674163477;
bh=6RXa/HYJQNKpB5PIGtLn7v1NE/4T5FaqxBLWNHVRZu8=;
h=Date:To:Subject:From:MIME-Version:Content-Type;
b=VAY3x16QtXeH1rQxu6eEbzhfgZl69m1sG9XzN3ym4FbWiMg+K+IfMGF4yszGYk8yO
YXAAJZuQfG45pjthISDDSwhhBK0WGgufQ8ofnzhNUN9WT/okEATC+JfzksS9w2Ts4V
ALa/4HHXnikQV5AFNJJNJIvWMN/fJ5c49nLkW024=
域名中的 DKIM 公钥:
dig TXT +short s1024-2013-q3._domainkey.mail.instagram.com
"k=rsa; t=s; h=sha256; p=MIGfMA0GCSqGSIb3DQEBAQUAA4GNADCBiQKBgQC7twdVo+BW8Pv2poU5129KYmE6npHdxUU8fktUKTE9TNovCvLy5LVjYc3TQcUFjOH" "VaZ89ZCjmpAcrA2QnTEKZ/2QWV56gn6bWdFW4SFxnQdHjguBZQykfKe5KTxy2a/OxuA0x2dHfdnYfw7RVzr4uednpKcWJy4Rl3gM6XB1zDwIDAQAB"
Python 编程中的应用
准备工作
- selector 选择 s2020 (随便)
- 生成密钥对
# 方法一:使用 OpenDKIM
sudo apt install -y opendkim-tools
opendkim-genkey --help
opendkim-genkey -D . -d markjour.com -s s2020
# 方法二:使用 OpenSSL
openssl genrsa -out dkim-markjour-s2020.pem 2048
openssl rsa -in dkim-markjour-s2020.pem -pubout -outform der 2>/dev/null | openssl base64 -A > dkim-markjour-s2020.pub
- 配置 DNS
s2020._domainkey.markjour.com
"v=DKIM1; h=sha256; k=rsa; p=MIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEAjnfmXLuyBt0Tus/Bdr87GpLqcRCZX7UC61OiPZ1Y3MG42qBcdEAMJfu7qop7KOLL8cywTRxiX39ehmf0ZovAXH1KkijiX/16tkI3cO9T6KS4vyr0Ip3fsGgNgjn5rH3M5AZAmbym6DIzYrtpTiAKgLYmFLALd9SLi/OhFIltWK+QJhaJgcuWUXCzlry01Fdsv1qj28WdZ6PQbQrSffc1qzkvOEOlmZXwWjQfg5X4E3DR4WKenC6f5WdcJeXk4pUeBOdQDoEM+4uCk4S6cN3OuYEQbvVmfQ5RAlCODccx7lJemWZZnlIf+03FppUEEMENZ8tu3iixD24m2q9wDLDL5QIDAQAB
v
版本,只有一种选择:DKIM1,必须放最前面,可以忽略
h
哈希算法,sha1 或者 sha256
k
密钥类型,只有一种选择:rsa,可以忽略
n
注释,可以忽略
p
公钥(ASN.1 DER-encoded + Base64)
s
服务类型,默认 *
,表示所有服务,可以忽略
t
逗号隔开的标记
- y 测试
- s DKIM 签名中的 i= 域名必须是 d= 域名完全相同(子域也不行)。
参考:
签名
headers = {'aaa': '123', 'bbb': '456', 'ccc': '789'}
校验签名
mail = """
DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=qq.com; s=s0907;
t=1331524836; bh=Pqr4lbxMcef/3IqsXx/edT0iwPe18N7n8qKmQSnLio8=;
h=X-QQ-SSF:X-QQ-Spam:X-QQ-BUSINESS-ORIGIN:X-Originating-IP:
X-QQ-STYLE:X-QQ-mid:From:To:Sender:Subject:Mime-Version:
Content-Type:Content-Transfer-Encoding:Date:X-Priority:Message-ID:
X-QQ-MIME:X-Mailer:X-QQ-Mailer:X-QQ-ReplyHash;
b=hF3hXt429Mp9WUJx9wQQYYk32EABCQST/OmV+dI+vJ/XIidVkc6fsh8l/vBz/optb
MDp0XIupHHkUozz6jwMryhHd/ZNjLNtBBAIOgl1wH7R016x8uTtDQink5uIPH+5
X-QQ-SSF: 0000000000010060
X-QQ-Spam: true
X-QQ-BUSINESS-ORIGIN: 2
X-Originating-IP: 61.151.148.196
X-QQ-STYLE:
X-QQ-mid: bizmail6t1331524835t2734595
From: "=?gb18030?B?wffE6g==?=" <thinkphp@qq.com>
To: "=?gb18030?B?uvqwug==?=" <ninedoors@126.com>
Sender: liuchen@topthink.net
Subject: =?gb18030?B?UmU60rvOu1RQZXK1xNLJu/M=?=
Mime-Version: 1.0
Content-Type: multipart/alternative;
boundary="----=_NextPart_4F5D74E3_DF6406C0_249C5176"
Content-Transfer-Encoding: 8Bit
Date: Mon, 12 Mar 2012 12:00:35 +0800
X-Priority: 3
Message-ID: <tencent_519100472CAA258E750FA58D@qq.com>
X-QQ-MIME: TCMime 1.0 by Tencent
X-Mailer: QQMail 2.x
X-QQ-Mailer: QQMail 2.x
X-QQ-ReplyHash: 3949397671
""".strip('\n')
软件设计
2020-02-08
:) 本文正在编辑中,暂时不提供浏览...
Python 加密
2020-02-05
和 PyCrypto 的关系
PyCrypto 是 Python 界最知名的加密模块,它提供了一系列的加密算法,包括对称加密、非对称加密、哈希算法、签名算法等。
不过有一个很大的问题:上一个版本 2.6.1 发布于 2013-10-18,已经很多年没有维护了。
PyCryptodome 是 PyCrypto 的分叉,该项目在统一套代码的基础上提供了两种包:pycryptodome
和 pycryptodomex
:
- 前者保持对 PyCrypto 的兼容,所有的代码都在
Crypto
名称下,
-
后者丢掉了历史包袱,放弃对 PyCrypto 的兼容,所有代码都在 Cryptodome
名称下。
-
https://pypi.org/project/pycryptodome/
- https://pypi.org/project/pycryptodomex/
- https://www.pycryptodome.org/en/latest/
- https://github.com/Legrandin/pycryptodome/
其他加密模块
示例
云计算
2020-02-01
公有云 Public Cloud
云计算厂商提供的公共服务,比如腾讯云、阿里云等。
私有云 Private Cloud
个人和组织自己搭建的(可能是厂商帮忙搭建),自用的云计算服务。机器由个人或组织持有(可能在云计算厂商的机房中)。
优点是数据完全掌握在自己手中。如果规模没有达到一定程度的话,这样做的成本会比较高(除了固定资产的投入之外,维护成本不可忽视)。
有一种形式,是一些利益相关的个体,对计算有着相同或相似的需求,然后共同搭建一个云服务平台。
比如 XX 市弄一个政务云,下属政府机构直接使用这个云服务平台。
又比如某地教育单位弄一个云服务平台,下属学校有啥服务的话,就都使用这个云服务平台。
专有云
有些厂商提供 “专有云” 的服务:整套设施就为单个客户专门提供服务。设备属于厂商所有。
感觉这个算公有云,私有云的交叉地带,只好单独算一类。
知乎上看到一个比喻:公有云是小姐,私有云是老婆,专有云是小三 (包养)。比较贴切。
混合云 Hybrid Cloud
一般意义上,混合云是指公有云和私有云的结合。
PS: 多个私有云之间,或多个公有云之间,相互共享资源,也可以叫做混合云。
应该是通过一些对接好了各大厂商(比如腾讯云,阿里云)和常见私有云平台(比如 OpenStack,CloudStack)的管理工具,对所有资源进行整合管理。
也可能部分二次开发过的私有云平台支持对接公有云,可以直接调度和整合公有云资源。
开发者 ASN1 网络编程
2020-01-30
我印象中曾在某个项目中接触到了这种格式,但是一时间竟也想不起来。
PS: 可能是有一次涉及 LDAP 协议的时候。
概念
ASN 全名 Abstract Syntax Notation, 翻译过来就是:抽象语法标记。
ASN.1 可能是第一版的意思(?)。
asn.1 是一套国际标准,用来定义一种通用的、严谨的数据表示(标记)方法,以及对应的数据编码格式。
PS:对数据 Scheme 的定义独立于硬件架构和编程语言。
- ITU-T Rec. X.680 (2015) | ISO/IEC 8824-1:2015
Specification of basic notation
- ITU-T Rec. X.681 (2015) | ISO/IEC 8824-2:2015
Information object specification
- ITU-T Rec. X.682 (2015) | ISO/IEC 8824-3:2015
Constraint specification
- ITU-T Rec. X.683 (2015) | ISO/IEC 8824-4:2015
Parameterization of ASN.1 specifications
- ITU-T Rec. X.690 (2015) | ISO/IEC 8825-1:2015
BER, CER and DER
PS:常见证书格式 der 就是来自这个 DER。
- ITU-T Rec. X.691 (2015) | ISO/IEC 8825-2:2015
PER (Packed Encoding Rules)
- ITU-T Rec. X.692 (2015) | ISO/IEC 8825-3:2015
ECN (Extended Component Notation)
- ITU-T Rec. X.693 (2015) | ISO/IEC 8825-4:2015
XER (XML Encoding Rules)
- ITU-T Rec. X.694 (2015) | ISO/IEC 8825-5:2015
Mapping W3C XML schema definitions into ASN.1
- ITU-T Rec. X.695 (2015) | ISO/IEC 8825-6:2015
Registration and application of PER encoding instructions
- ITU-T Rec. X.696 (2015) | ISO/IEC 8825-7:2015
OER (Octet Encoding Rules)
- ITU-T Rec. X.697 (2017) | ISO/IEC 8825-8:2018
JER (JSON Encoding Rules)
一般又被称之为 X.680 系列,最早是 1995 年出第一版。最新的是 2018 年出的 5.4 版(X.680 (2015) Amd. 1)
PS:2021 年 X.680 出了第六版。
部分应用层的网络协议就使用了 ASN.1 格式,比如 X.500 Directory Services,LDAP,VoIP,PKCS,Kerberos,移动通信(2G/GSM,GRPS,一直到 5G)。
它和 JSON 这种通用数据交换格式完全不同,更加类似与 protobuf,msgpack,thrift 这样,提供一个完备的数据定义语法用来声明 Schema(ASN.1 称之为模块),然后基于二进制紧凑地表示数据。所以非常适合用在 C/S 架构的网络编程上,作为服务通讯协议的一部分,负责内外数据交换,也就是 TCP/UDP 服务的接口部分。
如果要将 ASN.1 归类的话,更贴切的应该是接口定义语言,或者叫协议定义语言。
要是了解到 ASN.1 出现的年份(1984)的话,对照它的竞争者出现的时间,会发现它的设计确实比较超前。不管怎么说,这些晚辈确实更加流行,作为国际标准的 ASN.1 不够卖座,肯定是也有不好的地方。
PS:可能是 ASN.1 历史包袱太重, 不够轻便 (我看到的一些评论和我的猜想比较符合)。
数据定义
先来一个示例(维基上找来的,感觉没啥意义):
FooProtocol DEFINITIONS ::= BEGIN
FooQuestion ::= SEQUENCE {
-- 跟踪编号,后面括号是限制值的范围
trackingNumber INTEGER(0..199),
-- 问题内容,字符串
question IA5String
}
FooAnswer ::= SEQUENCE {
-- 问题编号
questionNumber INTEGER(10..20),
-- 答案内容
answer BOOLEAN
}
FooHistory ::= SEQUENCE {
-- 问题数组
questions SEQUENCE(SIZE(0..10)) OF FooQuestion,
-- 答案数组
answers SEQUENCE(SIZE(1..10)) OF FooAnswer,
-- 一个整型数组
anArray SEQUENCE(SIZE(100)) OF INTEGER(0..1000),
...
}
END
基本语法
- 大小写字母,数字,短横杠,空格
标识符:小写字母开头
类型名称:大写字母开头
- 多个空白符号(空格、换行)会当作一个空格
- 数据类型都有一个 TagNumber
--
注释
数据类型
简单类型
结构化类型
标记类型
其他类型:CHOICE
,ANY
类别:
- 0 Universal 通用类型
- 1 Application 应用协议相关类型
- 2 Context-specific
- 3 Private 自定义
结构化:
原始类型:
Type |
Tag number |
备注 |
INTEGER |
2 |
整型 |
BIT STRING |
3 |
|
OCTET STRING |
4 |
|
NULL |
5 |
NULL |
OBJECT IDENTIFIER |
6 |
对象 |
SEQUENCE and
SEQUENCE OF |
16 |
数组 |
SET and SET OF |
17 |
集合 |
PrintableString |
19 |
字符串 |
T61String |
20 |
|
IA5String |
22 |
|
UTCTime |
23 |
时间 |
示例:
编码规则
- 基本编码规则(BER,Basic Encoding Rules)
- 规范编码规则(CER,Canonical Encoding Rules)
- 唯一编码规则(DER,Distinguished Encoding Rules)
- 压缩编码规则(PER,Packed Encoding Rules)
- XML 编码规则(XER,XML Encoding Rules)
Python
https://www.cnblogs.com/20175211lyz/p/12769883.html
https://github.com/etingof/pyasn1
上面的示例通过 asn1ate /tmp/foo.asn > /tmp/foo.py
生成 Python 代码:
PS:并不是一定需要定义成这样类的结构,只是 pyasn1 库适合这样用而已。
from pyasn1.type import univ, char, namedtype, namedval, tag, constraint, useful
class FooAnswer(univ.Sequence):
pass
FooAnswer.componentType = namedtype.NamedTypes(
namedtype.NamedType('questionNumber', univ.Integer().subtype(subtypeSpec=constraint.ValueRangeConstraint(10, 20))),
namedtype.NamedType('answer', univ.Boolean())
)
class FooQuestion(univ.Sequence):
pass
FooQuestion.componentType = namedtype.NamedTypes(
namedtype.NamedType('trackingNumber', univ.Integer().subtype(subtypeSpec=constraint.ValueRangeConstraint(0, 199))),
namedtype.NamedType('question', char.IA5String())
)
class FooHistory(univ.Sequence):
pass
FooHistory.componentType = namedtype.NamedTypes(
namedtype.NamedType('questions', univ.SequenceOf(componentType=FooQuestion()).subtype(subtypeSpec=constraint.ValueSizeConstraint(0, 10))),
namedtype.NamedType('answers', univ.SequenceOf(componentType=FooAnswer()).subtype(subtypeSpec=constraint.ValueSizeConstraint(1, 10))),
namedtype.NamedType('anArray', univ.SequenceOf(componentType=univ.Integer().subtype(subtypeSpec=constraint.ValueRangeConstraint(0, 1000))).subtype(subtypeSpec=constraint.ValueSizeConstraint(100, 100)))
)
然后就可以使用了:
import foo
from pyasn1.codec.der.encoder import encode
fa = foo.FooAnswer()
fa['questionNumber'] = 10
fa['answer'] = False
fa_encoded = encode(fa)
print(fa_encoded) # b'0\x06\x02\x01\n\x01\x01\x00'
print(binascii.b2a_hex(fa_encoded).decode()) # 300602010a010100
from pyasn1.codec.der.decoder import decode
obj, rest = decode(fa_encoded)
print(obj)
# Sequence:
# field-0=10
# field-1=False
for k, v in obj.items():
print([k, v])
# ['field-0', <Integer value object, tagSet <TagSet object, tags 0:0:2>, payload [10]>]
# ['field-1', <Boolean value object, tagSet <TagSet object, tags 0:0:1>, subtypeSpec <ConstraintsIntersection object, consts <SingleValueConstraint object, consts 0, 1>>, namedValues <NamedValues object, enums False=0, True=1>, payload [False]>]
obj, rest = decode(fa_encoded, asn1Spec=foo.FooAnswer())
print(obj)
# FooAnswer:
# questionNumber=10
# answer=False
# print(dict(obj.items()))
print(dict([(k, str(v)) for k, v in obj.items()]))
# {'questionNumber': '10', 'answer': 'False'}
print(obj['questionNumber'].__dict__)
print(obj['questionNumber']._value) # 10
print(obj['answer'].__dict__)
print(obj['answer']._value) # 0
print([int(obj['questionNumber']), bool(obj['answer'])])
GitHub 找到的几个相关库:
- wbond/asn1crypto
Python ASN.1 library with a focus on performance and a pythonic API
- etingof/pyasn1
Generic ASN.1 library for Python
- eerimoq/asn1tools
ASN.1 parsing, encoding and decoding.
- P1sec/pycrate
A Python library to ease the development of encoders and decoders for various protocols and file formats; contains ASN.1
参考资料与拓展阅读