Python中使用OpenSSH格式Ed25519密钥进行JWT签名验证时出现InvalidKeyError的问题
你在尝试用PyJWT库实现EdDSA算法的JWT签名验证时遇到了一个问题:用硬编码的PKCS8格式Ed25519密钥可以正常完成签名和验证,但读取本地由ssh-keygen生成的密钥文件(id_ed25519私钥和id_ed25519.pub公钥)时,就会抛出InvalidKeyError,提示Expecting a EllipticCurvePrivateKey/EllipticCurvePublicKey. Wrong key provided for EdDSA algorithms。
你的测试代码
import jwt private_key = "-----BEGIN PRIVATE KEY-----\nMC4CAQAwBQYDK2VwBCIEIPtUxyxlhjOWetjIYmc98dmB2GxpeaMPP64qBhZmG13r\n-----END PRIVATE KEY-----\n" public_key = "-----BEGIN PUBLIC KEY-----\nMCowBQYDK2VwAyEA7p4c1IU6aA65FWn6YZ+Bya5dRbfd4P6d4a6H0u9+gCg=\n-----END PUBLIC KEY-----\n" encoded = jwt.encode({"some": "payload"}, private_key, algorithm="EdDSA") jwt.decode(encoded, public_key, algorithms=["EdDSA"]) print("pass") with open("id_ed25519", "r") as f: private_key = f.read() print(private_key) with open("id_ed25519.pub", "r") as f: public_key = f.read() print(public_key) encoded = jwt.encode({"some": "payload"}, private_key, algorithm="EdDSA") jwt.decode(encoded, public_key, algorithms=["EdDSA"]) print("it works!")
运行后得到的输出及错误
pass -----BEGIN OPENSSH PRIVATE KEY----- b3BlbnNzaC1rZXktdjEAAAAABG5vbmUAAAAEbm9uZQAAAAAAAAABAAAAMwAAAAtzc2gtZW QyNTUxOQAAACCx/iwn0j++zhjIzFYUYzQEIUUS9LJuAOPUPIsjAvi6HQAAAJgS8hOAEvIT gAAAAAtzc2gtZWQyNTUxOQAAACCx/iwn0j++zhjIzFYUYzQEIUUS9LJuAOPUPIsjAvi6HQ AAAEAT87A79bj9AFXc0iAgBKPnDoxGE6wcxZMVRgnfnGaoJbH+LCfSP77OGMjMVhRjNAQh RRL0sm4A49Q8iyMC+LodAAAAFWNib25naW9yQGNib25naW9yLW1hYw== -----END OPENSSH PRIVATE KEY----- ssh-ed25519 AAAAC3NzaC1lZDI1NTE5AAAAILH+LCfSP77OGMjMVhRjNAQhRRL0sm4A49Q8iyMC+Lod raise InvalidKeyError( jwt.exceptions.InvalidKeyError: Expecting a EllipticCurvePrivateKey/EllipticCurvePublicKey. Wrong key provided for EdDSA algorithms
问题原因
PyJWT库的EdDSA算法实现,预期接收的是标准PKCS#8格式的密钥,而ssh-keygen默认生成的Ed25519密钥是OpenSSH自定义的格式:
- 私钥开头标识是
-----BEGIN OPENSSH PRIVATE KEY-----,而非PKCS#8的-----BEGIN PRIVATE KEY----- - 公钥是
ssh-ed25519开头的单行文本格式,也不是标准PKCS#8的公钥格式
这种自定义格式无法被PyJWT解析为它需要的EllipticCurvePrivateKey或EllipticCurvePublicKey对象,因此触发了InvalidKeyError。
解决办法
你可以通过两种方式解决这个问题:
1. 转换现有OpenSSH格式密钥为PKCS#8格式
打开终端,执行以下命令完成转换:
- 转换私钥(
-N ""表示不设置保护密码,如果你的密钥原本有密码,把空字符串替换成你的密钥密码即可):
ssh-keygen -p -f id_ed25519 -m PKCS8 -N ""
- 转换公钥:
ssh-keygen -e -f id_ed25519.pub -m PKCS8 > id_ed25519_pkcs8.pub
转换完成后,私钥会变成标准PKCS#8格式,公钥也会生成对应的PKCS#8格式文件。修改代码中读取公钥的路径为id_ed25519_pkcs8.pub,再运行代码就能正常执行了。
2. 直接生成PKCS#8格式的Ed25519密钥
如果允许重新生成密钥,可以直接用以下命令生成标准PKCS#8格式的Ed25519密钥,这样生成的密钥文件可以直接被PyJWT识别:
ssh-keygen -t ed25519 -m PKCS8 -f id_ed25519 -N ""
验证
按上述方法处理密钥后,再次运行你的测试代码,就能成功打印出it works!了。
备注:内容来源于stack exchange,提问作者Christian Bongiorno

