Initial commit

This commit is contained in:
Zorchenhimer 2024-06-05 17:01:17 -04:00
commit 2fbb3215bd
6 changed files with 527 additions and 0 deletions

2
.gitignore vendored Normal file
View File

@ -0,0 +1,2 @@
certs/
*.exe

19
Makefile Normal file
View File

@ -0,0 +1,19 @@
PARTS= root server client
KEYS = $(addprefix certs/,$(addsuffix .key,$(PARTS)))
CERTS= $(addprefix certs/,$(addsuffix .pem,$(PARTS)))
all: certs/ $(KEYS) $(CERTS)
certs/:
-mkdir certs
certs/root.pem certs/root.key &:
go run generate.go root certs/root
certs/server.pem certs/server.key &: certs/root.pem certs/root.key
go run generate.go server certs/server certs/root
certs/client.pem certs/client.key &: certs/root.pem certs/root.key
go run generate.go client certs/client certs/root

53
client.go Normal file
View File

@ -0,0 +1,53 @@
package main
import (
"fmt"
"net/http"
"io"
"crypto/tls"
"crypto/x509"
"os"
)
func main() {
rawpem, err := os.ReadFile("certs/root.pem")
if err != nil {
fmt.Println("pem read error:", err)
return
}
cert, err := tls.LoadX509KeyPair("certs/client.pem", "certs/client.key")
if err != nil {
fmt.Println("cert load error:", err)
return
}
pool := x509.NewCertPool()
if !pool.AppendCertsFromPEM(rawpem) {
fmt.Println("add pem not ok")
return
}
client := &http.Client{
Transport: &http.Transport{
TLSClientConfig: &tls.Config{
RootCAs: pool,
Certificates: []tls.Certificate{cert},
},
},
}
resp, err := client.Get("https://localhost:8080")
if err != nil {
fmt.Println("get error:", err)
return
}
raw, err := io.ReadAll(resp.Body)
if err != nil {
fmt.Println("readall error:", err)
return
}
fmt.Println(">", string(raw))
}

355
generate.go Normal file
View File

