google domain, invalid City

google domain, invalid City 填写联系人资料的时候国家选择中国,填写城市 的时候要写成 Dalian Shi Google Domain 填写出错 http://www.flaskfox.com/2015/11/16/google-domain-%e5%a1%ab%e5%86%99%e5%87%ba%e9%94%99/embed/#?secret=2UAujlnd36

2019-03-04 · 1 min · 12 words · -

java data convert

java data convert NumberFormat formatter = new DecimalFormat("#0.00"); System.out.println(formatter.format(4.0));

2019-03-01 · 1 min · 9 words · -

intellij idea 配置 git ssh key

intellij idea 配置git ssh key 把私钥放入目录 C:\Users\user0\.ssh, 私钥不需要转换成 ppk 格式. https://blog.csdn.net/u010348570/article/details/81204371 1 安装git,登录官网https://www.git-scm.com/download/ ,选择相应系统版本,下载后安装好。 公司网慢的可以用第三方的软件管家下载。 2 打开git bash,不需要进入任何目录,直接输入 ssh-keygen -t rsa -C ‘xxx@xxx.com’,‘xxx@xxx.com’为gitlab上的登录账户。一路回车。 3 打开生成的密钥文件,目录为当前系统登录者的用户目录 4 将id_rsa.pub文件里面的内容拷贝,登录公司gitlab服务器,找到ssh key配置位置Settings。有的在左侧目录处,有的则需要在自己头像的位置单击。 5 点击SSH Keys ,将上一步拷贝的内容拷贝到key下的方框中。Title可以填写一个自己的标识。 6 打开intellij idea , File -> Settings,输入git,配置下git.exe 7 选择 VCS -> Checkout from Version Control -> Git,将gitlab上面项目的ssh路径复制,点击Test,提示Connection successful,后面一路点击next即可 作者: 叫我放猪之人 来源: CSDN 原文: https://blog.csdn.net/u010348570/article/details/81204371 版权声明: 本文为博主原创文章,转载请附上博文链接!

2019-02-27 · 1 min · 58 words · -

idea install

idea install default plugins build tool ant - disable maven gradle version control cvs - disable mercurial - disable git subversion github test tools junit testng-j - disable swing - disable android - disable othre tools plugin development - disable

2019-02-20 · 1 min · 40 words · -

android basic

android basic

2019-02-19 · 1 min · 2 words · -

grafana

grafana docker pull grafana/grafana:12.0.2 docker run -d --name=grafana -e "GF_SECURITY_ADMIN_PASSWORD=password0" -p 3000:3000 -v grafana-storage:/var/lib/grafana -v /etc/localtime:/etc/localtime:ro grafana/grafana:12.0.2 # podman podman run \ -d \ --name=grafana \ -e "GF_SERVER_ROOT_URL=http://grafana.wiloon.com" \ -e "GF_SECURITY_ADMIN_PASSWORD=password0" \ -p 3100:3000 \ -v grafana-storage:/var/lib/grafana \ -v /etc/localtime:/etc/localtime:ro \ grafana/grafana:8.5.6 # in pod podman run \ -d \ --name=grafana \ -e "GF_SERVER_ROOT_URL=http://grafana.wiloon.com" \ -e "GF_SECURITY_ADMIN_PASSWORD=password0" \ --pod monitor \ -v grafana-storage:/var/lib/grafana \ -v /etc/localtime:/etc/localtime:ro \ grafana/grafana variable for host SHOW TAG VALUES ON "telegraf" FROM "system" WITH KEY = "host" Q. How do I use the second y axis, secondYAxis function does not work ...

2019-02-17 · 3 min · 427 words · -

RSSX

