0%

X.509

X.509是国际电联ITU-T的标准,用于规范基于公钥密码体系PKI(public key infrastructure)体系的数字证书管理。其标准主要由RFC5280[1]描述,现在常用的数字证书正是基于X.509标准的。

证书结构

X.509 v3数字证书的结构如下:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
Certificate
Version #版本号,区分不同的X.509版本
Serial Number #序列号,CA分配给证书的唯一编号
#签名算法,CA签发证书时使用的公开密钥算法和摘要算法,比如PKCS #1 SHA-1 With RSA Encryption
Certificate Signature Algorithm
Issuer #证书签发机构
Validity #有效期限
Not Before #起始日期
Not After #终止日期
Subject #证书主体信息,证书主体即证书的持有人
Subject Public Key Info #证书主体的公钥信息
Public Key Algorithm #证书主体使用的公钥算法
Subject Public Key #证书主体的公钥
Issuer Unique Identifier (optional) #证书签发者唯一标识符
Subject Unique Identifier (optional) #证书主体唯一标识符
Extensions (optional) #扩展
...
Certificate Signature Algorithm #证书签名算法,比如PKCS #1 SHA-1 With RSA Encryption
Certificate Signature #证书的数字签名
Fingerprints #指纹
SHA-256 Fingerprint #
SHA-1 Fingerprint #

数字证书编码和扩展名

数字证书使用文件作为载体,目前有两种编码方法,多种文件扩展名。

编码格式(同时可以作为对应编码格式的扩展名)

  • DER(Distinguished Encoding Rules)
    DER[4]是一种二进制编码格式。可以使用.der作为DER格式编码的数字证书的文件扩展名。通常应该这样说,”我有一个DER编码格式的数字证书”,而不是,”我有一个DER数字证书”。

  • PEM(Privacy-Enhanced Mail)
    PEM采用BASE64文本编码格式,用于不同类型的X.509 v3数字证书。PEM一般以BEGIN XXX开头,以END XXX结束。

比如:

使用PEM格式存储的数字证书:

1
2
3
-----BEGIN CERTIFICATE-----
...
-----END CERTIFICATE-----

使用PEM格式存储的私钥

1
2
3
-----BEGIN PRIVATE KEY-----
...
-----END PRIVATE KEY-----

或者

1
2
3
-----BEGIN RSA PRIVATE KEY-----
...
-----END RSA PRIVATE KEY-----

使用PEM格式存储的证书请求文件

1
2
3
-----BEGIN CERTIFICATE REQUEST-----
...
-----END CERTIFICATE REQUEST-----

证书文件扩展名

  • .crt
    CeRTificate的缩写,用于证书文件,可以是DER或者PEM编码格式。

  • .cer
    CERtificate的缩写,用于证书文件,可以是DER或者PEM编码格式。

  • .key
    用于存储私钥或者公钥,可以使DER或者PEM编码格式。

CRT文件和CER文件只有在使用相同编码的时候才可以安全地相互替代。

查看数字证书
PEM编码格式证书

1
$ openssl x509 -in cert.(pem cer crt) -text -noout

DER编码格式证书

1
$ openssl x509 -in cert.(der cer crt) -inform der -text -noout

数字证书编码格式转换

PEM to DER

1
$ openssl x509 -in cert.crt -outform der -out cert.der

DER to PEM

1
openssl x509 -in cert.crt -inform der -outform pem -out cert.pem

其他名词

  • CSR
    证书请求文件(Certificate Signing Request),生成 X509 数字证书前,一般先由用户提交证书申请文件CSR,然后由 CA 来签发证书。
    大致流程是,用户自行生成公私密钥对,然后生成证书请求文件CSR,主要包含用户的身份信息,公钥以及一些其他信息,用户使用私钥对上述信息进行签名。CSR文件递交给CA机构后,CA会审核用户的真实身份,通过之后,CA使用自己的私钥为用户签发数字证书。CA的签名可以使用CA公开的根证书来验证。
  • CRL
    证书撤销列表 (Certification Revocation List) 是一种包含撤销的证书列表的签名数据结构。用于公布某些数字证书不再有效。CRL文件同样需要数字签名。
  • OCSP
    在线证书状态协议(OCSP,Online Certificate Status Protocol,rfc2560)用于实时表明证书状态。OCSP通过在线方式来查询证书的有效性,避免了CRL的缺陷。
  • PKCS
    公钥加密标准(Public Key Cryptography Standards)包含一系列的标准,从PKCS#1到PKCS#15,分别定义了PKCS的不同方面。