@ -0,0 +1,355 @@
package main
import (
"crypto/rsa"
"crypto/rand"
"crypto/x509"
"crypto/x509/pkix"
"encoding/pem"
"time"
"os"
"math/big"
"fmt"
"net"
"github.com/alexflint/go-arg"
)
func run(args *Arguments) error {
switch args.Type {
case "root":
return selfSign(args.Name)
case "server":
if args.Signing == "" {
return fmt.Errorf("Signing key required")
}
return serverCert(args.Name, args.Signing)
case "client":
if args.Signing == "" {
return fmt.Errorf("Signing key required")
}
return clientCert(args.Name, args.Signing)
case "verify":
//if args.Signing == "" {
// return fmt.Errorf("root cert required")
//}
return verify(args.Name, args.Signing)
}
return fmt.Errorf("unknown type: %s", args.Type)
}
func verify(target, root string) error {
rawcert, err := os.ReadFile(target+".pem")
if err != nil {
return err
}
block, _ := pem.Decode(rawcert)
cert, err := x509.ParseCertificate(block.Bytes)
if err != nil {
return err
}
fmt.Printf("cert pubkey type: %T\n", cert.PublicKey)
switch k := cert.PublicKey.(type) {
case *rsa.PublicKey:
size := k.N.BitLen();
pub := cert.PublicKey.(*rsa.PublicKey)
fmt.Printf("pubkey size: %d (%d)\n", pub.Size(), size)
default:
}
pool := x509.NewCertPool()
//rawroot, err := os.ReadFile(root+".pem")
//if err != nil {
// return err
//}
//block, _ := pem.Decode(rawroot)
//rootcert, err := x509.ParseCertificate(block.Bytes)
//if err != nil {
// return err
//}
if root != "" {
fmt.Println("loading "+root+".pem")
rootRaw, err := os.ReadFile(root+".pem")
if err != nil {
return err
}
if !pool.AppendCertsFromPEM(rootRaw) {
return fmt.Errorf("add pem not ok")
}
}
fmt.Printf("primary: %d (%08b)\n", cert.KeyUsage, cert.KeyUsage)
for _, usage := range cert.ExtKeyUsage {
fmt.Printf("> %d (%08b)\n", usage, usage)
}
chains, err := cert.Verify(x509.VerifyOptions{
Roots: pool,
KeyUsages: []x509.ExtKeyUsage{
x509.ExtKeyUsageClientAuth,
},
})
fmt.Println("len(chains):", len(chains))
if err != nil {
return err
}
fmt.Println("verify OK")
return nil
}
func selfSign(name string) error {
notBefore := time.Now()
notAfter := notBefore.Add(90*24*time.Hour)
/*
Root Cert
*/
rootkey, err := rsa.GenerateKey(rand.Reader, 4096)
if err != nil {
return err
}
err = os.WriteFile(name+".key", pem.EncodeToMemory(&pem.Block{Type:"RSA PRIVATE KEY", Bytes: x509.MarshalPKCS1PrivateKey(rootkey)}), 0700)
if err != nil {
return err
}
rootTemplate := &x509.Certificate{
SerialNumber: big.NewInt(1),
Subject: pkix.Name{
Organization: []string{"Zorchenhimer LLC"},
CommonName: "Root CA",
},
NotBefore: notBefore,
NotAfter: notAfter,
KeyUsage: x509.KeyUsageCertSign,
ExtKeyUsage: []x509.ExtKeyUsage{x509.ExtKeyUsageServerAuth},
BasicConstraintsValid: true,
IsCA: true,
}
rootcert, err := x509.CreateCertificate(rand.Reader, rootTemplate, rootTemplate, &rootkey.PublicKey, rootkey)
if err != nil {
return err
}
err = os.WriteFile(name+".pem", pem.EncodeToMemory(
&pem.Block{
Type:"CERTIFICATE",
Bytes: rootcert,
}), 0700)
if err != nil {
return err
}
return nil
}
func loadRoot(name string) (*x509.Certificate, *pem.Block, *rsa.PrivateKey, error) {
rawroot, err := os.ReadFile(name+".pem")
if err != nil {
return nil, nil, nil, err
}
rootpem, _ := pem.Decode(rawroot)
rootcert, err := x509.ParseCertificate(rootpem.Bytes)
if err != nil {
return nil, nil, nil, err
}
rawkey, err := os.ReadFile(name+".key")
if err != nil {
return nil, nil, nil, err
}
block, _ := pem.Decode(rawkey)
rootkey, err := x509.ParsePKCS1PrivateKey(block.Bytes)
if err != nil {
return nil, nil, nil, err
}
return rootcert, rootpem, rootkey, nil
}
func serverCert(name, rootname string) error {
rootcert, _, rootkey, err := loadRoot(rootname)
if err != nil {
return err
}
notBefore := time.Now()
notAfter := notBefore.Add(90*24*time.Hour)
/*
Server Cert
*/
serverkey, err := rsa.GenerateKey(rand.Reader, 4096)
if err != nil {
return err
}
err = os.WriteFile(name+".key",
pem.EncodeToMemory(&pem.Block{
Type:"RSA PRIVATE KEY",
Bytes: x509.MarshalPKCS1PrivateKey(serverkey),
}), 0700)
if err != nil {
return err
}
serverTemplate := &x509.Certificate{
SerialNumber: big.NewInt(2),
Subject: pkix.Name{
Organization: []string{"Zorchenhimer LLC"},
CommonName: "Server Cert",
},
NotBefore: notBefore,
NotAfter: notAfter,
KeyUsage: x509.KeyUsageDigitalSignature |
x509.KeyUsageKeyEncipherment,
ExtKeyUsage: []x509.ExtKeyUsage{
x509.ExtKeyUsageServerAuth,
x509.ExtKeyUsageClientAuth,
},
BasicConstraintsValid: true,
IsCA: false,
DNSNames: []string{
"localhost",
"ryuko.local",
},
IPAddresses: []net.IP{
net.ParseIP("127.0.0.1"),
net.ParseIP("192.168.1.10"),
},
}
servercert, err := x509.CreateCertificate(
rand.Reader, serverTemplate, rootcert, &serverkey.PublicKey, rootkey)
if err != nil {
return err
}
err = os.WriteFile(name+".pem",
pem.EncodeToMemory(&pem.Block{Type:"CERTIFICATE", Bytes: servercert}), 0700)
if err != nil {
return err
}
return nil
}
func clientCert(name, root string) error {
rootcert, rootblock, rootkey, err := loadRoot(root)
if err != nil {
return err
}
notBefore := time.Now()
notAfter := notBefore.Add(90*24*time.Hour)
/*
Client Cert
*/
clientkey, err := rsa.GenerateKey(rand.Reader, 4096)
if err != nil {
return err
}
err = os.WriteFile(name+".key", pem.EncodeToMemory(
&pem.Block{
Type:"RSA PRIVATE KEY",
Bytes: x509.MarshalPKCS1PrivateKey(clientkey),
}), 0700)
if err != nil {
return err
}
clientTemplate := &x509.Certificate{
SerialNumber: big.NewInt(3),
Subject: pkix.Name{
Organization: []string{"Zorchenhimer LLC"},
CommonName: "Client Cert",
},
NotBefore: notBefore,
NotAfter: notAfter,
//KeyUsage: x509.KeyUsageDigitalSignature |
// x509.KeyUsageDataEncipherment |
// x509.KeyUsageKeyEncipherment |
// x509.KeyUsageContentCommitment |
// x509.KeyUsageKeyAgreement,
KeyUsage: x509.KeyUsageKeyEncipherment |
x509.KeyUsageDigitalSignature,
ExtKeyUsage: []x509.ExtKeyUsage{
//x509.ExtKeyUsageAny,
x509.ExtKeyUsageClientAuth,
},
BasicConstraintsValid: true,
IsCA: false,
IPAddresses: []net.IP{
net.ParseIP("127.0.0.1"),
},
}
clientcert, err := x509.CreateCertificate(
rand.Reader, clientTemplate, rootcert, &clientkey.PublicKey, rootkey)
if err != nil {
return err
}
file, err := os.Create(name+".pem")
if err != nil {
return err
}
defer file.Close()
err = pem.Encode(file, &pem.Block{
Type:"CERTIFICATE",
Bytes: clientcert,
})
if err != nil {
return err
}
//err = pem.Encode(file, &pem.Block{
// Type: "PUBLIC KEY",
// Bytes: clientkey.Pubkey
//})
err = pem.Encode(file, rootblock)
if err != nil {
return err
}
//err = os.WriteFile(name+".pem",
// pem.EncodeToMemory(&pem.Block{Type:"CERTIFICATE", Bytes: clientcert}), 0700)
//if err != nil {
// return err
//}
return nil
}
type Arguments struct {
Type string `arg:"positional,required"`
Name string `arg:"positional,required"`
Signing string `arg:"positional"`
}
func main() {
args := &Arguments{}
arg.MustParse(args)
err := run(args)
if err != nil {
fmt.Fprintln(os.Stderr, err)
os.Exit(1)
}
}