RSSX A RSS Reader redis key 某一个feed的 所有 news id,按时间排序的 key: feed_news:feedId0 type: sort set, zset value: newsId 文章内容 key: news:newsId0 type: hash value: 文章内容, 数据量最大 记录用户阅读位置 用已读索引和已读集合记录用户阅读位置 已读索引 用于记录用户feed已读和未读的边界, 记录连续的已读未读位置 已读集合 用于记录已读边界外,用户分散阅读的文章,记录不连续的已读集合 已读索引 key: read_index:userId0:feedId0 type: string value: feed_news(zset)的score值 已读集合, 已读文章标记为灰色 key: read_mark:userId0:feedId0 type: set value: newsId 显示feed 列表时,显示未读文章数,feed总数-索引=未读数量 按feed id 加载一页未读文章时,按索引range取 标记某一页为已读时,取上一次的已读索引位置, 加每页显示数,记录新的已读索引 加载某个feed的一页未读文章时,查询大于等于某一个 score 的第一条数据的索引 ZSCORE, 成员member的score值 ZRANGE, 返回指定区间内的成员 ZRANGEBYSCORE, 返回有序集合中指定分数区间的成员列表 - 正序 ZRANK, 返回指定成员的排名(位置值,0表示第一个成员) - 正序 移除有序集中,指定排名(rank)区间内的所有成员 移除有序集中,指定分数(score)区间内的所有成员 部署 redis sql db: sqlite3 ...

2019-02-17 · 1 min · 179 words · -

Ceph

Ceph Ceph是一个统一的分布式存储系统,设计初衷是提供较好的性能、可靠性和可扩展性。 Ceph项目最早起源于Sage就读博士期间的工作(最早的成果于2004年发表),并随后贡献给开源社区。在经过了数年的发展之后,目前已得到众多云计算厂商的支持并被广泛应用。RedHat及OpenStack都可与Ceph整合以支持虚拟机镜像的后端存储。 1.2 Ceph特点 高性能 a. 摒弃了传统的集中式存储元数据寻址的方案,采用CRUSH算法,数据分布均衡,并行度高。 b.考虑了容灾域的隔离,能够实现各类负载的副本放置规则,例如跨机房、机架感知等。 c. 能够支持上千个存储节点的规模,支持TB到PB级的数据。 高可用性 a. 副本数可以灵活控制。 b. 支持故障域分隔,数据强一致性。 c. 多种故障场景自动进行修复自愈。 d. 没有单点故障,自动管理。 高可扩展性 a. 去中心化。 b. 扩展灵活。 c. 随着节点增加而线性增长。 特性丰富 a. 支持三种存储接口:块存储、文件存储、对象存储。 b. 支持自定义接口,支持多种语言驱动。 作者:lihanglucien 链接:https://www.jianshu.com/p/cc3ece850433 来源:简书 著作权归作者所有。商业转载请联系作者获得授权,非商业转载请注明出处。 GlusterFS GlusterFS系统是一个可扩展的网络文件系统,相比其他分布式文件系统,GlusterFS具有高扩展性、高可用性、高性能、可横向扩展等特点,并且其没有元数据服务器的设计,让整个服务没有单点故障的隐患。 当客户端访问GlusterFS存储时,首先程序通过访问挂载点的形式读写数据,对于用户和程序而言,集群文件系统是透明的,用户和程序根本感觉不到文件系统是本地还是在远程服务器上。读写操作将会被交给VFS(Virtual File System)来处理,VFS会将请求交给FUSE内核模块,而FUSE又会通过设备/dev/fuse将数据交给GlusterFS Client。最后经过GlusterFS Client的计算,并最终经过网络将请求或数据发送到GlusterFS Server上。

2019-02-16 · 1 min · 44 words · -

Java 压缩字符串

