Repository: AlistGo/alist (language: go)

Issue:
invalid byte sequence while adding ssh pubkey
### Please make sure of the following things

- [x] I have read the [documentation](https://alist.nn.ci).
我已经阅读了[文档](https://alist.nn.ci)。

- [x] I'm sure there are no duplicate issues or discussions.
我确定没有重复的issue或讨论。

- [x] I'm sure it's due to `AList` and not something else(such as [Network](https://alist.nn.ci/faq/howto.html#tls-handshake-timeout-read-connection-reset-by-peer-dns-lookup-failed-connect-connection-refused-client-timeout-exceeded-while-awaiting-headers-no-such-host) ,`Dependencies` or `Operational`).
我确定是`AList`的问题，而不是其他原因（例如[网络](https://alist.nn.ci/zh/faq/howto.html#tls-handshake-timeout-read-connection-reset-by-peer-dns-lookup-failed-connect-connection-refused-client-timeout-exceeded-while-awaiting-headers-no-such-host)，`依赖`或`操作`）。

- [x] I'm sure this issue is not fixed in the latest version.
我确定这个问题在最新版本中没有被修复。


### AList Version / AList 版本

v3.43.0

### Driver used / 使用的存储驱动

移动网盘

### Describe the bug / 问题描述

添加ssh pubkey时出现错误 
```
ERROR: invalid byte sequence for encoding "UTF8": 0x00 (SQLSTATE 22021)
```

日志
```
Mar 22 12:48:52 eihort alist[326674]: [GIN] 2025/03/22 - 12:48:52 | 200 |    1.639136ms |         fdcc::2 | POST     "/api/me/sshkey/add"
Mar 22 12:49:04 eihort alist[326674]: ERRO[2025-03-22 12:49:04] ERROR: invalid byte sequence for encoding "UTF8": 0x00 (SQLSTATE 22021)
```

添加的公钥为ssh ed25519公钥，使用`postgres`作为数据库后端：

```
  "database": {
    "type": "postgres",
    "host": "127.0.0.1",
    "port": 5432,
    "user": "alist",
    "password": "",
    "name": "alist",
    "db_file": "/var/lib/alist/data.db",
    "table_prefix": "x_",
    "ssl_mode": "disable",
    "dsn": ""
  },

```

### Reproduction / 复现链接

不知道写什么 放个配置吧 https://github.com/oluceps/nixos-config/blob/trival/modules/alist.nix

### Config / 配置


```
{
  "force": false,
  "site_url": "https://alist..xyz",
  "cdn": "",
  "jwt_secret": "",
  "token_expires_in": 48,
  "database": {
    "type": "postgres",
    "host": "127.0.0.1",
    "port": 5432,
    "user": "alist",
    "password": "",
    "name": "alist",
    "db_file": "/var/lib/alist/data.db",
    "table_prefix": "x_",
    "ssl_mode": "disable",
    "dsn": ""
  },
  "meilisearch": {
    "host": "http://localhost:7700",
    "api_key": "",
    "index_prefix": "alist"
  },
  "scheme": {
    "address": "0.0.0.0",
    "http_port": 5244,
    "https_port": -1,
    "force_https": false,
    "cert_file": "",
    "key_file": "",
    "unix_file": "",
    "unix_file_perm": ""
  },
  "temp_dir": "/var/lib/alist/temp",
  "bleve_dir": "data/bleve",
  "dist_dir": "",
  "log": {
    "enable": true,
    "name": "data/log/log.log",
    "max_size": 50,
    "max_backups": 30,
    "max_age": 28,
    "compress": false
  },
  "delayed_start": 0,
  "max_connections": 0,
  "max_concurrency": 64,
  "tls_insecure_skip_verify": true,
  "tasks": {
    "download": {
      "workers": 5,
      "max_retry": 1,
      "task_persistant": false
    },
    "transfer": {
      "workers": 5,
      "max_retry": 2,
      "task_persistant": false
    },
    "upload": {
      "workers": 5,
      "max_retry": 0,
      "task_persistant": false
    },
    "copy": {
      "workers": 5,
      "max_retry": 2,
      "task_persistant": false
    },
    "decompress": {
      "workers": 5,
      "max_retry": 2,
      "task_persistant": false
    },
    "decompress_upload": {
      "workers": 5,
      "max_retry": 2,
      "task_persistant": false
    },
    "allow_retry_canceled": false
  },
  "cors": {
    "allow_origins": [
      "*"
    ],
    "allow_methods": [
      "*"
    ],
    "allow_headers": [
      "*"
    ]
  },
  "s3": {
    "enable": true,
    "port": 5246,
    "ssl": false
  },
  "ftp": {
    "enable": false,
    "listen": ":5221",
    "find_pasv_port_attempts": 50,
    "active_transfer_port_non_20": false,
    "idle_timeout": 900,
    "connection_timeout": 30,
    "disable_active_mode": false,
    "default_transfer_binary": false,
    "enable_active_conn_ip_check": true,
    "enable_pasv_conn_ip_check": true
  },
  "sftp": {
    "enable": true,
    "listen": ":5222"
  },
  "last_launched_version": "3.43.0"
}
```

### Logs / 日志

以上已提供


Discussion / hints:

Fix patch:
diff --git a/internal/op/sshkey.go b/internal/op/sshkey.go
index 6ed55658abf..139698e6ca5 100644
--- a/internal/op/sshkey.go
+++ b/internal/op/sshkey.go
@@ -17,7 +17,6 @@ func CreateSSHPublicKey(k *model.SSHPublicKey) (error, bool) {
 	if err != nil {
 		return err, false
 	}
-	k.KeyStr = string(pubKey.Marshal())
 	k.Fingerprint = ssh.FingerprintSHA256(pubKey)
 	k.AddedTime = time.Now()
 	k.LastUsedTime = k.AddedTime
diff --git a/server/handles/sshkey.go b/server/handles/sshkey.go
index c53b46f2932..6f8d46b4969 100644
--- a/server/handles/sshkey.go
+++ b/server/handles/sshkey.go
@@ -6,6 +6,7 @@ import (
 	"github.com/alist-org/alist/v3/server/common"
 	"github.com/gin-gonic/gin"
 	"strconv"
+	"strings"
 )
 
 type SSHKeyAddReq struct {
@@ -30,7 +31,7 @@ func AddMyPublicKey(c *gin.Context) {
 	}
 	key := &model.SSHPublicKey{
 		Title:  req.Title,
-		KeyStr: req.Key,
+		KeyStr: strings.TrimSpace(req.Key),
 		UserId: userObj.ID,
 	}
 	err, parsed := op.CreateSSHPublicKey(key)
diff --git a/server/sftp.go b/server/sftp.go
index 0455c96230f..42c676e8c17 100644
--- a/server/sftp.go
+++ b/server/sftp.go
@@ -113,11 +113,15 @@ func (d *SftpDriver) PublicKeyAuth(conn ssh.ConnMetadata, key ssh.PublicKey) (*s
 	}
 	marshal := string(key.Marshal())
 	for _, sk := range keys {
-		if marshal == sk.KeyStr {
-			sk.LastUsedTime = time.Now()
-			_ = op.UpdateSSHPublicKey(&sk)
-			return nil, nil
+		if marshal != sk.KeyStr {
+			pubKey, _, _, _, e := ssh.ParseAuthorizedKey([]byte(sk.KeyStr))
+			if e != nil || marshal != string(pubKey.Marshal()) {
+				continue
+			}
 		}
+		sk.LastUsedTime = time.Now()
+		_ = op.UpdateSSHPublicKey(&sk)
+		return nil, nil
 	}
 	return nil, errors.New("public key refused")
 }


Test patch: