GO语言的进阶之路-网络安全之proxy
作者:尹正杰
版权声明:原创作品,谢绝转载!否则将追究法律责任。
在党的带领下,我们大陆的孩子身心健康还是杠杠的,尤其是像我这种农村孩纸,从来不会像《人命的名义》的法院陈清泉哪样学英语,也不知道什么你们城里人总是挂嘴边的“亚麻得”是啥意思哈~就是偶尔会问一些我二师兄(谷歌)一些我不太懂的知识,相信我大师兄大家也都认识,顶顶大名的百度是也。当然,偶尔也要下载一些github的一些插件用于学习研究。就是因为这么一点点的需求感,导致我不得不学习一下代理,即proxy。
我之前发表过一篇文章,是有关代理的2个协议,即,HTTP协议和SOCKS5协议。通过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/rc4 34 */ 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 main 10 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 break 35 } 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 123 <md5.go>1111 50 解密:[root@yinzhengjie ~]# go run rc4.go -yinzhengjie 123 <1111 >2222 51 52 53 我们用加密tar包: 54 加密:[root@yinzhengjie ~]# tar czf - * | ./rc4 -yinzhengjie 666666 > yinzhengjie.tar.gz 55 解密:[root@yinzhengjie ~]# ./rc4 -yinzhengjie 666666 <yinzhengjie.tar.gz | tar tzf - 56 */
三.其他加密方式扩展。
通过上面的学习,我们知道了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, err 42 } 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 := date 48 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, err 56 } 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,err 22 } 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,nil 29 } 30 31 func My3DesDecrypt(data, key []byte) ([]byte,error) { 32 block,err := des.NewTripleDESCipher(key) 33 if err != nil { 34 return nil,err 35 } 36 BlockMode := cipher.NewCBCDecrypter(block,key[:8]) 37 BlockMode.CryptBlocks(data,data) //原地解密 38 data = data[:len(data)-1] 39 return data,nil 40 } 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,nil 49 } 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,nil 62 //}
如果你想要了解Golang如何使和其他语言进行加解密的可以查看这位大神的博客:http://blog.studygolang.com/2013/01/go%E5%8A%A0%E5%AF%86%E8%A7%A3%E5%AF%86%E4%B9%8Bdes/
四.手写TCP代理。
/* #!/usr/bin/env gorun @author :yinzhengjie 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/ EMAIL:y1053419035@qq.com */ package main import ( "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代理脚本
在现在的互联网时代,每个老板都会考虑到数据的安全性,我们上次分享的代码只是将明文的数据进行转发作用,但是数据时透明的,其结构图如下:
我们可以在上面的拓扑图中对数据进行加密 操作。
未完待续。。。。。