PKCS#11是目前最常用的标准之一。

PKCS#11为加密令牌定义了一组平台无关的API ,如硬件安全模块和智能卡。PKCS#11称为Cyptoki,定义了一套独立于技术的程序设计接口,USBKey安全应用需要实现的接口。由于没有一个真正的标准加密令牌,这个API已经发展成为一个通用的加密令牌的抽象层。 PKCS#11 API定义最常用的加密对象类型(RSA密钥,X.509证书,DES /三重DES密钥等)和所有需要使用的功能,创建/生成,修改和删除这些对象。pkcs#11只提供了接口的定义, 不包括接口的实现,一般接口的实现是由设备提供商提供的,如usbkey的生产厂商会提供 符合PKCS#11接口标准的API的实现。这样你只要通过接口调用API函数即可实现其功能。

References:
[1]rfc5280
[2]X.509
[3]DER vs. CRT vs. CER vs. PEM Certificates and How To Convert Them
[4]DER (Distinguished Encoding Rules) certificate encoding

===
[erq]

公钥经CA签名后才成为数字证书。数字证书用来保证公钥是可信任的,这是一个始自CA根证书(root certificate)的信任链。CA根证书内置CA的公钥和身份信息,CA的根证书都是自签的。CA使用其私钥签发数字证书,也就是将申请人的公钥和身份信息按X.509标准进行数字签名。比如使用浏览器浏览https站点时,就可以使用内置的CA根证书来验证服务器端的数字证书是不是有效。当然使用数字证书的领域包含但不限于web,任何需要身份鉴别的地方都可以使用数字证书。

安装

编辑文件 /etc/apt/sources.list.d/jenkins.list,其内容如下:

1
deb http://pkg.jenkins-ci.org/debian binary/

然后执行以下命令:

1
2
3
$ wget -q -O - http://pkg.jenkins-ci.org/debian/jenkins-ci.org.key sudo apt-key add -
$ sudo apt-get update
$ sudo apt-get install jenkins

配置

端口

Jenkins默认监听8080端口,如果与其他应用程序端口冲突,修改/etc/default/jenkins文件:

HTTP_PORT=8082

然后

1
# /etc/init.d/jenkins restart

就可以了

访问控制

Jenkins默认安装是没有启用访问控制的,也就是可以随便匿名访问,要启用安全控制,访问Jenkins web界面,从”系统管理->安全设置”中配置即可。

邮件通知

打开Manage Jenkins -> Configure System:

Jenkins Location -> System Admin e-mail address 设置管理员邮箱地址
E-mail Notification -> SMTP server 输入stmp服务器地址
Default user e-mail suffix 用户邮箱后缀,比如@openwares.net
Advanced -> Use SMTP Authentication 输入smtp认证需要的User Name和password
Use SSL 如果服务器使用SSL则勾选,如果使用TLS/STARTTLS则不要勾选
SMTP Port 指定端口,默认25

如果smtp服务器使用TLS,则需要在jenkins配置文件/etc/default/jenkins中添加JAVA选项:
JAVA_ARGS=”-Dmail.smtp.starttls.enable=true” # enable STARTTLS
否则测试邮件发送会有异常:

1
2
3
4
5
6
7
8
Failed to send out e-mail

javax.mail.MessagingException: Could not connect to SMTP host: mail.openwares.net, port: 25;
nested exception is:
javax.net.ssl.SSLException: Unrecognized SSL message, plaintext connection?
at com.sun.mail.smtp.SMTPTransport.openServer(SMTPTransport.java:1934)
at com.sun.mail.smtp.SMTPTransport.protocolConnect(SMTPTransport.java:638)
...

然后重新启动jenkins。

但是如果启用STARTSSL时服务器的SSL证书是自签的,又会抛出异常:

1
2
3
4
5
6
7
Failed to send out e-mail

javax.mail.MessagingException: Could not convert socket to TLS;
nested exception is:
javax.net.ssl.SSLHandshakeException: sun.security.validator.ValidatorException: PKIX path building failed: sun.security.provider.certpath.SunCertPathBuilderException: unable to find valid certification path to requested target
at com.sun.mail.smtp.SMTPTransport.startTLS(SMTPTransport.java:1880)
...

