commit 111fc341a6b1e5289e1cfe9f000bb76e69ca53d0 Author: Evan Chen Date: Thu Sep 2 18:15:02 2021 +0800 first commit diff --git a/aes.go b/aes.go new file mode 100644 index 0000000..7948f89 --- /dev/null +++ b/aes.go @@ -0,0 +1,96 @@ +package shttp + +import ( + "crypto/aes" + "crypto/cipher" + "crypto/rand" + "encoding/hex" + "fmt" + "io" + "os" +) + +// AESKey generates a random 32 byte AES Key +func AESKey() string { + bytes := make([]byte, 32) //generate a random 32 byte key for AES-256 + if _, err := rand.Read(bytes); err != nil { + fmt.Println(err) + os.Exit(1) + } + return hex.EncodeToString(bytes) +} + +// EncryptAESByte use system dynamic key if keyString = "" +func EncryptAESByte(ByteToEncrypt []byte, keyString string) ([]byte, error) { + if keyString == "" { + keyString = AESKey() + } + //Since the key is in string, we need to convert decode it to bytes + key, _ := hex.DecodeString(keyString) + + //Create a new Cipher Block from the key + block, err := aes.NewCipher(key) + if err != nil { + return nil, err + } + + //Create a new GCM + aesGCM, err := cipher.NewGCM(block) + if err != nil { + return nil, err + } + + //Create a nonce. Nonce should be from GCM + nonce := make([]byte, aesGCM.NonceSize()) + if _, err = io.ReadFull(rand.Reader, nonce); err != nil { + return nil, err + } + + //Encrypt the data using aesGCM.Seal + //Since we don't want to save the nonce somewhere else in this case, we add it as a prefix to the encrypted data. The first nonce argument in Seal is the prefix. + return aesGCM.Seal(nonce, nonce, ByteToEncrypt, nil), nil +} + +func EncryptAES(stringToEncrypt string, keyString string) (string, error) { + ciphertext, err := EncryptAESByte([]byte(stringToEncrypt), keyString) + return hex.EncodeToString(ciphertext), err +} + +// DecryptAESByte use system dynamic key if keyString = "" +func DecryptAESByte(encrypted []byte, keyString string) ([]byte, error) { + if keyString == "" { + keyString = AESKey() + } + key, _ := hex.DecodeString(keyString) + + //Create a new Cipher Block from the key + block, err := aes.NewCipher(key) + if err != nil { + return nil, err + } + + //Create a new GCM + aesGCM, err := cipher.NewGCM(block) + if err != nil { + return nil, err + } + + //Get the nonce size + nonceSize := aesGCM.NonceSize() + + //Extract the nonce from the encrypted data + nonce, ciphertext := encrypted[:nonceSize], encrypted[nonceSize:] + + //Decrypt the data + return aesGCM.Open(nil, nonce, ciphertext, nil) +} + +func DecryptAES(encryptedString string, keyString string) (string, error) { + encrypted, err := hex.DecodeString(encryptedString) + if err != nil { + panic(err.Error()) + } + decrypted, err := DecryptAESByte(encrypted, keyString) + return string(decrypted), err + +} diff --git a/cmd/main/main.go b/cmd/main/main.go new file mode 100644 index 0000000..f370bfd --- /dev/null +++ b/cmd/main/main.go @@ -0,0 +1,130 @@ +package main + +import ( + "bytes" + "encoding/json" + "fmt" + "io/ioutil" + "net/http" + "os" + "path/filepath" + "runtime" + "shttp" + "time" +) + +func main() { + rk := &shttp.RSA{} + + home, err := os.UserHomeDir() + if err != nil { + Fatal(err) + } + path := filepath.Join(home, ".ssh", "id_rsa") + file, err := os.ReadFile(path) + if err != nil { + Fatal(err) + } + pub := string(file) + if err := rk.LoadPrivate(pub); err != nil { + Fatal(err) + } + + err = rk.LoadPublic("ssh-rsa AAAAB3NzaC1yc2EAAAADAQABAAABgQDO1y6CLE3ZoPEy24ird1izypdVN0VEIcQiVeqZY3IOe2rQWzMG3+UJzmPj2wKFoSDxcVQxuuGe2MMX4xFCl1ChCEt85xfGrUWWAGX2IV83+UMtTS5dXLcSATs5NBhFQLgDSGBCDioSxkzBayz4IUkRihRPhd/rDSnlGITBbO6+dAtCdqIfLg/Ai7fvE2PMOcxZO7OLcZYHVkYbUn/n/DuIQbqAi2XTLWf9Z3zpKnBkqIa1RZVnQpkEO5IDj2F1X6edbDf8ScdsQwYfpBYwpQJfjLWJnRqhKy1iO9VpGoPPipiL9fJA7dA1LWlzQadaMT0/7GngFn/eFdyGvxR4zIgo4IZBzvHTjcFgaCkeVe3TOQXCVp7LwAxZc+OBzqEsuF5sjak+NE8zRjonzi3kItaG2Ldd8PonOokR45D0Vmm26fTSCPAz8exix7xm6RxZ11Y5RHrFTblQ/U/T71dxCwOgVbWbnpddm5O6Uzc5wk+BvPyscgbjJgybiXQ+kKvmiMk= AzureAD+YiFanChen@LAPTOP-SCQSNETG") + if err != nil { + Fatal(err) + } + + go func() { + http.HandleFunc("/con", func(rw http.ResponseWriter, r *http.Request) { + aes := shttp.AESKey() + aesc, _ := rk.Encrypt(aes) + rw.Header().Set("aesc", aesc) + + body, _ := shttp.EncryptAESByte([]byte(rk.PublicKey()), aes) + rw.Write(body) + }) + http.HandleFunc("/report", func(rw http.ResponseWriter, r *http.Request) { + aesc := r.Header.Get("aesc") + aes, err := rk.Decrypt(aesc) + if err != nil { + Fatal(err) + } + defer r.Body.Close() + datab, err := ioutil.ReadAll(r.Body) + if err != nil { + Fatal(err) + } + datar, err := shttp.DecryptAESByte(datab, aes) + if err != nil { + Fatal(err) + } + data := map[string]interface{}{} + json.Unmarshal(datar, &data) + fmt.Println(data) + rw.Write([]byte("ok")) + }) + err = http.ListenAndServe("localhost:8080", nil) + if err != nil { + Fatal(err) + } + }() + + func() { + time.Sleep(time.Second * 10) + lk := &shttp.RSA{} + func() { + r, err := http.Get("http://localhost:8080/con") + if err != nil { + Fatal(err) + } + aesc := r.Header.Get("aesc") + aes, err := rk.Decrypt(aesc) + if err != nil { + Fatal(err) + } + defer r.Body.Close() + datab, err := ioutil.ReadAll(r.Body) + if err != nil { + Fatal(err) + } + datar, err := shttp.DecryptAESByte(datab, aes) + if err != nil { + Fatal(err) + } + if err = lk.LoadPublic(string(datar)); err != nil { + Fatal(err) + } + }() + + func() { + data := map[string]interface{}{"msg": "cool"} + obj, err := json.Marshal(data) + if err != nil { + Fatal(err) + } + aes := shttp.AESKey() + body, err := shttp.EncryptAESByte(obj, aes) + if err != nil { + Fatal(err) + } + aesc, err := lk.Encrypt(aes) + if err != nil { + Fatal(err) + } + req, _ := http.NewRequest("POST", "http://localhost:8080/report", bytes.NewReader(body)) + req.Header.Set("aesc", aesc) + http.DefaultClient.Do(req) + + time.Sleep(time.Second * 5) + }() + + }() +} + +func Fatal(i ...interface{}) { + _, _, line, _ := runtime.Caller(1) + prt := []interface{}{line, ": "} + fmt.Println(append(prt, i)...) + os.Exit(1) +} diff --git a/go.mod b/go.mod new file mode 100644 index 0000000..7090dcb --- /dev/null +++ b/go.mod @@ -0,0 +1,5 @@ +module shttp + +go 1.16 + +require golang.org/x/crypto v0.0.0-20210817164053-32db794688a5 diff --git a/go.sum b/go.sum new file mode 100644 index 0000000..706465d --- /dev/null +++ b/go.sum @@ -0,0 +1,10 @@ +golang.org/x/crypto v0.0.0-20210817164053-32db794688a5 h1:HWj/xjIHfjYU5nVXpTM0s39J9CbLn7Cc5a7IC5rwsMQ= +golang.org/x/crypto v0.0.0-20210817164053-32db794688a5/go.mod h1:GvvjBRRGRdwPK5ydBHafDWAxML/pGHZbMvKqRZ5+Abc= +golang.org/x/net v0.0.0-20210226172049-e18ecbb05110/go.mod h1:m0MpNAwzfU5UDzcl9v0D8zg8gWTRqZa9RBIspLL5mdg= +golang.org/x/sys v0.0.0-20201119102817-f84b799fce68/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20210615035016-665e8c7367d1 h1:SrN+KX8Art/Sf4HNj6Zcz06G7VEz+7w9tdXTPOZ7+l4= +golang.org/x/sys v0.0.0-20210615035016-665e8c7367d1/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= +golang.org/x/term v0.0.0-20201126162022-7de9c90e9dd1 h1:v+OssWQX+hTHEmOBgwxdZxK4zHq3yOs8F9J7mk0PY8E= +golang.org/x/term v0.0.0-20201126162022-7de9c90e9dd1/go.mod h1:bj7SfCRtBDWHUb9snDiAeCFNEtKQo2Wmx5Cou7ajbmo= +golang.org/x/text v0.3.3/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ= +golang.org/x/tools v0.0.0-20180917221912-90fa682c2a6e/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ= diff --git a/http.go b/http.go new file mode 100644 index 0000000..319af63 --- /dev/null +++ b/http.go @@ -0,0 +1 @@ +package shttp diff --git a/rsa.go b/rsa.go new file mode 100644 index 0000000..ecb40a6 --- /dev/null +++ b/rsa.go @@ -0,0 +1,144 @@ +package shttp + +import ( + "crypto/rand" + "crypto/rsa" + "crypto/sha256" + "crypto/x509" + "encoding/hex" + "encoding/pem" + "fmt" + + "golang.org/x/crypto/ssh" +) + +type RSA struct { + bits int + key *rsa.PrivateKey + Comment string +} + +func (r *RSA) SetBits(b int) { + r.bits = b +} + +func (r *RSA) New() (err error) { + if r.bits < 1024 || r.bits > 4096 { + r.bits = 2048 + } + r.key, err = rsa.GenerateKey(rand.Reader, r.bits) + return +} + +func (r *RSA) Key() *rsa.PrivateKey { + return r.key +} + +func (r *RSA) PublicKey() string { + if r.key == nil { + return "" + } + return string(pem.EncodeToMemory(&pem.Block{Type: "RSA PUBLIC KEY", Bytes: x509.MarshalPKCS1PublicKey(&r.key.PublicKey)})) +} + +func (r *RSA) PrivateKey() string { + if r.key == nil { + return "" + } + return string(pem.EncodeToMemory(&pem.Block{Type: "RSA PRIVATE KEY", Bytes: x509.MarshalPKCS1PrivateKey(r.key)})) +} + +func (r *RSA) EncryptByte(msg []byte) ([]byte, error) { + if r.key == nil { + return nil, fmt.Errorf("no key found") + } + b, err := rsa.EncryptOAEP(sha256.New(), rand.Reader, &r.key.PublicKey, msg, []byte("")) + if err != nil { + return nil, err + } + return b, nil +} + +func (r *RSA) Encrypt(msg string) (string, error) { + b, err := r.EncryptByte([]byte(msg)) + return hex.EncodeToString(b), err +} + +func (r *RSA) DecryptByte(msg []byte) ([]byte, error) { + if r.key == nil { + return nil, fmt.Errorf("no key found") + } + b, err := rsa.DecryptOAEP(sha256.New(), rand.Reader, r.key, msg, []byte("")) + return b, err +} + +func (r *RSA) Decrypt(cypher string) (string, error) { + block, err := hex.DecodeString(cypher) + if err != nil { + return "", err + } + msg, err := r.DecryptByte(block) + if err != nil { + return "", err + } + return string(msg), nil +} + +func (r *RSA) LoadPublic(pub string) error { + if r.key == nil { + r.key = &rsa.PrivateKey{} + } + parsed, comment, _, _, err := ssh.ParseAuthorizedKey([]byte(pub)) + if err != nil { + block, rest := pem.Decode([]byte(pub)) + if block == nil || block.Type != "RSA PUBLIC KEY" { + return fmt.Errorf("ssh: no key found") + } + if len(rest) != 0 { + r.Comment = string(rest) + } + p, err := x509.ParsePKCS1PublicKey(block.Bytes) + if err != nil { + return err + } + r.key.PublicKey = *p + return nil + } + parsedCryptoKey := parsed.(ssh.CryptoPublicKey) + pubCrypto := parsedCryptoKey.CryptoPublicKey() + p := pubCrypto.(*rsa.PublicKey) + + r.key.PublicKey = *p + r.Comment = comment + return nil +} + +func (r *RSA) LoadPrivate(pri string) error { + var p *rsa.PrivateKey + var err error + opssh, err := ssh.ParseRawPrivateKey([]byte(pri)) + if err != nil { + return err + } + p = opssh.(*rsa.PrivateKey) + if r.key != nil { + p.PublicKey = r.key.PublicKey + } + r.key = p + return nil +} + +func (r *RSA) LoadPrivateWithPassphrase(pri, passphrase string) error { + var p *rsa.PrivateKey + var err error + opssh, err := ssh.ParseRawPrivateKeyWithPassphrase([]byte(pri), []byte(passphrase)) + if err != nil { + return err + } + p = opssh.(*rsa.PrivateKey) + if r.key != nil { + p.PublicKey = r.key.PublicKey + } + r.key = p + return nil +} diff --git a/shttp.go b/shttp.go new file mode 100644 index 0000000..319af63 --- /dev/null +++ b/shttp.go @@ -0,0 +1 @@ +package shttp