1. 程式人生 > >Go實戰--golang中使用HTTPS以及TSL(.crt、.key、.pem區別以及crypto/tls包介紹)

Go實戰--golang中使用HTTPS以及TSL(.crt、.key、.pem區別以及crypto/tls包介紹)

HTTP與HTTPS

在WWDC 2016上,蘋果在釋出iOS 9的同時也向開發者傳遞了一個訊息,那就是到2017年1月1日時App Store中所有應用都必須啟用 App Transport Security應用程式安全傳輸協議,從而提升應用和系統安全性。

HTTPS是Hyper Text Transfer Protocol Secure的縮寫,相比http,多了一個secure,這一個secure是怎麼來的呢?這是由TLS(SSL)提供的。

https和http都屬於應用層,基於TCP(以及UDP)協議。但是不同的是: 
HTTP 預設工作在TCP協議80埠 
HTTPS預設工作在TCP協議443埠

通俗一句話:相比http,https對於大部分人來說,意味著比較安全。

TLS

安全傳輸層協議(TLS)用於在兩個通訊應用程式之間提供保密性和資料完整性。 
The TLS/SSL is a public/private key infrastructure (PKI). For most common cases, each client and server must have a private key.

TLS與SSL的差異

SSLv2 and SSLv3 are completely different (and both are now considered insecure). SSLv3 and TLSv1.0 are very similar, but have a few differences.

You could consider TLSv1.0 as SSLv3.1

  • 版本號:TLS記錄格式與SSL記錄格式相同,但版本號的值不同,TLS的版本1.0使用的版本號為SSLv3.1。

  • 報文鑑別碼:SSLv3.0和TLS的MAC演算法及MAC計算的範圍不同。TLS使用了RFC-2104定義的HMAC演算法。SSLv3.0使用了相似的演算法,兩者差別在於SSLv3.0中,填充位元組與金鑰之間採用的是連線運算,而HMAC演算法採用的是異或運算。但是兩者的安全程度是相同的。

  • 偽隨機函式:TLS使用了稱為PRF的偽隨機函式來將金鑰擴充套件成資料塊,是更安全的方式。

  • 報警程式碼:TLS支援幾乎所有的SSLv3.0報警程式碼,而且TLS還補充定義了很多報警程式碼,如解密失敗(decryption_failed)、記錄溢位(record_overflow)、未知CA(unknown_ca)、拒絕訪問(access_denied)等。

  • 密文族和客戶證書:SSLv3.0和TLS存在少量差別,即TLS不支援Fortezza金鑰交換、加密演算法和客戶證書。

  • certificate_verify和finished訊息:SSLv3.0和TLS在用certificate_verify和finished訊息計算MD5和SHA-1雜湊碼時,計算的輸入有少許差別,但安全性相當。

  • 加密計算:TLS與SSLv3.0在計算主密值(master secret)時採用的方式不同。

  • 填充:使用者資料加密之前需要增加的填充位元組。在SSL中,填充後的資料長度要達到密文塊長度的最小整數倍。而在TLS中,填充後的資料長度可以是密文塊長度的任意整數倍(但填充的最大長度為255位元組),這種方式可以防止基於對報文長度進行分析的攻擊。

openssl 
openssl(www.openssl.org) 是sslv2,sslv3,tlsv1的一份完整實現,內部包含了大量加密演算法程式.其命令列提供了豐富的加密,驗證,證書生成等功能,甚至可以用其建立 一個完整的CA.與其同時,它也提供了一套完整的庫函式,可用開發用SSL/TLS的通訊程式.

插曲: 
2016年10月18日,錘子科技CEO羅永浩在錘子手機發佈會上,宣佈將200多萬元門票收入,以及原計劃成立的 Smartisan 公益基金近100萬元,全部捐贈給 OpenSSL 基金會和 OpenBSD 基金會。

crt、key以及pem的區別以及生成

crt — Alternate synonymous most common among *nix systems .pem (pubkey).

csr — Certficate Signing Requests (synonymous most common among *nix systems).

