GO语言的进阶之路-网络安全之proxy
作者:尹正杰
版权声明:原创作品,谢绝转载!否则将追究法律责任。
在党的带领下,我们大陆的孩子身心健康还是杠杠的,尤其是像我这种农村孩纸,从来不会像《人命的名义》的法院陈清泉哪样学英语,也不知道什么你们城里人总是挂嘴边的“亚麻得”是啥意思哈~就是偶尔会问一些我二师兄(谷歌)一些我不太懂的知识,相信我大师兄大家也都认识,顶顶大名的百度是也。当然,偶尔也要下载一些github的一些插件用于学习研究。就是因为这么一点点的需求感,导致我不得不学习一下代理,即proxy。
我之前发表过一篇文章,是有关代理的2个协议,即,。通过socks5协议实现了一个proxy基本框架,但是为了考虑它的安全性,我们就需要在传输的过程中进行数据加密,这个时候我们了解一些加密的方法就是很有必要了,本篇博客主要讲用Golang实现数据加密的两种常用方式。
一.对称加密和非对称加密。
下面是对对称加密和非对称加密的一个简单总结,以及rc4加密解密的玩法。
1 /* 2 #!/usr/bin/env gorun 3 @author :yinzhengjie 4 Blog:http://www.cnblogs.com/yinzhengjie/tag/GO%E8%AF%AD%E8%A8%80%E7%9A%84%E8%BF%9B%E9%98%B6%E4%B9%8B%E8%B7%AF/ 5 EMAIL:y1053419035@qq.com 6 */ 7 8 package main 9 10 import (11 "crypto/rc4"12 "log"13 "crypto/md5"14 15 )16 17 /*18 对称加密和非对称加密:19 我们生活中常见的就是秘钥,说道秘钥就得说私钥和公钥。20 对称加密 :表现为:公钥可以解开私钥,而私钥也可以解开公钥。加密速度更快!21 非对称加密:表现为:公钥和公钥之间是无法互相解开,私钥和私钥之间也是无法相互解开的。22 一般而言,对称加密速度更快,但是生产环节中都是对称秘钥和非对称秘钥相结合使用的。生产环境中会先用非对称加密协商出来23 一个秘钥,然后再用这个秘钥进行对称加密,这也是SSL传输数据的方式哟!24 现代的文本加密主要还是对称加密。非对称加密太慢,而且也不适合对全文本加密,所以一般只是用在小数据加密上,比如加密文本25 对称加密密钥再传给对方。然后文本本身还是用对称加密。非对称加密还有一个用处就是核实发件人身份。26 现代主要有两种对称加密,数据流加密和数据块加密。数据流加密就是用算法和密钥一起产生一个随机码流,再和数据流XOR一起产生27 加密后的数据流。解密方只要产生同样的随机码流就可以了。数据块加密把原数据分成固定大小的数据块(比如64位),加密器使用密钥28 对数据块进行处理。一般来说数据流加密更快,但块加密更安全一些。常见的加密法里,des和3des是使用最多的数据块加密,aes是更新29 一些的块加密法,rc4是数据流加密,等等。30 二战以后,大家一般都放弃了保护加密算法的做法,因为太难了。而且数学上很强的算法就这么几种。所以现在都是公开算法。这些31 算法特性都不错,如果一个密钥长度不够强了,只要加长密钥长度就可以了。当然这种改变涉及改变加密硬软件,在使用中有些不便,不32 过一般认为算法本身还是够强不必改变。33 关于rc4流式加密官方文档:https://godoc.org/crypto/rc434 */35 36 37 38 func main() {39 key := "123456" //定义一个秘钥40 md5sum := md5.Sum([]byte(key)) //我们可以将秘钥生成MD5值就会得到一个16字节的,即128位,相对来说安全一些!41 cipher,err := rc4.NewCipher([]byte(md5sum[:])) //定义一个加密器“cipher”,把我们的秘钥穿进去就OK拉!42 if err != nil {43 log.Fatal(err)44 }45 buf := []byte("yinzhengjie") //我们定义一串字节。46 47 cipher.XORKeyStream(buf,buf) //第一个参数表示加密后的数据存放在那个变量中,第二个参数表示我们需要对谁进行加密。我们这里写同名就是进行原地加密,这是Golang比较牛逼的地方。48 log.Printf("加密后的样子:%s",string(buf)) //我们可以看下加密后的样子49 { //上面是进行加密,现在我们进行解密:首先创建一个作用域,这样我们就可以和上面定义相同的变量名,哈哈~50 cipher,err := rc4.NewCipher([]byte(md5sum[:])) //注意,我们传入的秘钥要和加密的秘钥要一致哟!51 if err != nil {52 log.Fatal(err)53 }54 cipher.XORKeyStream(buf,buf) //进行原地解密!55 log.Printf("解密后的样子:%s",string(buf))56 }57 }58 59 60 61 #以上代码输出结果如下:62 2017/08/15 23:05:20 加密后的样子:�gňu�:Job�63 2017/08/15 23:05:20 解密后的样子:yinzhengjie
二.手写加密工具。
1 /* 2 #!/usr/bin/env gorun 3 @author :yinzhengjie 4 Blog:http://www.cnblogs.com/yinzhengjie/tag/GO%E8%AF%AD%E8%A8%80%E7%9A%84%E8%BF%9B%E9%98%B6%E4%B9%8B%E8%B7%AF/ 5 EMAIL:y1053419035@qq.com 6 */ 7 8 9 package main10 11 import (12 "crypto/rc4"13 "log"14 "crypto/md5"15 "io"16 "os"17 "flag"18 )19 20 var (21 yinzhengjie_key = flag.String("yinzhengjie","","yinzhengjie + string")22 )23 24 func Crypto(w io.Writer,f io.Reader,key string) {25 md5sum := md5.Sum([]byte(key)) //首先对秘钥进行MD5运算,使得秘钥的长度更长!26 cipher,err :=rc4.NewCipher([]byte(md5sum[:])) //定义一个加密器27 if err != nil {28 log.Fatal(err)29 }30 buf := make([]byte,4096) //定义一个指定大小的容器。31 for {32 n,err := f.Read(buf) //将“f”的内容读取到“buf”中去,但是读取的大小是固定的哟!33 if err == io.EOF { //当读取到结 尾的时候就中止循环,我们这里不考虑其他异常!34 break35 }36 src := buf[:n] //将读取到的内容取出来,即都是字节。而非一个长度数字。37 cipher.XORKeyStream(src,src) //进行原地加密38 w.Write(src) //将加密后的数据写入到“w”中。39 }40 }41 42 func main() {43 flag.Parse()44 Crypto(os.Stdout,os.Stdin,*yinzhengjie_key)45 }46 47 /*48 用法展示:49 加密:[root@yinzhengjie ~]# go run rc4.go -yinzhengjie 123111150 解密:[root@yinzhengjie ~]# go run rc4.go -yinzhengjie 123 <1111 >222251 52 53 我们用加密tar包:54 加密:[root@yinzhengjie ~]# tar czf - * | ./rc4 -yinzhengjie 666666 > yinzhengjie.tar.gz55 解密:[root@yinzhengjie ~]# ./rc4 -yinzhengjie 666666
三.其他加密方式扩展。
通过上面的学习,我们知道了rc4是一种流式加密,也基本上回使用rc4进行数据加密了,但是还有一种方式是基于块加密的,比如des,和aes等等。还有一些大牛实现了和其他语言混搭的加密解密套路,我已经对Golang的知识库目不暇接了。觉得Golang这个语言越来越有意思了。(如果想要了解des和aes的区别,)
1.des加密解密。
DES(Data Encryption Standard)是对称加密算法,也就是加密和解密用相同的密钥。其入口参数有三个:key、data、mode。
1>.key为加密解密使用的密钥;
2>.data为加密解密的数据;
3>.mode为其工作模式。当模式为加密模式时,明文按照64位(即8bytes,字节)进行分组,形成明文组,key用于对数据加密,当模式为解密模式时,key用于对数据解密。
想要了解更多Golang在实现使用des的使用方法,可以参考官网说明:https://godoc.org/crypto/des,以下是关于des加密方法的一个实例:
1 /* 2 #!/usr/bin/env gorun 3 @author :yinzhengjie 4 Blog:http://www.cnblogs.com/yinzhengjie/tag/GO%E8%AF%AD%E8%A8%80%E7%9A%84%E8%BF%9B%E9%98%B6%E4%B9%8B%E8%B7%AF/ 5 EMAIL:y1053419035@qq.com 6 */ 7 8 package main 9 10 import (11 "bytes"12 "crypto/cipher"13 "crypto/des"14 "encoding/base64"15 "fmt"16 )17 18 func main() {19 key := []byte("zhengjie") //这里你可以自定义秘钥长度,但是由于des的加密模式的特点,只会有8个字节生效。20 data := []byte("尹正杰")21 Mydes(data,key) // 调用DES加解密函数。22 }23 24 func Mydes(data ,key []byte) {25 key = key[:8] //用户输入的key可能长度可能会多余或少于8个字节,因此,我们可用对key进行切片操作,让代码的容错性增强。26 result, err := DesEncrypt(data, key) //我们进行加密操作,如要输入的字符串都是"[]byte"类型。最终我们会拿到切片数组。27 if err != nil {28 panic(err)29 }30 fmt.Println("加密后的样子:",base64.StdEncoding.EncodeToString(result))31 buf, err := DesDecrypt(result, key) //这是进行解密操作,将加密的数据和key传入进去进行解密操作。32 if err != nil {33 panic(err)34 }35 fmt.Println("解密后的样子:",string(buf))36 }37 38 func DesEncrypt(data, key []byte) ([]byte, error) {39 block, err := des.NewCipher(key) //定义一个加密器“block”,把我们的秘钥传进去就OK拉!40 if err != nil {41 return nil, err42 }43 padding:= block.BlockSize() - len(data)%block.BlockSize()44 padtext := bytes.Repeat([]byte{byte(padding)},padding)45 data = append(data,padtext...)46 blockMode := cipher.NewCBCEncrypter(block, key) //对块数据进行加密操作。47 crypted := make([]byte, len(data)) // 根据CryptBlocks方法的说明,如下方式初始化crypted也可以crypted := date48 blockMode.CryptBlocks(crypted, data) //将date进行加密生成crypted,其实我们也可以原地进行加密,就是直接对源数据进行修改吗。可以节省内存,根据的需求来。49 return crypted, nil //将加密后的数据crypte返回给用户。50 }51 52 func DesDecrypt(crypted, key []byte) ([]byte, error) {53 block, err := des.NewCipher(key) //定义一个加密器“block”,把我们的秘钥传进去就OK拉!54 if err != nil {55 return nil, err56 }57 blockMode := cipher.NewCBCDecrypter(block, key) //对块数据进行解密操作。58 data := crypted 当然,我们也可以这样写“date := make([]byte, len(crypted))”59 blockMode.CryptBlocks(data, crypted) //对块数据进行解密操作,将数据存放在data里面60 return data, nil //将data内容返回给用户。61 }62 63 64 65 66 #以上代码执行结果如下:67 加密后的样子: MAS7R6K+tVPPLUx3/P+hxA==68 解密后的样子: 尹正杰
2.3DES加解密
3DES是DES加密算法的一种模式,它使用3条64位的密钥对数据进行三次加密。数据加密标准(DES)是美国的一种由来已久的加密标准,它使用对称密钥加密法。3DES(即Triple DES)是DES向AES过渡的加密算法(1999年,NIST将3-DES指定为过渡的加密标准),是DES的一个更安全的变形。它以DES为基本模块,通过组合分组方法设计出分组加密算法。可以参考官网说明:https://godoc.org/crypto/des,下面是关于3DES的一个实例:
1 /* 2 #!/usr/bin/env gorun 3 @author :yinzhengjie 4 Blog:http://www.cnblogs.com/yinzhengjie/tag/GO%E8%AF%AD%E8%A8%80%E7%9A%84%E8%BF%9B%E9%98%B6%E4%B9%8B%E8%B7%AF/ 5 EMAIL:y1053419035@qq.com 6 */ 7 8 package main 9 10 import (11 "crypto/des"12 "bytes"13 "crypto/cipher"14 "log"15 "fmt"16 )17 18 func My3DesEncrypt(data, key []byte) ([]byte,error) {19 block,err := des.NewTripleDESCipher(key)20 if err != nil {21 return nil,err22 }23 padding := block.BlockSize() - len(data)%block.BlockSize()24 padtext := bytes.Repeat([]byte{byte(padding)},padding)25 data = append(data,padtext...)26 BlockMode := cipher.NewCBCEncrypter(block,key[:8])27 BlockMode.CryptBlocks(data,data) //原地加密28 return data,nil29 }30 31 func My3DesDecrypt(data, key []byte) ([]byte,error) {32 block,err := des.NewTripleDESCipher(key)33 if err != nil {34 return nil,err35 }36 BlockMode := cipher.NewCBCDecrypter(block,key[:8])37 BlockMode.CryptBlocks(data,data) //原地解密38 data = data[:len(data)-1]39 return data,nil40 }41 42 43 func main() {44 data := []byte("尹正杰")45 key := []byte("zhengjiezhengjiezhengjie") //根据des3的原理,key的长度必须是24个字节,即192bit。46 buf,err := My3DesEncrypt(data,key)47 if err != nil {48 log.Print(err)49 }50 fmt.Println("加密后的样子:",string(buf))51 buf1,err := My3DesDecrypt(buf,key)52 if err != nil {53 log.Print(err)54 }55 fmt.Println("解密后的样子:",string(buf1))56 }57 58 59 60 61 #以上代码执行结果如下:62 加密后的样子: 0�G���S�-Lw����63 解密后的样子: 尹正杰
3.aes加密解密
AES(Advanced Encryption Standard):高级加密标准,是下一代的加密算法标准,速度快,安全级别高。AES使用几种不同的方法来执行排列和置换运算。AES是一个迭代的、对称密钥分组的密码,它可以使用128、192和256位密钥,并且用128位(16字节)分组加密和解密数据。想要了解更多aes,
1 /* 2 #!/usr/bin/env gorun 3 @author :yinzhengjie 4 Blog:http://www.cnblogs.com/yinzhengjie/tag/GO%E8%AF%AD%E8%A8%80%E7%9A%84%E8%BF%9B%E9%98%B6%E4%B9%8B%E8%B7%AF/ 5 EMAIL:y1053419035@qq.com 6 */ 7 8 package main 9 10 import (11 "crypto/md5"12 "crypto/aes"13 "io"14 "crypto/rand"15 "crypto/cipher"16 "log"17 "fmt"18 )19 20 func main() {21 data := []byte("大河之剑天上来")22 key := []byte("yinzhengjie")23 encrypt_buf ,err := MyAesEncrypt(data,key)24 if err != nil {25 log.Print(err)26 }27 fmt.Println("加密后的样子:",string(encrypt_buf))28 29 //decipher_buf,err := MyAesDecrypt(encrypt_buf,key)30 //if err != nil {31 // log.Print(err)32 //}33 //fmt.Println("解密后的样子:",string(decipher_buf))34 35 }36 37 func MyAesEncrypt(data,key []byte)([]byte,error) {38 md5sum := md5.Sum([]byte(key)) //我们队密钥进行md4运算,得到的数字是一个16字节的数字。这样我们就不用考虑key的自身长度了,因为不管你多长都会被格式化成一个128的密钥。39 block,err := aes.NewCipher(md5sum[:])40 if err != nil {41 panic(err)42 }43 iv := make([]byte,block.BlockSize())44 io.ReadFull(rand.Reader,iv)45 46 stream := cipher.NewCFBEncrypter(block,iv)47 stream.XORKeyStream(data,data) //进行原地加密。48 return data,nil49 }50 51 //func MyAesDecrypt(data, key []byte) ([]byte, error) {52 // md5sum := md5.Sum([]byte(key))53 // block,err := aes.NewCipher(md5sum[:])54 // if err != nil {55 // panic(err)56 // }57 // BlockSize := block.BlockSize()58 // BlockMode := cipher.NewCBCDecrypter(block,md5sum[:BlockSize])59 // BlockMode.CryptBlocks(data,data)60 // data = data[:len(data)-1]61 // return data,nil62 //}
如果你想要了解Golang如何使和其他语言进行加解密的可以查看这位大神的博客:
四.手写TCP代理。
/*#!/usr/bin/env gorun@author :yinzhengjieBlog:http://www.cnblogs.com/yinzhengjie/tag/GO%E8%AF%AD%E8%A8%80%E7%9A%84%E8%BF%9B%E9%98%B6%E4%B9%8B%E8%B7%AF/EMAIL:y1053419035@qq.com*/package mainimport ( "net" "io" "flag" "log" "sync" "crypto/md5" "crypto/rc4")/*1>安装switchyomega软件;2.Golang编写代理服务3>以下代码是一个TCP代理程序,它是一个通用的四层代理。4.了解什么是透明代理。*/var ( target = flag.String("yinzhengjie","www.qq.com:80","yinzhengjie == host:port") //第一个参数是定义关键字,第二个参数是告诉让用户输入相应的主机+端口。第三个参数是告诉用户使用方法)func Crypto(w io.Writer,f io.Reader,key string) { md5sum := md5.Sum([]byte(key)) //首先对秘钥进行MD5运算,使得秘钥的长度更长! cipher,err :=rc4.NewCipher([]byte(md5sum[:])) //定义一个加密器 if err != nil { log.Fatal(err) } buf := make([]byte,4096) //定义一个指定大小的容器。 for { n,err := f.Read(buf) //将“f”的内容读取到“buf”中去,但是读取的大小是固定的哟! if err == io.EOF { //当读取到结尾的时候就中止循环,我们这里不考虑其他异常! break } src := buf[:n] //将读取到的内容取出来,即都是字节。而非一个长度数字。 cipher.XORKeyStream(src,src) //进行原地加密 w.Write(src) //将加密后的数据写入到“w”中。 }}func handle_conn(conn net.Conn) { var ( remote net.Conn //定义远端的服务器连接。 err error ) remote,err = net.Dial("tcp",*target) //建立到目标服务器的连接。 if err != nil { log.Print(err) conn.Close() return } wg := new(sync.WaitGroup) wg.Add(2) go func() { defer wg.Done() io.Copy(remote,conn) //读取原地址请求(conn),然后将读取到的数据发送给目标主机。 remote.Close() }() go func() { defer conn.Close() io.Copy(conn,remote) //与上面相反,就是讲目标主机的数据返回给客户端。 conn.Close() }() wg.Wait()}func main() { flag.Parse() listener,err := net.Listen("tcp",":8888") if err != nil { log.Fatal(err) } for { conn,err := listener.Accept() if err != nil { log.Fatal(err) } go handle_conn(conn) }}/*用法一: 不指定参数,会用代码默认的参数。 服务端:[root@yinzhengjie yinzhengjie]# go run tcp_proxy.go 客户端:[root@yinzhengjie ~]# curl -v 127.0.0.1:8888用法二: 指定参数时需要知名IP和端口号。 服务端:[root@yinzhengjie yinzhengjie]# go run tcp_proxy.go --yinzhengjie=127.0.0.1:22 客户端:[root@yinzhengjie ~]# ssh -p 8888 127.0.0.1用法三: 星球大战代理: 服务端:[root@yinzhengjie yinzhengjie]# go run tcp_proxy.go --yinzhengjie=towel.blinkenlights.nl:23 客户端:[root@yinzhengjie ~]# telnet 127.0.0.1 8888*/
五.优化SOCKS5代理脚本
在现在的互联网时代,每个老板都会考虑到数据的安全性,我们上次分享的代码只是将明文的数据进行转发作用,但是数据时透明的,其结构图如下:
我们可以在上面的拓扑图中对数据进行加密 操作。
未完待续。。。。。