golang http/https demo

HTTP Demo

client.go

package main

import (
	"fmt"
	"io/ioutil"
	"net/http"
	"strings"
	"time"
)

func Test(client *http.Client) error {
	body := "i am client"
	req, err := http.NewRequest(http.MethodPost, "http://fzz:joy@localhost:64896/test;word=222?id=1#frag", strings.NewReader(body))
	if err != nil {
		_ = fmt.Errorf("http new request fail, %s", err.Error())
		return fmt.Errorf("pub task err, build request failed %w", err)
	}
	req.Header["1header"] = []string{"head1", "head2"}
	req.Header["2header"] = []string{"head3", "head4"}
	resp, err := client.Do(req)
	if err != nil {
		fmt.Println("error:", err)
		return nil
	}
	defer resp.Body.Close()
	respBody, err := ioutil.ReadAll(resp.Body)
	if err != nil {
		fmt.Println(err)
	}
	fmt.Println(string(respBody))
	return nil
}

func main() {
	// init client
	client := http.Client{
		CheckRedirect: func(req *http.Request, via []*http.Request) error {
			return http.ErrUseLastResponse
		},
	}

	for {
		time.Sleep(3 * time.Second)
		err := Test(&client)
		if err != nil {
			_ = fmt.Errorf("err is %s", err)
		}
	}
}

server.go

package main

import (
	"fmt"
	"io/ioutil"
	"net/http"
)

func sayHelloName(w http.ResponseWriter, r *http.Request) {
	// 提取用户名 密码
	username, password, ok := r.BasicAuth()
	if ok {
		fmt.Println("req username:", username)
		fmt.Println("req password:", password)
	}

	// 获取原始的query参数
	fmt.Println("req raw query:", r.URL.RawQuery)

	// 获取method
	fmt.Println("req method:", r.Method)

	// 获取host
	fmt.Println("req host:", r.Host)

	// 获取访问的路径
	fmt.Println("req path:", r.URL.Path)

	// 获取Query参数
	var arg string
	values := r.URL.Query()
	arg=values.Get("id")
	fmt.Println("req query params -> id:", arg)

	// 获取Header
	fmt.Println("req.Header:", r.Header)

	// 获取body信息
	reqBody, err := ioutil.ReadAll(r.Body)
	if err != nil {
		fmt.Println(err)
	}
	fmt.Println("req.Body:", string(reqBody))

	_, _ = fmt.Fprintf(w, "{\"Hello\": \"Wrold\"}") //这个写入到w的是输出到客户端的
	fmt.Println("***********************************************")
}

func main() {
	// init server
	http.HandleFunc("/", sayHelloName) // 设置访问的路由
	err := http.ListenAndServe(":64896", nil) //设置监听的端口
	if err != nil {
		fmt.Errorf("http server error: %s", err)
		return
	}
}

HTTPS

⚠️ 从golang 1.15开始废弃 CommonName,因此推荐使用 SAN 证书。 如果想兼容之前的方式,需要设置环境变量 GODEBUG 为 x509ignoreCN=0。

“x509: certificate relies on legacy Common Name field, use SANs or temporarily enable Common Name matching with GODEBUG=x509ignoreCN=0”.

生成CA根证书

# 生成根CA证书的私钥 - Generate the private key of the root CA
$ openssl genrsa -out ca.key 2048
Generating RSA private key, 2048 bit long modulus
..............................................................................................................................+++
................+++
e is 65537 (0x10001)

# 生成自签名根 CA 证书 - Generate the self-signed root CA certificate
$ openssl req -new -x509 -days 3650 -key ca.key -out ca.pem
You are about to be asked to enter information that will be incorporated
into your certificate request.
What you are about to enter is what is called a Distinguished Name or a DN.
There are quite a few fields but you can leave some blank
For some fields there will be a default value,
If you enter '.', the field will be left blank.
-----
Country Name (2 letter code) []:cn
State or Province Name (full name) []:shanghai
Locality Name (eg, city) []:shanghai
Organization Name (eg, company) []:golang
Organizational Unit Name (eg, section) []:https
Common Name (eg, fully qualified host name) []:localhost
Email Address []:

$ ls
ca.key	ca.pem

配置openssl

默认安装的 OpenSSL 配置文件路径如下:

  • linux系统在 : /etc/pki/tls/openssl.cnf
  • Mac系统在: /System/Library/OpenSSL/openssl.cnf
  • Windows:安装目录下 openssl.cfg,比如 D:\Program Files\OpenSSL-Win64\bin\openssl.cfg

拷贝配置文件到当前目录下,然后修改如下:

  1. 找到 [ CA_default ],打开 copy_extensions = copy
  2. 找到[ req ],打开 req_extensions = v3_req # The extensions to add to a certificate request
  3. 找到[ v3_req ],添加 subjectAltName = @alt_names
  4. 添加新的标签 [ alt_names ] , 和标签字段
    [ alt_names ]
    DNS.1 = localhost
    DNS.2 = *.custer.fun
    
    这里填入需要加入到 Subject Alternative Names 段落中的域名名称,可以写入多个。

