Base64是一种基于64个可打印字符来表示二进制数据的表示方法。每6个比特为一个单元,对应某个可打印字符。3个字节相当于24个比特,对应于4个Base64单元,即3个字节可由4个可打印字符来表示。在Base64中的可打印字符包括字母A-Z、a-z、数字0-9,这样共有62个字符,此外两个可打印符号在不同的系统中而不同。一些如uuencode的其他编码方法,和之后BinHex的版本使用不同的64字符集来代表6个二进制数字,但是不被称为Base64。
Base64常用于在通常处理文本数据的场合,表示、传输、存储一些二进制数据,包括MIME的电子邮件及XML的一些复杂数据。

在MIME格式的电子邮件中,Base64可以用来将binary的字节序列数据编码成ASCII字符序列构成的文本。使用时,在传输编码方式中指定Base64。使用的字符包括大小写拉丁字母各26个、数字10个、加号+和斜杠/,共64个字符,等号=用来作为后缀用途。

编码后的数据比原始数据略长,为原来的4/3。在电子邮件中,根据RFC 822规定,每76个字符,还需要加上一个回车换行。可以估算编码后数据长度大约为原长的135.1%。
base64 go 语言示例

package main

import (
    b64 "encoding/base64"
    "fmt"
)

func main() {

    data := "abc123!?$*&()'-=@~"

    sEnc := b64.StdEncoding.EncodeToString([]byte(data))
    fmt.Println(sEnc)

    sDec, _ := b64.StdEncoding.DecodeString(sEnc)
    fmt.Println(string(sDec))
    fmt.Println()

    uEnc := b64.URLEncoding.EncodeToString([]byte(data))
    fmt.Println(uEnc)
    uDec, _ := b64.URLEncoding.DecodeString(uEnc)
    fmt.Println(string(uDec))
}

sha256withrsa 是先将数据使用sha256 计算hash值,然后再使用rsa 对hash sign的算法。

go sha256withrsa代码

func sha256withrsa(signContent string, strprikey string) string {
    hash := crypto.SHA256
    shaNew := hash.New()
    shaNew.Write([]byte(signContent))
    hashed := shaNew.Sum(nil)
    priKey, err := ParsePrivateKey(strprikey)
    if err != nil {
        panic(err)
    }

    signature, err := rsa.SignPKCS1v15(rand.Reader, priKey, hash, hashed)
    if err != nil {
        panic(err)
    }
    return b64.StdEncoding.EncodeToString(signature)
}
func ParsePrivateKey(privateKey string) (*rsa.PrivateKey, error) {
    block, _ := pem.Decode([]byte(privateKey))
    if block == nil {
        return nil, errors.New("私钥信息错误!")
    }
    priKey, err := x509.ParsePKCS1PrivateKey(block.Bytes)
    if err != nil {
        return nil, err
    }
    return priKey, nil
}

生成rsa私钥

openssl genrsa 

