GO语言的进阶之路-网络安全之proxy
作者:尹正杰
版权声明:原创作品,谢绝转载!否则将追究法律责任。
在党的带领下,我们大陆的孩子身心健康还是杠杠的,尤其是像我这种农村孩纸,从来不会像《人命的名义》的法院陈清泉哪样学英语,也不知道什么你们城里人总是挂嘴边的“亚麻得”是啥意思哈~就是偶尔会问一些我二师兄(谷歌)一些我不太懂的知识,相信我大师兄大家也都认识,顶顶大名的百度是也。当然,偶尔也要下载一些github的一些插件用于学习研究。就是因为这么一点点的需求感,导致我不得不学习一下代理,即proxy。
我之前发表过一篇文章,是有关代理的2个协议,即,HTTP协议和SOCKS5协议。通过socks5协议实现了一个proxy基本框架,但是为了考虑它的安全性,我们就需要在传输的过程中进行数据加密,这个时候我们了解一些加密的方法就是很有必要了,本篇博客主要讲用Golang实现数据加密的两种常用方式。
一.对称加密和非对称加密。
下面是对对称加密和非对称加密的一个简单总结,以及rc4加密解密的玩法。
/*
#!/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 (
"crypto/rc4"
"log"
"crypto/md5" ) /*
对称加密和非对称加密:
我们生活中常见的就是秘钥,说道秘钥就得说私钥和公钥。
对称加密 :表现为:公钥可以解开私钥,而私钥也可以解开公钥。加密速度更快!
非对称加密:表现为:公钥和公钥之间是无法互相解开,私钥和私钥之间也是无法相互解开的。
一般而言,对称加密速度更快,但是生产环节中都是对称秘钥和非对称秘钥相结合使用的。生产环境中会先用非对称加密协商出来
一个秘钥,然后再用这个秘钥进行对称加密,这也是SSL传输数据的方式哟!
现代的文本加密主要还是对称加密。非对称加密太慢,而且也不适合对全文本加密,所以一般只是用在小数据加密上,比如加密文本
对称加密密钥再传给对方。然后文本本身还是用对称加密。非对称加密还有一个用处就是核实发件人身份。
现代主要有两种对称加密,数据流加密和数据块加密。数据流加密就是用算法和密钥一起产生一个随机码流,再和数据流XOR一起产生
加密后的数据流。解密方只要产生同样的随机码流就可以了。数据块加密把原数据分成固定大小的数据块(比如64位),加密器使用密钥
对数据块进行处理。一般来说数据流加密更快,但块加密更安全一些。常见的加密法里,des和3des是使用最多的数据块加密,aes是更新
一些的块加密法,rc4是数据流加密,等等。
二战以后,大家一般都放弃了保护加密算法的做法,因为太难了。而且数学上很强的算法就这么几种。所以现在都是公开算法。这些
算法特性都不错,如果一个密钥长度不够强了,只要加长密钥长度就可以了。当然这种改变涉及改变加密硬软件,在使用中有些不便,不
过一般认为算法本身还是够强不必改变。
关于rc4流式加密官方文档:https://godoc.org/crypto/rc4
*/ func main() {
key := "" //定义一个秘钥
md5sum := md5.Sum([]byte(key)) //我们可以将秘钥生成MD5值就会得到一个16字节的,即128位,相对来说安全一些!
cipher,err := rc4.NewCipher([]byte(md5sum[:])) //定义一个加密器“cipher”,把我们的秘钥穿进去就OK拉!
if err != nil {
log.Fatal(err)
}
buf := []byte("yinzhengjie") //我们定义一串字节。 cipher.XORKeyStream(buf,buf) //第一个参数表示加密后的数据存放在那个变量中,第二个参数表示我们需要对谁进行加密。我们这里写同名就是进行原地加密,这是Golang比较牛逼的地方。
log.Printf("加密后的样子:%s",string(buf)) //我们可以看下加密后的样子
{ //上面是进行加密,现在我们进行解密:首先创建一个作用域,这样我们就可以和上面定义相同的变量名,哈哈~
cipher,err := rc4.NewCipher([]byte(md5sum[:])) //注意,我们传入的秘钥要和加密的秘钥要一致哟!
if err != nil {
log.Fatal(err)
}
cipher.XORKeyStream(buf,buf) //进行原地解密!
log.Printf("解密后的样子:%s",string(buf))
}
} #以上代码输出结果如下:
2017/08/15 23:05:20 加密后的样子:�gňu�:Job�
2017/08/15 23:05:20 解密后的样子:yinzhengjie
二.手写加密工具。
/*
#!/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 (
"crypto/rc4"
"log"
"crypto/md5"
"io"
"os"
"flag"
) var (
yinzhengjie_key = flag.String("yinzhengjie","","yinzhengjie + string")
) 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 main() {
flag.Parse()
Crypto(os.Stdout,os.Stdin,*yinzhengjie_key)
} /*
用法展示:
加密:[root@yinzhengjie ~]# go run rc4.go -yinzhengjie 123 <md5.go>1111
解密:[root@yinzhengjie ~]# go run rc4.go -yinzhengjie 123 <1111 >2222 我们用加密tar包:
加密:[root@yinzhengjie ~]# tar czf - * | ./rc4 -yinzhengjie 666666 > yinzhengjie.tar.gz
解密:[root@yinzhengjie ~]# ./rc4 -yinzhengjie 666666 <yinzhengjie.tar.gz | tar tzf -
*/
三.其他加密方式扩展。
通过上面的学习,我们知道了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加密方法的一个实例:
/*
#!/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 (
"bytes"
"crypto/cipher"
"crypto/des"
"encoding/base64"
"fmt"
) func main() {
key := []byte("zhengjie") //这里你可以自定义秘钥长度,但是由于des的加密模式的特点,只会有8个字节生效。
data := []byte("尹正杰")
Mydes(data,key) // 调用DES加解密函数。
} func Mydes(data ,key []byte) {
key = key[:8] //用户输入的key可能长度可能会多余或少于8个字节,因此,我们可用对key进行切片操作,让代码的容错性增强。
result, err := DesEncrypt(data, key) //我们进行加密操作,如要输入的字符串都是"[]byte"类型。最终我们会拿到切片数组。
if err != nil {
panic(err)
}
fmt.Println("加密后的样子:",base64.StdEncoding.EncodeToString(result))
buf, err := DesDecrypt(result, key) //这是进行解密操作,将加密的数据和key传入进去进行解密操作。
if err != nil {
panic(err)
}
fmt.Println("解密后的样子:",string(buf))
} func DesEncrypt(data, key []byte) ([]byte, error) {
block, err := des.NewCipher(key) //定义一个加密器“block”,把我们的秘钥传进去就OK拉!
if err != nil {
return nil, err
}
padding:= block.BlockSize() - len(data)%block.BlockSize()
padtext := bytes.Repeat([]byte{byte(padding)},padding)
data = append(data,padtext...)
blockMode := cipher.NewCBCEncrypter(block, key) //对块数据进行加密操作。
crypted := make([]byte, len(data)) // 根据CryptBlocks方法的说明,如下方式初始化crypted也可以crypted := date
blockMode.CryptBlocks(crypted, data) //将date进行加密生成crypted,其实我们也可以原地进行加密,就是直接对源数据进行修改吗。可以节省内存,根据的需求来。
return crypted, nil //将加密后的数据crypte返回给用户。
} func DesDecrypt(crypted, key []byte) ([]byte, error) {
block, err := des.NewCipher(key) //定义一个加密器“block”,把我们的秘钥传进去就OK拉!
if err != nil {
return nil, err
}
blockMode := cipher.NewCBCDecrypter(block, key) //对块数据进行解密操作。
data := crypted ////当然,我们也可以这样写“date := make([]byte, len(crypted))”
blockMode.CryptBlocks(data, crypted) //对块数据进行解密操作,将数据存放在data里面
return data, nil //将data内容返回给用户。
} #以上代码执行结果如下:
加密后的样子: MAS7R6K+tVPPLUx3/P+hxA==
解密后的样子: 尹正杰
2.3DES加解密
3DES是DES加密算法的一种模式,它使用3条64位的密钥对数据进行三次加密。数据加密标准(DES)是美国的一种由来已久的加密标准,它使用对称密钥加密法。3DES(即Triple DES)是DES向AES过渡的加密算法(1999年,NIST将3-DES指定为过渡的加密标准),是DES的一个更安全的变形。它以DES为基本模块,通过组合分组方法设计出分组加密算法。可以参考官网说明:https://godoc.org/crypto/des,下面是关于3DES的一个实例:
/*
#!/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 (
"crypto/des"
"bytes"
"crypto/cipher"
"log"
"fmt"
) func My3DesEncrypt(data, key []byte) ([]byte,error) {
block,err := des.NewTripleDESCipher(key)
if err != nil {
return nil,err
}
padding := block.BlockSize() - len(data)%block.BlockSize()
padtext := bytes.Repeat([]byte{byte(padding)},padding)
data = append(data,padtext...)
BlockMode := cipher.NewCBCEncrypter(block,key[:8])
BlockMode.CryptBlocks(data,data) //原地加密
return data,nil
} func My3DesDecrypt(data, key []byte) ([]byte,error) {
block,err := des.NewTripleDESCipher(key)
if err != nil {
return nil,err
}
BlockMode := cipher.NewCBCDecrypter(block,key[:8])
BlockMode.CryptBlocks(data,data) //原地解密
data = data[:len(data)-1]
return data,nil
} func main() {
data := []byte("尹正杰")
key := []byte("zhengjiezhengjiezhengjie") //根据des3的原理,key的长度必须是24个字节,即192bit。
buf,err := My3DesEncrypt(data,key)
if err != nil {
log.Print(err)
}
fmt.Println("加密后的样子:",string(buf))
buf1,err := My3DesDecrypt(buf,key)
if err != nil {
log.Print(err)
}
fmt.Println("解密后的样子:",string(buf1))
} #以上代码执行结果如下:
加密后的样子: 0�G���S�-Lw����
解密后的样子: 尹正杰
3.aes加密解密
AES(Advanced Encryption Standard):高级加密标准,是下一代的加密算法标准,速度快,安全级别高。AES使用几种不同的方法来执行排列和置换运算。AES是一个迭代的、对称密钥分组的密码,它可以使用128、192和256位密钥,并且用128位(16字节)分组加密和解密数据。想要了解更多aes,请使劲戳我!
/*
#!/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 (
"crypto/md5"
"crypto/aes"
"io"
"crypto/rand"
"crypto/cipher"
"log"
"fmt"
) func main() {
data := []byte("大河之剑天上来")
key := []byte("yinzhengjie")
encrypt_buf ,err := MyAesEncrypt(data,key)
if err != nil {
log.Print(err)
}
fmt.Println("加密后的样子:",string(encrypt_buf)) //decipher_buf,err := MyAesDecrypt(encrypt_buf,key)
//if err != nil {
// log.Print(err)
//}
//fmt.Println("解密后的样子:",string(decipher_buf)) } func MyAesEncrypt(data,key []byte)([]byte,error) {
md5sum := md5.Sum([]byte(key)) //我们队密钥进行md4运算,得到的数字是一个16字节的数字。这样我们就不用考虑key的自身长度了,因为不管你多长都会被格式化成一个128的密钥。
block,err := aes.NewCipher(md5sum[:])
if err != nil {
panic(err)
}
iv := make([]byte,block.BlockSize())
io.ReadFull(rand.Reader,iv) stream := cipher.NewCFBEncrypter(block,iv)
stream.XORKeyStream(data,data) //进行原地加密。
return data,nil
} //func MyAesDecrypt(data, key []byte) ([]byte, error) {
// md5sum := md5.Sum([]byte(key))
// block,err := aes.NewCipher(md5sum[:])
// if err != nil {
// panic(err)
// }
// BlockSize := block.BlockSize()
// BlockMode := cipher.NewCBCDecrypter(block,md5sum[:BlockSize])
// BlockMode.CryptBlocks(data,data)
// data = data[:len(data)-1]
// return data,nil
//}
如果你想要了解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代理脚本
在现在的互联网时代,每个老板都会考虑到数据的安全性,我们上次分享的代码只是将明文的数据进行转发作用,但是数据时透明的,其结构图如下:
我们可以在上面的拓扑图中对数据进行加密 操作。
未完待续。。。。。