30 Dec 2022
Variadic Functions,可變參數函數,讓函數參數數量可是未定義,可能是一個或多個,也可能是零個。
fmt.Println
就是 Go 常見的 Variadic Functions。
以加總函數 sum 為例,可以有下面兩種寫法:
package main
import "fmt"
// Variadic Functions
func sum(nums ...int) {
fmt.Println("nums: ", nums)
total := 0
for _, n := range nums {
total += n
}
fmt.Println("sum: ", total)
}
// 傳入陣列
func sum2(nums []int) {
fmt.Println("nums: ", nums)
total := 0
for _, n := range nums {
total += n
}
fmt.Println("sum: ", total)
}
func main() {
arr := []int{1, 3, 5, 7, 9}
sum(arr...)
sum2(arr)
}
28 Dec 2022
建立資料夾、進入資料夾、初始化
mkdir hello
cd hello
go mod init example
建立放 proto
檔案的資料夾、建立 .proto
mkdir person
cd person
touch person.proto
在person.proto
貼上以下程式碼
syntax = "proto3"; // 3 代表版本
option go_package = "example/person";
package person;
message Person {
string name = 1; // 有型別,和JSON不同,所以編譯器才能自動產生很多不同語言的版本
}
執行:
protoc *.proto --go_out=plugins=grpc:. --go_opt=paths=source_relative
回到根目錄並建立 main.go
在main.go
貼上以下程式碼
package main
import (
"fmt"
"io/ioutil"
"log"
pb "example/person"
proto "github.com/golang/protobuf/proto"
)
func main() {
person := &pb.Person{Name: "XXX"}
fmt.Printf("Person's name is %s\n", person.GetName())
//把 person object 寫到檔案 person.bin(人類不可讀)
out, err := proto.Marshal(person)
if err != nil {
log.Fatalf("Serialization error: %s", err.Error())
}
if err := ioutil.WriteFile("person.bin", out, 0644); err != nil {
log.Fatalf("Write File Error: %s ", err.Error())
}
fmt.Println("Write Success")
//讀檔然後印出來
in, err := ioutil.ReadFile("person.bin")
if err != nil {
log.Fatalf("Read File Error: %s ", err.Error())
}
person2 := &pb.Person{}
err2 := proto.Unmarshal(in, person2)
if err2 != nil {
log.Fatalf("DeSerialization error: %s", err.Error())
}
fmt.Println("Read Success")
fmt.Printf("Person2's name is %s\n", person2.GetName())
}
下載需要的 package
go get -u github.com/golang/protobuf/proto
執行程式
參考資料
Protocol Buffers and Go: Getting started
14 Dec 2022
Go 簡單的加解密可以用 base64 來實現
import (
"encoding/base64"
"fmt"
)
func main() {
data := "你好世界"
sEnc := base64.StdEncoding.EncodeToString([]byte(data))
fmt.Println(sEnc)
sDec, err := base64.StdEncoding.DecodeString(sEnc)
if err == nil {
fmt.Println(string(sDec))
}
}
其他還有例如 DES、AES,這兩個演算法在 crypto
package 內被實現。AES 的範例如下:
import (
"crypto/aes"
"crypto/cipher"
"fmt"
"os"
)
var commonIV = []byte{0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, 0x08, 0x09, 0x0a, 0x0b, 0x0c, 0x0d, 0x0e, 0x0f}
func main() {
//需要去加密的字串
plaintext := []byte("你好世界")
//如果傳入加密串的話,plaintext 就是傳入的字串
if len(os.Args) > 1 {
plaintext = []byte(os.Args[1])
}
//aes 的加密字串
key_text := "astaxie12798akljzmknm.ahkjkljl;k"
if len(os.Args) > 2 {
key_text = os.Args[2]
}
// 建立加密演算法 aes
c, err := aes.NewCipher([]byte(key_text))
if err != nil {
fmt.Printf("Error: NewCipher(%d bytes) = %s", len(key_text), err)
os.Exit(-1)
}
//加密字串
cfb := cipher.NewCFBEncrypter(c, commonIV)
ciphertext := make([]byte, len(plaintext))
cfb.XORKeyStream(ciphertext, plaintext)
fmt.Printf("%s=>%x\n", plaintext, ciphertext)
// 解密字串
cfbdec := cipher.NewCFBDecrypter(c, commonIV)
plaintextCopy := make([]byte, len(plaintext))
cfbdec.XORKeyStream(plaintextCopy, ciphertext)
fmt.Printf("%x=>%s\n", ciphertext, plaintextCopy)
}
參考資料
程式碼來源
13 Dec 2022
目前常見儲存密碼的方式是使用單向雜湊演算法將明文雜湊後儲存。單向雜湊演算法,顧名思義,無法將雜湊過後的摘要(digest)還原成明文,而這個過程具有確定性,也就是每次輸入相同內容得到的摘要都相同。在真實情況下,每次輸入密碼後,會將其進行雜湊,再與資料庫內儲存的摘要做比對。
常用的單向雜湊演算法包括 SHA-256, SHA-1, MD5 等 :
import (
"crypto/md5"
"crypto/sha1"
"crypto/sha256"
"fmt"
"io"
)
func main() {
h := sha256.New()
io.WriteString(h, "密碼")
fmt.Printf("% x\n", h.Sum(nil)) // %x base 16, with lower-case letters for a-f
h = sha1.New()
io.WriteString(h, "密碼")
fmt.Printf("% x\n", h.Sum(nil))
h = md5.New()
io.WriteString(h, "密碼")
fmt.Printf("%x", h.Sum(nil))
}
結果:
ef 8b 49 45 8c 14 f6 59 63 50 24 2e d3 73 a7 0d 63 b8 ba 13 32 b0 1c d6 f9 80 23 35 ae ae 63 7c
6e 25 cb 22 24 f0 e4 ff 01 4a 51 c3 82 82 f7 b7 59 88 dc 31
6662c848a80c30c8d042bfd17cf5ae2c
Rainbow table
有些密碼特別常見,因此有個摘要組合叫做 Rainbow table,其實就是很多常見的密碼與其雜湊過後的摘要,和資料庫儲存的摘要進行比對後,就可以推導出原本的明文。所以一旦資料庫被洩露,駭客可以照著 Rainbow table 比對出很多用戶的明文密碼。
加鹽
為了防止駭客輕易比對出密碼,有個方法叫做「加鹽」,除了使用單向雜湊法將明文雜湊一次,另外加上指定字串或用戶名等隨機字串再做一次加密。
import (
"crypto/md5"
"fmt"
"io"
)
func main() {
h := md5.New()
io.WriteString(h, "密碼")
pwmd5 := fmt.Sprintf("%x", h.Sum(nil))
salt1 := "@#$%"
salt2 := "^&*()"
// 和上面兩個鹽拼接
io.WriteString(h, salt1)
io.WriteString(h, salt2)
io.WriteString(h, pwmd5)
fmt.Printf("% x", h.Sum(nil))
}
scrypt / bcrypt
還有其他的套件例如 scrypt 與 bcrypt:
scrypt
package main
import (
"fmt"
"golang.org/x/crypto/scrypt"
)
func main() {
salt := []byte("asdfasdf")
h, err := scrypt.Key([]byte("some password"), salt, 16384, 8, 1, 32)
if err == nil {
fmt.Printf("% x", h)
}
}
bcrypt
package main
import (
"fmt"
"golang.org/x/crypto/bcrypt"
)
func main() {
password := []byte("密碼")
// Hashing the password with the default cost of 10
h, err := bcrypt.GenerateFromPassword(password, bcrypt.DefaultCost)
if err == nil {
fmt.Printf("% x", h)
}
// Comparing the password with the hash
err = bcrypt.CompareHashAndPassword(h, password)
fmt.Println(err) // nil means it is a match
}
參考資料