Java 压缩字符串 https://www.cnblogs.com/EasonJim/p/8256906.html 说明: 一般来说要实现压缩,那么返回方式一般是用byte[]数组。 研究发现byte[]数组在转成可读的String时,大小会还原回原来的。 如果采用压缩之后不可读的String时,互相转换大小会变小,唯一缺点就是转出的String不可读,需要再次解码之后才可读。 对于压缩一般最近常听的应该就是gzip这些。 实现一: 复制代码 /*** 压缩GZip @param data @return */ public static byte[] gZip(byte[] data) { byte[] b = null; try { ByteArrayOutputStream bos = new ByteArrayOutputStream(); GZIPOutputStream gzip = new GZIPOutputStream(bos); gzip.write(data); gzip.finish(); gzip.close(); b = bos.toByteArray(); bos.close(); } catch (Exception ex) { ex.printStackTrace(); } return b; } /*** 解压GZip @param data @return */ public static byte[] unGZip(byte[] data) { byte[] b = null; try { ByteArrayInputStream bis = new ByteArrayInputStream(data); ...

2019-02-09 · 5 min · 981 words · -

usb win10

usb win10 https://www.microsoft.com/zh-cn/software-download/windows10ISO Rufus https://rufus.ie/ 简单几步制作 Windows 10 正式版U盘可启动安装盘图文教程 (全新安装Win10) https://www.iplaysoft.com/windows-10-udisk-install.html/embed#?secret=a084u1M2LZ

2019-02-02 · 1 min · 11 words · -

let's encrypt, nginx, debian

let’s encrypt, nginx, debian sudo apt-get install certbot python-certbot-nginx -t stretch-backports sudo certbot –nginx https://certbot.eff.org/lets-encrypt/debianstretch-nginx https://www.jianshu.com/p/c5c9d071e395

2019-02-02 · 1 min · 16 words · -

procd

procd https://openwrt.org/docs/guide-developer/procd-init-scripts https://blog.csdn.net/liangdsing/article/details/53906445 #!/bin/sh /etc/rc.common # Copyright (C) 2008 OpenWrt.org #执行的顺序,按照字符串顺序排序并不是数字排序 START=98 #使用procd启动 USE_PROCD=1 #start_service 函数必须要重新定义 start_service() { #创建一个实例, 在procd看来一个应用程序可以多个实例 procd_open_instance #定义respawn参数,告知procd当binloader程序退出后尝试进行重启 procd_set_param respawn # binloader执行的命令是"/usr/bin/binloader", 若后面有参数可以直接在后面加上 procd_set_param command "$BINLOADER_BIN" #关闭实例 procd_close_instance } #stop_service重新定义,退出服务器后需要做的操作 stop_service() { rm -f /var/run/binloader.pid } restart() { stop start } start_service() 为注册服务到procd中,如果自己的应用程序没有配置文件,只要实现start_service()就好, procd_set_param设置设置好多参数,command为自己的应用路径, respawn可以检测自己的应用,如果挂掉可以重启,也可以设置重启间隔,其它参数可以自己查阅。 stop_service() 这个时procd kill自己的应用程序后调用的,若果你的应用程序关掉后,需要一些清理工作,需要实现这个。 service_triggers() 如果自己的应用需要关联一个配置文件test, (需要放在/etc/config/目录下) ,可以跟踪文件的修改情况,如果这个文件有改变,就调用reload_service().在service_triggers也可以添加跟踪网络的修改,也可以同时跟踪多个配置文件。 reload_service() 配置文件改变后,需要调用这个函数,可以根据自己需要实现功能。 注: start和reload区别是,start一般是指应用程序启动, reload一般是指只是重新加载与配置文件改变相关的部分,不把整个应用程序重新启动。这种方式应该是推荐的,如果你再reload里重新启动应用也是可以的。 ...

2019-01-28 · 1 min · 62 words · -

Let's Encrypt 证书

Let’s Encrypt 证书 https://www.jianshu.com/p/c5c9d071e395 Let’s Encrypt

2019-01-26 · 1 min · 6 words · -

Linux密码策略