cer — Microsoft alternate form of .crt, you can use MS to convert .crt to .cer (DER encoded .cer, or base64[PEM] encoded cer).

pem = The PEM extension is used for different types of X.509v3 files which contain ASCII (Base64) armored data prefixed with a «—– BEGIN …» line. These files may also bear the cer or the crt extension.

der — The DER extension is used for binary DER encoded certificates.

證書(Certificate) .cer .crt 
私鑰(Private Key).key 
證書籤名請求(Certificate sign request) .csr 
至於pem和der,是編碼方式,以上三類均可以使用這兩種編碼方式,因此.pem和.der(少見)不一定是以上三種(Cert,Key,CSR)中的某一種

PEM - Privacy Enhanced Mail,開啟看文字格式,以”—–BEGIN…”開頭, “—–END…”結尾,內容是BASE64編碼. 
檢視PEM格式證書的資訊:openssl x509 -in certificate.pem -text -noout 
Apache和*NIX伺服器偏向於使用這種編碼格式.

DER - Distinguished Encoding Rules,開啟看是二進位制格式,不可讀. 
檢視DER格式證書的資訊:openssl x509 -in certificate.der -inform der -text -noout 
Java和Windows伺服器偏向於使用這種編碼格式.

x509 
X.509是一種非常通用的證書格式。所有的證書都符合ITU-T X.509國際標準,因此(理論上)為一種應用建立的證書可以用於任何其他符合X.509標準的應用。

x509證書一般會用到三類文,key,csr,crt。 
Key 是私用金鑰openssl格,通常是rsa演算法。 
Csr 是證書請求檔案,用於申請證書。在製作csr檔案的時,必須使用自己的私鑰來簽署申,還可以設定一個金鑰。 
crt是CA認證後的證書文,(windows下面的,其實是crt),簽署人用自己的key給你簽署的憑證。

生成.key 
rsa演算法:

openssl genrsa -out server.key 2048

ECDSA演算法:

openssl ecparam -genkey -name secp384r1 -out server.key

生成.crt

openssl req -new -x509 -sha256 -key server.key -out server.crt -days 3650

需要輸入一些資訊:

openssl req -new -x509 -sha256 -key server.key -out server.crt -days 3650
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) [AU]:cn
State or Province Name (full name) [Some-State]:Beijing
Locality Name (eg, city) []:Beijing
Organization Name (eg, company) [Internet Widgits Pty Ltd]:wangshubo
Organizational Unit Name (eg, section) []:wangshubo
Common Name (e.g. server FQDN or YOUR name) []:wangshubo
Email Address []:[email protected]

生成pem和key

openssl req -new -nodes -x509 -out server.pem -keyout server.key -days 3650 -subj "/C=DE/ST=NRW/L=Earth/O=Random Company/OU=IT/CN=www.random.com/[email protected]"

crypto/tls包介紹

golang中為我們提供了tls包: 
Package tls partially implements TLS 1.2, as specified in RFC 5246.

func LoadX509KeyPair

func LoadX509KeyPair(certFile, keyFile string) (Certificate, error)

LoadX509KeyPair reads and parses a public/private key pair from a pair of files. The files must contain PEM encoded data. The certificate file may contain intermediate certificates following the leaf certificate to form a certificate chain. On successful return, Certificate.Leaf will be nil because the parsed form of the certificate is not retained.

type Config 
A Config structure is used to configure a TLS client or server. After one has been passed to a TLS function it must not be modified. A Config may be reused; the tls package will also not modify it.