Generating RSA private key, 2048 bit long modulus (2 primes)
...........................................................+++++
...............................................................+++++
e is 65537 (0x010001)
-----BEGIN RSA PRIVATE KEY-----
MIIEowIBAAKCAQEAw3PgS6PELTZtchDBmFSzK329CaZLV/mOr1IP595Q3pVsYkr/
uNHVqvdtyEiJ42uj+Q9d2u4cOLU8G9DWsWBdHgbWZORKJjMrgpIFb4CVfWs9eqil
XO2vDqDiUjL1idPHLXwm7Z8SDEOUaFLTFKCJJxS3RkArRtcRgrCekA10XNHnnOm5
6+96SD/X2edUH++fiWkrmNvi+P2tLoTphCRePmucyOLKJH/V+nBhV83zq+4KyxBs
xLUwOeEDaf6JbOGUb81WYDUCmkBjyffdLDaD1gJRxtj2zkSx0GL4ROnWGMXbrIj4
ZxiJSktGi0jatzI7HhnCaCB+ya+y8MP/5ExRDwIDAQABAoIBAQCu879XFa1ZpzmK
e9M+7ro+eNvIknEou3rsFjGkpIyQ4QHWbjm/Dx0KX1aGi9/ZkRWCsC2b9b2PSTMX
1vUGazKO2SH5x5tT3EPbKafx6hBl5qNucx0qNJuZJYvQdkrxZ38HiPUziErYbXmf
dFv7P8PlFpcDLBdQ3tyOtlgIq/J3zd/MabjSXvOI7sr89TJjpfXexGYI/gZvhGw1
CeXHu4b+iVbGKBpD0SD3m8T7LkJ9ELifPCQS6wmJpmZOqpFIGzcZGSuYV29H5nFr
+sDfAqfbkulKzqJkS1HNQvRpwyua7SdIOb+HMQ+bRo56hnDXJG5xB14sJlaBjDiZ
Hl9Y/9IpAoGBAOTWQSPSlPDDKm54AS5u1z2yXClb3c8TjX9N1C7uvHMPvjI2HLRH
Xs0AAZZUGLYfkFBlJRrb39z5drRJ0u70pFkPLOBf7Z0x0rMOCmi8Rd8b19myNhZH
4UmssiCqOUKFPvUaPDEVsFPlYkNJ/99gMSz6PT1luxqS+ySH8IHz7f07AoGBANqn
KXS12mirOiW61a0js/vcbQzut3+T4/R1sPKk3azz5OOS2lUExOIi4O7P1Ugcpi3Z
kpQZ3IB4yL6IF88WNeUztBvNHKc9iosKicrjKHMgQrPS4ZTJBsYPsSqjORdOCGK7
VWg4TsJWl20XsWm0dvujs/TGidg95w8lziuZXU49AoGARed9dqf5f6Le72gRVFHf
xqUZqji6BWv/qYfo7X1Ya/2/KC+HjTrQJud9lKKT+e6icyyyrQXF2QcLz/9NRFnP
arVMDC7Bs2do6fG8FEqPPZyPz5y1ERFcLNlogLn/S+bQWKdvA7+QYdCMt+l/FImZ
/BdlZcAR7AhCV+J/AfO2G8kCgYBl/PzYRI7Yra2Utb4/YVztiNaQ5rRO3MPCXjco
JwCDUMwd3nzalTvkoxI6MlUwqrRUxJybWijBABXa0Y4ReTGwdFp5cUYBODJsQoEd
UrGmaZDTYfT4un/fWKhx0+qoVSySKQFgeIs8GLnPF/MX9CZfn+8rqIPZip5BMYgm
TAX6nQKBgE6e3iYTj7CqXSOpzkFyQnsvY/P7vZxk/feR/JB8dHvhAJ3JTuiVDaM6
PVP5oq7+HfIZo1rkjID6W4WuqLPm/I+h4qx+gDw+Jiepolt7jML3F7tle32ifDa8
zTmQ57Qa/fT5Wx5sXberOok233bRHSPoct09q2TgZ34vA90BHzUT
-----END RSA PRIVATE KEY-----

