shell - JohnHau/mis GitHub Wiki

vim

support chinese in .vimrc setup below lines

set fileencodings=utf-8,ucs-bom,gb18030,gbk,gb2312,cp936

set termencoding=utf-8

set encoding=utf-8

全选(高亮显示):按esc后,然后ggvG或者ggVG

**全部复制:**按esc后,然后ggyG

**全部删除:**按esc后,然后dG

解析:

gg:是让光标移到首行,在vim才有效,vi中无效

v : 是进入Visual(可视)模式

**G :**光标移到最后一行

中内容以后就可以其他的操作了,比如:

d 删除中内容

y 复制中内容到0号寄存器

"+y 复制中内容到+寄存器,也就是系统的剪贴板,供其他程序用

ci'、ci"、ci(、ci[、ci{、ci< - 分别更改这些配对标点符号中的文本内容

di'、di"、di(或dib、di[、di{或diB、di< - 分别删除这些配对标点符号中的文本内容

yi'、yi"、yi(、yi[、yi{、yi< - 分别复制这些配对标点符号中的文本内容

vi'、vi"、vi(、vi[、vi{、vi< - 分别选中这些配对标点符号中的文本内容

比如要操作的文本如下:

111"222"333

将光标移到"222"的任何一个字符处输入命令 di" ,文本会变成: 111""333

若输入命令 da" ,文本会变成: 111333

比如我要复制从第9行到第15行的数据,复制到第16行1、这个最好用,强烈推荐

:行号9 ,行号15 copy 行号16 将行号9到行号15的内容复制到行号16所在行的后面。

:行号9 ,行号15 move 行号16 将行号9到行号15的文本内容移动到行号16所在行的后面。

把光标移到第9行

shift + v

再把光标移到第15行

ctrl + c

再再把光标移到第16行

p

备注:

查询行号,在不可编辑模式下输入:set number

