OT 安全和IEC62443标准
与IT相比,工业控制系统(ICS)的安全性也称为OT安全性,它始于诸如电力,天然气和水等关键基础设施。随着向开放系统的发展以及与IIoT和工业4.0趋势一致的威胁变化,各个领域的工作正全面展开。据说Stuxnet在2010年成为了全球触发者。在过去的十年中,各个国家和行业都积极制定了指南和框架,但是它们似乎缺乏连贯性。最近,已经整合了多个指南,可以说作为全球标准的两个引人注目的标准是IEC62443。从智能工厂的安全性角度来看,还有NIST CSF,SP800系列。
IEC 62443。这个标准是ISA99和IEC委员会共同开发的。IEC62443是一组14个文档,提供了通用工业控制系统(IACS:在本标准中称为工业自动化控制系统)的安全技术规范。该标准是由国际自动化学会(ISA)和国际电工委员会(IEC)制定的,迄今为止已发布14个文档中的8个,并且正在进行修订和开发。我粗略地看了一下,好像蛮复杂的样子。
信息安全的基本知识
结合IEC61499 分布式功能块技术,最近通过协议分析,发现施耐德公司EAE 中底层协议使用了websocket,hash 摘要和加密数据等。估计EAE采用的还是相对简单的,hash 数字摘要+AES对称加密,是否有证书机制,没有看出来。AES 的加密key 也不知道。按说,作为一个开放性系统,加密方式也需要Open 的。要不然,不同厂商额设备和软件工具无法兼容。
在这里,了解一些关于openSSL,TLS 基本的信息安全技术。为IEC61499 安全平台做一些准备工作。
信息安全问题
在信息安全性问题中,我们常常要做到三点才能保证信息的安全:
- 信息的保密性
- 信息的完整性
- 身份识别
SSL(Secure Socket Layer)代表安全套接字层,而TLS(Transport Layer Security)代表传输层安全性。安全套接字层和传输层安全性都是用于在Web浏览器和Web服务器之间提供安全性的协议。
三项基础技术
散列函数Hash
常见的有 MD5、SHA1、SHA256,该类函数特点是函数单向不可逆、对输入非常敏感、输出长度固定,针对数据的任何修改都会改变散列函数的结果,用于防止信息篡改并验证数据的完整性;
在信息传输过程中,散列函数不能单独实现信息防篡改,因为明文传输,中间人可以修改信息之后重新计算信息摘要,因此需要对传输的信息以及信息摘要进行加密;
hash 函数产生的hash 值称为文本的数字摘要。如果接收端计算出来的数字摘要和发送端发送来的数字摘要是一致的,说明数据是完整的没有被修改。这有点类似于CRC 校验码,只是当文本很长的时候,简单地使用CRC 码无法完全判断文本是否完整。
Go 程序
package main
import "crypto/sha1"
import "fmt"
func main() {
s := "Hello 8gwifi.org"
// The pattern for generating a hash is `sha1.New()`,
// `sha1.Write(bytes)`, then `sha1.Sum([]byte{})`.
// Here we start with a new hash.
h := sha1.New()
// `Write` expects bytes. If you have a string `s`,
// use `[]byte(s)` to coerce it to bytes.
h.Write([]byte(s))
// This gets the finalized hash result as a byte
// slice. The argument to `Sum` can be used to append
// to an existing byte slice: it usually isn't needed.
bs := h.Sum(nil)
// SHA1 values are often printed in hex, for example
// in git commits. Use the `%x` format verb to convert
// a hash results to a hex string.
fmt.Println(s)
fmt.Printf("%x\n", bs)
}
对称加密
常见的有 AES-CBC、DES、3DES、AES-GCM等,相同的密钥可以用于信息的加密和解密,掌握密钥才能获取信息,能够防止信息窃听,通信方式是1对1;
对称加密的优势是信息传输1对1,需要共享相同的密码,密码的安全是保证信息安全的基础,服务器和 N 个客户端通信,需要维持 N 个密码记录,且缺少修改密码的机制;
对称加密的例子,参考下列地址
Go Encryption and Decryption using AES - Tutorial
加密程序
package main
import (
"crypto/aes"
"crypto/cipher"
"crypto/rand"
"fmt"
"io/ioutil"
"io"
)
func main() {
fmt.Println("Encryption Program v0.01")
text := []byte("My Super Secret Code Stuff")
key := []byte("passphrasewhichneedstobe32bytes!")
// generate a new aes cipher using our 32 byte long key
c, err := aes.NewCipher(key)
// if there are any errors, handle them
if err != nil {
fmt.Println(err)
}
// gcm or Galois/Counter Mode, is a mode of operation
// for symmetric key cryptographic block ciphers
// - https://en.wikipedia.org/wiki/Galois/Counter_Mode
gcm, err := cipher.NewGCM(c)
// if any error generating new GCM
// handle them
if err != nil {
fmt.Println(err)
}
// creates a new byte array the size of the nonce
// which must be passed to Seal
nonce := make([]byte, gcm.NonceSize())
// populates our nonce with a cryptographically secure
// random sequence
if _, err = io.ReadFull(rand.Reader, nonce); err != nil {
fmt.Println(err)
}
// here we encrypt our text using the Seal function
// Seal encrypts and authenticates plaintext, authenticates the
// additional data and appends the result to dst, returning the updated
// slice. The nonce must be NonceSize() bytes long and unique for all
// time, for a given key.
fmt.Println(gcm.Seal(nonce, nonce, text, nil))
// the WriteFile method returns an error if unsuccessful
err = ioutil.WriteFile("myfile.data", gcm.Seal(nonce, nonce, text, nil), 0777)
// handle this error
if err != nil {
// print it out
fmt.Println(err)
}
}
解密程序
package main
import (
"crypto/aes"
"crypto/cipher"
"fmt"
"io/ioutil"
)
func main() {
fmt.Println("Decryption Program v0.01")
key := []byte("passphrasewhichneedstobe32bytes!")
ciphertext, err := ioutil.ReadFile("./myfile.data")
// if our program was unable to read the file
// print out the reason why it can't
if err != nil {
fmt.Println(err)
}
c, err := aes.NewCipher(key)
if err != nil {
fmt.Println(err)
}
gcm, err := cipher.NewGCM(c)
if err != nil {
fmt.Println(err)
}
nonceSize := gcm.NonceSize()
if len(ciphertext) < nonceSize {
fmt.Println(err)
}
nonce, ciphertext := ciphertext[:nonceSize], ciphertext[nonceSize:]
plaintext, err := gcm.Open(nil, nonce, ciphertext, nil)
if err != nil {
fmt.Println(err)
}
fmt.Println(string(plaintext))
}
非对称加密
即常见的 RSA 算法,还包括 ECC、DH 等算法,算法特点是,密钥成对出现,一般称为公钥(公开)和私钥(保密),公钥加密的信息只能私钥解开,私钥加密的信息只能公钥解开。因此掌握公钥的不同客户端之间不能互相解密信息,只能和掌握私钥的服务器进行加密通信,服务器可以实现1对多的通信,客户端也可以用来验证掌握私钥的服务器身份。
非对称加密的特点是信息传输1对多,服务器只需要维持一个私钥就能够和多个客户端进行加密通信,但服务器发出的信息能够被所有的客户端解密,且该算法的计算复杂,加密速度慢。
非对称加密算法比对称加密算法要复杂的多,处理起来也要慢得多。如果所有的网络数据都用非对称加密算法来加密,那效率会很低。所以在实际中,非对称加密只会用来传递一条信息,那就是用于对称加密的密钥。当用于对称加密的密钥确定了,A和B还是通过对称加密算法进行网络通信。这样,既保证了网络通信的安全性,又不影响效率,A和B也不用见面商量密钥了。
加/解密主要步骤
所以,在现代,A和B之间要进行安全,省心的网络通信,需要经过以下几个步骤
- 通过CA体系交换public key
- 通过非对称加密算法,交换用于对称加密的密钥
- 通过对称加密算法,加密正常的网络通信
公钥/私钥
当客户端发送请求到服务端的时候,将会收到服务端发送的公钥,服务器有对应的私钥; 服务端与客户端进行通讯的时候,使用私钥将数据进行加密传输,客户端使用公钥进行解密后获得内容,而客户端发送数据到服务端的时候会使用公钥将内容进行加密,服务器接收后使用私钥解密。
CA 证书身份识别
在传输的过程中,客户端如何获得服务器端的公钥呢?当时是服务器分发给客户端,如果一开始服务端发送的公钥到客户端的过程中有可能被第三方劫持,然后第三方自己伪造一对密钥,将公钥发送给客户端,当服务器发送数据给客户端的时候,中间人将信息进行劫持,用一开始劫持的公钥进行解密后,然后使用自己的私钥将数据加密发送给客户端,而客户端收到后使用公钥解密,反过来亦是如此,整个过程中间人是透明的,但信息泄露却不得而知。
服务器端生成私钥
openssl genrsa -out server.key 2048
服务器端生成证书
openssl req -new -x509 -key server.key -out server.pem -days 3650
Golang TLS 服务器/客户端
服务器端
package main
import (
"bufio"
"crypto/tls"
"log"
"net"
)
func main() {
cert, err := tls.LoadX509KeyPair("server.pem", "server.key")
if err != nil {
log.Println(err)
return
}
config := &tls.Config{Certificates: []tls.Certificate{cert}}
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 handleConn(conn)
}
}
func handleConn(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("World\n"))
if err != nil {
log.Println(n, err)
return
}
}
}
客户端
func main() {
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("Hello\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]))
}
Crypto++® 库
crypto++ 是一个·C++ 的加密库。网站地址:https://www.cryptopp.com/
它支持下列算法:
Algorithm | Name |
---|---|
authenticated encryption schemes | GCM, CCM, EAX, ChaCha20Poly1305, XChaCha20Poly1305 |
high speed stream ciphers | ChaCha (8/12/20), ChaCha (IETF) HC (128/256), Panama, Rabbit (128/256), Sosemanuk, Salsa20 (8/12/20), XChaCha (8/12/20), XSalsa20 |
AES and AES candidates | AES (Rijndael), RC6, MARS, Twofish, Serpent, CAST-256 |
other block ciphers | ARIA, Blowfish, Camellia, CHAM, HIGHT, IDEA, Kalyna (128/256/512), LEA, SEED, RC5, SHACAL-2, SIMECK, SIMON (64/128), Skipjack, SPECK (64/128), Simeck, SM4,Threefish (256/512/1024), Triple-DES (DES-EDE2 and DES-EDE3), TEA, XTEA |
block cipher modes of operation | ECB, CBC, CBC ciphertext stealing (CTS), CFB, OFB, counter mode (CTR), XTS |
message authentication codes | BLAKE2b, BLAKE2s, CMAC, CBC-MAC, DMAC, GMAC (GCM), HMAC, Poly1305, SipHash, Two-Track-MAC, VMAC |
hash functions | BLAKE2b, BLAKE2s, Keccack (F1600), SHA-1, SHA-2, SHA-3, SHAKE (128/256), SipHash, Tiger, RIPEMD (128/160/256/320), SM3, WHIRLPOOL |
public-key cryptography | RSA, DSA, Determinsitic DSA (RFC 6979), ElGamal, Nyberg-Rueppel (NR), Rabin-Williams (RW), EC-based German Digital Signature (ECGDSA), LUC, LUCELG, DLIES (variants of DHAES), ESIGN |
padding schemes for public-key systems | PKCS#1 v2.0, OAEP, PSS, PSSR, IEEE P1363 EMSA2 and EMSA5 |
key agreement schemes | Diffie-Hellman (DH), Unified Diffie-Hellman (DH2), Menezes-Qu-Vanstone (MQV), Hashed MQV (HMQV), Fully Hashed MQV (FHMQV), LUCDIF, XTR-DH |
elliptic curve cryptography | ECDSA, Determinsitic ECDSA (RFC 6979), ed25519, ECGDSA, ECNR, ECIES, x25519, ECDH, ECMQV |
insecure or obsolescent algorithms retained for backwards compatibility and historical value | MD2, MD4, MD5, Panama Hash, DES, ARC4, SEAL 3.0, WAKE-OFB, DESX (DES-XEX3), RC2, SAFER, 3-WAY, GOST, SHARK, CAST-128, Square |