调用示例,这里面有一个go语言输入多行字符的技巧,使用 ` 来包含即可。

prikey := `-----BEGIN RSA PRIVATE KEY-----
MIIEowIBAAKCAQEAw3PgS6PELTZtchDBmFSzK329CaZLV/mOr1IP595Q3pVsYkr/
.........此处有省略..............................................
zTmQ57Qa/fT5Wx5sXberOok233bRHSPoct09q2TgZ34vA90BHzUT
-----END RSA PRIVATE KEY-----
`  
str := sha256withrsa("const.net.cn", prikey)
fmt.Printf("sha256withrsa = %s", str)

运行结果:

Ye1ErpEaY/fdH13peaczvCzeTkGa4xl6Ms3AH99zl3Q3x1cGBtFt7+VeWSvLtyRuIt2b+e71q6V4QJM8VYGPDtVWtHARQlinznYvib0c4NSaVSvz6d6P99vDL1IWe1v9kJMtGNaBw0B5lwY2gxl4ta14Xka6T/XK5wPiSFiINIMizhpAbs7olu/csyLaYUMe1tTdVCIF8AQzkGSUZWcz2QhxiVHdh4/uC2PxV7Zhos7XVKhJPHw3e99ktpfgzqz0X7c8et2+4xrLYFM01MNUAnbRjNkGdqXrxkIprbSZwII8B+ttQgUUebUj9N/GFVjWH1pmqwLaKnpH1x2G40aDWw==

openssl 验证 sha256withrsa

将刚才生成的私钥保存为pri.pem,然后执行以下命令

echo -ne "const.net.cn" > test.txt
openssl dgst -sha256 -sign pri.pem test.txt |openssl base64

Ye1ErpEaY/fdH13peaczvCzeTkGa4xl6Ms3AH99zl3Q3x1cGBtFt7+VeWSvLtyRu
It2b+e71q6V4QJM8VYGPDtVWtHARQlinznYvib0c4NSaVSvz6d6P99vDL1IWe1v9
kJMtGNaBw0B5lwY2gxl4ta14Xka6T/XK5wPiSFiINIMizhpAbs7olu/csyLaYUMe
1tTdVCIF8AQzkGSUZWcz2QhxiVHdh4/uC2PxV7Zhos7XVKhJPHw3e99ktpfgzqz0
X7c8et2+4xrLYFM01MNUAnbRjNkGdqXrxkIprbSZwII8B+ttQgUUebUj9N/GFVjW
H1pmqwLaKnpH1x2G40aDWw==

echo -ne "const.net.cn" | openssl dgst -sha256 -sign pri.pem |openssl base64

Ye1ErpEaY/fdH13peaczvCzeTkGa4xl6Ms3AH99zl3Q3x1cGBtFt7+VeWSvLtyRu
It2b+e71q6V4QJM8VYGPDtVWtHARQlinznYvib0c4NSaVSvz6d6P99vDL1IWe1v9
kJMtGNaBw0B5lwY2gxl4ta14Xka6T/XK5wPiSFiINIMizhpAbs7olu/csyLaYUMe
1tTdVCIF8AQzkGSUZWcz2QhxiVHdh4/uC2PxV7Zhos7XVKhJPHw3e99ktpfgzqz0
X7c8et2+4xrLYFM01MNUAnbRjNkGdqXrxkIprbSZwII8B+ttQgUUebUj9N/GFVjW
H1pmqwLaKnpH1x2G40aDWw==

还有个php来验证的代码:

cat sha256withrsa.php 
<?php
$sign = "const.net.cn";
$binary_signature = "";
$prikey = "-----BEGIN RSA PRIVATE KEY-----
........................此处有省略..........
-----END RSA PRIVATE KEY-----";
$algo = "SHA256";
openssl_sign($sign, $binary_signature, $prikey, $algo);
$sign = base64_encode($binary_signature);
echo $sign;
?>
php7.4 sha256withrsa.php 

Ye1ErpEaY/fdH13peaczvCzeTkGa4xl6Ms3AH99zl3Q3x1cGBtFt7+VeWSvLtyRuIt2b+e71q6V4QJM8VYGPDtVWtHARQlinznYvib0c4NSaVSvz6d6P99vDL1IWe1v9kJMtGNaBw0B5lwY2gxl4ta14Xka6T/XK5wPiSFiINIMizhpAbs7olu/csyLaYUMe1tTdVCIF8AQzkGSUZWcz2QhxiVHdh4/uC2PxV7Zhos7XVKhJPHw3e99ktpfgzqz0X7c8et2+4xrLYFM01MNUAnbRjNkGdqXrxkIprbSZwII8B+ttQgUUebUj9N/GFVjWH1pmqwLaKnpH1x2G40aDWw==

RSA sign and verify using OpenSSL
创建待签名的文件,公私钥

$ echo abcdefghijklmnopqrstuvwxyz > myfile.txt

生成512位的私钥,这个有点短,现在默认都是使用2048位的私钥了。

$ openssl genrsa -out myprivate.pem 512

从私钥里面生成/得到公钥匙.

$ openssl rsa -in myprivate.pem -pubout > mypublic.pem

查看私钥内容

$ cat myprivate.pem

-----BEGIN RSA PRIVATE KEY-----
MIIBOwIBAAJBAMv7Reawnxr0DfYN3IZbb5ih/XJGeLWDv7WuhTlie//c2TDXw/mW
914VFyoBfxQxAezSj8YpuADiTwqDZl13wKMCAwEAAQJAYaTrFT8/KpvhgwOnqPlk
NmB0/psVdW6X+tSMGag3S4cFid3nLkN384N6tZ+na1VWNkLy32Ndpxo6pQq4NSAb
YQIhAPNlJsV+Snpg+JftgviV5+jOKY03bx29GsZF+umN6hD/AiEA1ouXAO2mVGRk
BuoGXe3o/d5AOXj41vTB8D6IUGu8bF0CIQC6zah7LRmGYYSKPk0l8w+hmxFDBAex
IGE7SZxwwm2iCwIhAInnDbe2CbyjDrx2/oKvopxTmDqY7HHWvzX6K8pthZ6tAiAw
w+DJoSx81QQpD8gY/BXjovadVtVROALaFFvdmN64sw==
-----END RSA PRIVATE KEY-----

使用Openssl命令签名
Message digest algorithm : SHA1
Padding scheme : PCKS#1 v1.5
使用 sha1 摘要 PKCS1 填充 进行签名

$ openssl dgst -sha1 -sign myprivate.pem -out sha1.sign myfile.txt

查看二进制文件

$ hexdump sha1.sign

0000000 91 39 be 98 f1 6c f5 3d 22 da 63 cb 55 9b b0 6a
0000010 93 33 8d a6 a3 44 e2 8a 42 85 c2 da 33 fa cb 70
0000020 80 d2 6e 7a 09 48 37 79 a0 16 ee bc 20 76 02 fc
0000030 3f 90 49 2c 2f 2f b8 14 3f 0f e3 0f d8 55 59 3d
0000040
使用openssl 验签
Openssl decrypts the signature to generate hash and compares it to the hash of the input file.

$ openssl dgst -sha1 -verify mypublic.pem -signature sha1.sign myfile.txt

Verified OK
Referenced from:https://medium.com/@bn121rajesh/rsa-sign-and-verify-using-openssl-behind-the-scene-bf3cac0aade2

urlencode
golang下可以使用net/url模块实现urlencode和urldecode操作。
urlencode具体实现的函数为url.QueryEscape

urldecode具体实现的函数为url.QueryUnescape

另外 url.ParseRequestURI

QueryUnescape会处理+以及%20变成空格,ParseRequestURI只把%20变成空格

根据最新的RFC3986

1 URI中的字段只能空格编码成%20

golang使用ParseRequestURI来解析,符合标准

2 query string作为application/x-www-form-urlencoded的方式,可以编码成+,也可以编码成%20

URL中不管是URI还是query string,编码成%20

示例:

package main

import (
    "fmt"
    "net/url"
)

func main() {
    s := "this will be esc@ped!"
    fmt.Println("http://example.com/say?message="+url.QueryEscape(s))
}

输出结果
http://example.com/say?message=this+will+be+esc%40ped%21

表单编码

params := url.Values{}
params.Add("message", "this will be esc@ped!")
params.Add("author", "golang c@fe >.<")
fmt.Println("http://example.com/say?"+params.Encode())

直接上代码

package main
import (
    "fmt"
    "io/ioutil"
    "net/http"                                                                                                                                                     
    "os"
    "encoding/json"
)

func main() { //生成client 参数为默认
    client := &http.Client{}
    //生成要访问的url
    url := "http://somesite/somepath/"
    //提交请求
    reqest, err := http.NewRequest("GET", url, nil)

    //增加header选项
    reqest.Header.Add("Cookie", "xxxxxx")
    reqest.Header.Add("User-Agent", "xxx")
    reqest.Header.Add("X-Requested-With", "xxxx")

    if err != nil {
        panic(err)
    }   
    //处理返回结果
    response, _ := client.Do(reqest)
    defer response.Body.Close()
}

go http.client timeout

最简单的方式就是使用http.Client的 Timeout字段。 它的时间计算包括从连接(Dial)到读完response body。

c := &http.Client{  
    Timeout: 5 * time.Second,
}
resp, err := c.Get("https://const.net.cn/")