因为证书是不受信任的:-(。

那么还需要在jenkins配置文件/etc/default/jenkins中添加一个JAVA选项:
JAVA_ARGS=”-Dcom.sun.net.ssl.checkRevocation=false” #disable cert verify

这样就可以正常发送邮件了。

References:
[1]Jenkins Debian packages
[2]Installing Jenkins on Ubuntu

===
[erq]

账户配置

提交到gerrit的changes中的用户名和邮箱地址必须与gerrit用户信息一致,否则会被拒绝,除非有Forge XXX权限。

1
2
$ git config --global user.name "username"
$ git config --global user.email "mailbox@domain.tld"

克隆gerrit仓库
一般来说为ssh主机设置别名可以省很多事,不用每次输入复杂的远程仓库地址了:

~/.ssh/config文件中添加如下行:

1
2
3
4
5
6
Host cr
Hostname review.domain.tld
User admin
Port 29418
#如果私钥名字为id_rsa,可以省略下面一行
IdentityFile ~/.ssh/admin_rsa

然后可以这样克隆远程仓库了

1
$ git clone ssh://cr/project

而且以后都可以使用cr这个别名来代替远程gerrit仓库地址

安装commit-msg钩子

commit-msg是gerrit提供的钩子脚本,会为每个提交添加Change-Id行。

1
$ scp cr:hooks/commit-msg .git/hooks/

提交changes

首先checkout出devel分支。根据不同的策略,master有可能是禁止推送更新的。

1
2
$ git checkout devel
$ git remote update #更新远程仓库

经过一段时间的工作,有了commit后,就可以将commit提交到服务器接受代码审核。

1
$ git push origin HEAD:refs/for/devel

直接git push推送到远程devel分支是被禁止的,推送到refs/for/devel会在gerrit服务器上生成新的需要评审的changes。

可以通过增加一个远程配置来进一步简化命令行:

.git/config中添加如下行:

1
2
3
\[remote "review"\]
url = ssh://cr/project
push = HEAD:refs/for/devel

之后就可以这样推送changes了:

1
$ git push review

使用git-review

git-review是针对gerrit的一个命令

1
# apt-get install git-review

git-review默认使用gerrit远程仓库别名

1
$ git remote add gerrit ssh://cr/project

然后在工程根目录下建立git-review配置文件.gitreview

1
2
3
4
5
6
7
\[gerrit\]
host=review.domain.tld
port=29418
project=project_name
defaultbranch=devel #提交changes到devel分支,也就是推送到refs/for/devel
defaultremote=gerrit #默认即为gerrit
defaultrebase=0 #默认提交前不执行rebase操作

最后通过

1
$ git review

就可以推送changes了。

verify和code review

通过不应该通过开发人员进行verify,CI服务器会在changes提交后自动进行verify。
Code Review可以通过gerrit web接口进行。通过verify和code review的changes可以通过submit合并到目标分支。

个人分支

如果gerrit服务器提供了sandbox个人分支,那么可以将自己的阶段性工作保存在sandbox中而不用提交到gerrit服务器进行评审,直到感觉可以参加评审时再向devel分支提交changes。

1
2
3
4
5
$ git checkout devel
$ git checkout -b sandbox/yourname/foo
$ git push --set-upstream origin sandbox/yourname/foo
...
$ git push

一般来说为了devel分支的整洁,建议先在个人分支工作,等工作比较成熟后再合并回devel分支,然后再向gerrit服务器推送changes。

References:
[1]Gerrit Code Review - Uploading Changes
[2]Gerrit Code Review - Change-Ids

===
[erq]

荷兰程序员Vincent Driessen的A successful Git branching model[1]对于集中式的中小型项目是一个相当不错的分支模型。他还制作了一副pdf大图Git-branching-model

分支模型

有两个常设分支,master和devel(或叫develop,or whatever)。master分支用于最终产品发布,而devel分支用于日常开发。

其他临时性分支包括特性分支feature或叫topic分支,预发布分支release,热补丁分支hotfix。

feature用于新功能开发,分支自devel,新功能开发完毕必须merge回devel分支,或者不再需要此特性,直接丢弃分支。命名方式一般为feature-特性名或者特性编号。

release用于产品正式发布前的预发布,分支自devel。命名方式一般为release-(即将发布的版本号),比如release-1.2。release分支功能上不应该再发生变化,只是一些小的完善或者bug的修复还有实施版本策略。确认版本可以发布后,将release合并到master,并在master上打版本tag。release同时要合并回devel分支,之后可以删除release分支。

hotfix用于正式发布产品的紧急bug修复,分支自master。命名方式一般为hotfix-bug编号,比如hotfix-1312,bug编号来自bug tracking系统,比如Trac。bug修复完毕后,将hotfix分支合并回master分支,并更新产品号以及打新的tag。如果当前存在release分支,则应将hotfix合并到release分支而不是master分支。hotfix还需要合并回devel分支。之后可以将hotfix分支删除。

合并分支时使用- -no-ff选项,不让分支fast forwarding以保持完整清晰的版本历史。

个人分支

除了常设分支和临时分支外,每个开发人员还可以设立自己的个人分支(personal branch)。个人分支以自己的名字命名,分支自devel。个人分支方便开发人员保存和在不同机器间同步未最终完成的工作成果,代码重构,并且可以减少devel分支的commit,保持devel分支的整洁。个人分支上的工作告一段落后,更新本地代码库,将个人分支上的工作成果合并到devel分支,然后推送devel分支到中央仓库。

代码审核

master分支只有项目管理员可以touch,其他开发人员无法向master推送更新。而开发人员向devel分支推送的更新必须经过gerrit代码审核服务器,在通过其他开发人员的code review和CI服务器的自动verify后,才可以正式merge到devel分支。

其他临时分支和个人分支不经过gerrit,直接进入中央仓库。

持续集成

每当开发人员向devel推送更新,这在gerrit叫做change,CI服务器会自动对新提交的change进行编译和运行单元测试,根据结果给于适当的verify值。

当代码通过审核merge到devel后,自动触发CI服务器,拉取devel分支,然后编译部署到测试环境进行自动化测试和人工测试。

而master分支发布产品时也可以通过触发CI进行自动编译和部署到产品环境。

References:
[1]A successful Git branching model
[2]Git分支管理策略
[3]实用 Git 工作流
[4]一个成功的Git分支模型

===
[erq]

gerrit从2.6开始,默认不再添加verified category,也就是changes上就看不到verified label了。

具体的原因见gerrit的Change 44084。这是为了简化out of the box工作流,如果需要与jenkins等CI环境集成,则需要手动添加verified category,只要在All-Projects的project.config文件里添加5行文本就可以了。

添加V标签

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
$ mkdir temp && cd temp
$ git clone ssh://cr/All-Projects.git

Cloning into 'All-Projects'...
remote: Counting objects: 22, done
remote: Finding sources: 100% (22/22)
remote: Total 22 (delta 1), reused 6 (delta 1)
Receiving objects: 100% (22/22), 5.33 KiB 0 bytes/s, done.
Resolving deltas: 100% (1/1), done.
Checking connectivity... done.
Note: checking out 'a30b5de24cdd7993bbe3398e57b1cb771cbb1fc2'.

You are in 'detached HEAD' state. You can look around, make experimental
changes and commit them, and you can discard any commits you make in this
state without impacting any branches by performing another checkout.

If you want to create a new branch to retain commits you create, you may
do so (now or later) by using -b with the checkout command again. Example:

git checkout -b new_branch_name

$ cd All-Projects
$ vim project.config

在文件project.config中添加如下5行:

1
2
3
4
5
\[label "Verified"\]
function = MaxWithBlock
value = -1 Fails
value = 0 No score
value = +1 Verified

然后提交到远程仓库

1
2
3
4
5
6
7
8
9
10
11
12
$ git commit -a -m "add verified category"
$ git push origin HEAD:refs/meta/config

Counting objects: 15, done.
Delta compression using up to 4 threads.
Compressing objects: 100% (3/3), done.
Writing objects: 100% (3/3), 323 bytes 0 bytes/s, done.
Total 3 (delta 1), reused 0 (delta 0)
remote: Resolving deltas: 100% (1/1)
remote: Processing changes: refs: 1, done
To ssh://cr/All-Projects.git
93dc4d8..22b46f7 HEAD -> refs/meta/config

因为在分离头(detached HEAD)状态,所以手工指定将当前HEAD push到远程引用refs/meta/config。

登录gerrit站点,changes上面就有V标签了。

verified label的用法见官方文档。

References:
[1]HOW TO EDIT THE PROJECT.CONFIG FOR ALL PROJECTS IN GERRIT

===
[erq]

Lambda表达式本质就是一个匿名函数,其理论基础来自于λ演算。不过Lambda表达式是受到很多限制的匿名函数,比如有些语言只允许有一条表达式。闭包是持有自由变量从而具有状态的函数,闭包通常使用匿名函数来实现。闭包拥有的自由变量类似于对象拥有的成员变量。