type Config struct {

        Rand io.Reader

        Time func() time.Time

        Certificates []Certificate

        NameToCertificate map[string]*Certificate

        GetCertificate func(*ClientHelloInfo) (*Certificate, error)

        GetClientCertificate func(*CertificateRequestInfo) (*Certificate, error)

        GetConfigForClient func(*ClientHelloInfo) (*Config, error)

        VerifyPeerCertificate func(rawCerts [][]byte, verifiedChains [][]*x509.Certificate) error

        RootCAs *x509.CertPool

        NextProtos []string

        ServerName string

        ClientAuth ClientAuthType

        ClientCAs *x509.CertPool

        InsecureSkipVerify bool

        CipherSuites []uint16

        PreferServerCipherSuites bool

        SessionTicketsDisabled bool

        SessionTicketKey [32]byte

        ClientSessionCache ClientSessionCache

        MinVersion uint16

        MaxVersion uint16

        CurvePreferences []CurveID

        DynamicRecordSizingDisabled bool

        Renegotiation RenegotiationSupport

        KeyLogWriter io.Writer
}

這裡主要關注一下Certificates,是我們要用到的。

func Listen

func Listen(network, laddr string, config *Config) (net.Listener, error)

Listen creates a TLS listener accepting connections on the given network address using net.Listen. The configuration config must be non-nil and must include at least one certificate or else set GetCertificate.

func Dial

func Dial(network, addr string, config *Config) (*Conn, error)

Dial connects to the given network address using net.Dial and then initiates a TLS handshake, returning the resulting TLS connection. Dial interprets a nil configuration as equivalent to the zero configuration; see the documentation of Config for the defaults.

應用

// Copyright 2009 The Go Authors. All rights reserved.
// Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file.

// Generate a self-signed X.509 certificate for a TLS server. Outputs to
// 'cert.pem' and 'key.pem' and will overwrite existing files.

package main

import (
    "crypto/ecdsa"
    "crypto/elliptic"
    "crypto/rand"
    "crypto/rsa"
    "crypto/x509"
    "crypto/x509/pkix"
    "encoding/pem"
    "flag"
    "fmt"
    "log"
    "math/big"
    "os"
    "time"
)

var (
    emailAddress = flag.String("email-address", "", "The email address of the user you wish to create the certificate for")
    validFrom    = flag.String("start-date", "", "Creation date formatted as Jan 1 15:04:05 2011")
    validFor     = flag.Duration("duration", 365*24*time.Hour, "Duration that certificate is valid for")
    isCA         = flag.Bool("ca", false, "whether this cert should be its own Certificate Authority")
    rsaBits      = flag.Int("rsa-bits", 2048, "Size of RSA key to generate. Ignored if --ecdsa-curve is set")
    ecdsaCurve   = flag.String("ecdsa-curve", "", "ECDSA curve to use to generate a key. Valid values are P224, P256, P384, P521")
)

func publicKey(priv interface{}) interface{} {
    switch k := priv.(type) {
    case *rsa.PrivateKey:
        return &k.PublicKey
    case *ecdsa.PrivateKey:
        return &k.PublicKey
    default:
        return nil
    }
}

func pemBlockForKey(priv interface{}) *pem.Block {
    switch k := priv.(type) {
    case *rsa.PrivateKey:
        return &pem.Block{Type: "RSA PRIVATE KEY", Bytes: x509.MarshalPKCS1PrivateKey(k)}
    case *ecdsa.PrivateKey:
        b, err := x509.MarshalECPrivateKey(k)
        if err != nil {
            fmt.Fprintf(os.Stderr, "Unable to marshal ECDSA private key: %v", err)
            os.Exit(2)
        }
        return &pem.Block{Type: "EC PRIVATE KEY", Bytes: b}
    default:
        return nil
    }
}