Linux密码策略 authconfig --passminlen=8 --passmaxrepeat=3 --enablereqlower --enablerequpper --enablereqdigit --enablereqother --update 基于RHEL的系统 (RHEL CentOS Scientific) RHEL7,CentOS7,Scientific7 设置密码中至少包含一个小写字符,执行命令: authconfig -enablereqlower -update 查看设置: grep “^lcredit” /etc/security/pwquality.conf 设置密码中至少包含一个大写字符,执行命令: authconfig -enablerequpper -update 查看设置: grep “^ucredit” /etc/security/pwquality.conf 设置密码中至少包含一个数字字符,执行命令: authconfig -enablereqdigit -update 查看设置: grep “^dcredit” /etc/security/pwquality.conf 设置密码中至少包含一个特殊字符,执行命令: authconfig -enablereqother -update 查看设置: grep “^ocredit” /etc/security/pwquality.conf RHEL6,CentOS6,Scientific6 编辑 /etc/pam.d/system-auth 文件: vim /etc/pam.d/system-auth 找到如下一行,修改: password requisite pam_cracklib.so try_first_pass retry=3 type= minlen=8 dcredit=-1 ucredit=-1 lcredit=-1 ocredit=-1 上面配置了密码至少8个字符长,并且分别包含大小写字母、数字和特殊字符。 设置密码过期时间 我们设置如下策略: 一个密码使用的最长天数 更改密码最少天数间隔,为了不让用户频繁更改密码 在密码过期前多少天提醒用户

2019-01-25 · 1 min · 70 words · -

ansible inventory

ansible inventory ansible_user=root

2019-01-25 · 1 min · 3 words · -

golang file,文件操作

