-
Notifications
You must be signed in to change notification settings - Fork 1.7k
Description
Description
There is an encoding mismatch regarding critical options with values between ssh-keygen
and cryptography
:
- Loading an SSH certificate generated by
ssh-keygen
withcryptography
does not yield expected values. - Reading an SSH certificates generated by
SSHCertificateBuilder
withssh-keygen
fails.
Steps
Re 1:
- Generate a key to be signed:
ssh-keygen -t ed25519
- Generate a CA signing key:
ssh-keygen -t ecdsa
- Sign a certificate:
ssh-keygen -s id_ed25519 -n cryptouser,testuser -I [email protected] -O "critical:force-command=echo aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa" -O "critical:verify-required" -V 0x63b2b264:0x767eb5c0 id_ecdsa.pub
- Load the certificate with
cryptography
:
from cryptography.hazmat.primitives.serialization import (
load_ssh_public_identity,
)
cert = load_ssh_public_identity(
b"[email protected] AAAAKGVjZHNhLXNoYTItbm"
b"lzdHAyNTYtY2VydC12MDFAb3BlbnNzaC5jb20AAAAgLfsFv9Gbc6LZSiJFWdYQl"
b"IMNI50GExXW0fBpgGVf+Y4AAAAIbmlzdHAyNTYAAABBBIzVyRgVLR4F38bIOLBN"
b"8CNm8Nf+eBHCVkKDKb9WDyLLD61CEmzjK/ORwFuSE4N60eIGbFidBf0D0xh7G6o"
b"TNxsAAAAAAAAAAAAAAAEAAAAUdGVzdEBjcnlwdG9ncmFwaHkuaW8AAAAaAAAACm"
b"NyeXB0b3VzZXIAAAAIdGVzdHVzZXIAAAAAY7KyZAAAAAB2frXAAAAAWAAAAA1mb"
b"3JjZS1jb21tYW5kAAAALAAAAChlY2hvIGFhYWFhYWFhYWFhYWFhYWFhYWFhYWFh"
b"YWFhYWFhYWFhYWFhAAAAD3ZlcmlmeS1yZXF1aXJlZAAAAAAAAACCAAAAFXBlcm1"
b"pdC1YMTEtZm9yd2FyZGluZwAAAAAAAAAXcGVybWl0LWFnZW50LWZvcndhcmRpbm"
b"cAAAAAAAAAFnBlcm1pdC1wb3J0LWZvcndhcmRpbmcAAAAAAAAACnBlcm1pdC1wd"
b"HkAAAAAAAAADnBlcm1pdC11c2VyLXJjAAAAAAAAAAAAAAAzAAAAC3NzaC1lZDI1"
b"NTE5AAAAICH6csEOmGbOfT2B/S/FJg3uyPsaPSZUZk2SVYlfs0KLAAAAUwAAAAt"
b"zc2gtZWQyNTUxOQAAAEDz2u7X5/TFbN7Ms7DP4yArhz1oWWYKkdAk7FGFkHfjtY"
b"/YfNQ8Oky3dCZRi7PnSzScEEjos7723dhF8/y99WwH"
)
print(cert.critical_options[b"force-command"])
assert cert.critical_options == {
b"force-command": b"echo aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa",
b"verify-required": b"",
}
b'\x00\x00\x00(echo aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa'
Traceback (most recent call last):
File "/home/user/test.py", line 21, in <module>
assert cert.critical_options == {b"force-command": b"echo aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa", b"verify-required": b""}
AssertionError
Re 2:
- Generate an SSH certificate with
cryptography
from datetime import datetime, timedelta
from cryptography.hazmat.primitives.asymmetric import ed25519
from cryptography.hazmat.primitives.serialization import (
SSHCertificateBuilder,
SSHCertificateType,
)
ca_key = ed25519.Ed25519PrivateKey.generate()
sign_key = ed25519.Ed25519PrivateKey.generate()
cert = (
SSHCertificateBuilder()
.public_key(sign_key.public_key())
.type(SSHCertificateType.USER)
.valid_for_all_principals()
.valid_before((datetime.now() + timedelta(days=1)).timestamp())
.valid_after(datetime.now().timestamp())
.add_critical_option(
b"force-command", b"echo aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa"
)
.sign(ca_key)
)
with open("/tmp/sshcert.pub", "wb") as f:
f.write(cert.public_bytes())
- Try to read it using
ssh-keygen
$ cat /tmp/sshcert.pub
[email protected] AAAAIHNzaC1lZDI1NTE5LWNlcnQtdjAxQG9wZW5zc2guY29tAAAAICgEmbEZIZTs/K0UREvoo+eBXJGJhfZFzm+Q2CeCdBQbAAAAIJ5oypqQoA7gt+MJER8tlWwJgdpocFnOcdJHT2rPXvdwAAAAAAAAAAAAAAABAAAAAAAAAAAAAAAAZKv6HwAAAABkrUufAAAAPQAAAA1mb3JjZS1jb21tYW5kAAAAKGVjaG8gYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWEAAAAAAAAAAAAAADMAAAALc3NoLWVkMjU1MTkAAAAgGaQ+IQWx7X2AbbbWE4Ua2N7aW/uT16yt8n1bcr2BytwAAABTAAAAC3NzaC1lZDI1NTE5AAAAQGCWJZWlpr6/m/xwxQOcCfg73CUgjvqD1jsTDeepemxZRWhByJ44qdASQ0ykiuQjzGrigYaynuRbA4tuap0K3A4=
$ ssh-keygen -Lf /tmp/sshcert.pub
/tmp/sshcert.pub:
Type: [email protected] user certificate
Public key: ED25519-CERT SHA256:oxxO0zve78LqnyWIf3DRrbIzVoWXDK/FuBRerotDrHY
Signing CA: ED25519 SHA256:HHnlyz4SqRKnCKwUVgN3hvJyKA8BQ5Hmy0SOsjgfnGQ (using ssh-ed25519)
Key ID: ""
Serial: 0
Valid: from 2023-07-10T12:31:27 to 2023-07-11T12:31:27
Principals: (none)
Critical Options:
show_options: parse critical: string is too large
Background
The specification is a bit confusing in regards to how critical options/extensions with values should be encoded:
The critical options section of the certificate specifies zero or more
options on the certificates validity. The format of this field
is a sequence of zero or more tuples:string name string data
So that's the first header for the data. You then have to look at the
table to determine the format of data's contents. E.g.force-command string Specifies a command that is executed
...So, for name=force-command, the contents of the data buffer are a string.
That's the second header.The intent is to support options/extensions with data types other
than string, e.g. integers or arrays of string.
golang/crypto
had the same issue: golang/go#10569