func main() {
    flag.Parse()

    if len(*emailAddress) == 0 {
        log.Fatalf("Missing required --email-address parameter")
    }

    var priv interface{}
    var err error
    switch *ecdsaCurve {
    case "":
        priv, err = rsa.GenerateKey(rand.Reader, *rsaBits)
    case "P224":
        priv, err = ecdsa.GenerateKey(elliptic.P224(), rand.Reader)
    case "P256":
        priv, err = ecdsa.GenerateKey(elliptic.P256(), rand.Reader)
    case "P384":
        priv, err = ecdsa.GenerateKey(elliptic.P384(), rand.Reader)
    case "P521":
        priv, err = ecdsa.GenerateKey(elliptic.P521(), rand.Reader)
    default:
        fmt.Fprintf(os.Stderr, "Unrecognized elliptic curve: %q", *ecdsaCurve)
        os.Exit(1)
    }
    if err != nil {
        log.Fatalf("failed to generate private key: %s", err)
    }

    var notBefore time.Time
    if len(*validFrom) == 0 {
        notBefore = time.Now()
    } else {
        notBefore, err = time.Parse("Jan 2 15:04:05 2006", *validFrom)
        if err != nil {
            fmt.Fprintf(os.Stderr, "Failed to parse creation date: %s\n", err)
            os.Exit(1)
        }
    }

    notAfter := notBefore.Add(*validFor)

    serialNumberLimit := new(big.Int).Lsh(big.NewInt(1), 128)
    serialNumber, err := rand.Int(rand.Reader, serialNumberLimit)
    if err != nil {
        log.Fatalf("failed to generate serial number: %s", err)
    }

    template := x509.Certificate{
        SerialNumber: serialNumber,
        Subject: pkix.Name{
            Organization: []string{"Acme Co"},
        },
        NotBefore: notBefore,
        NotAfter:  notAfter,

        KeyUsage: x509.KeyUsageKeyEncipherment | x509.KeyUsageDigitalSignature,
        ExtKeyUsage: []x509.ExtKeyUsage{x509.ExtKeyUsageServerAuth,
            x509.ExtKeyUsageClientAuth},
        BasicConstraintsValid: true,
    }
    template.DNSNames = append(template.DNSNames, "localhost")
    template.EmailAddresses = append(template.EmailAddresses, *emailAddress)

    if *isCA {
        template.IsCA = true
        template.KeyUsage |= x509.KeyUsageCertSign
    }

    derBytes, err := x509.CreateCertificate(rand.Reader, &template, &template, publicKey(priv), priv)
    if err != nil {
        log.Fatalf("Failed to create certificate: %s", err)
    }

    certOut, err := os.Create("cert.pem")
    if err != nil {
        log.Fatalf("failed to open cert.pem for writing: %s", err)
    }
    pem.Encode(certOut, &pem.Block{Type: "CERTIFICATE", Bytes: derBytes})
    certOut.Close()
    log.Print("written cert.pem\n")

    keyOut, err := os.OpenFile("key.pem", os.O_WRONLY|os.O_CREATE|os.O_TRUNC, 0600)
    if err != nil {
        log.Print("failed to open key.pem for writing:", err)
        return
    }
    pem.Encode(keyOut, pemBlockForKey(priv))
    keyOut.Close()
    log.Print("written key.pem\n")
}

golang中使用HTTPS

package main

import (
    "log"
    "net/http"
)

func HelloServer(w http.ResponseWriter, req *http.Request) {
    w.Header().Set("Content-Type", "text/plain")
    w.Write([]byte("This is an example using https in golang.\n"))
}

func main() {
    http.HandleFunc("/hello", HelloServer)
    err := http.ListenAndServeTLS(":443", "server.crt", "server.key", nil)
    if err != nil {
        log.Fatal("ListenAndServe: ", err)
    }
}

golang中使用tls 
server.go

package main

import (
    "bufio"
    "crypto/tls"
    "log"
    "net"
)

func main() {
    log.SetFlags(log.Lshortfile)

    cer, err := tls.LoadX509KeyPair("server.crt", "server.key")
    if err != nil {
        log.Println(err)
        return
    }

    config := &tls.Config{Certificates: []tls.Certificate{cer}}
    ln, err := tls.Listen("tcp", ":443", config)
    if err != nil {
        log.Println(err)
        return
    }
    defer ln.Close()

    for {
        conn, err := ln.Accept()
        if err != nil {
            log.Println(err)
            continue
        }
        go handleConnection(conn)
    }
}

func handleConnection(conn net.Conn) {
    defer conn.Close()
    r := bufio.NewReader(conn)
    for {
        msg, err := r.ReadString('\n')
        if err != nil {
            log.Println(err)
            return
        }

        println(msg)

        n, err := conn.Write([]byte("jude\n"))
        if err != nil {
            log.Println(n, err)
            return
        }
    }
}