golang file,文件操作 文件大小 func main() { fi,err:=os.Stat("water") if err ==nil { fmt.Println("file size is ",fi.Size(),err) } } package main import ( "bufio" "fmt" "io" "io/ioutil" "os" ) func main() { dir := "/tmp/foo/bar" fileName := "file0.txt" writeTxtFile(dir, fileName, "foo") out := readTxtFile(dir, fileName) fmt.Println("read result: " + out) } func writeTxtFile0(dir, fileName, content string) { if !isExist(dir) { err := os.MkdirAll(dir, 0777) if err != nil { fmt.Printf("%s\n", err) } else { fmt.Println("Create Directory OK!") } } fullFileName := dir + string(os.PathSeparator) + fileName file, e := os.OpenFile(fullFileName, os.O_WRONLY|os.O_TRUNC|os.O_CREATE, 0777) if e != nil { fmt.Println("open file: " + e.Error()) } defer file.Close() n, err := file.WriteString(content) if err != nil { fmt.Println("write string: " + err.Error()) return } fmt.Printf("write length: %v\n", n) } func writeTxtFile(dir, fileName, content string) { if !isExist(dir) { err := os.MkdirAll(dir, 0777) if err != nil { fmt.Printf("%s\n", err) } else { fmt.Println("Create Directory OK!") } } err := ioutil.WriteFile(dir+string(os.PathSeparator)+fileName, []byte(content), 0777) if err != nil { fmt.Printf("failed to write file, file name: %v, err: %v", fileName, err) return } } // read txt file func readTxtFile(dir, fileName string) string { path := dir + string(os.PathSeparator) + fileName dat, err := ioutil.ReadFile(path) if err != nil { fmt.Println("failed to read file: " + fileName) } return string(dat) } func check(e error) { if e != nil { panic(e) } } func isExist(fileName string) bool { _, err := os.Stat(fileName) if err == nil { fmt.Println("file exist: " + fileName) return true } else if os.IsNotExist(err) { fmt.Println("file not exist") return false } else { fmt.Println("file error") } return false } 判断是文件还是目录 f, _ := os.Stat("a.txt") f.IsDir() move file func main() { oldLocation := "/var/www/html/test.txt" newLocation := "/var/www/html/src/test.txt" err := os.Rename(oldLocation, newLocation) if err != nil { log.Fatal(err) } } 判断文件是否存在 os.Stat(parentDir) // 创建目录 os.Mkdir(parentDir, os.ModePerm) 删除文件 file := "test.txt" err := os.Remove(file) package main import ( "fmt" "os" ) // 判断文件夹是否存在 func PathExists(path string) (bool, error) { _, err := os.Stat(path) if err == nil { return true, nil } if os.IsNotExist(err) { return false, nil } return false, err } func main() { _dir := "./gzFiles2" exist, err := PathExists(_dir) if err != nil { fmt.Printf("get dir error![%v]\n", err) return } if exist { fmt.Printf("has dir![%v]\n", _dir) } else { fmt.Printf("no dir![%v]\n", _dir) // 创建文件夹 err := os.Mkdir(_dir, os.ModePerm) if err != nil { fmt.Printf("mkdir failed![%v]\n", err) } else { fmt.Printf("mkdir success!\n") } } } 写文件 /***************************** 第一种方式: 使用 io.WriteString 写入文件***/ if checkFileIsExist(filename) { //如果文件存在 f, err1 = os.OpenFile(filename, os.O_APPEND, 0666) //打开文件 fmt.Println("文件存在") } else { f, err1 = os.Create(filename) //创建文件 fmt.Println("文件不存在") } check(err1) n, err1 := io.WriteString(f, wireteString) //写入文件(字符串) check(err1) fmt.Printf("写入 %d 个字节n", n) /***************************** 第二种方式: 使用 ioutil.WriteFile 写入文件****/ var d1 = []byte(wireteString) err2 := ioutil.WriteFile("./output2.txt", d1, 0666) //写入文件(字节数组) check(err2) /***************************** 第三种方式: 使用 File(Write,WriteString) 写入文件***/ f, err3 := os.Create("./output3.txt") //创建文件 check(err3) defer f.Close() n2, err3 := f.Write(d1) //写入文件(字节数组) check(err3) fmt.Printf("写入 %d 个字节n", n2) n3, err3 := f.WriteString("writesn") //写入文件(字节数组) fmt.Printf("写入 %d 个字节n", n3) f.Sync() /***************************** 第四种方式: 使用 bufio.NewWriter 写入文件****/ w := bufio.NewWriter(f) //创建新的 Writer 对象 n4, err3 := w.WriteString("bufferedn") fmt.Printf("写入 %d 个字节n", n4) w.Flush() f.Close() 一般文件比较小的话可以将文件全部读入内存中,然后转换成string再按行分割一下 func GetFileContentAsStringLines(filePath string) ([]string, error) { logger.Infof("get file content as lines: %v", filePath) result := []string{} b, err := ioutil.ReadFile(filePath) if err != nil { logger.Errorf("read file: %v error: %v", filePath, err) return result, err } s := string(b) for _, lineStr := range strings.Split(s, "\\n") { lineStr = strings.TrimSpace(lineStr) if lineStr == "" { continue } result = append(result, lineStr) } logger.Infof("get file content as lines: %v, size: %v", filePath, len(result)) return result, nil } Golang 超大文件读取的两个方案 第一个是使用流处理方式代码如下 func ReadFile(filePath string, handle func(string)) error { f, err := os.Open(filePath) defer f.Close() if err != nil { return err } buf := bufio.NewReader(f) for { line, err := buf.ReadLine("\n") line = strings.TrimSpace(line) handle(line) if err != nil { if err == io.EOF{ return nil } return err } return nil } } 第二个方案就是分片处理, 当读取的是二进制文件,没有换行符的时候,使用下面的方案处理大文件 func ReadBigFile(fileName string, handle func([]byte)) error { f, err := os.Open(fileName) if err != nil { fmt.Println("can't opened this file") return err } defer f.Close() s := make([]byte, 4096) for { switch nr, err := f.Read(s[:]); true { case nr < 0: fmt.Fprintf(os.Stderr, "cat: error reading: %s\n", err.Error()) os.Exit(1) case nr == 0: // EOF return nil case nr > 0: handle(s[0:nr]) } } return nil } https://learnku.com/articles/23559/two-schemes-for-reading-golang-super-large-files https://blog.csdn.net/xielingyun/article/details/50324423 https://blog.csdn.net/robertkun/article/details/78776585

