You cannot select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
star/keygen/keygen.go

226 lines
5.6 KiB
Go

package keygen
import (
"b612.me/starcrypto"
"b612.me/staros"
"crypto"
"crypto/ecdsa"
"crypto/ed25519"
"crypto/elliptic"
"crypto/rand"
"crypto/rsa"
"crypto/x509"
"crypto/x509/pkix"
"encoding/hex"
"encoding/pem"
"errors"
"fmt"
"golang.org/x/crypto/ssh"
"io"
"math/big"
"os"
"path/filepath"
"strings"
"time"
)
type KeyGen struct {
Type string
Encrypt string
Bits int
Outfolder string
Prefix string
Force bool
//
Country string
Locality string
Organization string
OrganizationalUnit string
CommonName string
StartDate time.Time
EndDate time.Time
}
func (k *KeyGen) Gen() error {
if !k.Force && staros.Exists(filepath.Join(k.Outfolder, k.Prefix+".crt")) {
return errors.New("crt file exists")
}
if !k.Force && staros.Exists(filepath.Join(k.Outfolder, k.Prefix)) {
return errors.New("key file exists")
}
if !k.Force && staros.Exists(filepath.Join(k.Outfolder, k.Prefix+".pub")) {
return errors.New("ssh pub file exists")
}
if !k.Force && staros.Exists(filepath.Join(k.Outfolder, k.Prefix+".openssh")) {
return errors.New("ssh priv file exists")
}
if !k.Force && staros.Exists(filepath.Join(k.Outfolder, k.Prefix+".key.pub")) {
return errors.New("pub file exists")
}
var sshPubByte, sshPrivByte, keyPubByte, keyPrivByte, Crt []byte
var priv, pub any
var err error
switch strings.ToLower(k.Type) {
case "rsa":
priv, pub, err = starcrypto.GenerateRsaKey(k.Bits)
if err != nil {
return err
}
case "ecdsa", "ecdh":
var cr elliptic.Curve
switch k.Bits {
case 224:
cr = elliptic.P224()
case 256:
cr = elliptic.P256()
case 384:
cr = elliptic.P384()
case 521:
cr = elliptic.P521()
default:
return errors.New("invalid bits,should be 224,256,384,521")
}
priv, pub, err = starcrypto.GenerateEcdsaKey(cr)
if err != nil {
return err
}
case "ed25519":
pub, priv, err = ed25519.GenerateKey(rand.Reader)
if err != nil {
return err
}
default:
return errors.New("invalid key type,only support rsa,ecdsa")
}
sshPubByte, err = starcrypto.EncodeSSHPublicKey(pub)
if err != nil {
return err
}
keyPubByte, err = starcrypto.EncodePublicKey(pub)
if err != nil {
return err
}
keyPrivByte, err = starcrypto.EncodePrivateKey(priv, k.Encrypt)
if err != nil {
return err
}
var block *pem.Block
if k.Encrypt != "" {
block, err = ssh.MarshalPrivateKey(priv, "")
} else {
block, err = ssh.MarshalPrivateKeyWithPassphrase(priv, "", []byte(k.Encrypt))
}
if err != nil {
return err
}
sshPrivByte = pem.EncodeToMemory(block)
if err != nil {
return err
}
_, Crt, err = k.GenerateCert(priv)
if err != nil {
return err
}
err = os.WriteFile(filepath.Join(k.Outfolder, k.Prefix+".openssh"), sshPrivByte, 0644)
if err != nil {
return err
}
err = os.WriteFile(filepath.Join(k.Outfolder, k.Prefix+".crt"), Crt, 0644)
if err != nil {
return err
}
err = os.WriteFile(filepath.Join(k.Outfolder, k.Prefix), keyPrivByte, 0644)
if err != nil {
return err
}
err = os.WriteFile(filepath.Join(k.Outfolder, k.Prefix+".pub"), sshPubByte, 0644)
if err != nil {
return err
}
err = os.WriteFile(filepath.Join(k.Outfolder, k.Prefix+".key.pub"), keyPubByte, 0644)
if err != nil {
return err
}
return nil
}
func (k *KeyGen) GenerateCert(priv crypto.PrivateKey) ([]byte, []byte, error) {
//csr,pub
tmpByte := make([]byte, 64)
io.ReadFull(rand.Reader, tmpByte)
hexStr := starcrypto.String(tmpByte)
data, _ := hex.DecodeString(hexStr)
num := new(big.Int).SetBytes(data)
var country, locality, organization, organizationalUnit []string
if k.Country != "" {
country = []string{k.Country}
}
if k.Locality != "" {
locality = []string{k.Locality}
}
if k.Organization != "" {
organization = []string{k.Organization}
}
if k.OrganizationalUnit != "" {
organizationalUnit = []string{k.OrganizationalUnit}
}
var rootCsr = &x509.Certificate{
Version: 3,
SerialNumber: num,
Subject: pkix.Name{
Country: country,
Locality: locality,
Organization: organization,
OrganizationalUnit: organizationalUnit,
CommonName: k.CommonName,
},
NotBefore: k.StartDate,
NotAfter: k.EndDate,
BasicConstraintsValid: true,
IsCA: false,
MaxPathLenZero: false,
ExtKeyUsage: []x509.ExtKeyUsage{x509.ExtKeyUsageAny},
KeyUsage: x509.KeyUsageCertSign | x509.KeyUsageCRLSign | x509.KeyUsageKeyEncipherment | x509.KeyUsageKeyAgreement | x509.KeyUsageDigitalSignature,
}
var cert []byte
var err error
switch priv.(type) {
case *rsa.PrivateKey:
cert, err = MakeCert(priv.(*rsa.PrivateKey), rootCsr, rootCsr, priv.(*rsa.PrivateKey).Public())
if err != nil {
return nil, nil, err
}
case *ecdsa.PrivateKey:
cert, err = MakeCert(priv.(*ecdsa.PrivateKey), rootCsr, rootCsr, priv.(*ecdsa.PrivateKey).Public())
case ed25519.PrivateKey:
cert, err = MakeCert(priv.(ed25519.PrivateKey), rootCsr, rootCsr, priv.(ed25519.PrivateKey).Public())
default:
return nil, nil, errors.New("invalid private key type:" + fmt.Sprintf("%T", priv))
}
csrPem := pem.EncodeToMemory(&pem.Block{
Type: "CERTIFICATE REQUEST",
Bytes: rootCsr.Raw,
})
return csrPem, cert, nil
}
func MakeCert(caKey any, caCrt *x509.Certificate, csr *x509.Certificate, pub any) ([]byte, error) {
der, err := x509.CreateCertificate(rand.Reader, csr, caCrt, pub, caKey)
if err != nil {
return nil, err
}
cert, err := x509.ParseCertificate(der)
if err != nil {
return nil, err
}
certBlock := &pem.Block{
Type: "CERTIFICATE",
Bytes: cert.Raw,
}
pemData := pem.EncodeToMemory(certBlock)
return pemData, nil
}