client.go

package main

import (
    "crypto/tls"
    "log"
)

func main() {
    log.SetFlags(log.Lshortfile)

    conf := &tls.Config{
        InsecureSkipVerify: true,
    }

    conn, err := tls.Dial("tcp", "127.0.0.1:443", conf)
    if err != nil {
        log.Println(err)
        return
    }
    defer conn.Close()

    n, err := conn.Write([]byte("hi\n"))
    if err != nil {
        log.Println(n, err)
        return
    }

    buf := make([]byte, 100)
    n, err = conn.Read(buf)
    if err != nil {
        log.Println(n, err)
        return
    }

    println(string(buf[:n]))
}

這裡編寫client程式碼時候需要注意:InsecureSkipVerify: true 
也就是說上面的程式碼中客戶端不對服務端的證書進行驗證。 
go實現的Client端預設也是要對服務端傳過來的數字證書進行校驗的,但客戶端提示:這個證書是由不知名CA簽發的! 

相關推薦

Go實戰--golang使用HTTPS以及TSL(.crt.key.pem區別以及crypto/tls介紹)

HTTP與HTTPS 在WWDC 2016上,蘋果在釋出iOS 9的同時也向開發者傳遞了一個訊息,那就是到2017年1月1日時App Store中所有應用都必須啟用 App Transport Security應用程式安全傳輸協議,從而提升應用和系統安全性。 HTTP

Go實戰--golang檔案以及資料夾路徑相關操作

生命不止,繼續 go go go!!! 之前介紹過golang的標準庫:path/filepath, os 今天就跟大家分享幾個關於檔案以及資料夾的相關操作。 獲取目錄中所有檔案 使用包: io/ioutil 使用方法: ioutil.Re

Go實戰--golang讀寫檔案的幾種方式

