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