|
|
|
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
|
|
|
|
}
|