2019-01-23 · 4 min · 716 words · -

WebDAV

WebDAV Web 分布式创作和版本管理 (WebDAV) 是 HTTP 协议的扩展,支持将 Web 服务器显示为标准的网络驱动器。WebDAV 客户端默认安装在大部分常见的操作系统上,可用于直接装载和访问 QNAP NAS 上的共享文件夹。WebDAV 访问具有以下好处: 256 位 AES SSL 加密 绕过防火墙和代理 速度比使用 VPN 的 Microsoft 网络连接 (SMB/CIFS) 更快 https://www.qnap.com/zh-cn/how-to/tutorial/article/%E5%A6%82%E4%BD%95%E9%80%8F%E8%BF%87-webdav-%E8%BF%9C%E7%A8%8B%E8%AE%BF%E9%97%AE%E6%82%A8%E7%9A%84-nas/ windows挂栽webdav # run in cmd net use * https://xxx.wiloon.com/remote.php/webdav/ Error 0x800700DF: The file size exceeds the limit allowed and cannot be saved. Regedit HKEY_LOCAL_MACHINE\SYSTEM\CurrentControlSet\Services\WebClient\Parameters image FileSizeLimitInBytes Set to the following Decimal: 4294967295 Click OK Restart the WebClient service Refresh the Windows Explorer WebDAV window ...

2019-01-22 · 1 min · 84 words · -

ACL (Access Control List)

ACL (Access Control List) https://www.cnblogs.com/sparkdev/p/5536868.html ACL的全称是 Access Control List (访问控制列表) ,一个针对文件/目录的访问控制列表。它在UGO权限管理的基础上为文件系统提供一个额外的、更灵活的权限管理机制。它被设计为UNIX文件权限管理的一个补充。ACL允许你给任何的用户或用户组设置任何文件/目录的访问权限。 setfacl 命令可以用来细分linux下的文件权限。 chmod 命令可以把文件权限分为 u,g,o三个组, 而 setfacl可以对每一个文件或目录设置更精确的文件权限。 换句话说, setfacl 可以更精确的控制权限的分配。 比如: 让某一个用户对某一个文件具有某种权限。 这种独立于传统的u,g,o的rwx权限之外的具体权限设置叫ACL (Access Control List) ACL 可以针对单一用户、单一文件或目录来进行r,w,x的权限控制,对于需要特殊权限的使用状况有一定帮助。 如,某一个文件,不让单一的某个用户访问。 setfacl 参数 Usage: setfacl [-bkndRLP] { -m|-M|-x|-X … } file … -m, -modify=acl modify the current ACL(s) of file(s) -M, -modify-file=file read ACL entries to modify from file -x, -remove=acl remove entries from the ACL(s) of file(s) -X, -remove-file=file read ACL entries to remove from file ...

2019-01-19 · 2 min · 343 words · -

java Keytool

