356 lines
7.2 KiB
Go
356 lines
7.2 KiB
Go
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)
|
|
}
|
|
}
|