GO语言的进阶之路-网络安全之proxy

                  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)是DESAES过渡的加密算法(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是一个迭代的对称密钥分组的密码,它可以使用128192256位密钥,并且用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代理脚本

  在现在的互联网时代,每个老板都会考虑到数据的安全性,我们上次分享的代码只是将明文的数据进行转发作用,但是数据时透明的,其结构图如下:

 

  我们可以在上面的拓扑图中对数据进行加密 操作。

 

 

  未完待续。。。。。

 

posted @ 2017-08-15 21:59  尹正杰  阅读(3806)  评论(0编辑  收藏  举报