54
server.go Normal file
View File

@ -0,0 +1,54 @@
package main
import (
"fmt"
"net/http"
"crypto/tls"
"crypto/x509"
"os"
)
func main() {
mux := &http.ServeMux{}
mux.HandleFunc("/", handle_default)
serverCert, err := tls.LoadX509KeyPair("certs/server.pem", "certs/server.key")
if err != nil {
fmt.Println("cert load error:", err)
return
}
rootRaw, err := os.ReadFile("certs/root.pem")
if err != nil {
fmt.Println("root cert read error:", err)
return
}
pool := x509.NewCertPool()
if !pool.AppendCertsFromPEM(rootRaw) {
fmt.Println("add pem not ok")
return
}
sv := &http.Server{
Addr: ":8080",
Handler: mux,
TLSConfig: &tls.Config{
MinVersion: tls.VersionTLS13,
PreferServerCipherSuites: true,
Certificates: []tls.Certificate{serverCert},
ClientAuth: tls.RequireAndVerifyClientCert,
//ClientAuth: tls.RequireAnyClientCert,
ClientCAs: pool,
},
}
//sv.ListenAndServeTLS("certs/server.crt", "certs/server.key")
fmt.Println("starting...")
err = sv.ListenAndServeTLS("", "")
fmt.Println(err)
}
func handle_default(w http.ResponseWriter, req *http.Request) {
fmt.Fprintln(w, "hello.")
}

44
show.go Normal file
View File

@ -0,0 +1,44 @@
package main
import (
"fmt"
"os"
"encoding/pem"
"crypto/x509"
"github.com/alexflint/go-arg"
)
func run(args *Arguments) error {
raw, err := os.ReadFile(args.Input)
if err != nil {
return err
}
block, rest := pem.Decode(raw)
fmt.Println(block.Type)
fmt.Println("len(rest):", len(rest))
cert, err := x509.ParseCertificate(block.Bytes)
if err != nil {
return err
}
fmt.Println(cert)
return nil
}
type Arguments struct {
Input string `arg:"positional,required"`
}
func main() {
args := &Arguments{}
arg.MustParse(args)
err := run(args)
if err != nil {
fmt.Fprintln(os.Stderr, err)
os.Exit(1)
}
}