mirror of https://github.com/Xhofe/alist
106 lines
2.8 KiB
Go
106 lines
2.8 KiB
Go
package sftp
|
|
|
|
import (
|
|
"crypto/rand"
|
|
"crypto/rsa"
|
|
"crypto/x509"
|
|
"encoding/pem"
|
|
"fmt"
|
|
"github.com/alist-org/alist/v3/cmd/flags"
|
|
"github.com/alist-org/alist/v3/pkg/utils"
|
|
"golang.org/x/crypto/ssh"
|
|
"os"
|
|
"path/filepath"
|
|
)
|
|
|
|
var SSHSigners []ssh.Signer
|
|
|
|
func InitHostKey() {
|
|
if SSHSigners != nil {
|
|
return
|
|
}
|
|
sshPath := filepath.Join(flags.DataDir, "ssh")
|
|
if !utils.Exists(sshPath) {
|
|
err := utils.CreateNestedDirectory(sshPath)
|
|
if err != nil {
|
|
utils.Log.Fatalf("failed to create ssh directory: %+v", err)
|
|
return
|
|
}
|
|
}
|
|
SSHSigners = make([]ssh.Signer, 0, 4)
|
|
if rsaKey, ok := LoadOrGenerateRSAHostKey(sshPath); ok {
|
|
SSHSigners = append(SSHSigners, rsaKey)
|
|
}
|
|
// TODO Add keys for other encryption algorithms
|
|
}
|
|
|
|
func LoadOrGenerateRSAHostKey(parentDir string) (ssh.Signer, bool) {
|
|
privateKeyPath := filepath.Join(parentDir, "ssh_host_rsa_key")
|
|
publicKeyPath := filepath.Join(parentDir, "ssh_host_rsa_key.pub")
|
|
privateKeyBytes, err := os.ReadFile(privateKeyPath)
|
|
if err == nil {
|
|
var privateKey *rsa.PrivateKey
|
|
privateKey, err = rsaDecodePrivateKey(privateKeyBytes)
|
|
if err == nil {
|
|
var ret ssh.Signer
|
|
ret, err = ssh.NewSignerFromKey(privateKey)
|
|
if err == nil {
|
|
return ret, true
|
|
}
|
|
}
|
|
}
|
|
_ = os.Remove(privateKeyPath)
|
|
_ = os.Remove(publicKeyPath)
|
|
privateKey, err := rsa.GenerateKey(rand.Reader, 4096)
|
|
if err != nil {
|
|
utils.Log.Fatalf("failed to generate RSA private key: %+v", err)
|
|
return nil, false
|
|
}
|
|
publicKey, err := ssh.NewPublicKey(&privateKey.PublicKey)
|
|
if err != nil {
|
|
utils.Log.Fatalf("failed to generate RSA public key: %+v", err)
|
|
return nil, false
|
|
}
|
|
ret, err := ssh.NewSignerFromKey(privateKey)
|
|
if err != nil {
|
|
utils.Log.Fatalf("failed to generate RSA signer: %+v", err)
|
|
return nil, false
|
|
}
|
|
privateBytes := rsaEncodePrivateKey(privateKey)
|
|
publicBytes := ssh.MarshalAuthorizedKey(publicKey)
|
|
err = os.WriteFile(privateKeyPath, privateBytes, 0600)
|
|
if err != nil {
|
|
utils.Log.Fatalf("failed to write RSA private key to file: %+v", err)
|
|
return nil, false
|
|
}
|
|
err = os.WriteFile(publicKeyPath, publicBytes, 0644)
|
|
if err != nil {
|
|
_ = os.Remove(privateKeyPath)
|
|
utils.Log.Fatalf("failed to write RSA public key to file: %+v", err)
|
|
return nil, false
|
|
}
|
|
return ret, true
|
|
}
|
|
|
|
func rsaEncodePrivateKey(privateKey *rsa.PrivateKey) []byte {
|
|
privateKeyBytes := x509.MarshalPKCS1PrivateKey(privateKey)
|
|
privateBlock := &pem.Block{
|
|
Type: "RSA PRIVATE KEY",
|
|
Headers: nil,
|
|
Bytes: privateKeyBytes,
|
|
}
|
|
return pem.EncodeToMemory(privateBlock)
|
|
}
|
|
|
|
func rsaDecodePrivateKey(bytes []byte) (*rsa.PrivateKey, error) {
|
|
block, _ := pem.Decode(bytes)
|
|
if block == nil {
|
|
return nil, fmt.Errorf("failed to parse PEM block containing the key")
|
|
}
|
|
privateKey, err := x509.ParsePKCS1PrivateKey(block.Bytes)
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
return privateKey, nil
|
|
}
|