java Keytool keytool -list -v -keystore /usr/java/default/jre/lib/security/cacerts sudo /usr/lib/jvm/java-8-openjdk/bin/keytool -importcert -keystore /usr/lib/jvm/java-8-openjdk/jre/lib/security/cacerts -storepass changeit -noprompt -file xxx.crt -alias "xxx.crt" # jdk 导入 证书 keytool.exe -importcert -keystore "C:\Program Files\Java\jdk1.8.0_201\jre\lib\security\cacerts" -storepass changeit -noprompt -file E:\xxx.cer -alias "xxx" Keytool 是一个Java 数据证书的管理工具 ,Keytool 将密钥 (key) 和证书 (certificates) 存在一个称为keystore的文件中 在keystore里,包含两种数据: 密钥实体 (Key entity) ——密钥 (secret key) 又或者是私钥和配对公钥 (采用非对称加密) 可信任的证书实体 (trusted certificate entries) ——只包含公钥 ailas(别名)每个keystore都关联这一个独一无二的alias,这个alias通常不区分大小写 JDK中keytool 常用命令: param comments -genkey 在用户主目录中创建一个默认文件".keystore",还会产生一个mykey的别名,mykey中包含用户的公钥、私钥和证书 -alias 产生别名 -keystore 指定密钥库的名称(产生的各类信息将不在.keystore文件中) -keysize 指定密钥长度 -validity 指定创建的证书有效期多少天 -keyalg 指定密钥的算法 (如 RSA DSA (如果不指定默认采用DSA) ) -dname 指定证书拥有者信息 例如: “CN=名字与姓氏,OU=组织单位名称,O=组织名称,L=城市或区域名称,ST=州或省份名称,C=单位的两字母国家代码” -keypass 指定别名条目的密码(私钥的密码) -storepass 指定密钥库的密码(获取keystore信息所需的密码) -list 显示密钥库中的证书信息 -v 显示密钥库中的证书详细信息 -export 将别名指定的证书导出到文件 -printcert 查看导出的证书信息 -file 参数指定导出到文件的文件名 -import 将已签名数字证书导入密钥库, keytool -import -alias 指定导入条目的别名 -keystore 指定keystore -file 需导入的证书

2019-01-06 · 1 min · 108 words · -

SSL/TLS 握手过程

SSL/TLS 握手过程 Client Hello 握手第一步是客户端向服务端发送 Client Hello 消息,这个消息里包含了一个客户端生成的随机数 Random1、客户端支持的加密套件 (Support Ciphers) 和 SSL Version 等信息。通过 Wireshark 抓包,我们可以看到如下信息: Wireshark 抓包的用法可以参考这篇文章: https://segmentfault.com/a/1190000018746027 Server Hello 第二步是服务端向客户端发送 Server Hello 消息,这个消息会从 Client Hello 传过来的 Support Ciphers 里确定一份加密套件,这个套件决定了后续加密和生成摘要时具体使用哪些算法,另外还会生成一份随机数 Random2。注意,至此客户端和服务端都拥有了两个随机数 (Random1+ Random2) ,这两个随机数会在后续生成对称秘钥时用到。 Certificate 这一步是服务端将自己的证书下发给客户端,让客户端验证自己的身份,客户端验证通过后取出证书中的公钥。 Server Key Exchange 如果是DH算法,这里发送服务器使用的DH参数。RSA算法不需要这一步。 Certificate Request Certificate Request 是服务端要求客户端上报证书,这一步是可选的,对于安全性要求高的场景会用到。 Server Hello Done Server Hello Done 通知客户端 Server Hello 过程结束。 Certificate Verify 客户端收到服务端传来的证书后,先从 CA 验证该证书的合法性,验证通过后取出证书中的服务端公钥,再生成一个随机数 Random3,再用服务端公钥非对称加密 Random3 生成 PreMaster Key。 Client Key Exchange 上面客户端根据服务器传来的公钥生成了 PreMaster Key,Client Key Exchange 就是将这个 key 传给服务端,服务端再用自己的私钥解出这个 PreMaster Key 得到客户端生成的 Random3。至此,客户端和服务端都拥有 Random1 + Random2 + Random3,两边再根据同样的算法就可以生成一份秘钥,握手结束后的应用层数据都是使用这个秘钥进行对称加密。为什么要使用三个随机数呢?这是因为 SSL/TLS 握手过程的数据都是明文传输的,并且多个随机数种子来生成秘钥不容易被暴力破解出来。客户端将 PreMaster Key 传给服务端的过程如下图所示: ...

2019-01-06 · 1 min · 193 words · -