feature/ui - zatim ne uplne uhlazena ale celkem pouzitelna appka #1
3
.vscode/settings.json
vendored
3
.vscode/settings.json
vendored
@ -1,3 +0,0 @@
|
|||||||
{
|
|
||||||
"nuxt.isNuxtApp": false
|
|
||||||
}
|
|
||||||
159
lib/encrypt.go
Normal file
159
lib/encrypt.go
Normal file
@ -0,0 +1,159 @@
|
|||||||
|
package encrypt
|
||||||
|
|
||||||
|
import (
|
||||||
|
"bytes"
|
||||||
|
"crypto/rand"
|
||||||
|
"crypto/rsa"
|
||||||
|
"crypto/x509"
|
||||||
|
"crypto/x509/pkix"
|
||||||
|
"encoding/json"
|
||||||
|
"encoding/pem"
|
||||||
|
"fmt"
|
||||||
|
"io/fs"
|
||||||
|
"log"
|
||||||
|
"math/big"
|
||||||
|
"os"
|
||||||
|
"time"
|
||||||
|
)
|
||||||
|
|
||||||
|
// ParsePeerPublicKey umí vzít buď PEM PUBLIC KEY, nebo PEM CERT a vrátí *rsa.PublicKey
|
||||||
|
func ParsePeerPublicKey(pemOrCert string) (*rsa.PublicKey, error) {
|
||||||
|
block, _ := pem.Decode([]byte(pemOrCert))
|
||||||
|
if block == nil {
|
||||||
|
return nil, errf("nenašel jsem PEM blok")
|
||||||
|
}
|
||||||
|
switch block.Type {
|
||||||
|
case "PUBLIC KEY":
|
||||||
|
k, err := x509.ParsePKIXPublicKey(block.Bytes)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
rsaPub, ok := k.(*rsa.PublicKey)
|
||||||
|
if !ok {
|
||||||
|
return nil, errf("očekávám RSA PUBLIC KEY")
|
||||||
|
}
|
||||||
|
return rsaPub, nil
|
||||||
|
case "CERTIFICATE":
|
||||||
|
c, err := x509.ParseCertificate(block.Bytes)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
rsaPub, ok := c.PublicKey.(*rsa.PublicKey)
|
||||||
|
if !ok {
|
||||||
|
return nil, errf("cert neobsahuje RSA klíč")
|
||||||
|
}
|
||||||
|
return rsaPub, nil
|
||||||
|
default:
|
||||||
|
return nil, errf("nepodporovaný PEM typ: %s", block.Type)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func generateSelfSignedCert(pub *rsa.PublicKey, priv *rsa.PrivateKey) []byte {
|
||||||
|
tpl := x509.Certificate{
|
||||||
|
SerialNumber: big.NewInt(1),
|
||||||
|
Subject: pkix.Name{
|
||||||
|
CommonName: "Encryptor Local Identity",
|
||||||
|
},
|
||||||
|
NotBefore: time.Now(),
|
||||||
|
NotAfter: time.Now().AddDate(1, 0, 0),
|
||||||
|
KeyUsage: x509.KeyUsageDigitalSignature | x509.KeyUsageKeyEncipherment,
|
||||||
|
// Tenhle cert je jen „vizitka“ (není pro TLS).
|
||||||
|
BasicConstraintsValid: true,
|
||||||
|
}
|
||||||
|
der, _ := x509.CreateCertificate(rand.Reader, &tpl, &tpl, pub, priv)
|
||||||
|
buf := &bytes.Buffer{}
|
||||||
|
_ = pem.Encode(buf, &pem.Block{Type: "CERTIFICATE", Bytes: der})
|
||||||
|
return buf.Bytes()
|
||||||
|
}
|
||||||
|
|
||||||
|
// malá helper chyba
|
||||||
|
type strErr string
|
||||||
|
|
||||||
|
func (e strErr) Error() string { return string(e) }
|
||||||
|
func errf(s string, a ...any) error { return strErr(fmtS(s, a...)) }
|
||||||
|
func fmtS(format string, a ...any) string {
|
||||||
|
var b bytes.Buffer
|
||||||
|
b.WriteString(format)
|
||||||
|
if len(a) > 0 {
|
||||||
|
b.WriteString(": ")
|
||||||
|
}
|
||||||
|
for i, v := range a {
|
||||||
|
if i > 0 {
|
||||||
|
b.WriteString(", ")
|
||||||
|
}
|
||||||
|
b.WriteString(anyToString(v))
|
||||||
|
}
|
||||||
|
return b.String()
|
||||||
|
}
|
||||||
|
func anyToString(v any) string {
|
||||||
|
switch t := v.(type) {
|
||||||
|
case string:
|
||||||
|
return t
|
||||||
|
default:
|
||||||
|
j, _ := json.Marshal(t)
|
||||||
|
return string(j)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func LoadOrGenerateKeys(privPath, pubPath, certPath string) (priv *rsa.PrivateKey, pubPEM, certPEM []byte, err error) {
|
||||||
|
|
||||||
|
// existuje private key?
|
||||||
|
if fileExists(privPath) && fileExists(pubPath) && fileExists(certPath) {
|
||||||
|
// načti privátní klíč
|
||||||
|
pkBytes, err := os.ReadFile(privPath)
|
||||||
|
if err != nil {
|
||||||
|
return nil, nil, nil, err
|
||||||
|
}
|
||||||
|
block, _ := pem.Decode(pkBytes)
|
||||||
|
if block == nil || block.Type != "RSA PRIVATE KEY" {
|
||||||
|
return nil, nil, nil, fmt.Errorf("invalid private key PEM")
|
||||||
|
}
|
||||||
|
key, err := x509.ParsePKCS1PrivateKey(block.Bytes)
|
||||||
|
if err != nil {
|
||||||
|
return nil, nil, nil, err
|
||||||
|
}
|
||||||
|
priv = key
|
||||||
|
|
||||||
|
// načti public & cert
|
||||||
|
pubPEM, _ = os.ReadFile(pubPath)
|
||||||
|
certPEM, _ = os.ReadFile(certPath)
|
||||||
|
|
||||||
|
log.Println("Načtena existující identita z disku.")
|
||||||
|
return priv, pubPEM, certPEM, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// jinak vygeneruj nové
|
||||||
|
key, err := rsa.GenerateKey(rand.Reader, 2048)
|
||||||
|
if err != nil {
|
||||||
|
return nil, nil, nil, err
|
||||||
|
}
|
||||||
|
priv = key
|
||||||
|
|
||||||
|
// public
|
||||||
|
pubASN1, _ := x509.MarshalPKIXPublicKey(&priv.PublicKey)
|
||||||
|
pubPEM = pem.EncodeToMemory(&pem.Block{Type: "PUBLIC KEY", Bytes: pubASN1})
|
||||||
|
|
||||||
|
// cert
|
||||||
|
certPEM = generateSelfSignedCert(&priv.PublicKey, priv)
|
||||||
|
|
||||||
|
// ulož
|
||||||
|
if err := os.WriteFile(privPath,
|
||||||
|
pem.EncodeToMemory(&pem.Block{Type: "RSA PRIVATE KEY", Bytes: x509.MarshalPKCS1PrivateKey(priv)}),
|
||||||
|
fs.FileMode(0600)); err != nil {
|
||||||
|
return nil, nil, nil, err
|
||||||
|
}
|
||||||
|
if err := os.WriteFile(pubPath, pubPEM, 0644); err != nil {
|
||||||
|
return nil, nil, nil, err
|
||||||
|
}
|
||||||
|
if err := os.WriteFile(certPath, certPEM, 0644); err != nil {
|
||||||
|
return nil, nil, nil, err
|
||||||
|
}
|
||||||
|
|
||||||
|
log.Println("Vygenerována nová identita a uložena na disk.")
|
||||||
|
return priv, pubPEM, certPEM, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func fileExists(p string) bool {
|
||||||
|
info, err := os.Stat(p)
|
||||||
|
return err == nil && !info.IsDir()
|
||||||
|
}
|
||||||
302
main.go
302
main.go
@ -1,25 +1,11 @@
|
|||||||
package main
|
package main
|
||||||
|
|
||||||
import (
|
import (
|
||||||
"bytes"
|
|
||||||
"crypto/aes"
|
|
||||||
"crypto/cipher"
|
|
||||||
"crypto/rand"
|
|
||||||
"crypto/rsa"
|
"crypto/rsa"
|
||||||
"crypto/sha256"
|
encrypt "fckeuspy-go/lib"
|
||||||
"crypto/x509"
|
|
||||||
"crypto/x509/pkix"
|
|
||||||
"encoding/base64"
|
|
||||||
"encoding/json"
|
|
||||||
"encoding/pem"
|
|
||||||
"fmt"
|
|
||||||
"html/template"
|
"html/template"
|
||||||
"io/fs"
|
|
||||||
"log"
|
"log"
|
||||||
"math/big"
|
|
||||||
"net/http"
|
"net/http"
|
||||||
"os"
|
|
||||||
"time"
|
|
||||||
)
|
)
|
||||||
|
|
||||||
var (
|
var (
|
||||||
@ -41,292 +27,16 @@ type envelope struct {
|
|||||||
}
|
}
|
||||||
|
|
||||||
func main() {
|
func main() {
|
||||||
|
var err error
|
||||||
// 1) načti nebo vygeneruj klíče
|
// 1) načti nebo vygeneruj klíče
|
||||||
if err := loadOrGenerateKeys(); err != nil {
|
priv, pubPEM, certPEM, err = encrypt.LoadOrGenerateKeys(privPath, pubPath, certPath)
|
||||||
|
if err != nil {
|
||||||
log.Fatal(err)
|
log.Fatal(err)
|
||||||
}
|
}
|
||||||
|
|
||||||
// 2) šablony
|
// 2) šablony
|
||||||
tmpl = template.Must(template.ParseGlob("templates/*.html"))
|
tmpl = template.Must(template.ParseGlob("templates/*.html"))
|
||||||
|
|
||||||
// 3) routing
|
muxServer := NewServer()
|
||||||
http.HandleFunc("/", indexHandler)
|
log.Fatal(http.ListenAndServe(":8080", muxServer))
|
||||||
http.HandleFunc("/public.pem", publicKeyHandler)
|
|
||||||
http.HandleFunc("/public.crt", publicCertHandler)
|
|
||||||
http.HandleFunc("/encrypt", encryptHandler)
|
|
||||||
http.HandleFunc("/decrypt", decryptHandler)
|
|
||||||
http.Handle("/static/", http.StripPrefix("/static/", http.FileServer(http.Dir("static"))))
|
|
||||||
|
|
||||||
log.Println("Server běží na http://localhost:8080")
|
|
||||||
log.Fatal(http.ListenAndServe(":8080", nil))
|
|
||||||
}
|
|
||||||
|
|
||||||
func indexHandler(w http.ResponseWriter, r *http.Request) {
|
|
||||||
_ = tmpl.ExecuteTemplate(w, "index.html", nil)
|
|
||||||
}
|
|
||||||
|
|
||||||
func publicKeyHandler(w http.ResponseWriter, r *http.Request) {
|
|
||||||
w.Header().Set("Content-Type", "application/x-pem-file")
|
|
||||||
w.Write(pubPEM)
|
|
||||||
}
|
|
||||||
|
|
||||||
func publicCertHandler(w http.ResponseWriter, r *http.Request) {
|
|
||||||
w.Header().Set("Content-Type", "application/x-pem-file")
|
|
||||||
w.Write(certPEM)
|
|
||||||
}
|
|
||||||
|
|
||||||
func encryptHandler(w http.ResponseWriter, r *http.Request) {
|
|
||||||
if err := r.ParseForm(); err != nil {
|
|
||||||
http.Error(w, "Bad form", http.StatusBadRequest)
|
|
||||||
return
|
|
||||||
}
|
|
||||||
msg := r.Form.Get("message")
|
|
||||||
peer := r.Form.Get("pubkey") // může to být PEM PUBLIC KEY nebo CERT
|
|
||||||
|
|
||||||
pubKey, err := parsePeerPublicKey(peer)
|
|
||||||
if err != nil {
|
|
||||||
http.Error(w, "Neplatný public key/cert: "+err.Error(), http.StatusBadRequest)
|
|
||||||
return
|
|
||||||
}
|
|
||||||
|
|
||||||
// --- hybrid: AES-GCM + RSA-OAEP(SHA-256) ---
|
|
||||||
// 1) vygeneruj náhodný sym. klíč
|
|
||||||
aesKey := make([]byte, 32) // AES-256
|
|
||||||
if _, err := rand.Read(aesKey); err != nil {
|
|
||||||
http.Error(w, "Rand fail", 500)
|
|
||||||
return
|
|
||||||
}
|
|
||||||
|
|
||||||
block, err := aes.NewCipher(aesKey)
|
|
||||||
if err != nil {
|
|
||||||
http.Error(w, "AES fail", 500)
|
|
||||||
return
|
|
||||||
}
|
|
||||||
gcm, err := cipher.NewGCM(block)
|
|
||||||
if err != nil {
|
|
||||||
http.Error(w, "GCM fail", 500)
|
|
||||||
return
|
|
||||||
}
|
|
||||||
|
|
||||||
nonce := make([]byte, gcm.NonceSize())
|
|
||||||
if _, err := rand.Read(nonce); err != nil {
|
|
||||||
http.Error(w, "Nonce fail", 500)
|
|
||||||
return
|
|
||||||
}
|
|
||||||
|
|
||||||
ciphertext := gcm.Seal(nil, nonce, []byte(msg), nil)
|
|
||||||
|
|
||||||
// 2) zašifruj AES klíč cizím RSA klíčem (OAEP SHA-256)
|
|
||||||
label := []byte{} // prázdný label
|
|
||||||
ek, err := rsa.EncryptOAEP(sha256.New(), rand.Reader, pubKey, aesKey, label)
|
|
||||||
if err != nil {
|
|
||||||
http.Error(w, "RSA-OAEP fail: "+err.Error(), 500)
|
|
||||||
return
|
|
||||||
}
|
|
||||||
|
|
||||||
env := envelope{
|
|
||||||
EK: base64.StdEncoding.EncodeToString(ek),
|
|
||||||
N: base64.StdEncoding.EncodeToString(nonce),
|
|
||||||
CT: base64.StdEncoding.EncodeToString(ciphertext),
|
|
||||||
}
|
|
||||||
|
|
||||||
payload, _ := json.MarshalIndent(env, "", " ")
|
|
||||||
_ = tmpl.ExecuteTemplate(w, "encrypt.html", string(payload))
|
|
||||||
}
|
|
||||||
|
|
||||||
func decryptHandler(w http.ResponseWriter, r *http.Request) {
|
|
||||||
if err := r.ParseForm(); err != nil {
|
|
||||||
http.Error(w, "Bad form", http.StatusBadRequest)
|
|
||||||
return
|
|
||||||
}
|
|
||||||
in := r.Form.Get("payload")
|
|
||||||
|
|
||||||
var env envelope
|
|
||||||
if err := json.Unmarshal([]byte(in), &env); err != nil {
|
|
||||||
http.Error(w, "Neplatné JSON", 400)
|
|
||||||
return
|
|
||||||
}
|
|
||||||
|
|
||||||
ek, err := base64.StdEncoding.DecodeString(env.EK)
|
|
||||||
if err != nil {
|
|
||||||
http.Error(w, "ek base64", 400)
|
|
||||||
return
|
|
||||||
}
|
|
||||||
nonce, err := base64.StdEncoding.DecodeString(env.N)
|
|
||||||
if err != nil {
|
|
||||||
http.Error(w, "nonce base64", 400)
|
|
||||||
return
|
|
||||||
}
|
|
||||||
ct, err := base64.StdEncoding.DecodeString(env.CT)
|
|
||||||
if err != nil {
|
|
||||||
http.Error(w, "ct base64", 400)
|
|
||||||
return
|
|
||||||
}
|
|
||||||
|
|
||||||
// 1) rozšifruj AES klíč naším RSA
|
|
||||||
label := []byte{}
|
|
||||||
aesKey, err := rsa.DecryptOAEP(sha256.New(), rand.Reader, priv, ek, label)
|
|
||||||
if err != nil {
|
|
||||||
http.Error(w, "RSA-OAEP decrypt fail: "+err.Error(), 400)
|
|
||||||
return
|
|
||||||
}
|
|
||||||
|
|
||||||
block, err := aes.NewCipher(aesKey)
|
|
||||||
if err != nil {
|
|
||||||
http.Error(w, "AES fail", 500)
|
|
||||||
return
|
|
||||||
}
|
|
||||||
gcm, err := cipher.NewGCM(block)
|
|
||||||
if err != nil {
|
|
||||||
http.Error(w, "GCM fail", 500)
|
|
||||||
return
|
|
||||||
}
|
|
||||||
plain, err := gcm.Open(nil, nonce, ct, nil)
|
|
||||||
if err != nil {
|
|
||||||
http.Error(w, "GCM open fail: "+err.Error(), 400)
|
|
||||||
return
|
|
||||||
}
|
|
||||||
|
|
||||||
_ = tmpl.ExecuteTemplate(w, "decrypt.html", string(plain))
|
|
||||||
}
|
|
||||||
|
|
||||||
// parsePeerPublicKey umí vzít buď PEM PUBLIC KEY, nebo PEM CERT a vrátí *rsa.PublicKey
|
|
||||||
func parsePeerPublicKey(pemOrCert string) (*rsa.PublicKey, error) {
|
|
||||||
block, _ := pem.Decode([]byte(pemOrCert))
|
|
||||||
if block == nil {
|
|
||||||
return nil, errf("nenašel jsem PEM blok")
|
|
||||||
}
|
|
||||||
switch block.Type {
|
|
||||||
case "PUBLIC KEY":
|
|
||||||
k, err := x509.ParsePKIXPublicKey(block.Bytes)
|
|
||||||
if err != nil {
|
|
||||||
return nil, err
|
|
||||||
}
|
|
||||||
rsaPub, ok := k.(*rsa.PublicKey)
|
|
||||||
if !ok {
|
|
||||||
return nil, errf("očekávám RSA PUBLIC KEY")
|
|
||||||
}
|
|
||||||
return rsaPub, nil
|
|
||||||
case "CERTIFICATE":
|
|
||||||
c, err := x509.ParseCertificate(block.Bytes)
|
|
||||||
if err != nil {
|
|
||||||
return nil, err
|
|
||||||
}
|
|
||||||
rsaPub, ok := c.PublicKey.(*rsa.PublicKey)
|
|
||||||
if !ok {
|
|
||||||
return nil, errf("cert neobsahuje RSA klíč")
|
|
||||||
}
|
|
||||||
return rsaPub, nil
|
|
||||||
default:
|
|
||||||
return nil, errf("nepodporovaný PEM typ: %s", block.Type)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
func generateSelfSignedCert(pub *rsa.PublicKey) []byte {
|
|
||||||
tpl := x509.Certificate{
|
|
||||||
SerialNumber: big.NewInt(1),
|
|
||||||
Subject: pkix.Name{
|
|
||||||
CommonName: "Encryptor Local Identity",
|
|
||||||
},
|
|
||||||
NotBefore: time.Now(),
|
|
||||||
NotAfter: time.Now().AddDate(1, 0, 0),
|
|
||||||
KeyUsage: x509.KeyUsageDigitalSignature | x509.KeyUsageKeyEncipherment,
|
|
||||||
// Tenhle cert je jen „vizitka“ (není pro TLS).
|
|
||||||
BasicConstraintsValid: true,
|
|
||||||
}
|
|
||||||
der, _ := x509.CreateCertificate(rand.Reader, &tpl, &tpl, pub, priv)
|
|
||||||
buf := &bytes.Buffer{}
|
|
||||||
_ = pem.Encode(buf, &pem.Block{Type: "CERTIFICATE", Bytes: der})
|
|
||||||
return buf.Bytes()
|
|
||||||
}
|
|
||||||
|
|
||||||
// malá helper chyba
|
|
||||||
type strErr string
|
|
||||||
|
|
||||||
func (e strErr) Error() string { return string(e) }
|
|
||||||
func errf(s string, a ...any) error { return strErr(fmtS(s, a...)) }
|
|
||||||
func fmtS(format string, a ...any) string {
|
|
||||||
var b bytes.Buffer
|
|
||||||
b.WriteString(format)
|
|
||||||
if len(a) > 0 {
|
|
||||||
b.WriteString(": ")
|
|
||||||
}
|
|
||||||
for i, v := range a {
|
|
||||||
if i > 0 {
|
|
||||||
b.WriteString(", ")
|
|
||||||
}
|
|
||||||
b.WriteString(anyToString(v))
|
|
||||||
}
|
|
||||||
return b.String()
|
|
||||||
}
|
|
||||||
func anyToString(v any) string {
|
|
||||||
switch t := v.(type) {
|
|
||||||
case string:
|
|
||||||
return t
|
|
||||||
default:
|
|
||||||
j, _ := json.Marshal(t)
|
|
||||||
return string(j)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
func loadOrGenerateKeys() error {
|
|
||||||
// existuje private key?
|
|
||||||
if fileExists(privPath) && fileExists(pubPath) && fileExists(certPath) {
|
|
||||||
// načti privátní klíč
|
|
||||||
pkBytes, err := os.ReadFile(privPath)
|
|
||||||
if err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
block, _ := pem.Decode(pkBytes)
|
|
||||||
if block == nil || block.Type != "RSA PRIVATE KEY" {
|
|
||||||
return fmt.Errorf("invalid private key PEM")
|
|
||||||
}
|
|
||||||
key, err := x509.ParsePKCS1PrivateKey(block.Bytes)
|
|
||||||
if err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
priv = key
|
|
||||||
|
|
||||||
// načti public & cert
|
|
||||||
pubPEM, _ = os.ReadFile(pubPath)
|
|
||||||
certPEM, _ = os.ReadFile(certPath)
|
|
||||||
|
|
||||||
log.Println("Načtena existující identita z disku.")
|
|
||||||
return nil
|
|
||||||
}
|
|
||||||
|
|
||||||
// jinak vygeneruj nové
|
|
||||||
key, err := rsa.GenerateKey(rand.Reader, 2048)
|
|
||||||
if err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
priv = key
|
|
||||||
|
|
||||||
// public
|
|
||||||
pubASN1, _ := x509.MarshalPKIXPublicKey(&priv.PublicKey)
|
|
||||||
pubPEM = pem.EncodeToMemory(&pem.Block{Type: "PUBLIC KEY", Bytes: pubASN1})
|
|
||||||
|
|
||||||
// cert
|
|
||||||
certPEM = generateSelfSignedCert(&priv.PublicKey)
|
|
||||||
|
|
||||||
// ulož
|
|
||||||
if err := os.WriteFile(privPath,
|
|
||||||
pem.EncodeToMemory(&pem.Block{Type: "RSA PRIVATE KEY", Bytes: x509.MarshalPKCS1PrivateKey(priv)}),
|
|
||||||
fs.FileMode(0600)); err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
if err := os.WriteFile(pubPath, pubPEM, 0644); err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
if err := os.WriteFile(certPath, certPEM, 0644); err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
|
|
||||||
log.Println("Vygenerována nová identita a uložena na disk.")
|
|
||||||
return nil
|
|
||||||
}
|
|
||||||
|
|
||||||
func fileExists(p string) bool {
|
|
||||||
info, err := os.Stat(p)
|
|
||||||
return err == nil && !info.IsDir()
|
|
||||||
}
|
}
|
||||||
|
|||||||
159
server.go
Normal file
159
server.go
Normal file
@ -0,0 +1,159 @@
|
|||||||
|
package main
|
||||||
|
|
||||||
|
import (
|
||||||
|
"crypto/aes"
|
||||||
|
"crypto/cipher"
|
||||||
|
"crypto/rand"
|
||||||
|
"crypto/rsa"
|
||||||
|
"crypto/sha256"
|
||||||
|
"encoding/base64"
|
||||||
|
"encoding/json"
|
||||||
|
"net/http"
|
||||||
|
|
||||||
|
encrypt "fckeuspy-go/lib"
|
||||||
|
)
|
||||||
|
|
||||||
|
func NewServer() *http.ServeMux {
|
||||||
|
server := &http.ServeMux{}
|
||||||
|
|
||||||
|
// 3) routing
|
||||||
|
server.HandleFunc("/", indexHandler)
|
||||||
|
server.HandleFunc("/public.pem", publicKeyHandler)
|
||||||
|
server.HandleFunc("/public.crt", publicCertHandler)
|
||||||
|
server.HandleFunc("/encrypt", encryptHandler)
|
||||||
|
server.HandleFunc("/decrypt", decryptHandler)
|
||||||
|
server.Handle("/static/", http.StripPrefix("/static/", http.FileServer(http.Dir("static"))))
|
||||||
|
|
||||||
|
// log.Println("Server běží na http://localhost:8080")
|
||||||
|
|
||||||
|
return server
|
||||||
|
}
|
||||||
|
|
||||||
|
func indexHandler(w http.ResponseWriter, r *http.Request) {
|
||||||
|
_ = tmpl.ExecuteTemplate(w, "index.html", nil)
|
||||||
|
}
|
||||||
|
|
||||||
|
func publicKeyHandler(w http.ResponseWriter, r *http.Request) {
|
||||||
|
w.Header().Set("Content-Type", "application/x-pem-file")
|
||||||
|
w.Write(pubPEM)
|
||||||
|
}
|
||||||
|
|
||||||
|
func publicCertHandler(w http.ResponseWriter, r *http.Request) {
|
||||||
|
w.Header().Set("Content-Type", "application/x-pem-file")
|
||||||
|
w.Write(certPEM)
|
||||||
|
}
|
||||||
|
|
||||||
|
func encryptHandler(w http.ResponseWriter, r *http.Request) {
|
||||||
|
if err := r.ParseForm(); err != nil {
|
||||||
|
http.Error(w, "Bad form", http.StatusBadRequest)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
msg := r.Form.Get("message")
|
||||||
|
peer := r.Form.Get("pubkey") // může to být PEM PUBLIC KEY nebo CERT
|
||||||
|
|
||||||
|
pubKey, err := encrypt.ParsePeerPublicKey(peer)
|
||||||
|
if err != nil {
|
||||||
|
http.Error(w, "Neplatný public key/cert: "+err.Error(), http.StatusBadRequest)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
// --- hybrid: AES-GCM + RSA-OAEP(SHA-256) ---
|
||||||
|
// 1) vygeneruj náhodný sym. klíč
|
||||||
|
aesKey := make([]byte, 32) // AES-256
|
||||||
|
if _, err := rand.Read(aesKey); err != nil {
|
||||||
|
http.Error(w, "Rand fail", 500)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
block, err := aes.NewCipher(aesKey)
|
||||||
|
if err != nil {
|
||||||
|
http.Error(w, "AES fail", 500)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
gcm, err := cipher.NewGCM(block)
|
||||||
|
if err != nil {
|
||||||
|
http.Error(w, "GCM fail", 500)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
nonce := make([]byte, gcm.NonceSize())
|
||||||
|
if _, err := rand.Read(nonce); err != nil {
|
||||||
|
http.Error(w, "Nonce fail", 500)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
ciphertext := gcm.Seal(nil, nonce, []byte(msg), nil)
|
||||||
|
|
||||||
|
// 2) zašifruj AES klíč cizím RSA klíčem (OAEP SHA-256)
|
||||||
|
label := []byte{} // prázdný label
|
||||||
|
ek, err := rsa.EncryptOAEP(sha256.New(), rand.Reader, pubKey, aesKey, label)
|
||||||
|
if err != nil {
|
||||||
|
http.Error(w, "RSA-OAEP fail: "+err.Error(), 500)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
env := envelope{
|
||||||
|
EK: base64.StdEncoding.EncodeToString(ek),
|
||||||
|
N: base64.StdEncoding.EncodeToString(nonce),
|
||||||
|
CT: base64.StdEncoding.EncodeToString(ciphertext),
|
||||||
|
}
|
||||||
|
|
||||||
|
payload, _ := json.MarshalIndent(env, "", " ")
|
||||||
|
_ = tmpl.ExecuteTemplate(w, "encrypt.html", string(payload))
|
||||||
|
}
|
||||||
|
|
||||||
|
func decryptHandler(w http.ResponseWriter, r *http.Request) {
|
||||||
|
if err := r.ParseForm(); err != nil {
|
||||||
|
http.Error(w, "Bad form", http.StatusBadRequest)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
in := r.Form.Get("payload")
|
||||||
|
|
||||||
|
var env envelope
|
||||||
|
if err := json.Unmarshal([]byte(in), &env); err != nil {
|
||||||
|
http.Error(w, "Neplatné JSON", 400)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
ek, err := base64.StdEncoding.DecodeString(env.EK)
|
||||||
|
if err != nil {
|
||||||
|
http.Error(w, "ek base64", 400)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
nonce, err := base64.StdEncoding.DecodeString(env.N)
|
||||||
|
if err != nil {
|
||||||
|
http.Error(w, "nonce base64", 400)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
ct, err := base64.StdEncoding.DecodeString(env.CT)
|
||||||
|
if err != nil {
|
||||||
|
http.Error(w, "ct base64", 400)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
// 1) rozšifruj AES klíč naším RSA
|
||||||
|
label := []byte{}
|
||||||
|
aesKey, err := rsa.DecryptOAEP(sha256.New(), rand.Reader, priv, ek, label)
|
||||||
|
if err != nil {
|
||||||
|
http.Error(w, "RSA-OAEP decrypt fail: "+err.Error(), 400)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
block, err := aes.NewCipher(aesKey)
|
||||||
|
if err != nil {
|
||||||
|
http.Error(w, "AES fail", 500)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
gcm, err := cipher.NewGCM(block)
|
||||||
|
if err != nil {
|
||||||
|
http.Error(w, "GCM fail", 500)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
plain, err := gcm.Open(nil, nonce, ct, nil)
|
||||||
|
if err != nil {
|
||||||
|
http.Error(w, "GCM open fail: "+err.Error(), 400)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
_ = tmpl.ExecuteTemplate(w, "decrypt.html", string(plain))
|
||||||
|
}
|
||||||
Loading…
Reference in New Issue
Block a user