Files
runesmith/main.go

262 lines
6.3 KiB
Go
Raw Normal View History

2025-11-20 16:57:34 +01:00
package main
import (
2025-11-21 16:55:38 +01:00
"crypto/x509"
"encoding/pem"
"fmt"
"log"
"os"
2025-11-24 19:51:04 +01:00
certpair "pkcs-generator/modules/cert-pairs"
2025-11-21 16:55:38 +01:00
"slices"
"fyne.io/fyne/v2"
"fyne.io/fyne/v2/app"
"fyne.io/fyne/v2/container"
"fyne.io/fyne/v2/dialog"
"fyne.io/fyne/v2/layout"
"fyne.io/fyne/v2/storage"
2025-11-21 16:55:38 +01:00
"fyne.io/fyne/v2/widget"
2025-11-20 16:57:34 +01:00
)
2025-11-21 22:04:52 +01:00
var (
windowSize fyne.Size = fyne.NewSize(700, 500)
keyTextFilter []string = []string{".key", ".txt", ".pem"}
crtTextFilter []string = []string{".crt", ".cer", ".txt", ".pem"}
2025-11-21 22:04:52 +01:00
)
2025-11-20 16:57:34 +01:00
func main() {
2025-11-21 22:04:52 +01:00
a := app.NewWithID("nl.systemec.pkcs12-generator")
2025-11-21 16:55:38 +01:00
w := a.NewWindow("Systemec PKCS12-Generator")
2025-11-21 22:04:52 +01:00
w.Resize(windowSize)
2025-11-21 16:55:38 +01:00
var keyPath, certPath string
// Labels to show selected filenames
fileLabel1 := widget.NewLabel("No file selected")
fileLabel2 := widget.NewLabel("No file selected")
radioLabel1 := widget.NewLabel("Select which Sectigo Intermediate")
// Certificate Keyfile
2025-11-21 22:04:52 +01:00
keyBtn := widget.NewButton("Upload Private Key File", func() {
keyDiag := dialog.NewFileOpen(func(r fyne.URIReadCloser, err error) {
2025-11-21 16:55:38 +01:00
if r != nil {
fileLabel1.SetText(r.URI().Name())
keyPath = r.URI().Path()
2025-11-21 22:04:52 +01:00
r.Close()
2025-11-21 16:55:38 +01:00
}
}, w)
keyFilter := storage.NewExtensionFileFilter(keyTextFilter)
keyDiag.SetFilter(keyFilter)
2025-11-21 22:04:52 +01:00
keyDiag.Resize(windowSize)
keyDiag.Show()
2025-11-21 16:55:38 +01:00
})
// Certificate file
2025-11-21 22:04:52 +01:00
certBtn := widget.NewButton("Upload Certificate File", func() {
// Use NewFileOpen to get the dialog object
certDiag := dialog.NewFileOpen(func(r fyne.URIReadCloser, err error) {
2025-11-21 16:55:38 +01:00
if r != nil {
fileLabel2.SetText(r.URI().Name())
certPath = r.URI().Path()
2025-11-21 22:04:52 +01:00
r.Close()
2025-11-21 16:55:38 +01:00
}
}, w)
certFilter := storage.NewExtensionFileFilter(crtTextFilter)
certDiag.SetFilter(certFilter)
2025-11-21 22:04:52 +01:00
// Resize the dialog
certDiag.Resize(windowSize)
certDiag.Show()
2025-11-21 16:55:38 +01:00
})
2025-11-24 19:51:04 +01:00
// Certificate Intermediate selector
// Basic in form but can easily be expanded.
var sectigo2025 bool
2025-11-24 16:55:16 +01:00
caRadio := widget.NewRadioGroup([]string{"New Sectigo (2025-03-22+)", "Old Sectigo (2025-03-22-)"}, func(selected string) {
2025-11-21 16:55:38 +01:00
switch selected {
case "New Sectigo (2025+)":
2025-11-24 19:51:04 +01:00
sectigo2025 = true
2025-11-21 16:55:38 +01:00
case "Old Sectigo (2025-)":
2025-11-24 19:51:04 +01:00
sectigo2025 = false
2025-11-21 16:55:38 +01:00
default: //Fallback
2025-11-24 19:51:04 +01:00
sectigo2025 = true
2025-11-21 16:55:38 +01:00
}
})
caRadio.SetSelected("New Sectigo (2025+)") // default
// Make a text grid to display text.
grid := widget.NewTextGrid()
grid.SetText("Status will appear here.")
2025-11-21 16:55:38 +01:00
actionBtn := widget.NewButton("Generate", func() {
2025-11-21 16:55:38 +01:00
files := []string{keyPath, certPath}
2025-11-24 19:51:04 +01:00
// The following structure is basic but can easily be expanded to fit more pairs
// 0 = ROOT
// 1 = CA
// As per spec in the certpair module.
var certPair []string
if sectigo2025 {
certPair = certpair.SectigoNewChain
2025-11-21 16:55:38 +01:00
} else {
2025-11-24 19:51:04 +01:00
certPair = certpair.SectigoOldChain
2025-11-21 16:55:38 +01:00
}
allPresent := true
if slices.Contains(files, "") {
allPresent = false
}
if !allPresent {
fmt.Println("One or more files missing!")
return
}
2025-11-24 19:51:04 +01:00
respText, pfxData := integrityCheckAndGo(keyPath, certPath, certPair[1], certPair[0])
2025-11-24 16:55:16 +01:00
if pfxData == nil {
log.Println(respText)
grid.SetText(respText)
return
}
// Show Save File dialog immediately
svDialog := dialog.NewFileSave(
func(writer fyne.URIWriteCloser, err error) {
if err != nil {
dialog.ShowError(err, w)
return
}
if writer == nil {
// User cancelled
return
}
_, writeErr := writer.Write(pfxData)
if writeErr != nil {
dialog.ShowError(writeErr, w)
return
}
writer.Close()
var dnText string = "PKCS file saved to: " + writer.URI().Path()
log.Println(dnText)
grid.SetText(dnText)
}, w)
svDialog.Resize(windowSize)
svDialog.SetFileName("certificate_store.pfx")
svDialog.SetFilter(storage.NewExtensionFileFilter([]string{".pfx"}))
svDialog.Show()
2025-11-21 16:55:38 +01:00
})
cancelBtn := widget.NewButton("Exit", func() {
fmt.Println("Quitting...")
os.Exit(0)
})
// CREATE LAYOUTS
bottom := container.NewHBox(
actionBtn, // left
layout.NewSpacer(), // flexible space
cancelBtn, // right
)
centerContent := container.NewVBox(
widget.NewLabel("Select relevant files."),
2025-11-21 22:04:52 +01:00
container.New(layout.NewGridLayout(2), keyBtn, fileLabel1),
container.New(layout.NewGridLayout(2), certBtn, fileLabel2),
widget.NewLabel("\n"),
2025-11-21 16:55:38 +01:00
container.New(layout.NewGridLayout(2), radioLabel1, caRadio),
layout.NewSpacer(), // optional flexible space
grid, // Add the referenced text grid container
widget.NewLabel(""), // Add empty line for space
2025-11-21 16:55:38 +01:00
)
content := container.NewBorder(
nil, // top
bottom, // bottom
nil, nil, // left, right
centerContent,
)
w.SetContent(content)
w.ShowAndRun()
}
func readFile(path string) []byte {
rawData, err := os.ReadFile(path)
if err != nil {
fmt.Printf("Error reading contents of %s, %e", path, err)
return nil
} else {
return rawData
}
}
func parsePrivateKey(keyData []byte) any {
block, _ := pem.Decode(keyData)
if block == nil {
log.Fatal("failed to decode key PEM")
}
privateKey, err := x509.ParsePKCS8PrivateKey(block.Bytes)
if err != nil {
log.Fatal(err)
}
return privateKey
}
func parseX509(certData []byte) *x509.Certificate {
block, _ := pem.Decode(certData)
if block == nil {
log.Fatal("failed to parse PEM block")
}
cert, err := x509.ParseCertificate(block.Bytes)
if err != nil {
log.Fatal(err)
}
return cert
}
func integrityCheckAndGo(keyPath, certPath, caCertString, caRootString string) (string, []byte) {
2025-11-21 16:55:38 +01:00
if _, err := os.Stat(keyPath); err != nil {
fmt.Printf("Error checking Private Keyfile, %e", err)
return "Error...", nil
2025-11-21 16:55:38 +01:00
}
if _, err := os.Stat(certPath); err != nil {
fmt.Printf("Error checking Certificate file, %e", err)
return "Error...", nil
2025-11-21 16:55:38 +01:00
}
keyData := readFile(keyPath) // Read the private key file (PEM/DER)
certData := readFile(certPath) // Read the certificate file (PEM/DER)
key := parsePrivateKey(keyData) // Convert bytes to Go private key object
cert := parseX509(certData) // Convert bytes to Go x509.Certificate
caCert := parseX509([]byte(caCertString))
rootCert := parseX509([]byte(caRootString))
2025-11-21 16:55:38 +01:00
checkPublicKey(cert) // Print the information about the key
if checkCertKeyPair(cert.PublicKey, key) {
2025-11-24 16:55:16 +01:00
log.Println("Private key matches certificate")
2025-11-21 16:55:38 +01:00
} else {
2025-11-24 16:55:16 +01:00
return "Private key does NOT match certificate", nil
2025-11-21 16:55:38 +01:00
}
caCertList := []*x509.Certificate{caCert, rootCert}
2025-11-21 16:55:38 +01:00
return generatePKCS12(key, cert, caCertList)
2025-11-20 16:57:34 +01:00
}