跳到主要内容
ecdsa.com
命令行

OpenSSH (ssh-keygen)

OpenSSH 10,一套 SSH 工具,其中的 ssh-keygen 还能转换密钥并为文件和 git 提交签名:默认 Ed25519,优先采用 ML-KEM 混合密钥交换,DSA 已移除——ECDSA 签名使用 SSH 自有的 mpint 编码。
许可证
SSH-OpenSSH
检查的版本
10.3p1 ·
源代码
github.com/openssh/openssh-portable
文档
www.openssh.com/manual.html

兼容性概览

ECDSAEdDSA后量子

ECDSA 曲线
P-256P-384P-521
EdDSA
Ed25519
后量子算法
ML-KEMsntrup761
公钥格式
spki-pemopensshpkcs1
私钥格式
pkcs8-pemencrypted-pkcs8sec1-pempkcs1openssh

除了 ssh 和 sshd,OpenSSH 还附带 ssh-keygen:它能生成和转换密钥,并能通过 -Y sign 为文件和 git 提交生成 SSHSIG 签名。本页的事实基于 Alpine 3.24.2 的软件包 OpenSSH 10.3p1 检查得出,它链接的是镜像中的 OpenSSL 3.5.8。不带 -t 时,ssh-keygen 生成 Ed25519 密钥。ECDSA 密钥只支持 P-256、P-384 和 P-521(-b 224 会被拒绝),没有 Ed448,DSA 也已移除:ssh-keygen -t dsa 会报错“unknown key type dsa”。可以用 SSH 公钥查看器查看任意密钥。

没有配置文件时,客户端首先提供混合密钥交换 mlkem768x25519-sha256,其次是 sntrup761x25519-sha512——为会话密钥提供后量子保护。ssh -Q sig 没有列出任何后量子签名算法,因此主机和用户的身份验证仍采用传统算法。使用 SHA-1 的 RSA(ssh-rsa)仍然有实现,但不在默认的主机密钥算法列表和公钥算法列表中,因此在默认设置下客户端从不使用它;如果对端只提供 ssh-rsa 而没有更新的算法,就需要显式地重新启用它,例如对用户密钥使用 PubkeyAcceptedAlgorithms +ssh-rsa。可以用 SSH 服务器审计检查服务器协商的结果。

SSH 中的 ECDSA 签名既不是 DER,也不是原始 r‖s:签名数据块是字符串 ecdsa-sha2-nistp256,后接一个以 SSH mpint 形式存放 r 和 s 的字符串(RFC 5656)。因此,把签名转换为 X.509、JWS 或 WebAuthn 所用的格式,或者反向转换,都意味着要重新编码这两个整数。nonce 是随机的——同一文件的两个签名不同——s 没有经过规范化,把 s 替换为 n − s 的签名也能验证通过。

SSHSIG 签名(ssh-keygen -Y sign)默认用 SHA-512 对消息计算哈希;-O hashalg=sha256 是唯一的替代选项,SHA-1 和 SHA-384 会被拒绝。用 -n 指定的命名空间是签名内容的一部分,把 file 签名按 email 验证会报错“namespace does not match”。失败原因可以在 stderr 上区分——数据块损坏时为“invalid format”,针对其他数据的有效签名为“incorrect signature”——而 stdout 在两种情况下都输出“Could not verify signature.”。可以用 SSH 签名验证工具检查签名。

ssh-keygen -e -m PKCS8 把公钥导出为 SPKI PEM,-i -m PKCS8 再把它导入回来;-m PEM 对 ECDSA 同样输出 SPKI,对 RSA 则输出 PKCS#1;-m RFC4716 输出“SSH2 PUBLIC KEY”格式。Ed25519 公钥无法用 -e -m PKCS8 导出(“unsupported key type ED25519”),不过 -p -m PKCS8 可以把 Ed25519 私钥改写为 PKCS#8。私钥默认以 OPENSSH PRIVATE KEY 格式保存;-p -m PEM 把 ECDSA 密钥改写为 SEC1、把 RSA 密钥改写为 PKCS#1,而带口令的 -m PKCS8 会写出加密的 PKCS#8。

事实及其依据的检查