生成服务端证书 - Mac环境

# 生成服务端私钥
$ openssl genpkey -algorithm RSA -out server.key
..................................+++
.............................................+++

# 根据私钥和CA证书信息生成证书请求文件:server.csr
# 完成后,会生成一个名为 server.pem 的证书文件,同时还创建了一个 ca.srl 文件。
# 这里要注意的几个参数值要和生成CA证书所用的是一样的
$ openssl req -new -nodes -key server.key -out server.csr -days 3650 -subj "/C=cn/OU=https/O=golang/CN=localhost" -config ./openssl.cnf -extensions v3_req

# 根据证书请求文件生成服务端证书,并签名认证
$ openssl x509 -req -days 3650 -in server.csr -out server.pem -CA ca.pem -CAkey ca.key -CAcreateserial -extfile ./openssl.cnf -extensions v3_req
Signature ok
subject=/C=cn/OU=https/O=golang/CN=localhost
Getting CA Private Key

$ ls
ca.key		ca.pem		ca.srl		openssl.cnf	server.csr	server.key	server.pem
  • server.csr: 是上面生成的CA证书的请求文件,ca.pem/ca.key是CA证书文件和私钥,用来对server.csr进行签名认证
  • server.key: 服务端私钥
  • server.pem: 经过CA证书签名后的服务端证书
  • ca.srl: 是CA使用的序列号文件,当使用"-CA"选项来签名时,它将会使用某个文件中指定的序列号来唯一标识此次签名后的证书文件。这个序列号文件的内容仅只有一行,这一行的值为16进制的数字,当某个序列号被使用后,该文件中的序列号将自动增加。

生成客户端证书 - 同理服务端

$ openssl genpkey -algorithm RSA -out client.key
...........................+++
.....+++

$ openssl req -new -nodes -key client.key -out client.csr -days 3650 -subj "/C=cn/OU=https/O=golang/CN=localhost" -config ./openssl.cnf -extensions v3_req

$ ls
ca.key		ca.pem		ca.srl		client.csr	client.key	openssl.cnf	server.csr	server.key	server.pem

$ cat ca.srl
CE137EED81F35C23

$ openssl x509 -req -days 3650 -in client.csr -out client.pem -CA ca.pem -CAkey ca.key -CAcreateserial -extfile ./openssl.cnf -extensions v3_req
Signature ok
subject=/C=cn/OU=https/O=golang/CN=localhost
Getting CA Private Key

# ca证书加签一次,序号+1
$ cat ca.srl
CE137EED81F35C24

$ ls
ca.key		ca.pem		ca.srl		client.csr	client.key	client.pem	openssl.cnf	server.csr	server.key	server.pem

Demo 测试

  • 双向认证
    在这里插入图片描述

  • client

package main

import (
	"crypto/tls"
	"crypto/x509"
	"fmt"
	"io/ioutil"
	"net/http"
)

func main() {
	clientCertFile := "client.pem"
	clientKeyFile := "client.key"
	caCertFile := "ca.pem"
	var cert tls.Certificate
	var err error
	if clientCertFile != "" && clientKeyFile != "" {
		cert, err = tls.LoadX509KeyPair(clientCertFile, clientKeyFile)
		if err != nil {
			fmt.Printf("error creating x509 keypair from client cert file %s and client key file %s", clientCertFile, clientKeyFile)
		}
	}
	caCert, err := ioutil.ReadFile(caCertFile)
	if err != nil {
		fmt.Printf("Error opening cert file %s, Error: %s", caCertFile, err)
	}
	caCertPool := x509.NewCertPool()
	caCertPool.AppendCertsFromPEM(caCert)

	tr := &http.Transport{
		TLSClientConfig: &tls.Config{
			Certificates: []tls.Certificate{cert},
			RootCAs:      caCertPool,
		},
	}

	client := &http.Client{Transport: tr}
	resp, err := client.Get("https://localhost:8869")

	if err != nil {
		fmt.Println("error:", err)
		return
	}
	defer resp.Body.Close()
	body, err := ioutil.ReadAll(resp.Body)
	if err != nil {
		fmt.Println(err)
	}
	fmt.Println(string(body))
}
  • server
package main

import (
	"fmt"
	"net/http"
)

func handler(w http.ResponseWriter, r *http.Request) {
	fmt.Fprintf(w,
		"Hi, This is an example of https service in golang!")
}

func main() {
	http.HandleFunc("/", handler)
	http.ListenAndServeTLS(":8869", "server.pem", "server.key", nil)
}

curl -kv https://localhost:8869

查看证书内容

使用cat指令也能查看,但信息不够全面

$ openssl x509 -in ca.pem -text

参考

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值