va{ 选中{}中间内容,包括{}

vi< 选中<>中间内容

vi[ 选中[]中间内容

vit 选中中间的内容

vi” 选中”"中间内容

vi’ 选中”中间的内容、

vis 选中一个句子

vib 选中一个block

viw 选中一个单词

vip 选中一个段落

用 v 命令进入的字符可视化模式(Characterwise visual mode)。文本选择是以字符为单位的。

用 V 命令进入的行可视化模式(Linewise visual mode)。文本选择是以行为单位的。

用 ctrl-V 进入的块可视化模式(Blockwise visual mode)。可以选择一个矩形内的文本。

cmake

project(Demo1) #表示项目名称是Demo1

add_executable(Demo main.c) # 将名为 main.c 的源文件编译成一个名称为 Demo 的可执行文件

add_executable(Demo main.c MathFunctions.c) # 将名为 main.c MathFunctions.c 的源文件编译成一个名称为 Demo 的可执行文件

如果源文件很多,把所有源文件的名字都加进去将是一件烦人的工作。

更省事的方法是使用 aux_source_directory 命令,该命令会查找指定目录下的所有源文件,然后将结果存进指定变量名。其语法如下:

aux_source_directory(<dir> <variable>)

使用命令 add_subdirectory 指明本项目包含一个子目录 math,这样 math 目录下的 CMakeLists.txt 文件和源代码也会被处理

使用命令 target_link_libraries 指明可执行文件 main 需要连接一个名为 MathFunctions 的链接库

# 查找当前目录下的所有源文件

# 并将名称保存到 DIR_LIB_SRCS 变量

aux_source_directory(. DIR_LIB_SRCS)

# 生成链接库

add_library (MathFunctions ${DIR_LIB_SRCS})

在该文件中使用命令 add_library 将 src 目录中的源文件编译为静态链接库

CMake 允许为项目增加编译选项,从而可以根据用户的环境和需求选择最合适的编译方案。

例如,可以将 MathFunctions 库设为一个可选的库,如果该选项为 ON ,就使用该库定义的数学函数来进行运算。否则就调用标准库中的数学函数库。

bash

send udp data from command line

echo “hello world!” > /dev/udp/192.168.1.27/8080

sed 's/\s\s*/ /g' m.log sed 's/:space::space:*/ /g' m.log

显示不可见字符

在Linux中,cat -A file可以把文件中的所有可见的和不可见的字符都显示出来,在Vim中,如何将不可见字符也显示出来呢?

如果只是想在Vim中查看的话,可以这样:%!cat -A在Vim中调用cat转换显示。这样的做法不便于编辑,其实Vim本身是可以设置显示不可见字符的。

只需要:set invlist即可以将不可见的字符显示出来,例如,会以^I表示一个tab符,$表示一个回车符等。

统计空行

$cat file.log | grep '^\s*$' | wc -l

print ascii

$printf %d 'a

$printf "%d" 'a

$printf %x 'a

$printf %x "'a"

$printf %x 117 75 $printf "\x75\n" u

tr -s " " < logfile #compress space

批量改文件名

for file in ls *.jpg;do mv $file echo $file|sed 's/_finished//g';done;

send udp data

echo "hello world" > /dev/udp/127.0.0.1/8900

tar

c – Creates a new .tar archive file.

v – Verbosely show the .tar file progress.

f – File name type of the archive file.

Create tar Archive File

tar -cvf tecmint-14-09-12.tar /home/tecmint/

Create tar.gz Archive File

tar cvzf MyImages-14-09-12.tar.gz /home/MyImages

OR

tar cvzf MyImages-14-09-12.tgz /home/MyImages

Create tar.bz2 Archive File

tar cvfj Phpfiles-org.tar.bz2 /home/php

OR

tar cvfj Phpfiles-org.tar.tbz /home/php

OR

tar cvfj Phpfiles-org.tar.tb2 /home/php

Untar tar Archive File

Untar files in Current Directory

tar -xvf public_html-14-09-12.tar

Untar files in specified Directory ##

tar -xvf public_html-14-09-12.tar -C /home/public_html/videos/

Uncompress tar.gz Archive File

tar -xvf thumbnails-14-09-12.tar.gz

Uncompress tar.bz2 Archive File

tar -xvf videos-14-09-12.tar.bz2

List Content of tar Archive File

tar -tvf uploadprogress.tar

List Content tar.gz Archive File

tar -tvf staging.tecmint.com.tar.gz

List Content tar.bz2 Archive File

tar -tvf Phpfiles-org.tar.bz2

Untar Single file from tar File

tar -xvf cleanfiles.sh.tar cleanfiles.sh

OR

tar --extract --file=cleanfiles.sh.tar cleanfiles.sh

Untar Single file from tar.bz2 File

tar -jxvf Phpfiles-org.tar.bz2 home/php/index.php

OR

tar --extract --file=Phpfiles-org.tar.bz2 /home/php/index.php

Untar Multiple files from tar, tar.gz and tar.bz2 File

tar -xvf tecmint-14-09-12.tar "file 1" "file 2"

tar -zxvf MyImages-14-09-12.tar.gz "file 1" "file 2"

tar -jxvf Phpfiles-org.tar.bz2 "file 1" "file 2"

Extract Group of Files using Wildcard

tar -xvf Phpfiles-org.tar --wildcards '*.php'

tar -zxvf Phpfiles-org.tar.gz --wildcards '*.php'

tar -jxvf Phpfiles-org.tar.bz2 --wildcards '*.php'

Add Files or Directories to tar Archive File

tar -rvf tecmint-14-09-12.tar xyz.txt

tar -rvf tecmint-14-09-12.tar php

Add Files or Directories to tar.gz and tar.bz2 files

tar -rvf MyImages-14-09-12.tar.gz xyz.txt

tar -rvf Phpfiles-org.tar.bz2 xyz.txt

How To Verify tar, tar.gz and tar.bz2 Archive File

tar tvfW tecmint-14-09-12.tar

Check the Size of the tar, tar.gz and tar.bz2 Archive File

tar -czf - tecmint-14-09-12.tar | wc -c

12820480

tar -czf - MyImages-14-09-12.tar.gz | wc -c

112640

tar -czf - Phpfiles-org.tar.bz2 | wc -c

20480

sed

sed 's/^$/d' logfile #delete blank lines

diff options

-a 把所有文件看出文本心事,并对它们进行逐行比较,即使它们看起来不是文件文件

-b 忽略空格

-B 忽略空白行

-i 忽略大小写

--brief 只报告两个文件是否相同,不报告详细的不同信息

-c 使用上下文输出格式

-d 改变算法,使得diff命令可能找到一个更小的文件差异

-e 产生一个将文件1变成文件2的“ed script”

-r 比较目录时递归比较子目录

-b 支持Unix的兼容性

-n RCS格式

-N 在目录比较中,如果一个文件只出现在第一个目录中,那么认为它在另外一个目录中也出现,并且为空

-p 显示不同的信息位于哪个C函数中

-P 在目录比较中,如果一个文件只出现在第二个目录中,那么认定它在另外一个目录中也出现,并且为空

--paginate 通过pr输出,并且标记页数

-q 不报告差异,只说明两者内容是否有差异

-s 当比较的两个文件相同时报告信息

-t 输出时,[tab]键按照空格键来输出

-T 输出时,[tab]键输出,而非空格键

-u 使用统一的输出格式

-y 输出使用对比格式

选择参数

-D<宏名字> 引入if-then-else输出格式来预处理宏名称

-s<文件> 比较文件时,从指定的文件进行比较

--from-file<文件> 同所有的操作数比较指定的文件,可以是目录

--new-group-format=<模式> 用指定的模式,按照 if-then-else方式,输出第二个文件中的一组行内容

--help 显示帮助信息

--version 显示版本信息

git

git create tag and push origin

git tag -a v1.2 15027957951b64c

git push origin v1.4

git设置本地分支跟踪远程分支的几种方法

  1. git checkout -b brach-name origin/branch-name

  2. git branch --set-upstream branch-name origin/branch-name

3.git branch -u origin/branch-name

7.配置远程仓库 origin是远程仓库的别名 代替xxx.git的地址

git remote add origin https://gitee.com/kingCould/HelloWord.git

开始推送

git push <远程主机名> <本地分支名>:<远程分支名>

git push origin master:master

git跟踪远程分支,查看本地分支追踪和远程分支的关系 跟踪远程分支

如果用git push指令时,当前分支没有跟踪远程分支(没有和远程分支建立联系),那么就会git就会报错

There is no tracking information for the current branch. Please specify which branch you want to merge with. 因为当前分支没有追踪远程指定的分支的话,当前分支指定的版本快照不知道要作为服务器哪一个分支的版本快照的子节点。简单来说就是:不知道要推送给哪一个分支。

那么如何建立远程分支:

克隆时自动将创建好的master分支追踪origin/master分支

git clone 服务器地址 git checkout -b develop origin/develop

git clone -b v1.0.0 git@gitlab (v1.0.0 是分支名)

在远程分支的基础上建立develop分支,并且让develop分支追踪origin/develop远程分支。

git branch --set-upstream branch-name origin/branch-name 将branch-name分支追踪远程分支origin/branch-name

git branch -u origin/serverfix 设置当前分支跟踪远程分支origin/serverfix

查看本地分支和远程分支的跟踪关系

git branch -vv

比如输入

$ git branch -vv develop 08775f9 [origin/develop] develop feature_1 b41865d [origin/feature_1] feature_1

  • master 1399706 [my_github/master] init commit develop分支跟踪origin/develop feature_1分支跟踪origin/feature_1 master跟踪了my_github/master,且当前分支为master分支

那么假如我此时想要将master的改变推送到origin服务器的master分支上:

$ git checkout master//切换到master分支 ... $ git branch -u origin/master//将当前分支跟踪origin/master Branch 'master' set up to track remote branch 'master' from 'origin'. 之后就可以执行git add和git commit了 现在再查看一下本地和远程的分支关系:

$ git branch -vv develop 08775f9 [origin/develop] develop feature_1 b41865d [origin/feature_1] feature_1

  • master 1399706 [origin/master] init commit master已经跟踪了origin/master了

也可以:

git clone XXXXXX

git checkout --track origin/org_dev

查看跟踪关系:

fengdeMacBook-Pro:riki fengma$ git branch -vv

master b7a26015 [origin/master] 1

  • org_dev 5c65d305 [origin/org_dev] modify user app name

git clone 含有子模块的项目

当一个 git 项目包含子模块(submodule) 时,直接克隆下来的子模块目录里面是空的。

有两种方法解决

方法一

如果项目已经克隆到了本地,执行下面的步骤:

  1. 初始化本地子模块配置文件

git submodule init

  1. 更新项目,抓取子模块内容。

git submodule update

方法二

另外一种更简单的方法,就是在执行 git clone 时加上 --recursive 参数。它会自动初始化并更新每一个子模块。例如:

git clone --recursive https://github.com/example/example.git

git分支的合并

原文:

http://gitbook.liuhui998.com/3_3.html

http://gitbook.liuhui998.com/5_3.html

如何分支的合并

在git中,可以使用git merge 和git rebase两个命令来进行分支的合并。

git merge 和git rebase在大体上都差不多,下文主要以git merge来例来讲解分支的合并流程。

如果你想了解分支合并的更多内容,请阅读《git merge简介》,《git rebase简介(基本篇)》和《git rebase简介(高级篇)》。

git merge命令示例:

$ git merge branchname

这个命令把分支"branchname"合并到了当前分支里面。

如有冲突(冲突--同一个文件在远程分支和本地分支里按不同的方式被修改了);那么命令的执行输出就像下面一样

$ git merge next

100% (4/4) done

Auto-merged file.txt

CONFLICT (content): Merge conflict in file.txt

Automatic merge failed; fix conflicts and then commit the result.

在有问题的文件上会有冲突标记,在你手动解决完冲突后就可以把此文件添 加到索引(index)中去,用git commit命令来提交,就像平时修改了一个文件 一样。

如果你用gitk来查看commit的结果,你会看到它有两个父分支:一个指向当前的分支,另外一个指向刚才合并进来的分支。

解决合并中的冲突

如果执行自动合并没有成功的话,git会在索引和工作树里设置一个特殊的状态, 提示你如何解决合并中出现的冲突。

有冲突(conflicts)的文件会保存在索引中,除非你解决了问题了并且更新了索引,否则执行 git commit都会失败:

$ git commit

file.txt: needs merge

如果执行 git status 会显示这些文件没有合并(unmerged),这些有冲突的文件里面会添加像下面的冲突标识符:

<<<<<<< HEAD:file.txt

Hello world

=======

Goodbye

77976da35a11db4580b80ae27e8d65caf5208086:file.txt

你所需要的做是就是编辑解决冲突,(接着把冲突标识符删掉),再执行下面的命令:

$ git add file.txt

$ git commit

注意:提交注释里已经有一些关于合并的信息了,通常是用这些默认信息,但是你可以添加一些你想要的注释。

上面这些就是你要做一个简单合并所要知道的,但是git提供更多的一些信息来 帮助解决冲突。

撒销一个合并

如果你觉得你合并后的状态是一团乱麻,想把当前的修改都放弃,你可以用下面的命令回到合并之前的状态:

$ git reset --hard HEAD

或者你已经把合并后的代码提交,但还是想把它们撒销:

$ git reset --hard ORIG_HEAD

但是刚才这条命令在某些情况会很危险,如果你把一个已经被另一个分支合并的分支给删了,那么 以后在合并相关的分支时会出错。

关于撤销的更多内容请参考《git reset简介》

快速向前合并

还有一种需要特殊对待的情况,在前面没有提到。通常,一个合并会产生一个合并提交(commit), 把两个父分支里的每一行内容都合并进来。

但是,如果当前的分支和另一个分支没有内容上的差异,就是说当前分支的每一个提交(commit)都已经存在另一个分支里了,git 就会执行一个“快速向前"(fast forward)操作;git 不创建任何新的提交(commit),只是将当前分支指向合并进来的分支。

在合并过程中得到解决冲突的协助

git会把所有可以自动合并的修改加入到索引中去, 所以git diff只会显示有冲突的部分. 它使用了一种不常见的语法:

$ git diff

diff --cc file.txt

index 802992c,2b60207..0000000

--- a/file.txt

+++ b/file.txt

@@@ -1,1 -1,1 +1,5 @@@

++<<<<<<< HEAD:file.txt

+Hello world

++=======

  • Goodbye

++>>>>>>> 77976da35a11db4580b80ae27e8d65caf5208086:file.txt

回忆一下, 在我们解决冲突之后, 得到的提交会有两个而不是一个父提交: 一个父提交是当前分支提交前的HEAD,; 另外一个父提交是被合并分支的HEAD, 被暂时存在MERGE_HEAD.

在合并过程中, 索引中保存着每个文件的三个版本. 三个"文件暂存(file stage)"中的每一个都代表了文件的不同版本:

$ git show :1:file.txt # 两个分支共同祖先中的版本.

$ git show :2:file.txt # HEAD中的版本.

$ git show :3:file.txt # MERGE_HEAD中的版本.

当你使用git diff去显示冲突时, 它在工作树(work tree), 暂存2(stage 2)和暂存3(stage 3)之间执行三路diff操作, 只显示那些两方都有的块(换句话说, 当一个块的合并结果只从暂存2中得到时, 是不会被显示出来的; 对于暂存3来说也是一样).

上面的diff结果显示了file.txt在工作树, 暂存2和暂存3中的差异. git不在每行前面加上单个'+'或者'-', 相反地, 它使用两栏去显示差异: 第一栏用于显示第一个父提交与工作目录文件拷贝的差异, 第二栏用于显示第二个父提交与工作文件拷贝的差异. (参见git diff-files中的"COMBINED DIFF FORMAT"取得此格式详细信息.)

在用直观的方法解决冲突之后(但是在更新索引之前), diff输出会变成下面的样子:

$ git diff

diff --cc file.txt

index 802992c,2b60207..0000000

--- a/file.txt

+++ b/file.txt

@@@ -1,1 -1,1 +1,1 @@@

  • Hello world

-Goodbye

++Goodbye world

上面的输出显示了解决冲突后的版本删除了第一个父版本提供的"Hello world"和第二个父版本提供的"Goodbye", 然后加入了两个父版本中都没有的"Goodbye world".

一些特别diff选项允许你对比工作目录和三个暂存中任何一个的差异:

$ git diff -1 file.txt # 与暂存1进行比较

$ git diff --base file.txt # 与上相同

$ git diff -2 file.txt # 与暂存2进行比较

$ git diff --ours file.txt # 与上相同

$ git diff -3 file.txt # 与暂存3进行比较

$ git diff --theirs file.txt # 与上相同.

还有,git log和gitk命令也为合并操作提供了特别的协助:

$ git log --merge

$ gitk --merge

这会显示所有那些只在HEAD或者只在MERGE_HEAD中存在的提交, 还有那些更新(touch)了未合并文件的提交.

你也可以使用git mergetool, 它允许你使用外部工具如emacs或kdiff3去合并文件.

每次你解决冲突之后, 应该更新索引:

$ git add file.txt

完成索引更新之后, git-diff(缺省地)不再显示那个文件的差异, 所以那个文件的不同暂存版本会被"折叠"起来.

多路合并

你可以一次合并多个头, 只需简单地把它们作为git merge的参数列出. 例如,

$ git merge scott/master rick/master tom/master

相当于:

$ git merge scott/master

$ git merge rick/master

$ git merge tom/master

子树

有时会出现你想在自己项目中引入其他独立开发项目的内容的情况. 在没有路径冲突的前提下, 你只需要简单地从其他项目拉取内容即可.

如果有冲突的文件, 那么就会出现问题. 可能的例子包括Makefile和其他一些标准文件名. 你可以选择合并这些冲突的文件, 但是更多的情况是你不愿意把它们合并. 一个更好解决方案是把外部项目作为一个子目录进行合并. 这种情况不被递归合并策略所支持, 所以简单的拉取是无用的.

在这种情况下, 你需要的是子树合并策略.

这下面例子中, 我们设定你有一个仓库位于/path/to/B (如果你需要的话, 也可以是一个URL). 你想要合并那个仓库的master分支到你当前仓库的dir-B子目录下.

下面就是你所需要的命令序列:

$ git remote add -f Bproject /path/to/B (1)

$ git merge -s ours --no-commit Bproject/master (2)

$ git read-tree --prefix=dir-B/ -u Bproject/master (3)

$ git commit -m "Merge B project as our subdirectory" (4)

$ git pull -s subtree Bproject master (5)

子树合并的好处就是它并没有给你仓库的用户增加太多的管理负担. 它兼容于较老(版本号小于1.5.2)的客户端, 克隆完成之后马上可以得到代码. 然而, 如果你使用子模块(submodule), 你可以选择不传输这些子模块对象. 这可能在子树合并过程中造成问题.

译者注: submodule是Git的另一种将别的仓库嵌入到本地仓库方法.

另外, 若你需要修改内嵌外部项目的内容, 使用子模块方式可以更容易地提交你的修改.

http://blog.csdn.net/hudashi/article/details/7668798

git reset revert 回退回滚取消提交返回上一版本

总有一天你会遇到下面的问题.

(1)改完代码匆忙提交,上线发现有问题,怎么办? 赶紧回滚.

(2)改完代码测试也没有问题,但是上线发现你的修改影响了之前运行正常的代码报错,必须回滚.

这些开发中很常见的问题,所以git的取消提交,回退甚至返回上一版本都是特别重要的.

大致分为下面2种情况:

1.没有push

这种情况发生在你的本地代码仓库,可能你add ,commit 以后发现代码有点问题,准备取消提交,用到下面命令

reset git reset [--soft | --mixed | --hard

上面常见三种类型

--mixed

会保留源码,只是将git commit和index 信息回退到了某个版本.

git reset 默认是 --mixed 模式 git reset --mixed 等价于 git reset

--soft

保留源码,只回退到commit 信息到某个版本.不涉及index的回退,如果还需要提交,直接commit即可.

--hard

源码也会回退到某个版本,commit和index 都回回退到某个版本.(注意,这种方式是改变本地代码仓库源码)

当然有人在push代码以后,也使用 reset --hard <commit...> 回退代码到某个版本之前,但是这样会有一个问题,你线上的代码没有变,线上commit,index都没有变,当你把本地代码修改完提交的时候你会发现权是冲突.....

所以,这种情况你要使用下面的方式

2.已经push

对于已经把代码push到线上仓库,你回退本地代码其实也想同时回退线上代码,回滚到某个指定的版本,线上,线下代码保持一致.你要用到下面的命令

revert

git revert用于反转提交,执行evert命令时要求工作树必须是干净的.

git revert用一个新提交来消除一个历史提交所做的任何修改.

revert 之后你的本地代码会回滚到指定的历史版本,这时你再 git push 既可以把线上的代码更新.(这里不会像reset造成冲突的问题)

revert 使用,需要先找到你想回滚版本唯一的commit标识代码,可以用 git log 或者在adgit搭建的web环境历史提交记录里查看.

git revert c011eb3c20ba6fb38cc94fe5a8dda366a3990c61

通常,前几位即可

git revert c011eb3

git revert是用一次新的commit来回滚之前的commit,git reset是直接删除指定的commit

看似达到的效果是一样的,其实完全不同.

第一:

上面我们说的如果你已经push到线上代码库, reset 删除指定commit以后,你git push可能导致一大堆冲突.但是revert 并不会.

第二:

如果在日后现有分支和历史分支需要合并的时候,reset 恢复部分的代码依然会出现在历史分支里.但是revert 方向提交的commit 并不会出现在历史分支里.

第三:

reset 是在正常的commit历史中,删除了指定的commit,这时 HEAD 是向后移动了,而 revert 是在正常的commit历史中再commit一次,只不过是反向提交,他的 HEAD 是一直向前的.

git stash命令可用于临时保存和回复修改,可跨分支。

注:在未add之前才能执行stash!!!!

git stash [save message] 保存,save为可选项,message为本次保存的注释 git stash list 所有保存的记录列表 git stash pop stash@{num} 恢复,num是可选项,通过git stash list可查看具体值。只能恢复一次 git stash apply stash@{num} 恢复,num是可选项,通过git stash list可查看具体值。可回复多次 git stash drop stash@{num} 删除某个保存,num是可选项,通过git stash list可查看具体值 git stash clear 删除所有保存

.gitignore *.o project/Objects/ project/Listings/

git rm -r --cached .

常用匹配示例 bin/: 忽略当前路径下的bin文件夹,该文件夹下的所有内容都会被忽略,不忽略 bin 文件

/bin: 忽略根目录下的bin文件

/*.c: 忽略 cat.c,不忽略 build/cat.c

debug/*.obj: 忽略 debug/io.obj,不忽略 debug/common/io.obj 和 tools/debug/io.obj

**/foo: 忽略/foo, a/foo, a/b/foo等

a/**/b: 忽略a/b, a/x/b, a/x/y/b等

!/bin/run.sh: 不忽略 bin 目录下的 run.sh 文件

*.log: 忽略所有 .log 文件

config.php: 忽略当前路径的 config.php 文件

.gitignore规则不生效

.gitignore只能忽略那些原来没有被track的文件,如果某些文件已经被纳入了版本管理中,则修改.gitignore是无效的。

解决方法就是先把本地缓存删除(改变成未track状态),然后再提交:

git rm -r --cached .

git add .

git commit -m 'update .gitignore'

git pull命令的作用是,取回远程主机某个分支的更新,再与本地的指定分支合并。它的完整格式稍稍有点复杂。

$ git pull <远程主机名> <远程分支名>:<本地分支名> 比如,取回origin主机的next分支,与本地的master分支合并,需要写成下面这样。

$ git pull origin next:master 如果远程分支是与当前分支合并,则冒号后面的部分可以省略。

$ git pull origin next

git merge --no-ff branchname

git push origin BEATS_i.MXRTsupport_SMB --force

git diff BEATS_i.MXRTsupport_SMB_t01 test_bapi_smb_bak_t01 > ../mylog

git diff --name-only BEATS_i.MXRTsupport_SMB_t01 test_bapi_smb_bak_t01 > ../mylog

git diff remotes/origin/BEATS_i.MXRTsupport_SMB BEATS_i.MXRTsupport_SMB_t01

git branch git branch -r git branch -a git branch -av git remote -v git branch //查看本地所有分支

git branch -r //查看远程所有分支

git branch -a //查看本地和远程的所有分支

git branch //新建分支

git branch -d //删除本地分支

git branch -d -r //删除远程分支,删除后还需推送到服务器 git push origin: //删除后推送至服务器

git branch -m //重命名本地分支 /** *重命名远程分支: *1、删除远程待修改分支 *2、push本地新分支到远程服务器 */

//git中一些选项解释:

-d --delete:删除

-D --delete --force的快捷键

-f --force:强制

-m --move:移动或重命名

-M --move --force的快捷键

-r --remote:远程

-a --all:所有

git remote show origin

git diff --name-only HEAD^ HEAD git diff HEAD^ HEAD

git diff --cached git diff HEAD git diff

git show //???

git reflog

git reset --hard 9e22c02

git push origin test:haha #将本地test分支推送到远程haha分支 git branch -a

git pull <远程主机名> <远程分支名>:<本地分支名

git branch //查看本地所有分支 git branch -r //查看远程所有分支 git branch -a //查看本地和远程的所有分支 git branch //新建分支 git branch -d //删除本地分支 git branch -d -r //删除远程分支,删除后还需推送到服务器 git push origin: //删除后推送至服务器 git branch -m //重命名本地分支

git fetch 命令:

取回远程master分支的更新 $git fetch origin master

比较本地版本与上面拉取的更新 $git diff HEAD FETCH_HEAD or $git diff master origin/master

$ git fetch <远程主机名> //这个命令将某个远程主机的更新全部取回本地

如果只想取回特定分支的更新,可以指定分支名: $ git fetch <远程主机名> <分支名> //注意之间有空格

最常见的命令如取回origin 主机的master 分支: $ git fetch origin master

取回更新后,会返回一个FETCH_HEAD ,指的是某个branch在服务器上的最新状态,我们可以在本地通过它查看刚取回的更新信息: $ git log -p FETCH_HEAD

前面提到,git pull 的过程可以理解为:

git fetch origin master //从远程主机的master分支拉取最新内容 git merge FETCH_HEAD //将拉取下来的最新内容合并到当前所在的分支中

即将远程主机的某个分支的更新取回,并与本地指定的分支合并,完整格式可表示为:

$ git pull <远程主机名> <远程分支名>:<本地分支名>

如果远程分支是与当前分支合并,则冒号后面的部分可以省略:

$ git pull origin next

一旦远程主机的版本库有了更新(Git术语叫做commit),需要将这些更新取回本地,这时就要用到git fetch命令;

常用命令 git fetch $ git fetch <远程主机名> 上面命令将某个远程主机的更新,全部取回本地; git fetch命令通常用来查看其他人的进程,因为它取回的代码对你本地的开发代码没有影响; git fetch <远程主机名> <分支名> $ git fetch <远程主机名> <分支名> 上面命令取回特定分支的更新; 例如:取回origin主机的master分支,如下: $ git fetch origin master 取回的更新,在本地主机上要用"远程主机名/分支名"的形式读取。比如origin主机的master,就要用origin/master读取; 代码合并 详细操作步骤

在本地新建一个temp分支,并将远程origin仓库的master分支代码下载到本地temp分支; $ git fetch origin master:temp

git fetch从远程分支拉取代码。

fetch常结合merge一起用,git fetch + git merge == git pull 一般要用git fetch+git merge,因为git pull会将代码直接合并,造成冲突等无法知道,fetch代码下来要git diff orgin/xx来看一下差异然后再合并。

  1. FETCH_HEAD概念

指定某个branch在服务器上最新状态。

我们切到 dev分支 上,git fetch一下,然后看看FETCH_HEAD内容。

$cat .git/FETCH_HEAD
01e8809a7861a55f7a403981f2f1bcd68603e33a branch 'dev' of https://github.com/Moonergfp/learngit
ff47932aba92e0eaec6c75ce8112d2f24d890dab not-for-merge
branch 'develop' of https://github.com/Moonergfp/learngit
ff47932aba92e0eaec6c75ce8112d2f24d890dab not-for-merge
branch 'master' of https://github.com/Moonergfp/learngit

第一列是版本号,第二列是当前FETCH_HEAD是否将要合并的,第三列是git版本库路径。 如上3行中,dev就是要默认指定merge的分支。直接git merge就可以merge把origin/dev到dev分支上。

切到master分支fetch看

ff47932aba92e0eaec6c75ce8112d2f24d890dab branch 'master' of https://github.com/Moonergfp/learngit
fb515ac7195a9cf210839a4b4941e1e31c55067d not-for-merge
branch 'dev' of https://github.com/Moonergfp/learngit
01e8809a7861a55f7a403981f2f1bcd68603e33a not-for-merge
branch 'dev2' of https://github.com/Moonergfp/learngit
ff47932aba92e0eaec6c75ce8112d2f24d890dab not-for-merge
branch 'develop' of https://github.com/Moonergfp/learngit

可以看到当前要合并的是master分支。

  1. 用法

    git fetch 具体细节分2步走: a. 创建并更新本 地远程分支。即创建并更新origin/xxx 分支,拉取代码到origin/xxx分支上。 b. 在FETCH_HEAD中设定当前分支-origin/当前分支对应,如直接到时候git merge就可以将origin/abc合并到abc分支上。

缺点:会拉取当前项目的所有分支的commit。这样没必要,如当前项目有很多人在参与,那么就会有很多分支,那么其他分支的提交也会拉取下来,你得等半天下下来。

$ git fetch
remote: Counting objects: 6, done.
remote: Compressing objects: 100% (4/4), done.
remote: Total 6 (delta 0), reused 0 (delta 0), pack-reused 0
Unpacking objects: 100% (6/6), done.
From https://github.com/Moonergfp/learngit
7c5a386..b008b08 abc -> origin/abc
ff47932..6f28960 master -> origin/master

git fetch origin
只是手动指定了要fetch的remote。

git fetch origin branch1
设定当前分支的 FETCH_HEAD' 为远程服务器的branch1分支`.

注意: 在这种情况下, 不会在本地创建本地远程分支, 这是因为:

这个操作是git pull origin branch1的第一步, 而对应的pull操作,并不会在本地创建新的branch.

一个附加效果是:

这个命令可以用来测试远程主机的远程分支branch1是否存在, 如果存在, 返回0, 如果不存在, 返回128, 抛出一个异常.

git fetch origin branch1:branch2
只要明白了上面的含义, 这个就很简单了,
首先执行上面的fetch操作
使用远程branch1分支在本地创建branch2(但不会切换到该分支),
如果本地不存在branch2分支, 则会自动创建一个新的branch2分支,
如果本地存在branch2分支, 并且是`fast forward', 则自动合并两个分支, 否则, 会阻止以上操作.

git fetch origin :branch2
等价于: git fetch origin master:branch2

git 本地分支追踪远程分支 在本地初始化仓库,提交代码时会出现,上游为空,当前分支为选择,等错误提示。其实就是本地仓库分支和远程仓库分支并未进行关联,即本地分支未追踪到远程分支。 1.本地和远程的状态 本地:本地所有的文件显示,是本地仓库的所有文件,当你在此文件夹进入git Bash Here首行末尾会显示当前所在分支(一般是默认的本地分支master)本地可以创建多个分支,但当前仓库会显示当前所在分支的文件,即当前文件夹文件的显示就是当前分支内的文件。

查看本地分支追踪远程分支 远程:远程有多个分支,但会有一个默认分支,用于服务器部署。

2.查看本地分支及追踪的分支 git branch -vv 显示本地所有分支,*表示当前所在分支,[远程分支]表示当前本地分支追踪的远程分支,最后一个是最近一次提交的注释。上图显示的追踪是正常的。

4.查看远程分支 git branch -a

3.设置远程跟踪 git branch --set-upstream-to=origin/ master 设置本地分支master跟踪origin/远程分支成功

在远程分支的基础上建立develop分支,并且让develop分支追踪origin/develop远程分支。 git checkout -b develop origin/develop

将branch-name分支追踪远程分支origin/branch-name git branch --set-upstream branch-name origin/branch-name

设置当前分支跟踪远程分支origin/serverfix git branch -u origin/serverfix

查看本地分支和远程分支的跟踪关系 git branch -vv

比如输入 $ git branch -vv develop 08775f9 [origin/develop] develop feature_1 b41865d [origin/feature_1] feature_1

  • master 1399706 [my_github/master] init commit

develop分支跟踪origin/develop feature_1分支跟踪origin/feature_1 master跟踪了my_github/master,且当前分支为master分支

那么假如我此时想要将master的改变推送到origin服务器的master分支上: $ git checkout master//切换到master分支 ... $ git branch -u origin/master//将当前分支跟踪origin/master Branch 'master' set up to track remote branch 'master' from 'origin'.

之后就可以执行git add和git commit了 现在再查看一下本地和远程的分支关系:

$ git branch -vv develop 08775f9 [origin/develop] develop feature_1 b41865d [origin/feature_1] feature_1

  • master 1399706 [origin/master] init commit

master已经跟踪了origin/master了

How To Set Upstream Branch on Git

When cloning a Git repository or creating new feature branches, you will have to set upstream branches in order to work properly.

But what are upstream branches?

Upstream branches are closely associated with remote branches.

Upstream branches define the branch tracked on the remote repository by your local remote branch (also called the remote tracking branch)

When creating a new branch, or when working with existing branches, it can be quite useful to know how you can set upstream branches on Git.

Set upstream branch using git push The easiest way to set the upstream branch is to use the “git push” command with the “-u” option for upstream branch.

$ git push -u Alternatively, you can use the “–set-upstream” option that is equivalent to the “-u” option.

$ git push --set-upstream As an example, let’s say that you created a branch named “branch” using the checkout command.

$ git checkout -b branch Switched to a new branch 'branch' You can check tracking branches by running the “git branch” command with the “-vv” option.

$ git branch -vv

  • branch 808b598 Initial commit master 808b598 [origin/master] Initial commit As you can see, compared to master, the branch “branch” has no tracking branches yet (and no upstream branches as a consequence) We can set the upstream branch using the “git push” command.

$ git push -u origin branch Total 0 (delta 0), reused 0 (delta 0)

  • [new branch] branch -> branch Branch 'branch' set up to track remote branch 'branch' from 'origin'. Let’s have a look at the tracking branches again with the branch command.

$ git branch -vv

  • branch 808b598 [origin/branch] Initial commit master 808b598 [origin/master] Initial commit Great!

We have successfully set the upstream branch for our newly created branch.

Set upstream branch using an alias Another way to set the upstream branch is to define an alias for your “git push” command. In fact, pushing to HEAD is equivalent to pushing to a remote branch having the same name as your current branch.

$ git push -u origin HEAD In order to avoid having to define the upstream everytime you create a new branch, define an alias for the command we just wrote.

For aliases, you have two choices, you can either create a git alias or a bash alias.

Using a git alias In order to create a new git alias, use the “git config” command and define a new alias named “pushd”

$ git config --global alias.pushd "push -u origin HEAD" When you are done adding and committing fiels to your repository, set the upstream branch using your newly defined alias.

$ git pushd Total 0 (delta 0), reused 0 (delta 0)

  • [new branch] HEAD -> branch Branch 'branch' set up to track remote branch 'branch' from 'origin'. Using a bash alias Alternatively, you can use a bash alias if you don’t want to modify your existing git commands.

Define a new bash alias using the “alias” command and define a name for it.

$ alias gp='git push -u origin HEAD' Let’s create a new branch and use our alias in order to push our code and create the upstream branch easily.

$ git checkout -b branch2 Total 0 (delta 0), reused 0 (delta 0)

  • [new branch] HEAD -> branch2 Branch 'branch2' set up to track remote branch 'branch2' from 'origin'. Set upstream branch for an existing remote branch In some cases, you may choose to link your local branches to existing remote branches that you just pulled or cloned from the main repository.

Let’s say for example that you pulled the “dev” branch located on the “origin” remote.

As a consequence, the tracking branch is named “origin/dev”. Set tracking branches for new local branches In order to switch to the local “dev” branch, and to set the “origin/dev” as the tracking branch (or upstream branch), use the “–track” option.

$ git checkout --track origin/dev

Branch 'dev' set up to track remote branch 'dev' from 'origin'. Switched to a new branch 'dev' To verify that you linked dev to the tracking branch “origin/dev” (which upstream branch is the remote dev), use the “git branch” command.

$ git branch -vv

  • dev 808b598 [origin/dev] Initial commit Set tracking branches for existing local branches On the other hand, you may have chosen to work on a local branch and to set the upstream branch (or the remote tracking branch later on).

It is perfectly fine, but you will have to use the “git branch” in order to set the existing branch upstream branch.

$ git branch -u / Let’s take the example of the “feature” branch that you just created to start working. $ git checkout -b feature Switched to a new branch 'feature' You created some commits in your branch, you want to set the tracking branch to be master.

$ git branch -u origin/master Branch 'feature' set up to track remote branch 'master' from 'origin'. Great! You successfully set the upstream branch for your existing local branch.

Why are upstream branches so useful in Git? Upstream branches are useful because :

You get references to your remote repositories and you essentially know if you are ahead of them or not. When performing a “git fetch” command, you can bring the new commits from your remote repository and you can choose to merge them at will.

You can perform pull and push easily When you set your upstream (or tracking) branches, you can simply execute pulls and pushes without having to specify the target branch. Git automatically knows that it has to fetch the new commits to the remote tracking branch. Similarly, Git already knows that it has to push new commits to the upstream branch.

But where does Git keep a reference of the upstream branches associated with local branches?

Git keeps references to upstream branches via its config file in the “.git” directory.

Inspecting tracking branches configuration In order to inspect your current Git configuration, list the hidden files and directories in your current working Git directory.

$ ls -al

total 16 drwxrwxr-x 3 schkn schkn 4096 Nov 5 16:10 . drwxrwxr-x 7 schkn schkn 4096 Nov 5 16:10 .. drwxrwxr-x 8 schkn schkn 4096 Nov 6 10:27 .git Now, inspect the content of the “config” file located in the .git directory. $ cat .git/config

[core] repositoryformatversion = 0 filemode = true bare = false logallrefupdates = true [remote "origin"] url = <repo_url> fetch = +refs/heads/:refs/remotes/origin/ [branch "master"] remote = origin merge = refs/heads/master As you can see, Git keeps a reference between your local branch, the name of the remote and the branch it has to merge with.

Conclusion In this tutorial, you learnt more about upstream branches and how they are related to remote tracking branches in Git.

You learnt different techniques in order to set remote tracking branches using a command or an alias to set it.

You also learnt how you can link your current local branches to existing remote tracking branches easily with the branch command.

If you are interested in Software Engineering, we have a complete section dedicated to it on the website so make sure to have a look.

比较本地代码与刚刚从远程下载下来的代码的区别; $ git diff temp

合并temp分支到本地的master分支; $ git merge temp

如果不想保留temp分支,删除; $ git branch -d temp 直接使用 git fetch 命令 git fetch 创建并更新本地远程分支。即创建并更新origin/xxx 分支,拉取代码到origin/xxx分支上; 在FETCH_HEAD中设定当前分支-origin/当前分支对应,如直接到时候git merge就可以将origin/abc合并到abc分支上; git fetch origin 手动指定了要fetch的remote。在不指定分支时通常默认为master; git fetch origin dev 指定远程remote和FETCH_HEAD,并且只拉取该分支的提交; 对比git pull 与git pull相比git fetch相当于是从远程获取最新版本到本地,但不会自动merge。如果需要有选择的合并git fetch是更好的选择。效果相同时git pull将更为快捷;

git reset --soft HEAD^ git reset HEAD file git checkout -- file

git checkout [ ...] git status git commit -m "'Merge' specific file from ''"

git-diff can show you the difference between two commits: git diff mybranch master -- myfile.cs Or, equivalently: git diff mybranch..master -- myfile.cs

--- a/main.c +++ b/main.c @@ -5,6 +5,5 @@ int main(int argc,char*argv[]) { printf("hello world github\n");

  • printf("from A\n"); return 0; }

Check HEAD pointer

如果想看 HEAD 指向,可以通过 cat .git/HEAD 查看, 如果 HEAD 指向的是一个引用,还可以用 git symbolic-ref HEAD 查看它的指向。 如果想看 FETCH_HEAD 指向,可以通过 cat .git/FETCH_HEAD 查看

创建分支: $ git branch mybranch 切换分支: $ git checkout mybranch 创建并切换分支: $ git checkout -b mybranch

更新master主线上的东西到该分支上:$git rebase master

切换到master分支:$git checkout master

更新mybranch分支上的东西到master上:$git rebase mybranch

提交:git commit -a

对最近一次commit的进行修改:git commit -a –amend

commit之后,如果想撤销最近一次提交(即退回到上一次版本)并本地保留代码:git reset HEAD^ 合并分支:(merge from) $ git checkout master $ git merge mybranch (merge from mybranch) 删除分支: $ git branch -d mybranch 强制删除分支: $ git branch -D mybranch 列出所有分支: $ git branch 查看各个分支最后一次提交: $ git branch -v

查看哪些分支合并入当前分支: $ git branch –merged

查看哪些分支未合并入当前分支: $ git branch –no-merged

更新远程库到本地: $ git fetch origin 推送分支: $ git push origin mybranch 取远程分支合并到本地: $ git merge origin/mybranch 取远程分支并分化一个新分支: $ git checkout -b mybranch origin/mybranch 删除远程分支:                 $ git push origin :mybranch

rebase: $ git checkout mybranch $ git rebase master (rebase from master)

举例: $ git checkout server $ git rebase –onto master server client $ git checkout master $ git merge client (fostforward) $ git rebase master server (checkout sever) $ git merge server $ git branch -d client $ git branch -d server

克隆该分支在本地,同样克隆后本地只有这一个分支 git clone -b <指定分支名> <远程仓库地址>

git checkout 的用法

用法一:切换分支

  git checkout

  eg: git checkout master (切换到master分支)

  git checkout -b

  eg: git checkout -b branch_1 (新建branch1分支 , 并切换到branch1分支)

用法二:撤销修改文件

  对A文件修改了,但是没有提交, 撤回:

  git checkout <A文件名>

  对A文件修改了,并提交了,撤回:

  首先,使用 git log 查看修改记录,并确定你想要恢复的版本号。

  git checkout <版本号> <文件名>

用法三:拉取远程文件,覆盖你修改并提交的文件

  git checkout origin <文件名>

Git - checkout远程分支 拉取远程分支,并创建本地分支

正确checkout操作

git checkout -b dev(本地分支名) origin/dev(远程分支名)

fatal: Cannot update paths and switch to branch ‘mq_bug_20180524’ at the same time. Did you intend to checkout ‘origin/mq_bug_20180524’ which can not be resolved as commit? 然后我们执行命令: git branch -a 里边并不包含远程都’origin/mq_bug_20180524分支。 远程仓库明明有’origin/mq_bug_20180524分支,可是为什么告诉没有呢? 原来这条命令并没有每一次都从远程更新仓库信息,这样子做是为了效率,我们可以手动更新一下: git fetch origin

然后我们在git branch -a 发现了我们的远程分支,然后我们在做新建一个分支: git checkout -b mq_bug_20180524 origin/mq_bug_20180524

VIM

set syntax=on set nu set autoindent set smartindent set cindent set nofixeol

Linux C

Over and over again: periodic tasks in Linux Fri, 09/25/2009 - 17:03 — csimmonds It is very common for real-time applications to have tasks that need to run periodically, for example to scan inputs or to generate regular outputs. A crude solution is to use a processing loop with a sleep at the end, but the periodicity will vary as the execution time varies. To create accurate periodic tasks you need to use timers. In this article I will show how timers work in Linux, especially with regard to multi-threaded applications.

Linux has several different timer interfaces, acquired over many years. Which to use depends on the versions of the kernel and C library you have. If you are using GNU libc 2.8 and kernel 2.6.25 or later, the timerfd interface is the best. If you are using GNU libc 2.3 and any version of the 2.6 kernel, the POSIX timers interface works well. If you are using uClibc you should use setitimer. I have examples of all three below.

In the examples I have separated out the timer code into two functions, make_periodic and wait_period:

struct periodic_info { /* Opaque data */ };

int make_periodic (unsigned int period, struct periodic_info *info); void wait_period (struct periodic_info *info); You call make_periodic at the start of the thread giving the period in microseconds and then call wait_period when execution is complete. This is inspired by RTAI [1] which has functions rt_task_make_periodic and rt_task_wait_period to do the same thing. To show you what I mean, here is an example of a thread with a period of 10 ms:

void *thread_1 (void *arg) { struct periodic_info info;

make_periodic (10000, &info);
while (1)
{
	/* Do useful work */
	wait_period (&info);
}
return NULL;

} Using timerfd The timerfd interface is a Linux-specific set of functions that present POSIX timers as file descriptors (hence the fd) rather than signals thus avoiding all that tedious messing about with signal handlers. It was first implemented in GNU libc 2.8 and kernel 2.6.25: if you have them I highly recommend this approach.

You create a timer by calling timerfd_create() giving the POSIX clock id CLOCK_REALTIME or CLOCK_MONOTONIC. For periodic timers such as we are creating it does not matter which you choose. For absolute timers the expiry time is changed if the system clock is changed and the clock is CLOCK_REALTIME. In almost all cases, CLOCK_MONOTONIC is the one to use. timerfd_create returns a file descriptor for the timer.

To set the timer running, call timerfd_settime() giving flag = TFD_TIMER_ABSTIME for an absolute timer or 0 for relative, as we want here, and the period in seconds and nanoseconds. To wait for the timer to expire, read from its file descriptor. It always returns an unsigned long long (8 byte unsigned integer) representing the number of timer events since the last read, which should be one if all is going well. If it is more than one then some events have been missed. In my example below I keep a record in "wakeups_missed".

#include

struct periodic_info { int timer_fd; unsigned long long wakeups_missed; };

static int make_periodic (unsigned int period, struct periodic_info *info) { int ret; unsigned int ns; unsigned int sec; int fd; struct itimerspec itval;

/* Create the timer */
fd = timerfd_create (CLOCK_MONOTONIC, 0);
info->wakeups_missed = 0;
info->timer_fd = fd;
if (fd == -1)
	return fd;

/* Make the timer periodic */
sec = period/1000000;
ns = (period - (sec * 1000000)) * 1000;
itval.it_interval.tv_sec = sec;
itval.it_interval.tv_nsec = ns;
itval.it_value.tv_sec = sec;
itval.it_value.tv_nsec = ns;
ret = timerfd_settime (fd, 0, &itval, NULL);
return ret;

}

static void wait_period (struct periodic_info *info) { unsigned long long missed; int ret;

/* Wait for the next timer event. If we have missed any the
   number is written to "missed" */
ret = read (info->timer_fd, &missed, sizeof (missed));
if (ret == -1)
{
	perror ("read timer");
	return;
}

/* "missed" should always be >= 1, but just to be sure, check it is not 0 anyway */
if (missed > 0)
	info->wakeups_missed += (missed - 1);

} Using POSIX timers If your glibc or kernel doesn't support timerfd, then you will have to use POSIX timers to generate signals and wait for the signal to arrive to indicate the start of the next period. This causes problems because signals are sent to the process not the thread. If you have several periodic threads, and therefore several timers, in a process each one must use a different signal to tell them apart. The obvious signals to use are the real time signals from SIGRTMIN (33) to SIGRTMAX (64), so you cannot have more than 32 timers per process. Note per process: it is perfectly acceptable to have 32 other timers in another process.

The way to wait for a signal to arrive is to block it and then call sigwait(). Here is another complication: although signals are sent to the parent process, each thread has its own signal mask. I will write another article on the reasons it is done this way, but in this case it has the implication that all the real time signals must be blocked before creating any threads so that they all inherit the same mask. Doing it any other way risks the race condition where the signal is delivered before all threads have blocked it, resulting in the process being killed.

You can detect missed timer events using the function timer_getoverrun (), which returns zero if none were missed. Here is the code:

struct periodic_info { int sig; sigset_t alarm_sig; int wakeups_missed; };

static int make_periodic (int unsigned period, struct periodic_info *info) { static int next_sig; int ret; unsigned int ns; unsigned int sec; struct sigevent sigev; timer_t timer_id; struct itimerspec itval;

/* Initialise next_sig first time through. We can't use static
   initialisation because SIGRTMIN is a function call, not a constant */
if (next_sig == 0)
	next_sig = SIGRTMIN;
/* Check that we have not run out of signals */
if (next_sig > SIGRTMAX)
	return -1;
info->sig = next_sig;
next_sig++;

info->wakeups_missed = 0;

/* Create the signal mask that will be used in wait_period */
sigemptyset (&(info->alarm_sig));
sigaddset (&(info->alarm_sig), info->sig);

/* Create a timer that will generate the signal we have chosen */
sigev.sigev_notify = SIGEV_SIGNAL;
sigev.sigev_signo = info->sig;
sigev.sigev_value.sival_ptr = (void *) &timer_id;
ret = timer_create (CLOCK_MONOTONIC, &sigev, &timer_id);
if (ret == -1)
	return ret;

/* Make the timer periodic */
sec = period/1000000;
ns = (period - (sec * 1000000)) * 1000;
itval.it_interval.tv_sec = sec;
itval.it_interval.tv_nsec = ns;
itval.it_value.tv_sec = sec;
itval.it_value.tv_nsec = ns;
ret = timer_settime (timer_id, 0, &itval, NULL);
return ret;

}

static void wait_period (struct periodic_info *info) { int sig; sigwait (&(info->alarm_sig), &sig); info->wakeups_missed += timer_getoverrun (info->timer_id); }

int main(int argc, char *argv[]) { sigset_t alarm_sig; int i;

/* Block all real time signals so they can be used for the timers.
   Note: this has to be done in main() before any threads are created
   so they all inherit the same mask. Doing it later is subject to
   race conditions */
sigemptyset (&alarm_sig);
for (i = SIGRTMIN; i <= SIGRTMAX; i++)
	sigaddset (&alarm_sig, i);
sigprocmask (SIG_BLOCK, &alarm_sig, NULL);

Using setitimer This ONLY works if you are using uClibc. Actually the real determinant is that you are using the Linux Threads library rather than the Native POSIX Threads Library. With very few exceptions, uClibc uses Linux Threads, glibc uses NPTL.

Using setitimer is somewhat similar to POSIX clocks except that it is hard coded to deliver a SIGALRM at the end of each period. Using NPTL that means that you can only have one periodic task per process, but with Linux Threads each thread IS a process so that is fine: you can have as many periodic threads as you like.

Setitimer is part of the base POSIX specification and has been present in Linux since the year dot. The time out is passed in a struct itimerval which contains an initial time out, it_value, and a periodic time out in it_interval which is reloaded into it_value every time it expires. At each expiry it sends a SIGALRM. The times are given in microseconds which will be rounded up the the granularity of your timers if they are greater than 1 us. The best way to handle the signal is to block it and then wait for the next one with sigwait() as shown below. There is no easy way to detect missed timer events.

Here is the code:

struct periodic_info { sigset_t alarm_sig; };

static int make_periodic (unsigned int period, struct periodic_info *info) { int ret; struct itimerval value;

/* Block SIGALRM in this thread */
sigemptyset (&(info->alarm_sig));
sigaddset (&(info->alarm_sig), SIGALRM);
pthread_sigmask (SIG_BLOCK, &(info->alarm_sig), NULL);

/* Set the timer to go off after the first period and then
   repetitively */
value.it_value.tv_sec = period/1000000;
value.it_value.tv_usec = period%1000000;
value.it_interval.tv_sec = period/1000000;
value.it_interval.tv_usec = period%1000000;
ret = setitimer (ITIMER_REAL, &value, NULL);
if (ret != 0)
	perror ("Failed to set timer");
return ret;

}

static void wait_period (struct periodic_info *info) { int sig;

/* Wait for the next SIGALRM */
sigwait (&(info->alarm_sig), &sig);

} Conclusion The accuracy of the timers will depend on the your kernel and scheduling policy and priority you use for the threads. By default all time-outs will be rounded up to the nearest 10 ms (actually 1/HZ, but in most cases HZ = 100). If your board support package supports High Resolution Timers (most do) enabling CONFIG_HIGH_RES_TIMERS will give you accuracy to a few microseconds. Periodic threads are almost by definition real-time, so you probably want to give them a real-time policy such as SCHED_FIFO (in a follow-up article I will look into the implications of real-time periodic threads). Finally, if you want to reduce jitter to sub millisecond, you should enable kernel pre-emption (CONFIG_PREEMPT) or for jitter in the 10's to 100's microsecond region you should apply the PREEMPT_RT patch [2].

The code samples are on GitHub, at https://github.com/csimmonds/periodic-threads.

These and other topics related to writing robust applications for embedded Linux are covered in my training class, System programming for embedded Linux, and in my book Mastering Embedded Linux Programming

References [1] RTAI: the Real Time Application Interface, https://www.rtai.org/

[2] The PREEMPT_RT real time patch series, http://www.kernel.org/pub/linux/kernel/projects/rt/

#include <stdio.h> #include <stdlib.h>

#include <signal.h> #include <sys/time.h> #include <unistd.h>

struct periodic_info { sigset_t alarm_sig; };

static int make_periodic (unsigned int period, struct periodic_info *info) { int ret; struct itimerval value;

/* Block SIGALRM in this thread */
sigemptyset (&(info->alarm_sig));
sigaddset (&(info->alarm_sig), SIGALRM);
pthread_sigmask (SIG_BLOCK, &(info->alarm_sig), NULL);

/* Set the timer to go off after the first period and then
   repetitively */

#if 0 value.it_value.tv_sec = period/1000000; value.it_value.tv_usec = period%1000000; value.it_interval.tv_sec = period/1000000; value.it_interval.tv_usec = period%1000000; #endif

value.it_value.tv_sec = 1;
value.it_value.tv_usec = 0;
value.it_interval.tv_sec = 1;
value.it_interval.tv_usec = 0;



ret = setitimer (ITIMER_REAL, &value, NULL);
if (ret != 0)
	perror ("Failed to set timer");
return ret;

}

static void wait_period (struct periodic_info *info) { int sig;

/* Wait for the next SIGALRM */
sigwait (&(info->alarm_sig), &sig);

}

int main(int argc,char*argv[]) { struct periodic_info info;

make_periodic (10000, &info);
while (1)
{
	/* Do useful work */
	printf("hello world!\n");
	wait_period (&info);
}

}

encrypt&decrypt

X.509证书DN详解

X.509使用DN(Distinct Name)来唯一标识一个实体,其功能类似我们平常使用的ID,不过不同的是,DN不再是类似 123456 这样得数字标识,而是采用多个字段来标识一个实体,例如”CN=老所,C=CN”,这样做的好处在于方便匹配到诸如LDAP一样的目录服务中。那么,DN的字段是否可以随意增加呢?比如我能否在”CN=老所,O=测试公司”这样一个DN上再增加一个ID属性,变成”CN=老所,O=测试公司,ID=123456″呢?

动手测试。首先采用微软的 XEnroll 组件来进行测试。XEnroll 是微软平台下的 ActiveX 控件,提供证书的加入服务,比如创建 PKCS#10 证书请求。我们可以用 Javascript 来调用这个 ActiveX 控件:

var sOID = ''; var sDN = 'CN="老所",O="测试公司",ID="123456"'; alert(sDN); var sPKCS10 = oXEnroll.createPKCS10(sDN, sOID); 其中,createPKCS10 函数接受两个参数,第一个是字符串类型的DN,第二个是用以标识该证书请求的用途的OID,这里,我们不在乎该证书的用途,所以就设为空好了。通过 IE 运行该段代码后,我得到了 object Error,看来,不能这样简单地添加DN字段。

于是开始搜索关于 X.509 DN 的信息。原来,X.509 证书里的 DN 属性,都是一些基于 ASN1 编码的对象,也就是说,对于我们熟知的 CN 属性,或者说 commonName 属性,程序是无法从证书里获得的,证书里是不会写诸如 CN=xxx 这样的信息的。所有字段都被表示为该字段类型的 OID 。OID 则是 ASN1 对象的统一表示方法,这种方法采用树状结构,由国际组织统一管理。一个公司如果要增加一个字段类型,并希望被广泛采用,它必须像管理 OID 的组织提出申请,就像我们常用的域名申请一样。

www.oid-info.com 这个网站,我们可以查询所有注册过的 OID 信息。这个网站提供两种方式的查询:按树状结构展开各级 OID,或者直接填写诸如”2.3.4″这样的 OID 来进行查询。

假如我们展开 2.5.4 这层 OID,我们就会发现很多我们常用的 DN 字段:

2.5.4.3 - commonName 2.5.4.4 - surname 2.5.4.13 - description 2.5.4.10 - organizationName …… 既然如此,那我们就采用 OID 形式来添加属性类型吧,比如我使用 OID=2.5.4.888,这是一个未被注册的 OID,可以用来测试:

var sOID = ''; var sDN = 'CN="老所",O="测试公司",2.5.4.888="123456"'; alert(sDN); var sPKCS10 = oXEnroll.createPKCS10(sDN, sOID); 运行该脚步,成功了,我们从机器里的证书申请库里找到该证书申请,查看其属性:

可以看出,虽然用 OID 添加属性成功了,但这个 OID 并不能被翻译成方便我们阅读的形式,就像我们只能使用 IP 而无法使用域名一样。要让该证书显示 ID= 而不是 2.5.4.888=,我们必须要:

向标准组织注册 2.5.4.888 为 ID; 通知微软,修改其 Windows 程序,将该 OID 翻译为 ID 显然,这非常不现实,呵呵。

接下来,我们再用 openssl 来测试一下。我选用的是 pyOpenSSL,一个 openssl 的 Python 包装库:

from OpenSSL import crypto req = crypto.X509Req() subject = req.get_subject() setattr(subject, 'CN', 'soloman') setattr(subject, 'ID', '123456') Traceback (most recent call last): File "", line 1, in AttributeError: No such attribute

看来,pyOpenSSL也只认识 CN,不认识 ID。查询了一下 pyopenssl 的源代码:

static int crypto_X509Name_setattr(crypto_X509NameObj *self, char *name, PyObject *value) { int nid; int result; char *buffer; if ((nid = OBJ_txt2nid(name)) == NID_undef) { PyErr_SetString(PyExc_AttributeError, "No such attribute"); return -1; } ...... 可以看出,pyOpenSSL 在设置 DN 的时候,首先调用 openssl 的 OBJ_txt2nid()函数来将方便我们记忆的对象名字翻译成 OID, 然后再翻译成 openssl 程序里自己的 NID。

那么继续查询 openssl 的源代码,在 objects.h 文件里我们找到了所有 DN 字段的定义:

...... #define SN_commonName "CN" #define LN_commonName "commonName" #define NID_commonName 13 #define OBJ_commonName OBJ_X509,3L #define SN_countryName "C" #define LN_countryName "countryName" #define NID_countryName 14 #define OBJ_countryName OBJ_X509,6L #define SN_localityName "L" #define LN_localityName "localityName" #define NID_localityName 15 #define OBJ_localityName OBJ_X509,7L ...... 这里,openssl 为每个常用的 DN 属性定义了4个内容:SN, LN, NID, OBJ。其中 SN 表示 Short Name, LN 表示 Long Name,都是一个 OID 方便我们阅读的名称的两个形式,而 OBJ 则定义了其 OID 信息。

在 openssl/crypto/asn1/a_strnid.c 这个文件里,绑定了 openssl 能够支持并翻译的 DN 字段:

static ASN1_STRING_TABLE tbl_standard[] = { {NID_commonName, 1, ub_common_name, DIRSTRING_TYPE, 0}, {NID_countryName, 2, 2, B_ASN1_PRINTABLESTRING, STABLE_NO_MASK}, {NID_localityName, 1, ub_locality_name, DIRSTRING_TYPE, 0}, {NID_stateOrProvinceName, 1, ub_state_name, DIRSTRING_TYPE, 0}, {NID_organizationName, 1, ub_organization_name, DIRSTRING_TYPE, 0}, {NID_organizationalUnitName, 1, ub_organization_unit_name, DIRSTRING_TYPE, 0}, {NID_pkcs9_emailAddress, 1, ub_email_address, B_ASN1_IA5STRING, STABLE_NO_MASK}, {NID_pkcs9_unstructuredName, 1, -1, PKCS9STRING_TYPE, 0}, {NID_pkcs9_challengePassword, 1, -1, PKCS9STRING_TYPE, 0}, {NID_pkcs9_unstructuredAddress, 1, -1, DIRSTRING_TYPE, 0}, {NID_givenName, 1, ub_name, DIRSTRING_TYPE, 0}, {NID_surname, 1, ub_name, DIRSTRING_TYPE, 0}, {NID_initials, 1, ub_name, DIRSTRING_TYPE, 0}, {NID_serialNumber, 1, ub_serial_number, B_ASN1_PRINTABLESTRING, STABLE_NO_MASK}, {NID_friendlyName, -1, -1, B_ASN1_BMPSTRING, STABLE_NO_MASK}, {NID_name, 1, ub_name, DIRSTRING_TYPE, 0}, {NID_dnQualifier, -1, -1, B_ASN1_PRINTABLESTRING, STABLE_NO_MASK}, {NID_domainComponent, 1, -1, B_ASN1_IA5STRING, STABLE_NO_MASK}, {NID_ms_csp_name, -1, -1, B_ASN1_BMPSTRING, STABLE_NO_MASK} }; 可以看出,如果要让我们的基于 openssl 的程序支持一个我们自定义的属性,我们得修改 openssl 源代码,添加我们的新字段的”名称-OID” 绑定(可以只有Long Name),然后重新编译 openssl 和我们的程序,使其能使用新的属性。

我们回过头来想想,为什么标准化组织没有为我们提供一个类似 ID 这样的属性呢?这是因为,DN本来就是作为 ID 来使用的,它把一些属性绑定起来,来标识一个实体,如果再有一个通用的 ID 属性,逻辑上就冲突了。当然针对具体的应用,你可以注册一个特殊的 ID 属性,比如 openID ,但这需要实力雄厚的公司来进行注册和推广。

然而实际应用中,当我们想将证书系统与某个特别的应用中的某个 ID 进行绑定,而又不具备向国际组织进行 OID 注册和推广的能力时,我们完全可以利用现有的属性来进行实现,比如,针对这个需求,我可以设计出两个方案:

方案一:CN=老所@123456 方案二:CN=123456, surname=老, givenName=所 方案三:CN=123456, name=老所 方案四:CN=老所, description=123456 方案一,通过一定的方式,将用户的姓名和ID组合到CN里,然后在应用中解析出ID,这个方法不是很便于用户书写,用户在填写证书申请资料的时候必须注意符合该规范。

方案二与三的概念就是用CN来记录ID,毕竟,Common Name 嘛,随你如何定义。而用户的姓名由 surname + givenName 或者 name 来指定。该方法通过了 openssl 测试:

req = crypto.X509Req() subject = req.get_subject() setattr(subject, 'surname', 'Lao') setattr(subject, 'givenName', 'Suo') setattr(subject, 'name', 'Lao Suo') 但是,XEnroll 仿佛不支持 surname + givenName 或者 name 属性,其文档上说明是支持 X.500 规范,而不是 X.509 规范。

方案四,则利用了 description 属性来记录和具体应用相关的讯息,比如我们的 ID,该方法通过了 openssll 的测试:

req = crypto.X509Req() subject = req.get_subject() setattr(subject, 'CN', 'Lao Suo') setattr(subject, 'description', '123456') 和 XEnroll 的测试:

深入理解加密、解密、数字签名(签名证书、加密证书)的组成和数字证书

随着电子商务的迅速发展,信息安全已成为焦点问题之一,尤其是网上支付和网络银行对信息安全的要求显得更为突出。为了能在因特网上开展安全的电子商务活动,公开密钥基础设施( PKI, Public Key Infrastructure )逐步在国内外得到广泛应用。我们是否真的需要 PKI , PKI 究竟有什么用?下面通过一个案例一步步地来剖析这个问题 : 甲想将一份合同文件通过 Internet 发给远在国外的乙,此合同文件对双方非常重要,不能有丝毫差错,而且此文件绝对不能被其他人得知其内容。如何才能实现这个合同的安全发送? 问题 1: 最自然的想法是,甲必须对文件加密才能保证不被其他人查看其内容,那么 , 到底应该用什么加密技术,才能使合同传送既安全又快速呢 ? 可以采用一些成熟的对称加密算法 , 如 DES 、 3DES 、 RC5 等对文件加密。对称加密采用了对称密码编码技术,它的特点是文件加密和解密使用相同的密钥,即加密密钥也可以用做解密密钥,这种方法在密码学中叫做对称加密算法,

问题 2: 如果黑客截获此文件,是否用同一算法就可以解密此文件呢 ? 不可以 , 因为加密和解密均需要两个组件 : 加密算法和对称密钥 , 加密算法需要用一个对称密钥来解密 , 黑客并不知道此密钥。

问题 3: 既然黑客不知密钥,那么乙怎样才能安全地得到其密钥呢?用电话通知,若电话被窃听,通过 Internet 发此密钥给乙,可能被黑客截获,怎么办 ? 方法是用非对称密钥算法加密对称密钥后进行传送。与对称加密算法不同,非对称加密算法需要两个密钥:公开密钥( Public Key )和私有密钥( Private Key )。公开密钥与私有密钥是一对,如果用公开密钥对数据进行加密,只有用对应的私有密钥才能解密;如果用私有密钥对数据进行加密,只有用对应的公开密钥才能解密。因为加密和解密使用的是两个不同的密钥,所以这种算法叫做非对称加密算法 ( 公 / 私钥可由专门软件生成 ) 。甲乙双方各有一对公 / 私钥,公钥可在 Internet 上传送,私钥自己保存。这样甲就可以用乙的公钥加密问题 1 中提到的对称加密算法中的对称密钥。即使黑客截获到此密钥,也会因为黑客不知乙的私钥,而解不开对称密钥,因此也解不开密文,只有乙才能解开密文。

问题 4 :既然甲可以用乙的公钥加密其对称密钥,为什么不直接用乙的公钥加密其文件呢?这样不仅简单,而且省去了用对称加密算法加密文件的步骤? 不可以这么做。因为非对称密码算法有两个缺点 : 加密速度慢 , 比对称加密算法慢 10 ~ 100 倍 , 因此只可用其加密小数据 ( 如对称密钥 ) ,另外加密后会导致得到的密文变长。因此一般采用对称加密算法加密其文件 , 然后用非对称算法加密对称算法所用到的对称密钥。

问题 5 : 如果黑客截获到密文,同样也截获到用公钥加密的对称密钥,由于黑客无乙的私钥,因此他解不开对称密钥,但如果他用对称加密算法加密一份假文件 , 并用乙的公钥加密一份假文件的对称密钥,并发给乙,乙会以为收到的是甲发送的文件,会用其私钥解密假文件 , 并很高兴地阅读其内容,但却不知已经被替换。换句话说,乙并不知道这不是甲发给他的,怎么办 ? 答案是用数字签名证明其身份。数字签名是通过散列算法 , 如 MD5 、 SHA-1 等算法从大块的数据中提取一个摘要。而从这个摘要中不能通过散列算法恢复出任何一点原文,即得到的摘要不会透露出任何最初明文的信息,但如果原信息受到任何改动,得到的摘要却肯定会有所不同。因此甲可以对文件进行散列算法得到摘要,并用自己的私钥加密 ( 因为非对称算法可逆,即用私钥可解开公钥加密的文件,反之亦然 ) ,这样即使黑客截获也无用。因为黑客不会从摘要内获得任何信息,但乙却不一样,他可用甲的公钥解密,得到其摘要 ( 如果用甲的公钥能够解开此摘要,说明此摘要肯定是甲发的,因为只有甲的公钥才能解开用甲的私钥加密的信息 , 而甲的私钥只有甲自己知道 ) ,并对收到的文件 ( 解密后的合同文件 ) 也进行同样的散列算法,通过比较其摘要是否一样 , 就可得知此文件是否被篡改过 ( 因为若摘要相同,则肯定信息未被改动,这是散列算法的特点 ) 。这样不仅解决了证明发送人身份的问题,同时还解决了文件是否被篡改问题。

问题 6 : 通过对称加密算法加密其文件,再通过非对称算法加密其对称密钥 , 又通过散列算法证明其发送者身份和其信息的正确性,这样是否就万无一失了 ? 回答是否定的。问题在于乙并不能肯定他所用的所谓甲的公钥一定是甲的 , 解决办法是用数字证书来绑定公钥和公钥所属人。 数字证书是一个经证书授权中心数字签名的包含公开密钥拥有者信息以及公开密钥的文件 , 是网络通信中标识通信各方身份信息的一系列数据,它提供了一种在 Internet 上验证身份的方式,其作用类似于司机的驾驶执照或日常生活中的身份证,人们可以在交往中用它来识别对方的身份。 最简单的证书包含一个公开密钥、名称以及证书授权中心的数字签名。一般情况下证书中还包括密钥的有效时间、发证机关 ( 证书授权中心 ) 名称、该证书的序列号等信息。它是由一个权威机构—— CA 机构,又称为证书授权 (Certificate Authority) 中心发放的。 CA 机构作为电子商务交易中受信任的第三方,承担公钥体系中公钥的合法性检验的责任。 CA 中心为每个使用公开密钥的用户发放一个数字证书,数字证书的作用是证明证书中列出的用户合法拥有证书中列出的公开密钥。 CA 机构的数字签名使得攻击者不能伪造和篡改证书, CA 是 PKI 的核心,负责管理 PKI 结构下的所有用户(包括各种应用程序)的证书,把用户的公钥和用户的其他信息捆绑在一起,在网上验证用户的身份。 因为数字证书是公开的,就像公开的电话簿一样,在实践中,发送者(即甲)会将一份自己的数字证书的拷贝连同密文、摘要等放在一起发送给接收者(即乙),而乙则通过验证证书上权威机构的签名来检查此证书的有效性(只需用那个可信的权威机构的公钥来验证该证书上的签名就可以了),如果证书检查一切正常,那么就可以相信包含在该证书中的公钥的确属于列在证书中的那个人(即甲)。

问题 7 : 至此似乎很安全了。但仍存在安全漏洞,例如:甲虽将合同文件发给乙 , 但甲拒不承认在签名所显示的那一时刻签署过此文件 ( 数字签名就相当于书面合同的文字签名 ) ,并将此过错归咎于电脑,进而不履行合同,怎么办 ? 解决办法是采用可信的时钟服务 ( 由权威机构提供 ) ,即由可信的时间源和文件的签名者对文件进行联合签名。在书面合同中,文件签署的日期和签名一样均是十分重要的防止文件被伪造和篡改的关键性内容 ( 例如合同中一般规定在文件签署之日起生效 ) 。在电子文件中,由于用户桌面时间很容易改变 ( 不准确或可人为改变 ) ,由该时间产生的时间戳不可信赖,因此需要一个第三方来提供时间戳服务(数字时间戳服务( DTS )是网上安全服务项目,由专门的机构提供)。此服务能提供电子文件发表时间的安全保护。 时间戳产生的过程为 : 用户首先将需要加时间戳的文件用哈希编码加密形成摘要,然后将该摘要发送到 DTS , DTS 在加入了收到文件摘要的日期和时间信息后再对该文件加密(数字签名),然后送回用户。因此时间戳 (time-stamp) 是一个经加密后形成的凭证文档,它包括三个部分:需加时间戳的文件的摘要, DTS 收到文件的日期和时间, DTS 的数字签名。由于可信的时间源和文件的签名者对文件进行了联合签名 , 进而阻止了文档签名的那一方 ( 即甲方 ) 在时间上欺诈的可能性 , 因此具有不可否认性。

问题 8: 有了数字证书将公 / 私钥和身份绑定 , 又有权威机构提供时钟服务使其具有不可否认性 , 是不是就万无一失了 ? 不 , 仍然有问题。乙还是不能证明对方就是甲,因为完全有可能是别人盗用了甲的私钥 ( 如别人趁甲不在使用甲的电脑 ), 然后以甲的身份来和乙传送信息 , 这怎么解决呢 ? 解决办法是使用强口令、认证令牌、智能卡和生物特征等技术对使用私钥的用户进行认证,以确定其是私钥的合法使用者。 解决这个问题之前我们先来看看目前实现的基于 PKI 的认证通常是如何工作的。以浏览器或者其他登记申请证书的应用程序为例说明,在第一次生成密钥的时候会创建一个密钥存储,浏览器用户会被提示输入一个口令,该口令将被用于构造保护该密钥存储所需的加密密钥。如果密钥存储只有脆弱的口令保护或根本没有口令保护,那么任何一个能够访问该电脑浏览器的用户都可以访问那些私钥和证书。在这种场景下 , 又怎么可能信任用 PKI 创建的身份呢 ? 正因为如此,一个强有力的 PKI 系统必须建立在对私钥拥有者进行强认证的基础之上,现在主要的认证技术有:强口令、认证令牌、智能卡和生物特征(如指纹,眼膜等认证)。 以认证令牌举例 : 假设用户的私钥被保存在后台服务器的加密容器里,要访问私钥,用户必须先使用认证令牌认证(如用户输入账户名、令牌上显示的通行码和 PIN 等),如果认证成功,该用户的加密容器就下载到用户系统并解密。 通过以上问题的解决,就基本满足了安全发送文件的需求。下面总结一下这个过程 , 对甲而言整个发送过程如下 :

  1. 创建对称密钥 ( 相应软件生成,并且是一次性的 ) ,用其加密合同,并用乙的公钥打包对称密钥。
  2. 创建数字签名,对合同进行散列算法 ( 如 MD5 算法 ) 并产生原始摘要,甲用自己的私钥加密该摘要 ( 公 / 私钥既可自己创建也可由 CA 提供 ) 。
  3. 最后 , 甲将加密后的合同、打包后的密钥、加密后的摘要 , 以及甲的数字证书 ( 由权威机构 CA 签发 ) 一起发给乙。 而乙接收加密文件后,需要完成以下动作 :
  4. 接收后,用乙的私钥解密得到对称密钥 , 并用对称密钥解开加密的合同 , 得到合同明文。
  5. 通过甲的数字证书获得属于甲的公钥 , 并用其解开摘要 ( 称做摘要 1) 。
  6. 对解密后的合同使用和发送者同样的散列算法来创建摘要 ( 称做摘要 2) 。
  7. 比较摘要 1 和摘要 2, 若相同 , 则表示信息未被篡改 , 且来自于甲。 甲乙传送信息过程看似并不复杂 , 但实际上它由许多基本成分组成 , 如 : 对称 / 非对称密钥密码技术、数字证书、数字签名、证书发放机构( CA )、公开密钥的安全策略等 , 这其中最重要、最复杂的是证书发放机构( CA )的构建。 在此强调下证书和签名: 证书实际对于非对称加密算法(公钥加密)来说的,一般证书包括公钥、姓名、数字签名三个部分。证书好比身份证,证书机构(ca)就好比是公安局,职责就是负责管理用户的证书也就是身份证。比如我的公钥是FrankKey,姓名是Frank Xu Lei。公安局可以给我登记,但是怎么保证我和别的Frank XuLei区别开呢,于是公安局(证书机构)就使用我的名字和密钥做了个组合,再使用一种哈希算法,得出一串值,来标识我的唯一性,这个值就是我的身份证号码,也就是证书里的数字签名(消息摘要),同时为了不能让黑客仿造数字证书,数字证书的发行者用自己的私钥对数字签名进行加密,这样,使用该数字证书的网络交易实体就可以用证书发行者的公钥进行解密验证。(即:数字签名的目的是证明自己的身份的确是真实的自己而非其他人,所以需要用自己的私钥进行加密,让使用者用公钥进行解密验证,从而从技术上杜绝了伪造自己的黑客。) 假设一个朋友给我写信,他就可以到公安局(证书机构)来查找我的身份证(证书)。上面包括我的个人信息,可以保证这个公钥就是我的。然后他把新建进行加密,邮寄给我。别人即使拆开我的信件,因为没有密钥进行解密,所以无法阅读我的信件内容。这样就保证了信息安全。 所以说加密不一定要证书,取决于你数据安全具体的需求。一般大型的电子商务网站都有自己特定的证书。证书管理的机构比较有名的就是VeriSign(可以 说是互联网上的身份证管理局)。企业可以申请注册,它会给申请者生成特定的签名。我们自己的企业内部应用如果需要的话,可以在企业局域网内部建立企业私有的证书服务器,来产生和管理证书。其实X.509是由国际电信联盟(ITU-T)制定的一种定义证书格式和分布的国际标准(相当于制作身份证的规范)。为了提供公用网络用户目录信息服务,并规定了实体鉴别过程中广泛适用的证书语法和数据接口, X.509 称之为证书,或者说是身份证的一种形式,类似与我们现在的二代身份证,也是身份证的一种,根据特定的标准制作出来的。另外证书使用的时候还有有效期的限制,和我们的身份证的10年有效期一样。证书也可以设置有效期。

附:1.U盾的工作原理介绍 一、什么是U盾 U盾,即工行2003年推出并获得国家专利的客户证书USBkey,是工行为您提供的办理网上银行业务的高级别安全工具。它外形酷似U盘,像一面盾牌,时刻保护着您的网上银行资金安全。 从技术角度看,U盾是用于网上银行电子签名和数字认证的工具,它内置微型智能卡处理器,采用1024位非对称密钥算法对网上数据进行加密、解密和数字签名,确保网上交易的保密性、真实性、完整性和不可否认性 二、工作原理 U盾又作移动数字证书,它存放着你个人的数字证书,并不可读取。同样,银行也记录着你的数字证书。 当你尝试进行网上交易时,银行会向你发送由时间字串,地址字串,交易信息字串,防重放攻击字串组合在一起进行加密后得到的字串A,你的U盾将跟据你的个人证书对字串A进行不可逆运算得到字串B,并将字串B发送给银行,银行端也同时进行该不可逆运算,如果银行运算结果和你的运算结果一致便认为你合法,交易便可以完成,如果不一致便认为你不合法,交易便会失败。(理论上,不同的字串A不会得出相同的字串B,即一个字串A对应一个唯一的字串B;但是字串B和字串A无法得出你的数字证书,而且U盾具有不可读取性,所以任何人都无法获行你的数字证书。并且银行每次都会发不同的防重放字串(随机字串)和时间字串,所以当一次交易完成后,刚发出的B字串便不再有效。综上所述,理论上U盾是绝对安全的****注意是理论上发生伪造概率大约为2的80次方分之一,但是如果有像变形金刚中的那种DNAbasecomputer的话。 参考文献:http://blog.chinaunix.net/uid-23637692-id-3057988.html(介绍了PKI体系和常见证书)

2.推荐一本书籍,可以系统性学习: 《信息安全原理与实践》

详解公钥、私钥、数字证书的概念 加密和认证   首先我们需要区分加密和认证这两个基本概念。    加密是将数据资料加密,使得非法用户即使取得加密过的资料,也无法获取正确的资料内容,所以数据加密可以保护数据,防止监听攻击。其重点在于数据的安全性。身份认证是用来判断某个身份的真实性,确认身份后,系统才可以依不同的身份给予不同的权限。其重点在于用户的真实性。两者的侧重点是不同的。

公钥和私钥 公钥和私钥就是俗称的不对称加密方式,是从以前的对称加密(使用用户名与密码)方式的提高。

  在现代密码体制中加密和解密是采用不同的密钥(公开密钥),也就是非对称密钥密码系统,每个通信方均需要两个密钥,即公钥和私钥,这两把密钥可以互为加解密。公钥是公开的,不需要保密,而私钥是由个人自己持有,并且必须妥善保管和注意保密。

  公钥私钥的原则:

一个公钥对应一个私钥。 密钥对中,让大家都知道的是公钥,不告诉大家,只有自己知道的,是私钥。 如果用其中一个密钥加密数据,则只有对应的那个密钥才可以解密。 如果用其中一个密钥可以进行解密数据,则该数据必然是对应的那个密钥进行的加密。 用电子邮件的方式说明一下原理。 使用公钥与私钥的目的就是实现安全的电子邮件,必须实现如下目的: 1. 我发送给你的内容必须加密,在邮件的传输过程中不能被别人看到。 2. 必须保证是我发送的邮件,不是别人冒充我的。 要达到这样的目标必须发送邮件的两人都有公钥和私钥。 公钥,就是给大家用的,你可以通过电子邮件发布,可以通过网站让别人下载,公钥其实是用来加密/验章用的。私钥,就是自己的,必须非常小心保存,最好加上 密码,私钥是用来解密/签章,首先就Key的所有权来说,私钥只有个人拥有。公钥与私钥的作用是:用公钥加密的内容只能用私钥解密,用私钥加密的内容只能 用公钥解密。 比如说,我要给你发送一个加密的邮件。首先,我必须拥有你的公钥,你也必须拥有我的公钥。 首先,我用你的公钥给这个邮件加密,这样就保证这个邮件不被别人看到,而且保证这个邮件在传送过程中没有被修改。你收到邮件后,用你的私钥就可以解密,就能看到内容。 其次我用我的私钥给这个邮件加密,发送到你手里后,你可以用我的公钥解密。因为私钥只有我手里有,这样就保证了这个邮件是我发送的。

  非对称密钥密码的主要应用就是公钥加密和公钥认证,而公钥加密的过程和公钥认证的过程是不一样的,下面我就详细讲解一下两者的区别。

基于公开密钥的加密过程

比如有两个用户Alice和Bob,Alice想把一段明文通过双钥加密的技术发送给Bob,Bob有一对公钥和私钥,那么加密解密的过程如下:

Bob将他的公开密钥传送给Alice。 Alice用Bob的公开密钥加密她的消息,然后传送给Bob。 Bob用他的私人密钥解密Alice的消息。  Alice使用Bob的公钥进行加密,Bob用自己的私钥进行解密。

基于公开密钥的认证过程

  身份认证和加密就不同了,主要用来鉴别用户的真伪。这里我们只要能够鉴别一个用户的私钥是正确的,就可以鉴别这个用户的真伪。

  还是Alice和Bob这两个用户,Alice想让Bob知道自己是真实的Alice,而不是假冒的,因此Alice只要使用公钥密码学对文件 签名发送给Bob,Bob使用Alice的公钥对文件进行解密,如果可以解密成功,则证明Alice的私钥是正确的,因而就完成了对Alice的身份鉴 别。整个身份认证的过程如下:

Alice用她的私人密钥对文件加密,从而对文件签名。 Alice将签名的文件传送给Bob。 Bob用Alice的公钥解密文件,从而验证签名。  Alice使用自己的私钥加密,Bob用Alice的公钥进行解密。

根证书

   根证书是CA认证中心给自己颁发的证书,是信任链的起始点。安装根证书意味着对这个CA认证中心的信任。

总结

  根据非对称密码学的原理,每个证书持有人都有一对公钥和私钥,这两把密钥可以互为加解密。公钥是公开的,不需要保密,而私钥是由证书持有人自己持有,并且必须妥善保管和注意保密。

  数字证书则是由证书认证机构(CA)对证书申请者真实身份验证之后,用CA的根证书对申请人的一些基本信息以及申请人的公钥进行签名(相当于加盖发证书机构的公章)后形成的一个数字文件。CA完成签发证书后,会将证书发布在CA的证书库(目录服务器)中,任何人都可以查询和下载,因此数字证书和公钥一样是公开的。  

  可以这样说,数字证书就是经过CA认证过的公钥,而私钥一般情况都是由证书持有者在自己本地生成的,由证书持有者自己负责保管。具体使用时,签名操作是发 送方用私钥进行签名,接受方用发送方证书来验证签名;加密操作则是用接受方的证书进行加密,接受方用自己的私钥进行解密。

签名证书VS加密证书 从证书的用途来看,数字证书可以分为: 签名证书:主要用于对用户信息进行签名,以保证信息的不可否认性。 加密证书:主要用于对用户传送信息进行加密,以保证信息的真实性和完整性。

什么是加密证书和签名证书?

数字证书可分为签名证书和加密证书。

签名证书主要用于对用户信息进行签名,以保证信息的有效性和不可否认性;

加密证书主要用于对用户传送信息进行加密,以保证信息的保密性和完整性。

每个证书都包含一对密钥即签名公钥和签名私钥,加密公钥和加密私钥,将签名证书和加密证书的公钥公布于外。

签名时,用签名证书的私钥进行签名,其他用户可以利用公布于外网的签名公钥对签名进行验证。

加密时,用户B利用用户A公布于外网的加密公钥对信息进行加密传送给用户A,用户A利用自己的加密私钥对加密后的信

息进行解密得到完整的明文信息。

=================================================================

数字证书格式-摘要 版本 :该证书使用的是哪种版本的X.509标准 序列号:本项是CA分配给每一个证书的唯一的数字型编号,证书序列号的长度在不同的 CA系统中是可配置的。 签名算法:本项用来指定颁发机构CA签发证书时使用的签名算法 颁发者:issuer 本项标识了颁发该证书的机构DN 有效期:validity 本项是证书的有效期和终止日期 主题:subject 证书拥有者的唯一是别名,这个字段必须是非空的。 有效的DN(Distinct Name)标识: c country code (一般是c=cn) o organization(组织) ou organizational unit name(组织单位名) cn common name (普通名) e email (邮件地址) l locality name (地址) st state, or province name (国家或省份名) dc domain Component (领域) uid user id (用户标识符) t title (标题) sn device serial number name 公钥 public key: 本项用来标识公钥,公钥算法和公钥长度。 微缩图算法:和证书本身没多大关系,就是哈希算法,通常用MD5或SHA1. 微缩图:是指对整个证书进行hash运算之后生成的一段数据,就是对证书的哈希摘要。 数字证书中主题(Subject)中字段的含义 一般的数字证书产品的主题通常含有如下字段: 公用名称 (Common Name) 简称:CN 字段,对于 SSL 证书,一般为网站域名或IP地址;而对于代码签名证书则为申请单位名称;而对于客户端证书则为证书申请者的姓名; 单位名称 (Organization Name) :简称:O 字段,对于 SSL 证书,一般为网站域名;而对于代码签名证书则为申请单位名称;而对于客户端单位证书则为证书申请者所在单位名称; 证书申请单位所在地: 所在城市 (Locality) 简称:L 字段 所在省份 (State/Provice) 简称:S 字段 所在国家 (Country) 简称:C 字段,只能是国家字母缩写,如中国:CN 其他一些字段: 电子邮件 (Email) 简称:E 字段 多个姓名字段 简称:G 字段 介绍:Description 字段 电话号码:Phone 字段,格式要求 + 国家区号 城市区号 电话号码,如: +86 732 88888888 地址:STREET 字段 邮政编码:PostalCode 字段 显示其他内容 简称:OU 字段

SM2国密算法证书解析 一、数字证书的组成

1)证书数据结构

数字证书使用ASN.1编码,证书文件以二进制或Base64格式存放,数据格式使用TLV(Tag Length Value)形式,T代表类型标识符,L是长度值标识符,V代表值编码。数字证书中的每一项都有个对应的类型T。一个数字证书就是一个大的TLV序列,然后V又由多个TLV组合而成。

SM2证书数据和RSA算法证书一样,包含证书版本、序列号、颁发者、使用者主体信息、使用者公钥、有效期、证书扩展项等,只不过SM2证书的公钥算法是使用ECC算法的Oid标识(1.2.840.10045.2.1),然后公钥参数使用SM2国密算法的Oid标识(1.2.156.10197.1.301)。

2)签名算法

SM2证书配套的签名算法是基于SM3的SM2签名算法,算法Oid标识为1.2.156.10197.1.501,另外SM2国密算法还定义基于SHA_1、SHA_256的签名,以及使用SM3算法的RSA的签名,只不过签名算法Oid标识不一样。

3)签名数据

SM2的签名数据由2个BigInteger大数组成,再使用Der编码存放签名数据。证书的签名数据由根证书私钥进行签名,使用根证书公钥验证,顶级根证书使用自己的证书公钥验证。

二、数字证书的对象标识符

数字证书的每项都有对象标识Oid,SM2数字证书的主要区别就是公钥算法、公钥参数、签名算法标识不一样,其余的都是X509里标准项。数字证书常见得对象标识有如下:

对象标识符 名称 OID rsaEncryption RSA算法标识 1.2.840.113549.1.1.1 sha1withRSAEncryption SHA1的RSA签名 1.2.840.113549.1.1.5 ECC ECC算法标识 1.2.840.10045.2.1 SM2 SM2算法标识 1.2.156.10197.1.301 SM3WithSM2 SM3的SM2签名 1.2.156.10197.1.501 sha1withSM2 SHA1的SM2签名 1.2.156.10197.1.502 sha256withSM2 SHA256的SM2签名 1.2.156.10197.1.503 sm3withRSAEncryption SM3的RSA签名 1.2.156.10197.1.504 commonName 主体名 2.5.4.3 emailAddress 邮箱 1.2.840.113549.1.9.1 cRLDistributionPoints CRL分发点 2.5.29.31 extKeyUsage 扩展密钥用法 2.5.29.37 subjectAltName 使用者备用名称 2.5.29.17 CP 证书策略 2.5.29.32 clientAuth 客户端认证 1.3.6.1.5.5.7.3.2 三、数字证书的解析

数字证书的解析主要是根据ASN.1语法和对象标识符来获取值,然后再是证书的有效期、颁发机构根证书、CRL吊销状态和使用目的等验证。RSA证书是标准算法大部分平台都可以解析,而SM2国密算法证书的解析就不是那么通用的。在Windows的一些较高版本打开SM2证书会显示“验证信任关系时,系统层上出现了一个错误”,这是因为Windows还不支持SM2算法证书验证,不能识别SM2签名的算法标识,因此需要自行验证SM2证书的签名数据,可基于BouncyCastle开源加密库来实现SM2验证签名,详细参见我的国密算法SM2证书制作。

免费SSL证书

SSL证书,用于加密HTTP协议,也就是HTTPS。随着淘宝、百度等网站纷纷实现全站Https加密访问,搜索引擎对于Https更加友好,加上互联网上越来越多的人重视隐私安全,站长们给网站添加SSL证书似乎成为了一种趋势。

给自己的网站添加SSL证书其实并不复杂,但是关键一点就是首先要拥有一个SSL证书。由于SSL证书价格不菲,很多个人站长会选择放弃使用Https。但是,自从开源、免费的Let's Encrypt证书出现后,我觉得SSL也是我们草根站长可以玩的了。

SSL 证书级别

分为三种类型,域名型SSL证书(DV SSL)、企业型SSL证书(OVSSL)、增强型SSL证书(EVSSL)

  1. 域名型 SSL 证书(DV SSL - Domain Validation SSL)

即证书颁布机构只对域名的所有者进行在线检查,通常是验证域名下某个指定文件的内容,或者验证与域名相关的某条 TXT 记录;

比如访问 [http|https]://www.mimvp.com/.../test.txt,文件内容: 2016082xxxxxmimvpcom2016

或添加一条 TXT 记录:www.mimvp.com –> TXT –> 20170xxxxxmimvpcom2066

企业型 SSL 证书(OV SSL - Organization Validation SSL)

是要购买者提交组织机构资料和单位授权信等在官方注册的凭证,

证书颁发机构在签发 SSL 证书前,不仅仅要检验域名所有权,

还必须对这些资料的真实合法性进行多方查验,只有通过验证的才能颁发 SSL 证书。

增强型 SSL 证书(EV SSL - Extended Validation SSL)

与其他 SSL 证书一样,都是基于 SSL/TLS 安全协议,

但是验证流程更加具体详细,验证步骤更多,

这样一来证书所绑定的网站就更加的可靠、可信。

它跟普通 SSL 证书的区别也是明显的,安全浏览器的地址栏变绿,

如果是不受信的 SSL 证书则拒绝显示,如果是钓鱼网站,地址栏则会变成红色,以警示用户。

当前可供大家免费使用的SSL证书:

一、Let's Encrypt (推荐)

网址:www.mianfeissl.com/

1、Let's Encrypt是国外一个公共的免费SSL项目,由 Linux 基金会托管,它的来头不小,由Mozilla、思科、Akamai、IdenTrust和EFF等组织发起,目的就是向网站自动签发和管理免费证书,以便加速互联网由HTTP过渡到HTTPS。

2、Let's Encrypt安装部署简单、方便,目前Cpanel、Oneinstack等面板都已经集成了Let's Encrypt一键申请安装,网上也有不少的利用Let's Encrypt开源的特性制作的在线免费SSL证书申请网站,总之Let's Encrypt得到大家的认可。

3、最后选择Let's Encrypt,一方面是Let’s Encrypt SSL已经被Firefox、Chrome、IE等浏览器所支持,另一方面,Let’s Encrypt SSL证书下载和安装已经是傻瓜式的简单了。

二、StartSSL

网址:www.startcomca.com

1、StartSSL是StartCom公司旗下的SSL证书,应该算是免费SSL证书中的“鼻祖”,最早提供完全免费的SSL证书并且被各大浏览器所支持的恐怕就只有StartSSL证书了。任何个人都可以从StartSSL中申请到免费一年的SSL证书。

2、首次申请StartSSL免费SSL证书是免费一年,但是你可以在第二年继续续期。之前StartSSL管理SSL证书只有本地浏览器安装数字证书一种,所以一旦本地的数字证书丢失的话就无法获取到自己之前申请的证书了,不过现在已经增加了邮箱登录了。

3、如果你有看新闻,也许已经知道了“Mozilla正式提议将停止信任 WoSign 和 StartCom 签发的新证书”,使用有风险,不推荐。

三、COMODO PositiveSSL

网址:www.sslchaoshi.com/ssl/brand/5

1、COMODO官网只有免费90天的SSL证书试用申请,这个COMODO PositiveSSL证书来自UK2公司,VPS.net等就是UK2公司旗下的产品。目前获取UK2提供的免费COMODO PositiveSSL不需要额外的操作,只需要你将域名的IP地址解析到指定的IP即可。

四、CloudFlare SSL

网址:www.cloudflare.com/ssl/

1、CloudFlare提供的免费SSL证书是UniversalSSL,即通用SSL,用户无需向证书发放机构申请和配置证书就可以使用的SSL证书,CloudFlare向所有用户(包括免费用户)提供SSL加密功能。

2、不过Universal SSL的服务对免费用户有限制,CloudFlare只支持扩展支持Server Name Indication(SNI)协议的现代浏览器,这意味着它不支持IE6及之前版本、运行Android 2.2或更旧版本的Android浏览器。

五、腾讯云DV SSL 证书(推荐)

网址:cloud.tencent.com/product/ssl

腾讯云DV SSL 域名型证书由赛门铁克提供自动审核认证,快速签发,

支持自动 CSR 生成、域名身份 DNS 自动验证,一步提交申请,审核签发流程全自动。

可以一键部署到腾讯云资源,轻松获得数据安全。

可以下载证书,用于其它云商,例如用于阿里云

可以多次申请,每次一个二级域名,如有多个域名可申请多次

六、阿里云DV SSL证书(推荐)

网址:common-buy.aliyun.com/?commodityCode=cas

赛门铁克是 SSL/TLS 证书的领先提供商,为全球一百多万台网络服务器提供安全防护。

选择赛门铁克后,证书颁发机构 (CA) 将妥善保护您的网站和信誉,让您安枕无忧。

免费数字证书,最多保护一个明细子域名,不支持通配符,一个阿云帐户最多签发20张免费证书

保护一个明细域名,例如:mimvp.com,proxy.mimvp.com,blog.mimvp.com,各个明细子域名都算一个域名,

如果每一个明细域名,都需要配置SSL,则需要分别申请多个免费的SSL证书

七、360网站卫士、百度云加速免费SSL、又拍云、七牛云

百度云:console.bce.baidu.com/cas/(有免费,不支持导出,只可用于百度云产品)

七牛云:SSL证书服务(有免费,需登录查看)

又拍云:SSL证书申购(有免费,需登录查看)

360网站卫士、百度云加速与Symantec等合作推出了免费的SSL证书,其实类似于上面的腾讯云DV SSL 证书,

只不过360网站卫士如果要使用SSL证书必须得实名认证,而且还得使用他们家的CDN。

而百度云加速,只能使用百度云服务器才可以申请免费SSL证书。

八、AlphaSSL

网址:www.alphassl.com

AlphaSSL 是国外一个网站,背景不详,只是从今年四月份开始陆续有朋友告诉我这个网站提供了免费AlphaSSL证书申请,部落自己试了一下发现申请容易,但是成功率并不是100%。

便宜SSL证书的有很多家:

Namecheap:Cheap SSL Certificates from $7.95/yr • Namecheap.com

ssls:SSL Certificates. Buy Cheap SSL Certs from $4.99/yr

cheapssl:Cheap SSL Certificates. Buy or Renew Cheapest SSL at $4.80

Gogetssl:www.gogetssl.com/domain-validation/comodo-positive-ssl/

Starfieldtech:www.starfieldtech.com/

小结

1、几年前使用SSL证书的网站,仅限于一些电子商务类的网站,但是现在各大搜索引擎、各类行业网站都纷纷上马了Https,而SSL证书价格也是越来越低,免费的SSL证书也越来越多了,可供大家的选择也是越来越多了,全站和全网使用SSL HTTPS已经是大势所趋了。

2、上面介绍的八大免费SSL证书,要说最让人放心的当属Let's Encrypt、COMODO PositiveSSL,腾讯云和阿里云的DV SSL证书,效果可以参考米扑科技的多个产品网站,覆盖了全部使用场景。其它免费SSL证书,建议大家谨慎使用,对于一些重要的网站还是建议你直接购买SSL证书。

ROOT证书、CA证书和使用CA签发的X.509证书

简介 日常开发中,我们程序员不怎么会接触证书相关的问题,对信息安全领域相关的内容知之甚少。因为平时主要实现的业务很少要直接面向底层的通信,也就很少关注这证书这样的知识。在一般情况下,我们仅仅只是在使用一些高层的依赖中会引入证书、加密相关的依赖包,比如:

org.bouncycastle bcprov-jdk15on 1.58 然而, bouncy castle这样广泛使用证书密码项目(Github)并不是广受关注的明星项目,文档也就不怎么完备,甚至有点儿法语焉不详,不够系统完整。而且,这方面的内容包含了很多密码学方面专业名词,文档读起来就更晦涩难懂。可以参与bouncy castle的WIKI。我将大致的列举一下BC在证书方面的使用方法。

概念 ROOT证书、CA证书和使用CA证签发的X.509证书之间的联系,其实就是一个使用私钥签发(issue)的关系。使用ROOT证书的私钥签发CA证书,再使用CA证书签发其他的X.509证书,这样就形成了一条可以信的Path。在被签发的证书中含有使用签发者使用自己私钥加过密的签名(sign)、签名算法(比如Sha256WithHash)以及签名者的一些身份信息,这个我们可以在验证签名的过程中得过启发。

从上面可以看出,我们可以把一个证书主要分为两类:自签名和Ca签名,并且我在上面提到的证书中只有ROOT的是自签名的。

使用Bouncy Castle 生成一个签名证书 ROOT证书是用来签发和验证CA证书的,在整个可信证书链条中处于最上层的地位,它是一个自签名的证书。签名的作用是防止在不知道签名者私钥的情况下恶意篡改证书的内容,这是证书可信保证重要的步骤。如下代码是生成一个签名的证书:

// must add BC Provider before use it in code Security.addProvider(new BouncyCastleProvider());

// generate EC key pair ECGenParameterSpec ecGenSpec = new ECGenParameterSpec("prime192v1"); KeyPairGenerator generator = KeyPairGenerator.getInstance("ECDSA", "BC"); generator.initialize(ecGenSpec, new SecureRandom()); KeyPair kp = generator.genKeyPair();

Date startDate = new Date(System.currentTimeMillis() - 24 * 60 * 60 * 1000); Date endDate = new Date(System.currentTimeMillis() + 365 * 24 * 60 * 60 * 1000);

// create x.509 certificate build X509v3CertificateBuilder v3CertGen = new JcaX509v3CertificateBuilder( new X500Principal("CN=Test"), BigInteger.valueOf(System.currentTimeMillis()), startDate, endDate, new X500Principal("CN=Test"), kp.getPublic());

// Content Signer ContentSigner signer = new JcaContentSignerBuilder("SHA256withECDSA") .setProvider("BC").build(kp.getPrivate());

// build x.509 certificate X509CertificateHolder holder = v3CertGen.build(signer); X509Certificate cert = new JcaX509CertificateConverter().getCertificate(holder);

holder即我们生成的X.509证书,对于生成的证书我们可以使用org.bouncycastle.openssl.jcajce.JcaPEMWriter转化成字符串:

// certificate to write to string X509CertificateHolder holder = ...;

ByteArrayOutputStream bos = new ByteArrayOutputStream(); JcaPEMWriter jcaPEMWriter = new JcaPEMWriter(new PrintWriter(bos)); jcaPEMWriter.writeObject(holder); jcaPEMWriter.flush();

System.out.printf(bos.toString());

这个我们生成的证书的内容:

-----BEGIN CERTIFICATE----- MIHwMIGmoAMCAQICBgFfoSV4vTAKBggqhkjOPQQDAjAPMQ0wCwYDVQQDEwRUZXN0 MB4XDTE3MTEwODE0MTgyOFoXDTE3MTEyNjE0NTg1N1owDzENMAsGA1UEAxMEVGVz dDBJMBMGByqGSM49AgEGCCqGSM49AwEBAzIABLgAmZuZxd6IFZ7tJS8H0VY1Wify 2i+YTIwF5cw0O7t1pcAh6dkF021bh+W0gk/rzzAKBggqhkjOPQQDAgM5ADA2AhkA 6Vb1VWTQrwcrEr2+7eTkAcwiHJ+YqGFoAhkAxWvJu2L+8dQbXXa6bd48zvR0ifX8 849E -----END CERTIFICATE----- 验证证书的签名 以下代码用于验证我们的签名是否合法。

// public key from issuer, used to verify signature PublicKey publicKey = ...;

ContentVerifierProvider contentVerifierProvider = new JcaContentVerifierProviderBuilder() .setProvider("BC").build(publicKey);

if (!certHolder.isSignatureValid(contentVerifierProvider)) { System.err.println("signature invalid"); } else { System.out.printf("signature valid"); } 使用来自Oracle的证书支持 oracle Java使用keystore来存储证书及其私钥,并提供一个命令行工具——keytool。

//generate key pair with alias test keytool -genkeypair -alias test -keyalg EC -validity 365 -keypass password -keysize 571 -keystore ./test.jks -storepass password

//generate certificate with public key from keypair test keytool -certreq -alias test -sigalg SHA256WithECDSA -keypass password -keysize 571 -keystore ./test.jks -storepass password

//list jdk content keytool -list -rfc -keystore ./test.jks 加载Keystore文件 使用java.security.KeyStore加载Keystore文件:

KeyStore keyStore = KeyStore.getInstance("jks"); keyStore.load(new FileInputStream("C:\...\test.jks"), "password".toCharArray()); Certificate certificate = keyStore.getCertificate("test"); 1 2 3 可以看到X.509的证书结构:

[ [ Version: V3 Subject: CN=yinkn, OU=ericsson, O=ericsson, L=GZ, ST=GD, C=CN Signature Algorithm: SHA256withECDSA, OID = 1.2.840.10045.4.3.2

Key: Sun EC public key, 571 bits public x coord: 3002605725884979889022476092292003776673075892528685922593430921126421431956026876782257630852231195990030412442522229709626574940546289855595376742293701765308565676110538 public y coord: 3969672869627949486403678776244029006985355959068388562809699610619530481229193224794938137041924106460645494573259209667845661791858569871701054704389588094823968191257337 parameters: sect571k1 [NIST K-571] (1.3.132.0.38) Validity: [From: Thu Nov 09 23:02:59 GMT+08:00 2017, To: Fri Nov 09 23:02:59 GMT+08:00 2018] Issuer: CN=yinkn, OU=ericsson, O=ericsson, L=GZ, ST=GD, C=CN SerialNumber: [ 1ae30239]

Certificate Extensions: 1 [1]: ObjectId: 2.5.29.14 Criticality=false SubjectKeyIdentifier [ KeyIdentifier [ 0000: 02 50 11 0F 4C F1 C3 FA FC BB 0F 19 5C 83 D6 36 .P..L.........6 0010: 95 C3 E3 3E ...> ] ]

] Algorithm: [SHA256withECDSA] Signature: 0000: 30 81 93 02 48 00 9B D1 92 9E 7B 95 FC 0E 11 19 0...H........... 0010: 28 25 C2 C2 35 2E D5 B1 74 5C F4 EA A9 D8 71 95 (%..5...t....q. 0020: F1 72 BE 8B 53 A2 8D CA AC 88 96 A5 86 B3 46 14 .r..S.........F. 0030: 1B B1 8B A5 12 08 B7 7D ED 23 BF 64 DD 46 E2 33 .........#.d.F.3 0040: E0 6B C1 62 14 25 BE 5C B9 AB 45 04 AD 02 47 6F .k.b.%...E...Go 0050: D0 B9 B2 9E A1 D3 D6 98 8A 56 16 38 6B BB 27 B1 .........V.8k.'. 0060: 42 53 D7 27 95 2F 96 2E E4 39 19 3D A3 54 54 8B BS.'./...9.=.TT. 0070: 71 DA 0E 91 54 5F 78 37 CA 03 58 4F 56 FD C1 BD q...T_x7..XOV... 0080: 43 9D 06 CF DF 6A B1 C6 4D 5C 0B 06 62 FF DF 8C C....j..M..b... 0090: 33 0B A7 62 0A ED 3..b..

]

验证签名是否合法 代码来自项目Californiumnn,类org.eclipse.californium.scandium.dtls.CertificateVerify:

/**

  • Tries to verify the client's signature contained in the CertificateVerify

  • message.

  • @param clientPublicKey

  •        the client's public key.
    
  • @param handshakeMessages

  •        the handshake messages exchanged so far.
    
  • @throws HandshakeException if the signature could not be verified. */ public void verifySignature(PublicKey clientPublicKey, byte[] handshakeMessages) throws HandshakeException { boolean verified = false; try { Signature signature = Signature.getInstance(signatureAndHashAlgorithm.jcaName()); signature.initVerify(clientPublicKey);

    signature.update(handshakeMessages);

    verified = signature.verify(signatureBytes);

} catch (SignatureException | InvalidKeyException | NoSuchAlgorithmException e) { LOGGER.log(Level.SEVERE,"Could not verify the client's signature.", e); }

if (!verified) { String message = "The client's CertificateVerify message could not be verified."; AlertMessage alert = new AlertMessage(AlertLevel.FATAL, AlertDescription.HANDSHAKE_FAILURE, getPeer()); throw new HandshakeException(message, alert); } } 验证Trust Chain /**

  • Validates the X.509 certificate chain provided by the the peer as part of this message.
  • This method checks
  • that each certificate's issuer DN equals the subject DN of the next certiciate in the chain
  • that each certificate is currently valid according to its validity period
  • that the chain is rooted at a trusted CA
  • @param certificate certificate to be verified
  • @param trustedCertificates the list of trusted root CAs
  • @throws HandshakeException if any of the checks fails */ public void verifyCertificate(X509Certificate certificate, X509Certificate[] trustedCertificates) throws HandshakeException { CertificateFactory factory = CertificateFactory.getInstance("X.509"); CertPath certPath = factory.generateCertPath(Arrays.asList(certificate));

Set trustAnchors = getTrustAnchors(trustedCertificates);

try { PKIXParameters params = new PKIXParameters(trustAnchors); // TODO: implement alternative means of revocation checking params.setRevocationEnabled(false);

CertPathValidator validator = CertPathValidator.getInstance("PKIX");
validator.validate(certPath, params);

} catch (GeneralSecurityException e) { if (LOGGER.isLoggable(Level.FINEST)) { LOGGER.log(Level.FINEST, "Certificate validation failed", e); } else if (LOGGER.isLoggable(Level.FINE)) { LOGGER.log(Level.FINE, "Certificate validation failed due to {0}", e.getMessage()); } AlertMessage alert = new AlertMessage(AlertLevel.FATAL, AlertDescription.BAD_CERTIFICATE, getPeer()); throw new HandshakeException("Certificate chain could not be validated", alert); } }

private static Set getTrustAnchors(X509Certificate[] trustedCertificates) { Set result = new HashSet<>(); if (trustedCertificates != null) { for (X509Certificate cert : trustedCertificates) { result.add(new TrustAnchor((X509Certificate) cert, null)); } } return result;

到现在为止,本文涉及的只是安全甚至证书领域的一鳞半爪,也只是一些简单的内容。要深刻理解这方面的内容,必须深入实际的应用。

SSL/TLS工作原理

一: SSL/TLS介绍 什么是SSL,什么是TLS呢?官话说SSL是安全套接层(secure sockets layer),TLS是SSL的继任者,叫传输层安全(transport layer security)。说白点,就是在明文的上层和TCP层之间加上一层加密,这样就保证上层信息传输的安全。如HTTP协议是明文传输,加上SSL层之后,就有了雅称HTTPS。它存在的唯一目的就是保证上层通讯安全的一套机制。它的发展依次经历了下面几个时期,像手机软件升级一样,每次更新都添加或去除功能,比如引进新的加密算法,修改握手方式等。 SSL1.0: 已废除 SSL2.0: RFC6176,已废除 SSL3.0: RFC6101,基本废除 TLS1.0: RFC2246,目前大都采用此种方式 TLS1.1: RFC4346 TLS1.2: RFC5246,没有广泛使用 TLS1.3: IETF正在酝酿中 下面我们将介绍TLS1.x 如何保证通讯安全。

二: CA & SSL Server & SSL Client 介绍 如何保证安全呢?你说安全就安全吗,究竟是怎么实现的呢?绝对安全吗? 哈,有人的地方就有江湖,有江湖的地方就没有绝对的安全。但SSL/TLS确实可以极大程度保证信息安全。下面根据图一 SSL/TLS 工作流来一览实现过程。

2.1 SSL/TLS 工作流

图一 SSL/TLS 工作流

CA: 证书授权中心( certificate authority)。 它呢,类似于国家出入境管理处一样,给别人颁发护照;也类似于国家工商管理局一样,给公司企业颁发营业执照。 它有两大主要性质:

  1. CA本身是受信任的 // 国际认可的
  2. 给他受信任的申请对象颁发证书 // 和办理护照一样,要确定你的合法身份,你不能是犯罪分子或造反派。当然,你需要被收保护费,同时,CA可以随时吊销你的证书。 证书长啥样?其实你的电脑中有一堆CA证书。你可以看一看嘛: 360浏览器: 选项/设置-> 高级设置 -> 隐私于安全 -> 管理 HTTPS/SSL 证书 -> 证书颁发机构 火狐浏览器: 首选项 -> 高级 -> 证书 -> 查看证书 -> 证书机构 chrome浏览器: 设置 -> 高级 -> 管理证书 -> 授权中心 ubuntu: /etc/ssl/certs 这些都是 CA 的证书! CA 的证书 ca.crt 和 SSL Server的证书 server.crt 是什么关系呢?
  3. SSL Server 自己生成一个 私钥/公钥对。server.key/server.pub // 私钥加密,公钥解密!
  4. server.pub 生成一个请求文件 server.req. 请求文件中包含有 server 的一些信息,如域名/申请者/公钥等。
  5. server 将请求文件 server.req 递交给 CA,CA验明正身后,将用 ca.key和请求文件加密生成 server.crt
  6. 由于 ca.key 和 ca.crt 是一对, 于是 ca.crt 可以解密 server.crt. 在实际应用中:如果 SSL Client 想要校验 SSL server.那么 SSL server 必须要将他的证书 server.crt 传给 client.然后 client 用 ca.crt 去校验 server.crt 的合法性。如果是一个钓鱼网站,那么CA是不会给他颁发合法server.crt证书的,这样client 用ca.crt去校验,就会失败。比如浏览器作为一个 client,你想访问合法的淘宝网站https://www.taobao.com, 结果不慎访问到 https://wwww.jiataobao.com ,那么浏览器将会检验到这个假淘宝钓鱼网站的非法性,提醒用户不要继续访问!这样就可以保证了client的所有https访问都是安全的。

2.2 单向认证双向认证 何为SSL/TLS单向认证,双向认证? 单向认证指的是只有一个对象校验对端的证书合法性。 通常都是client来校验服务器的合法性。那么client需要一个ca.crt,服务器需要server.crt,server.key 双向认证指的是相互校验,服务器需要校验每个client,client也需要校验服务器。 server 需要 server.key 、server.crt 、ca.crt client 需要 client.key 、client.crt 、ca.crt

2.3 证书详细工作流

图二 证书详细工作流

1)申请认证:服务器需自己生成公钥私钥对pub_svr & pri_svr,同时根据 pub_svr 生成请求文件 csr,提交给CA,csr中含有公钥、组织信息、个人信息(域名)等信息。(图一中server.req就是csr请求文件) 2)审核信息:CA通过线上、线下等多种手段验证申请者提供信息的真实性,如组织是否存在、企业是否合法,是否拥有域名的所有权等。 3)签发证书:如信息审核通过,CA会向申请者签发认证文件-证书。 证书包含以下信息:申请者公钥、申请者的组织信息和个人信息、签发机构 CA的信息、有效时间、证书序列号等信息的明文,同时包含一个签名。 签名的产生算法:首先,使用散列函数计算公开的明文信息的信息摘要,然后,采用 CA的私钥对信息摘要进行加密,密文即签名。(图一中生成server.crt) 4)返回证书:client如果请求验证服务器,服务器需返回证书文件。(图一中handshake传回server.crt) 5)client验证证书:client读取证书中的相关的明文信息,采用相同的散列函数计算得到信息摘要,然后,利用对应 CA的公钥解密签名数据,对比证书的信息摘要,如果一致,则可以确认证书的合法性,即公钥合法。客户端然后验证证书相关的域名信息、有效时间是否吊销等信息。 客户端会内置信任CA的证书信息(包含公钥),如果CA不被信任,则找不到对应 CA的证书,证书也会被判定非法。(图一中check可选,我们可以选择不验证服务器证书的有效性) 6)秘钥协商:验证通过后,Server和Client将进行秘钥协商。接下来Server和Client会采用对称秘钥加密。(对称加密时间性能优)(图一中 pre-master/change_cipher_spec/encrypted_handshake_message过程) 7)数据传输:Server和Client采用对称秘钥加密解密数据。 2.4 SSL/TLS单向认证流程

(1)client_hello 客户端发起请求,以明文传输请求信息,包含版本信息,加密套件候选列表,压缩算法候选列表,随机数,扩展字段等信息,相关信息如下:

支持的最高TSL协议版本version,从低到高依次 SSLv2 SSLv3 TLSv1 TLSv1.1 TLSv1.2,当前基本不再使用低于 TLSv1 的版本; 客户端支持的加密套件 cipher suites 列表, 每个加密套件对应前面 TLS 原理中的四个功能的组合:认证算法 Au (身份验证)、密钥交换算法 KeyExchange(密钥协商)、对称加密算法 Enc (信息加密)和信息摘要 Mac(完整性校验); 支持的压缩算法 compression methods 列表,用于后续的信息压缩传输; 随机数 random_C,用于后续的密钥的生成; 扩展字段 extensions,支持协议与算法的相关参数以及其它辅助信息等,常见的 SNI 就属于扩展字段,后续单独讨论该字段作用。 (2).server_hello+server_certificate+sever_hello_done server_hello, 服务端返回协商的信息结果,包括选择使用的协议版本 version,选择的加密套件 cipher suite,选择的压缩算法 compression method、随机数 random_S 等,其中随机数用于后续的密钥协商; server_certificates, 服务器端配置对应的证书链,用于身份验证与密钥交换; server_hello_done,通知客户端 server_hello 信息发送结束; (3).证书校验 [证书链]的可信性 trusted certificate path,方法如前文所述; 证书是否吊销 revocation,有两类方式离线 CRL 与在线 OCSP,不同的客户端行为会不同; 有效期 expiry date,证书是否在有效时间范围; 域名 domain,核查证书域名是否与当前的访问域名匹配,匹配规则后续分析; (4).client_key_exchange+change_cipher_spec+encrypted_handshake_message client_key_exchange,合法性验证通过之后,客户端计算产生随机数字 Pre-master,并用证书公钥加密,发送给服务器; 此时客户端已经获取全部的计算协商密钥需要的信息:两个明文随机数 random_C 和 random_S 与自己计算产生的 Pre-master,计算得到协商密钥; enc_key=Fuc(random_C, random_S, Pre-Master) change_cipher_spec,客户端通知服务器后续的通信都采用协商的通信密钥和加密算法进行加密通信; encrypted_handshake_message,结合之前所有通信参数的 hash 值与其它相关信息生成一段数据,采用协商密钥 session secret 与算法进行加密,然后发送给服务器用于数据与握手验证; (5).change_cipher_spec+encrypted_handshake_message 服务器用私钥解密加密的 Pre-master 数据,基于之前交换的两个明文随机数 random_C 和 random_S,计算得到协商密钥:enc_key=Fuc(random_C, random_S, Pre-Master); 计算之前所有接收信息的 hash 值,然后解密客户端发送的 encrypted_handshake_message,验证数据和密钥正确性; change_cipher_spec, 验证通过之后,服务器同样发送 change_cipher_spec 以告知客户端后续的通信都采用协商的密钥与算法进行加密通信; encrypted_handshake_message, 服务器也结合所有当前的通信参数信息生成一段数据并采用协商密钥 session secret 与算法加密并发送到客户端; (6).握手结束 客户端计算所有接收信息的 hash 值,并采用协商密钥解密 encrypted_handshake_message,验证服务器发送的数据和密钥,验证通过则握手完成;

(7).加密通信 开始使用协商密钥与算法进行加密通信。

2.5 实际wireshark分析

我们搭建的SSL/TLS服务器是192.168.111.100,client是192.168.111.101. client 需要认证 server的合法性。 我们只看 TLSv1.1 的数据包: 第一包(No. 25) Client Hello 包,即SSL/TLS单向认证流程的(1) 第二包(No. 27) Server Hello 包,包含服务器证书等。即SSL/TLS单向认证流程的(2) 第三包(No. 28) 服务器证书验证完成,同时发送client key exchange+change cipher spec + encrypted handshake message.即SSL/TLS单向认证流程的(4) 第四包(No. 29)秘钥协商,change cipher spec + encrypted hanshake message.即SSL/TLS单向认证流程的(5) 第五包(No. 30)握手完成。开始上层数据传输。SSL/TLS单向认证流程的(7)

2.6 SSL/TLS双向认证流程 和单向认证几乎一样,只是在client认证完服务器证书后,client会将自己的证书client.crt传给服务器。服务器验证通过后,开始秘钥协商。 实际wireshark分析:

和单向认证一样: 我们搭建的SSL/TLS服务器是192.168.111.100,client是192.168.111.101. client 需要认证 server的合法性,server也需要认证client的合法性! 我们只看 TLSv1.1 的数据包: 第一包(No. 55) Client Hello 包,即SSL/TLS单向认证流程的(1) 第二包(No. 57) Server Hello 包,包含服务器证书等。即SSL/TLS单向认证流程的(2) 第三包(No. 60) 服务器证书验证完成,同时发送客户端的证书client.crt ,同时包含client key exchange+change cipher spec + encrypted handshake message.即SSL/TLS单向认证流程的(4) 第四包(No. 61)服务器验证客户端证书的合法性。通过后进行秘钥协商,change cipher spec + encrypted hanshake message.即SSL/TLS单向认证流程的(5) 重传包(No. 62)由于网络原因,TCP重传第No. 60包。 第五包(No. 64)握手完成。开始上层数据传输。SSL/TLS单向认证流程的(7)

2.7 证书等格式说明 crt/key/req/csr/pem/der等拓展名都是什么东东?

  1. .crt 表示证书, .key表示私钥, .req 表示请求文件,.csr也表示请求文件, .pem表示pem格式,.der表示der格式。 文件拓展名你可以随便命名。只是为了理解需要而命名不同的拓展名。但文件中的信息是有格式的,和exe,PE格式一样,证书有两种格式。 pem格式和der格式。所有证书,私钥等都可以是pem,也可以是der格式,取决于应用需要。 pem和der格式可以互转:

    openssl x509 -in ca.crt -outform DER -out ca.der //pem -> der openssl x509 -inform der -in ca.der -out ca.pem // der -> pem 1 2 pem格式:经过加密的文本文件,一般有下面几种开头结尾: 1 -----BEGIN RSA PRIVATE KEY----- -----END RSA PRIVATE KEY----- or: -----BEGIN CERTIFICATE REQUEST----- -----END CERTIFICATE REQUEST----- or: ----BEGIN CERTIFICATE----- -----END CERTIFICATE----- 1 2 3 4 5 6 7 8 der格式: 经过加密的二进制文件。

  2. 证书中含有 申请者公钥、申请者的组织信息和个人信息、签发机构 CA的信息、有效时间、证书序列号等信息的明文,同时包含一个签名。如查看百度证书详细信息。 a) 先下载百度证书 火狐浏览器访问https://www.baidu.com/, 点击左上角绿色小锁,点击向右箭头,点击更多信息,点击查看证书,点击详细信息,点击导出。即可导出百度的证书 baiducom.crt

b) 命令查看证书详细信息

openssl x509 -noout -text -in baiducom.crt 1

详细信息中,有一个字段: X509v3 Basic Constraints: CA: FALSE 该字段指出该证书是否是 CA证书,还是一般性的非 CA 证书。详细描述见 RFC5280#section-4.2.1.9,同时 RFC5280 也详细描述证书工作方式等。

  1. 私钥加密,公钥解密!

2.8 SSL/TLS和 Openssl,mbedtls是什么关系? SSL/TLS是一种工作原理,openssl和mbedtls是SSL/TLS的具体实现,很类似于 TCP/IP协议和socket之间的关系。

三: 本地生成SSL相关文件 3.1 证书生成脚本 我们自己本地使用 makefile.sh 脚本建立一个CA(ca.crt + ca.key),用这个CA给server和client分别颁发证书。

makefile.sh

* Redistributions in binary form must reproduce the above copyright

notice, this list of conditions and the following disclaimer in the

documentation and/or other materials provided with the distribution.

* Neither the name of the axTLS project nor the names of its

contributors may be used to endorse or promote products derived

from this software without specific prior written permission.

THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS

"AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT

LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR

A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR

CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,

SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED

TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,

DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY

OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING

NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF

THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.

Generate the certificates and keys for testing.

PROJECT_NAME="TLS Project"

Generate the openssl configuration files.

cat > ca_cert.conf << EOF
[ req ] distinguished_name = req_distinguished_name prompt = no

[ req_distinguished_name ] O = $PROJECT_NAME Dodgy Certificate Authority EOF

cat > server_cert.conf << EOF
[ req ] distinguished_name = req_distinguished_name prompt = no

[ req_distinguished_name ] O = $PROJECT_NAME CN = 192.168.111.100 EOF

cat > client_cert.conf << EOF
[ req ] distinguished_name = req_distinguished_name prompt = no

[ req_distinguished_name ] O = $PROJECT_NAME Device Certificate CN = 192.168.111.101 EOF

mkdir ca mkdir server mkdir client mkdir certDER

private key generation

openssl genrsa -out ca.key 1024 openssl genrsa -out server.key 1024 openssl genrsa -out client.key 1024

cert requests

openssl req -out ca.req -key ca.key -new
-config ./ca_cert.conf openssl req -out server.req -key server.key -new
-config ./server_cert.conf openssl req -out client.req -key client.key -new
-config ./client_cert.conf

generate the actual certs.

openssl x509 -req -in ca.req -out ca.crt
-sha1 -days 5000 -signkey ca.key openssl x509 -req -in server.req -out server.crt
-sha1 -CAcreateserial -days 5000
-CA ca.crt -CAkey ca.key openssl x509 -req -in client.req -out client.crt
-sha1 -CAcreateserial -days 5000
-CA ca.crt -CAkey ca.key

openssl x509 -in ca.crt -outform DER -out ca.der openssl x509 -in server.crt -outform DER -out server.der openssl x509 -in client.crt -outform DER -out client.der

mv ca.crt ca.key ca/ mv server.crt server.key server/ mv client.crt client.key client/

mv ca.der server.der client.der certDER/

rm *.conf rm *.req rm *.srl 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 将上述代码保存为makefile.sh 做如下修改,终端执行。

  • 修改 CN 域中 IP 地址为你主机/设备的 IP 地址
  • [可选]加密位数 1024 修改为你需要的加密位数

将会看到:

ca目录:保存ca的私钥ca.key和证书ca.crt certDER目录:将证书保存为二进制文件 ca.der client.der server.der client目录: client.crt client.key server目录:server.crt server.key

3.2 删除脚本 $./makefile.sh 1 删除脚本rmfile.sh:

rm ca/ -rf rm certDER/ -rf rm client/ -rf rm server/ -rf 1 2 3 4 将上述代码保存为rmfile.sh,终端执行,将会删除产生过的目录和文件:

$./rmfile.sh 1 3.3 CA校验证书测试 我们可在本地使用 CA证书来分别校验由自己颁发的服务器证书 server.crt 和客户端证书 client.crt .

$openssl verify -CAfile ca/ca.crt server/server.crt 1 $openssl verify -CAfile ca/ca.crt client/client.crt 1

⚠️ **GitHub.com Fallback** ⚠️