以下内容均非抄自文档。检查脚本 content/libraries/verify/openssh/run.sh 于2026年10月20日在容器镜像 alpine:3.24.2 中运行了 OpenSSH (ssh-keygen) 10.3p1;本页内容必须与它的输出一致,每条事实都列出了对应的输出行。

  • 默认密钥类型

    ed25519

    不带 -t 时,ssh-keygen 生成了 ssh-ed25519 密钥。

    检查记录
    fact default-key-type = ed25519
    evidence default-key-type: ssh-keygen without -t wrote a ssh-ed25519 key
  • ECDSA 曲线

    P-256P-384P-521

    -b 256、384 和 521;-b 224 会被拒绝,并报错“valid lengths are 256, 384 or 521 bits”。

    检查记录
    fact ecdsa-curves = P-256, P-384, P-521
    evidence ecdsa-curves: ssh -Q key: ssh-ed25519 ssh-ed25519-cert-v01@openssh.com sk-ssh-ed25519@openssh.com sk-ssh-ed25519-cert-v01@openssh.com ecdsa-sha2-nistp256 ecdsa-sha2-nistp256-cert-v01@openssh.com ecdsa-sha2-nistp384 ecdsa-sha2-nistp384-cert-v01@openssh.com ecdsa-sha2-nistp521 ecdsa-sha2-nistp521-cert-v01@openssh.com sk-ecdsa-sha2-nistp256@openssh.com sk-ecdsa-sha2-nistp256-cert-v01@openssh.com ssh-rsa ssh-rsa-cert-v01@openssh.com
  • EdDSA

    Ed25519

    ssh -Q key 列出了 ssh-ed25519 及其 FIDO 变体;没有 Ed448 密钥类型。

    检查记录
    fact eddsa = Ed25519
  • DSA 密钥

    removed

    ssh-keygen -t dsa 报错“unknown key type dsa”,ssh -Q key 中也没有 ssh-dss。

    检查记录
    fact dsa-keys = removed
  • 默认密钥交换

    mlkem768x25519-sha256

    ssh -F none -G 输出中 kexalgorithms 的第一项。

    检查记录
    fact default-kex = mlkem768x25519-sha256
    evidence default-kex: ssh -F none -G: kexalgorithms mlkem768x25519-sha256,sntrup761x25519-sha512,sntrup761x25519-sha512@openssh.com,curve25519-sha256,curve25519-sha256@libssh.org,ecdh-sha2-nistp256,ecdh-sha2-nistp384,ecdh-sha2-nistp521,diffie-hellman-group-exchange-sha256,diffie-hellman-group16-sha512,diffie-hellman-group18-sha512,diffie-hellman-group14-sha256
  • 后量子算法

    ML-KEMsntrup761

    mlkem768x25519-sha256 和 sntrup761x25519-sha512 混合密钥交换;ssh -Q sig 中没有后量子签名算法。

    检查记录
    fact pqc = ML-KEM, sntrup761
    evidence pqc: ssh -Q kex: diffie-hellman-group1-sha1 diffie-hellman-group14-sha1 diffie-hellman-group14-sha256 diffie-hellman-group16-sha512 diffie-hellman-group18-sha512 diffie-hellman-group-exchange-sha1 diffie-hellman-group-exchange-sha256 ecdh-sha2-nistp256 ecdh-sha2-nistp384 ecdh-sha2-nistp521 curve25519-sha256 curve25519-sha256@libssh.org sntrup761x25519-sha512 sntrup761x25519-sha512@openssh.com mlkem768x25519-sha256
    evidence pqc: ssh -Q sig: ssh-ed25519 sk-ssh-ed25519@openssh.com ecdsa-sha2-nistp256 ecdsa-sha2-nistp384 ecdsa-sha2-nistp521 sk-ecdsa-sha2-nistp256@openssh.com webauthn-sk-ecdsa-sha2-nistp256@openssh.com ssh-rsa rsa-sha2-256 rsa-sha2-512
  • 使用 SHA-1 的 ssh-rsa 签名

    disabled-by-default

    ssh-rsa 出现在 ssh -Q sig 中,但不在默认的 pubkeyacceptedalgorithms 或 hostkeyalgorithms 中。

    检查记录
    fact ssh-rsa-sha1 = disabled-by-default
    evidence ssh-rsa-sha1: ssh-rsa in ssh -Q sig: yes; in the default pubkeyacceptedalgorithms or hostkeyalgorithms: no
    evidence ssh-rsa-sha1: with -o PubkeyAcceptedAlgorithms=+ssh-rsa it is in the list: yes
  • ECDSA 签名编码

    ssh-mpint

    SSHSIG 签名数据块包含类型字符串,以及以最小编码的 SSH mpint 表示的 r 和 s。

    检查记录
    fact ecdsa-signature-encoding = ssh-mpint
    evidence ecdsa-signature-encoding: ssh-keygen -Y sign: hash=sha512 type=ecdsa-sha2-nistp256 mpints=yes
  • ECDSA 的 nonce

    random

    同一文件的两个签名不同。

    检查记录
    fact ecdsa-nonce = random
    evidence ecdsa-nonce: two signatures of one file are different
  • 签名时的 low-S

    either

    s 没有经过规范化:32 个签名中出现了 high-S 值。

    检查记录
    fact ecdsa-low-s-sign = either
    evidence ecdsa-low-s-sign: signatures with s > n/2 among 32: yes
  • 验证时的 high-S

    accepts-high-s

    把 s 替换为 n − s 的签名验证结果为“Good”。

    检查记录
    fact ecdsa-low-s-verify = accepts-high-s
    evidence ecdsa-low-s-verify: s replaced by n - s: ok
  • 验证失败时的输出

    distinguishable

    对于原始 r‖s 和 3 字节的数据,stderr 输出“invalid format”;对于错误的消息,则输出“incorrect signature”;两种情况的退出状态码都是 255。

    检查记录
    fact cli-verify-output = distinguishable
  • SSHSIG 默认哈希

    sha512

    默认签名的 SSHSIG 头部注明的是 sha512。

    检查记录
    fact sshsig-default-hash = sha512
    evidence sshsig-default-hash: hash=sha512 type=ecdsa-sha2-nistp256 mpints=yes
  • SSHSIG 哈希算法

    sha256sha512

    -O hashalg= 接受 sha256 和 sha512,拒绝 sha1 和 sha384。

    检查记录
    fact sshsig-hash-algorithms = sha256, sha512
    evidence sshsig-hash-algorithms: ssh-keygen -Y sign -O hashalg= accepted: sha256, sha512
  • SSHSIG 命名空间

    enforced

    用 -n file 生成的签名在使用 -n email 验证时报错“namespace does not match”。

    检查记录
    fact sshsig-namespace = enforced
  • 公钥格式

    spki-pemopensshpkcs1

    SPKI PEM(-e -m PKCS8,可用 -i 导入回来)、OpenSSH 单行格式,以及 RSA 的 PKCS#1(-e -m PEM)。

    检查记录
    fact public-key-formats = spki-pem, openssh, pkcs1
    evidence public-key-formats: P-256 -e -m PKCS8: -----BEGIN PUBLIC KEY-----, -i back to the same key: yes
    evidence public-key-formats: P-256 -e -m PEM: -----BEGIN PUBLIC KEY-----; -e -m RFC4716: ---- BEGIN SSH2 PUBLIC KEY ----
    evidence public-key-formats: RSA -e -m PEM: -----BEGIN RSA PUBLIC KEY-----, -i back to the same key: yes
  • 私钥格式

    pkcs8-pemencrypted-pkcs8sec1-pempkcs1openssh

    除默认的 OpenSSH 格式外,还有 PKCS#8、加密的 PKCS#8、ECDSA 的 SEC1 和 RSA 的 PKCS#1,每种格式都能由 ssh-keygen -y 读回。

    检查记录
    fact private-key-formats = pkcs8-pem, encrypted-pkcs8, sec1-pem, pkcs1, openssh
    evidence private-key-formats: P-256 default: -----BEGIN OPENSSH PRIVATE KEY-----; -m PKCS8: -----BEGIN PRIVATE KEY----- (read back: yes); -m PKCS8 with a passphrase: -----BEGIN ENCRYPTED PRIVATE KEY----- (read back: yes); -m PEM: -----BEGIN EC PRIVATE KEY----- (read back: yes)
    evidence private-key-formats: RSA -m PEM: -----BEGIN RSA PRIVATE KEY----- (read back: yes); Ed25519 -m PKCS8: -----BEGIN PRIVATE KEY----- (read back: yes); Ed25519 -m PEM: refused

用法

生成 Ed25519 密钥,在 `file` 命名空间中为文件签名,并对照 allowed_signers 文件验证签名。
# Without -t, ssh-keygen generates an Ed25519 key.
ssh-keygen -q -N "" -C alice@example.com -f id_ed25519
printf 'order #1042 approved\n' > order.txt

# -n names what the signature is for; a different namespace fails verification.
ssh-keygen -q -Y sign -f id_ed25519 -n file order.txt   # writes order.txt.sig

# Verifiers trust keys through allowed_signers: a principal, then the public key.
echo "alice@example.com $(cut -d' ' -f1,2 id_ed25519.pub)" > allowed_signers
ssh-keygen -Y verify -f allowed_signers -I alice@example.com -n file -s order.txt.sig < order.txt

代码片段测试工具会在容器镜像 alpine:3.24.2 中原样运行每个示例,只要输出不匹配,运行就会失败。

环境准备
apk add --no-cache -q openssh-keygen=10.3_p1-r1 >/dev/null
命令
sh sign.sh
输出必须匹配(正则表达式)
Good "file" signature for alice@example\.com with ED25519 key SHA256:

库