讀寫檔案應該是在開發過程中經常遇到的,今天要跟大家一起分享的就是在golang的世界中,如何讀寫檔案。 使用io/ioutil進行讀寫檔案 其中提到了兩個方法: func ReadFile func ReadFile(filename string) ([]by

Go實戰 golang生成讀取二維碼 skip2/go qrcode和boombuler/barcode

                     生命不止,繼續go go go!!!這裡介紹一下,golang如何生成二維碼,當然是面向github程式設計了。QRCode百度百科: QR Code碼,是由Denso公司於1994年9月研製的一種矩陣二維碼符號,它具有一維條碼及其它二維條碼所具有的資訊容量大、可靠性

Go實戰 golang使用WebSocket實時聊天室 gorilla/websocket nkovacs/go s

                     生命不止,繼續 go go go!!!其實,早就應該跟大家分享golang中關於websocket的使用,但是一直不知道從何入手,也不能夠很清晰的描述出來。今天就淺嘗輒止,通過第三方庫實現websocket。WebSocketWebSocket協議是基於TCP的一種新

Go實戰--golang操作PDF(rsc.io/pdfjung-kurt/gofpdfsignintech/gopdf)

生命不止,繼續 go go go !!! 那麼今天就跟大家分享一下,golang中如何操作PDF。 PDF簡介 The Portable Document Format (PDF) is a file format used to present do

Go實戰--golang使用JWT(JSON Web Token)

今天就來跟大家簡單介紹一下golang中如何使用token,當然是要依賴一下github上的優秀的開源庫了。 首先,要搞明白一個問題,token、cookie、session的區別。 token、cookie、session的區別Cookie  Cookie總是儲存在客戶端中,按在

Go實戰--golang使用echo框架MongoDBJWT搭建REST API(labstack/echogopkg.in/mgo.v2dgrijalva/jwt-go)

生命不止,繼續go go go !!! 今天,繼續echo框架,這次加入mongodb作為持久化儲存,使用jwt進行驗證,來搭建一套rest api,類似Twitter。 程式碼結構: ./model post.go user.

Go實戰--golang使用firebase實時資料庫(zabawaba99/firego)

生命不止,繼續 go go go !!! 今天,就跟大家一起學習分享一下golang中如何使用firebase的實時資料庫。 Firebase 實時資料庫 利用我們的 NoSQL 雲端資料庫儲存和同步資料。資料會跨所有客戶端進行實時同步,無論您的應用是

Go實戰--golang使用Goji微框架(Goji+Mongodb構建微服務)

生命不止,繼續 go go go!!! 今天跟大家分享一個web微框架Goji. Goji What is Goji? 枸杞? Goji is a HTTP request multiplexer, similar to net/http.Serv

Go實戰--golang使用redis(redigo和go-redis/redis)

生命不止,繼續 go go go !!! 今天跟大家分享的是如何在golang中使用redis資料庫。 何為redis Redis is an in-memory database open-source software project impl

Go實戰--golang使用ssl連線MongoDB(mgo)

生命不止,繼續 go go go!!! 文中主要介紹了Windows下如何安裝mongodb,mongodb的簡單命令,golang如何操作mongodb,以及使用golang+mongodb建立的微服務。 今天繼續深深耕一點。 Windows下mon

Go實戰--golang上傳檔案到七牛雲物件儲存(github.com/qiniu/api.v7)

生命不止,繼續 go go go !!! 在國內,七牛絕對是golang的領導者。 七牛雲 關於七牛: (七牛雲)隸屬於上海七牛資訊科技有限公司,七牛雲是國內領先的企業級雲服務商,專注於以資料管理為中心的雲端計算業務研發和運營,圍繞富媒體場景推出了物件儲

js解析json時候的eval和$.parseJSON()的區別以及JSON.stringify()

對話 {} alert tle 用戶 sdn clas div 轉換成 1.第一個區別是:安全性 json格式非常受歡迎,而解析json的方式通常用JSON.parse()但是eval()方法也可以解析,這兩者之間有什麽區別呢? JSON.parse

golang array,slice,map 三個的理解和區別

array array是由[n]<byte>定義,其中的n標識array的長度,而<type>標示希望儲存的型別。對array的賦值或索引是由方括號完成的: var arr [10]int arr[0] = 21 arr[1] =

AndroidHttps通訊實現_中間人攻擊DNS欺騙和會話劫持

上一篇文章記述了在Android中使用Https進行單向認證的配置,但單向認證存在中嚴重的安全漏洞,其中最容易受到中間人攻擊和DNS欺騙以及會話劫持,本文主要講述進行中間人攻擊、DNS欺騙和會話劫持的方式。 概覽 什麼是中間人攻擊 模擬中間人攻擊

實戰c++的string系列--string與char*const char *的轉換(data() or c_str())

在工程中,我們也有很多時候用到string與char*之間的轉換,這裡有個一我們之前提到的函式 c_str(),看看這個原型: const char *c_str(); c_str()函式返回一個指向正規C字串的指標, 內容與本string串相同. 這

實戰c++的string系列--string的替換查詢(一些與路徑相關的操作)

今天繼續寫一些string操作。 string給我們提供了很多的方法,但是每在使用的時候,就要費些周折。 場景1: 得到一個std::string full_path = “D:\program files\csdn”,但是我想得到”D:\program

HSSFXSSF和SXSSF區別以及Excel導出優化

基本 per springmvc linu cell exce 功能 pch 臨時文件   之前有寫過運用POI的HSSF方式導出數據到Excel(見:springMVC中使用POI方式導出excel至客戶端、服務器實例),但這種方式當數據量大到一定程度時容易出現內存溢出等

SSL/TLS溫故知新-證書格式簡介(PEMDERPFXJKSKDBCERKEYCSRCRTCRL )

一.SSl證書格式簡介 (1)常見的格式有: PEM、JKS、CRT、PFX 等。 PEM - 它是 openssl 預設採用的資訊存放方式。Openssl 中的 PEM 檔案 JKS - keytool工具生成的的檔案轉換成的JKS格式證書。JKS證書主要用於java級系