Security
Introduction
虽然互联网在设计时系统是可以抵挡代理人攻击, 它在相对信任的实体环境中成长。可是,这样安全的环境已经一去不复返。垃圾邮件(spam mail),拒绝服务攻击, 网络钓鱼等等,都表示使用互联网不在安全。
应用程序必须建立在恶劣的情况下也能"正常"工作, "正常"不在仅仅是程序功能的正确。必须保证隐私和数据的完整性。仅能是合法的用户才能访问数据。
这就使用你的程序变得复杂。这涉及到复杂和精细的计算问题。如果你自己尝写一个加密的库,这注定会失败。所以你需使用安全专家设计的库。
ISO security architecture
ISO OSI(open system interconnect) 它是众所周知的七层模型描述了分布式系统。如下所示
整个ISO体系结构有很多的文档,我们在这里,只关注ISO 的安全结构模型。ISO 7498-2
Functions and levels
一个安全系统的主要功能要求是:
- Authentication - 身份证明
- Data integrity - 数据的完整性,没有被篡改
- Confidentiality - 保密性,数据不能向它人透露
- Notarization/signature - 证书和签名
- Access controll
- Assurance/availability - 保证/可用性
分别对应OSI协议栈的不同级别
- Peer entity authentication - 对等实体鉴别(3,4,7)
- Data origin authentication - 数据源认证(3, 4, 7)
- Access control service(3, 4, 7)
- Connection confidentiality -连接的保密性(1,2,3,4,6,7)
- Connectionless confidentiality (1, 2, 3, 4, 6, 7)
- Selective field confidentiality - 选择字段加密(6,7)
- Traffic flow confidentiality - 通信流加密(1, 3, 7)
- Connection integrity with recovery - 恢复连接完整性(4, 7)
- Connection integrity without recovery - 不恢复连接完整性 (4, 7)
- Connection integrity selective field (7)
- Connectionless integrity selective field (7)
- Non-repudiation at origin -不可抵赖性源 (7)
- Non-repudiation of receipt - 不可抵赖接收(7)
Mechanisms
- Peer entity authentication
- encryption
- digital signature
- authentication exchange
- Data origin authentication
- encryption
- digital signature
- Access control service
- access control lists
- passwords
- capabilities lists
- labels
- Connection confidentiality
- ecryption
- routing control
- Connectionless confidelity
- encryption
- routing control
- Selective field confidelity
- encryption
- Traffic flow confidelity
- encryption
- traffic padding
- routing control
- Connection integrity with recovery
- encryption
- data integrity
- Connection integrity without recovery
- encryption
- data integrity
- Connection integrity selective field
- encryption
- data integrity
- Connectionless integrity
- encryption
- digital signature
- data integrity
- Connectionless integrity selective field
- encryption
- digital signature
- data integrity
- Non-repudiation at origin
- digital signature
- data integrity
- notarisation
- Non-repudiation of receipt
- digital signature
- data integrity
- notarisation
Data integrity
为了保证数据的完整性,意味着我们需要测试数据是否被篡改过. 通常是根据数据中的字节数,生成一个简单的数据。这个处理可以称为hashing(散列法), 生成的结果称为hash or hash value(散列值)
一个最天真的散列算法是数据中所有的字节的总和。可是,它允许改变数据内部,而依旧保持散列值。比如,一个攻击者交换了两个字节的位置。散列值没有被改变,但$65,536变成了$256
所以散列算法用于安全的目的时必须强键。所以它必须增加攻击者找到不同字节序列却有相同hash value的难度。这就使得攻击者难以修改数据。安全研究人员会经常测试hash算法,看它们是否被攻破这些算法——寻找到一种简单的方式来找到跟散列值相匹配的字节序列。他们已经设计出一种cryptographic(加密杂凑)的散列算法,这种算法被人为更加强键。
Go支持多种散列算法,包括MD4, MD5, RIPEMD-160, SHA1, SHA224, SHA256, SHA384以及SHA512. 它们都遵循相同的模式: 即调用相应hash package中的New()函数,返回一个Hash对像。
一个Hash是一个io.Writer, 你写入的数据会被散列到这个writer.你可以通过Size获得散列值的字节数,对过Sum获得散列值。
典型的是MD5散列。它使用md5 package. 它的散列值是一个16字节的数组。通过以16进制ASCII进行打印。每一个为4字节。以下是一个简单的程序
package main
import (
"crypto/md5"
"fmt"
)
func main() {
hash := md5.New()
bytes := []byte("hello\n")
hash.Write(bytes)
hashValue := hash.Sum(nil)
hashSize := hash.Size()
fmt.Printf("%T\n", hashValue)
fmt.Printf("%x \n", hashValue)
for n := 0; n < hashSize; n += 4 {
var val uint32
val = uint32(hashValue[n])<<24 +
uint32(hashValue[n+1])<<16 +
uint32(hashValue[n+2])<<8 +
uint32(hashValue[n+3])
fmt.Printf("%x ", val)
}
fmt.Println()
}
Output
[]uint8
b1946ac92492d2347c6235b4d2611184
b1946ac9 2492d234 7c6235b4 d2611184
有一个升级的版本,称为HMAC(Keyed-Hash Message Authentication Code 密钥散列消息认证码). 它可以给散列算法添加密钥。在使用时有一点不同,如下所示
func NewMD5(key []byte) hash.Hash
Symmetric key encryption对称密钥加密
对于数据的加密有两种主要的机制。第一种是在加密和解密时使用单个的key. 在这个加密和解密的代理之间也需要知道这个key. 如何在这些代理之间传输key我们就不在此讨论了。
与散列一样,同样存在主多的加密算法。许多的算法存在弱点,并且随着计算机越来越块,许多算法就变得超来超容易破解。Go支持多个对称密钥加密,比如Bolwfish and DES.
这些算法都是块算法(block). 即它们加密的数据都是块数据,如果你的数据没有对齐块大小block 8字节。则你需要在末尾填充格外的空白。
每一个算法都是创建一个Cipher对像,这通过每一个包的NewCipher方法,这个方法的参数为对称密钥.
一旦你有了cipher对像,你可以使用它来加密和解密块数据。对于blowfish来说,它们是8-bit的块。程序如下
package main
import (
"bytes"
"fmt"
"golang.org/x/crypto/blowfish"
)
func main() {
key := []byte("my key")
cipher, err := blowfish.NewCipher(key)
if err != nil {
fmt.Println(err.Error())
}
src := []byte("hello\n\n\n\n")
var enc [512]byte
cipher.Encrypt(enc[0:], src)
fmt.Println(enc)
var decrypt [8]byte
cipher.Decrypt(decrypt[0:], enc[0:])
result := bytes.NewBuffer(nil)
result.Write(decrypt[0:8])
fmt.Println(string(result.Bytes()))
}
Public key encryption
公钥加密和解密需要有两个密钥(key):一个用来加密,另一个用来解密。加密的密钥通常是公开的,任何人都可以使用它来加密信息,然后发送给你。解密密钥必须为私有的,否则其它人就可以解密你的数据.公钥系统是非对称的,不同的key用于不同的阶段。
在Go中支持多种公钥加密系统。一个典型的是RSA模式
以下程序生成一个RSA私钥和公钥
package main
import (
"crypto/rand"
"crypto/rsa"
"crypto/x509"
"encoding/gob"
"encoding/pem"
"fmt"
"os"
)
func main() {
reader := rand.Reader
bitSize := 512
key, err := rsa.GenerateKey(reader, bitSize)
checkError(err)
fmt.Println("Private key primes", key.Primes[0].String(), key.Primes[1].String())
fmt.Println("Private key exponent", key.D.String())
publicKey := key.PublicKey
fmt.Println("Public key modules", publicKey.N.String())
fmt.Println("public key exponent", publicKey.E)
saveGobKey("private.key", key)
saveGobKey("public.key", publicKey)
savePEMKey("private.pem", key)
}
func saveGobKey(filename string, key interface{}) {
outFile, err := os.Create(filename)
checkError(err)
encoder := gob.NewEncoder(outFile)
err = encoder.Encode(key)
outFile.Close()
}
func savePEMKey(fileName string, key *rsa.PrivateKey) {
outFile, err := os.Create(fileName)
checkError(err)
var privateKey = &pem.Block{Type: "RSA PRIVATE KEY",
Bytes: x509.MarshalPKCS1PrivateKey(key)}
pem.Encode(outFile, privateKey)
outFile.Close()
}
func checkError(err error) {
if err != nil {
fmt.Fprintf(os.Stderr, "Fatal error:%s", err.Error())
os.Exit(1)
}
}
在上面的程序中,我们使用gob序列化证书。然后可以使用以下的程序读取证书
//LoadRSAKeys
package main
import (
"crypto/rsa"
"encoding/gob"
"fmt"
"os"
)
func main() {
var key rsa.PrivateKey
loadKey("private.key", &key)
fmt.Println("Private key primes", key.Primes[0].String(), key.Primes[1].String())
fmt.Println("Private key exponent", key.D.String())
var publicKey rsa.PublicKey
loadKey("public.key", &publicKey)
fmt.Println("Public key modules", publicKey.N.String())
fmt.Println("Public key exponent", publicKey.E)
}
func loadKey(filename string, key interface{}) {
inFile, err := os.Open(filename)
checkError(err)
decoder := gob.NewDecoder(inFile)
err = decoder.Decode(key)
checkError(err)
inFile.Close()
}
func checkError(err error) {
if err != nil {
fmt.Println("Fatal error ", err.Error())
os.Exit(1)
}
}
X.509证书
公钥基础设施(public key infrastructure PKI)是一个框架,它集合了所有的公开密钥,以及额外的信息,比如所有者的名字,地址,和它们之间的审批机制。
当前PKI使用的是X.509认证. 比如, web浏览器使用它们来识别网站.
以下是一个例子,用来生成我们网站的X.509证书,并且存储为.cer文件
package main
import (
"crypto/rand"
"crypto/rsa"
"crypto/x509"
"crypto/x509/pkix"
"encoding/gob"
"encoding/pem"
"fmt"
"math/big"
"os"
"time"
)
func main() {
random := rand.Reader
var key rsa.PrivateKey
loadKey("private.key", &key)
now := time.Now()
then := now.Add(60 * 60 * 24 * 365 * 1000 * 1000 * 1000) //one year
template := x509.Certificate{
SerialNumber: big.NewInt(1),
Subject: pkix.Name{
CommonName: "jan.newmarch.name",
Organization: []string{"Jan Newmarch"},
},
//NotBefore: time.Unix(now, 0).UTC()
//NotAfter: time.Unix(now+60*60*24*365, 0).UTC(),
NotBefore: now,
NotAfter: then,
SubjectKeyId: []byte{1, 2, 3, 4},
KeyUsage: x509.KeyUsageCertSign | x509.KeyUsageKeyEncipherment | x509.KeyUsageDigitalSignature,
BasicConstraintsValid: true,
IsCA: true,
DNSNames: []string{"jan.newmarch.name", "localhost"},
}
derBytes, err := x509.CreateCertificate(random, &template, &template, &key.PublicKey, &key)
checkError(err)
certCerFile, err := os.Create("jan.newmarch.name.cer")
checkError(err)
certCerFile.Write(derBytes)
certCerFile.Close()
certPEMFile, err := os.Create("jan.newmarch.name.pem")
checkError(err)
pem.Encode(certPEMFile, &pem.Block{Type: "CERTIFICATE", Bytes: derBytes})
certPEMFile.Close()
keyPEMFile, err := os.Create("private.pem")
checkError(err)
pem.Encode(keyPEMFile, &pem.Block{Type: "RAS PRIVATE KEY",
Bytes: x509.MarshalPKCS1PrivateKey(&key),
})
keyPEMFile.Close()
}
func loadKey(filename string, key interface{}) {
inFile, err := os.Open(filename)
checkError(err)
decoder := gob.NewDecoder(inFile)
err = decoder.Decode(key)
checkErrorh(err)
inFile.Close()
}
func checkError(err error) {
if err != nil {
fmt.Println("Fatal error", err.Error())
os.Exit(1)
}
}
读取一个证书
package main
import (
"crypto/x509"
"fmt"
"os"
)
func main() {
certCerFile, err := os.Open("jan.newmarch.name.cer")
checkError(err)
derBytes := make([]byte, 1000)
count, err := certCerFile.Read(derBytes)
checkError(err)
certCerFile.Close()
//trim the bytes to actual length in call
cert, err := x509.ParseCertificate(derBytes[0:count])
checkError(err)
fmt.Printf("Name %s\n", cert.Subject.CommonName)
fmt.Printf("Not before %s \n", cert.NotBefore.String())
fmt.Printf("Not after %s \n", cert.NotAfter.String())
}
func checkError(err error) {
if err != nil {
fmt.Println("Fatal error", err.Error())
os.Exit(1)
}
}
TLS
很少自己动手使用加密和解密,因为这样做的话,工作会每繁重。一般在互联网上是直接使用TLS上传输信息(Transport Layer Security), 其前身为SSL(Secure Socket Layer)
在TLS中,客户端和服务器先商议使用X.509来标识身份。当这一步完成后,会在它们之间创建一个安全密钥。所有的加密和解密都使用这个密钥。这个商议的过程非常慢,但一旦创建,一个快速的私人密钥机制就可以使用了
package main
import (
"crypto/rand"
"crypto/tls"
"fmt"
"net"
"os"
"time"
)
func main() {
cert, err := tls.LoadX509KeyPair("jan.newmarch.name.pem", "private.pem")
checkError(err)
config := tls.Config{Certificates: []tls.Certificate{cert}}
now := time.Now()
config.Time = func() time.Time { return now }
config.Rand = rand.Reader
service := ":1200"
listener, err := tls.Listen("tcp", service, &config)
checkError(err)
fmt.Println("listening")
for {
conn, err := listener.Accept()
if err != nil {
fmt.Println(err.Error())
continue
}
fmt.Println("Accepted")
go handleClient(conn)
}
}
func handleClient(conn net.Conn) {
defer conn.Close()
var buf [512]byte
for {
fmt.Println("trying to read")
n, err := conn.Read(buf[0:])
if err != nil {
fmt.Println(err)
}
_, err2 := conn.Write(buf[0:n])
if err2 != nil {
return
}
}
}
func checkError(err error) {
if err != nil {
fmt.Println("Fatal Error ", err.Error())
os.Exit(1)
}
}
package main
import (
"crypto/tls"
"fmt"
"os"
)
func main() {
if len(os.Args) != 2 {
fmt.Println("usage: ", os.Args[0], "host:port")
os.Exit(1)
}
service := os.Args[1]
conn, err := tls.Dial("tcp", service, nil)
checkError(err)
for n := 0; n < 10; n++ {
fmt.Println("Writing...")
conn.Write([]byte("Hello " + string(n+48)))
var buf [512]byte
n, err := conn.Read(buf[0:])
checkError(err)
fmt.Println(string(buf[0:n]))
}
os.Exit(0)
}
func checkError(err error) {
if err != nil {
fmt.Println("Fatal error", err.Error())
os.Exit(1)
}
}
此程序会出现: Fatal error x509: certificate signed by unknow authority