pgp-key-management/server.go

335 lines
6.9 KiB
Go
Raw Normal View History

2024-08-04 14:34:41 -07:00
package main
import (
"fmt"
"os"
"net/http"
"syscall"
"os/signal"
"time"
"context"
//"encoding/json"
"strings"
"github.com/ProtonMail/go-crypto/openpgp"
//"github.com/ProtonMail/go-crypto/openpgp/packet"
)
type Server struct {
PublicKeys openpgp.EntityList
PrivateKeys openpgp.EntityList
KeyMap map[string]*openpgp.Entity
2024-08-04 14:34:41 -07:00
//PubkeyGroups []*openpgp.Entity
}
func NewServer(publicKeys, privateKeys []string) (*Server, error) {
s := &Server{KeyMap: make(map[string]*openpgp.Entity)}
pub, err := s.loadKeysFromFiles(publicKeys)
2024-08-04 14:34:41 -07:00
if err != nil {
return nil, err
}
s.PublicKeys = pub
fmt.Println("Public keys:")
for _, k := range pub {
for name, _ := range k.Identities {
fmt.Println("\t"+name)
}
fmt.Printf("\t%X\n", k.PrimaryKey.KeyId)
if len(k.Subkeys) == 0 {
continue
}
fmt.Println("\tSubkeys:")
for _, sub := range k.Subkeys {
str := []string{}
if sub.PublicKey != nil {
str = append(str, fmt.Sprintf("public: %X", sub.PublicKey.KeyId))
}
if sub.PrivateKey != nil {
str = append(str, fmt.Sprintf("private: %X", sub.PrivateKey.KeyId))
}
fmt.Println("\t\t"+strings.Join(str, " / "))
}
}
priv, err := s.loadKeysFromFiles(privateKeys)
2024-08-04 14:34:41 -07:00
if err != nil {
return nil, err
}
s.PrivateKeys = priv
fmt.Println("Private keys:")
for _, k := range priv {
for name, _ := range k.Identities {
fmt.Println("\t"+name)
}
fmt.Printf("\t%X\n", k.PrimaryKey.KeyId)
if len(k.Subkeys) == 0 {
continue
}
fmt.Println("\tSubkeys:")
for _, sub := range k.Subkeys {
str := []string{}
if sub.PublicKey != nil {
str = append(str, fmt.Sprintf("public: %X", sub.PublicKey.KeyId))
}
if sub.PrivateKey != nil {
str = append(str, fmt.Sprintf("private: %X", sub.PrivateKey.KeyId))
}
fmt.Println("\t\t"+strings.Join(str, " / "))
}
}
return s, nil
2024-08-04 14:34:41 -07:00
}
func (s *Server) loadKeysFromFiles(filenames []string) (openpgp.EntityList, error) {
2024-08-04 14:34:41 -07:00
var keyring openpgp.EntityList
for _, f := range filenames {
file, err := os.Open(f)
if err != nil {
return nil, err
}
keys, err := openpgp.ReadArmoredKeyRing(file)
if err != nil {
file.Close()
return nil, err
}
file.Close()
for _, key := range keys {
keyring = append(keyring, key)
s.KeyMap[fmt.Sprintf("%x", key.PrimaryKey.KeyId)] = key
for _, sub := range key.Subkeys {
if sub.PublicKey != nil {
s.KeyMap[fmt.Sprintf("%x", sub.PublicKey.KeyId)] = key
}
if sub.PrivateKey != nil {
s.KeyMap[fmt.Sprintf("%x", sub.PrivateKey.KeyId)] = key
}
}
}
2024-08-04 14:34:41 -07:00
}
return keyring, nil
}
func (s *Server) handler_publickey(w http.ResponseWriter, r *http.Request) {
path := strings.Split(strings.Trim(r.URL.Path, "/"), "/")
if len(path) < 2 {
fmt.Fprintln(w, "missing path stuff")
return
}
fmt.Println(r.URL.Path)
search := strings.ToLower(path[1])
fmt.Println("searching for", search)
key := s.findPubKey(search)
if key == nil {
http.Error(w, fmt.Sprintf("Key not found; searched for %q", search), 500)
2024-08-04 14:34:41 -07:00
return
}
err := key.Serialize(w)
if err != nil {
fmt.Printf("Serialization error: %s\n", err)
}
}
func (s *Server) findPubKey(search string) *openpgp.Entity {
if k, found := s.KeyMap[search]; found {
for _, sub := range k.Subkeys {
if sub.PublicKey != nil && fmt.Sprintf("%x", sub.PublicKey.KeyId) == search {
return k
}
}
}
2024-08-04 14:34:41 -07:00
for _, k := range s.PublicKeys {
for _, id := range k.Identities {
if strings.ToLower(id.UserId.Name) == search {
return k
}
if strings.ToLower(id.UserId.Email) == search {
return k
}
}
}
return nil
}
func (s *Server) handler_privatekey(w http.ResponseWriter, r *http.Request) {
path := strings.Split(strings.Trim(r.URL.Path, "/"), "/")
if len(path) < 2 {
fmt.Fprintln(w, "missing path stuff")
return
}
fmt.Println(r.URL.Path)
search := strings.ToLower(path[1])
fmt.Println("searching for", search)
key := s.findPrivKey(search)
if key == nil {
http.Error(w, "Key not found", 500)
return
}
err := key.SerializePrivate(w, nil)
if err != nil {
fmt.Printf("Serialization error: %s\n", err)
}
}
func (s *Server) findPrivKey(search string) *openpgp.Entity {
if k, found := s.KeyMap[search]; found {
if k.PrivateKey != nil && fmt.Sprintf("%x", k.PrivateKey.KeyId) == search {
return k
}
for _, sub := range k.Subkeys {
if sub.PrivateKey != nil && fmt.Sprintf("%x", sub.PrivateKey.KeyId) == search {
return k
}
}
}
2024-08-04 14:34:41 -07:00
for _, k := range s.PrivateKeys {
fmt.Printf("%X\n", k.PrimaryKey.KeyId)
2024-08-04 14:34:41 -07:00
for _, id := range k.Identities {
if strings.ToLower(id.UserId.Name) == search {
return k
}
if strings.ToLower(id.UserId.Email) == search {
return k
}
}
}
return nil
}
func (s *Server) handler_debug(w http.ResponseWriter, r *http.Request) {
fmt.Fprintln(w, "Public Keys:")
for _, key := range s.PublicKeys {
//fmt.Fprintf(w, "%#v\n", key.PrimaryIdentity())
fmt.Fprintf(w,"%s\n\t%X\n\t%X\n",
key.PrimaryIdentity().Name,
key.PrimaryKey.Fingerprint,
key.PrimaryKey.KeyId,
)
//j, err := json.Marshal(key)
//if err != nil {
// fmt.Fprintf(w, "ERROR: %s", err)
// return
//}
//fmt.Fprintf(w,"\t\t%s\n", j)
}
fmt.Fprintln(w, "\nPrivate Keys:")
for _, key := range s.PrivateKeys {
//fmt.Fprintf(w, "%#v\n", key.PrimaryIdentity())
fmt.Fprintf(w,"%s\n\t%X\n\t%X\n",
key.PrimaryIdentity().Name,
key.PrimaryKey.Fingerprint,
key.PrivateKey.KeyId,
)
for _, sub := range key.Subkeys {
fmt.Fprintf(w, "\t>%X\n\t>%X\n",
sub.PublicKey.KeyId,
sub.PrivateKey.KeyId,
)
}
}
}
func handler_default(w http.ResponseWriter, r *http.Request) {
fmt.Fprintln(w, "henlo")
}
func run() error {
sv, err := NewServer(
[]string{
//"keys/gpg-generated/cust-a.pub",
//"keys/gpg-generated/cust-b.pub",
//"keys/gpg-generated/internal-public.asc",
"keys/public/Customer.asc",
"keys/public/Company.asc",
2024-08-04 14:34:41 -07:00
},
[]string{
//"keys/gpg-generated/internal-private.asc",
"keys/private/Company.asc",
2024-08-04 14:34:41 -07:00
},
)
if err != nil {
return err
}
mux := http.NewServeMux()
mux.HandleFunc("/pubkey/", sv.handler_publickey)
mux.HandleFunc("/privkey/", sv.handler_privatekey)
mux.HandleFunc("/debug/", sv.handler_debug)
mux.HandleFunc("/", handler_default)
server := &http.Server{
Addr: ":8080",
Handler: mux,
ReadTimeout: 10 * time.Second,
WriteTimeout: 10 * time.Second,
MaxHeaderBytes: 1 << 20,
}
done := make(chan os.Signal, 1)
signal.Notify(done, os.Interrupt, syscall.SIGINT, syscall.SIGTERM)
go func() {
fmt.Println("listening on", server.Addr)
if err := server.ListenAndServe(); err != nil && err != http.ErrServerClosed {
fmt.Println("Listen: ", err)
}
}()
<-done
fmt.Println("stopping")
ctx, cancel := context.WithTimeout(context.Background(), 5*time.Second)
defer cancel()
if err := server.Shutdown(ctx); err != nil {
fmt.Println("Clean shutdown failed: ", err)
}
fmt.Println("goodbye")
return nil
}
func main() {
err := run()
if err != nil {
fmt.Fprintln(os.Stderr, err)
os.Exit(1)
}
}