{"record":{"id":"d1f62c6fa1c48c36","repo":"FiloSottile/age","slug":"ssh-public-key-is-not-an-rsa-key","errorCode":null,"errorMessage":"SSH public key is not an RSA key","messagePattern":"SSH public key is not an RSA key","errorType":"exception","errorClass":null,"httpStatus":null,"severity":"error","filePath":"agessh/agessh.go","lineNumber":51,"sourceCode":")\n\nfunc sshFingerprint(pk ssh.PublicKey) string {\n\th := sha256.Sum256(pk.Marshal())\n\treturn format.EncodeToString(h[:4])\n}\n\nconst oaepLabel = \"age-encryption.org/v1/ssh-rsa\"\n\ntype RSARecipient struct {\n\tsshKey ssh.PublicKey\n\tpubKey *rsa.PublicKey\n}\n\nvar _ age.Recipient = &RSARecipient{}\n\nfunc NewRSARecipient(pk ssh.PublicKey) (*RSARecipient, error) {\n\tif pk.Type() != \"ssh-rsa\" {\n\t\treturn nil, errors.New(\"SSH public key is not an RSA key\")\n\t}\n\tr := &RSARecipient{\n\t\tsshKey: pk,\n\t}\n\n\tif pk, ok := pk.(ssh.CryptoPublicKey); ok {\n\t\tif pk, ok := pk.CryptoPublicKey().(*rsa.PublicKey); ok {\n\t\t\tr.pubKey = pk\n\t\t} else {\n\t\t\treturn nil, errors.New(\"unexpected public key type\")\n\t\t}\n\t} else {\n\t\treturn nil, errors.New(\"pk does not implement ssh.CryptoPublicKey\")\n\t}\n\tif r.pubKey.N.BitLen() < 2048 {\n\t\treturn nil, errors.New(\"RSA key size is too small\")\n\t}\n\treturn r, nil","sourceCodeStart":33,"sourceCodeEnd":69,"githubUrl":"https://github.com/FiloSottile/age/blob/b74dce4cdbe35b5e5f66c06d9612b72f89028758/agessh/agessh.go#L33-L69","documentation":"NewRSARecipient wraps an ssh.PublicKey as an age RSA recipient, but only accepts keys whose SSH wire type is exactly \"ssh-rsa\". This error is returned when the key's Type() is something else, e.g. an Ed25519, ECDSA (ecdsa-sha2-nistp256), or certificate (cert-v01@openssh.com) key. It is a pure input-validation failure; the key object itself was rejected before any crypto is attempted.","triggerScenarios":"Calling agessh.NewRSARecipient(pk) with pk.Type() != \"ssh-rsa\" — directly, or indirectly via agessh.ParseRecipient on an authorized_keys line for a non-RSA key, or via agessh.NewEncryptedSSHIdentity whose decrypted key is non-RSA.","commonSituations":"Parsing an authorized_keys/id_*.pub file containing multiple keys and passing an Ed25519 or ECDSA entry to NewRSARecipient; using an OpenSSH certificate instead of a raw public key; generating modern keys (ssh-keygen defaults to Ed25519 since OpenSSH 8.x) and assuming RSA.","solutions":["Check the key type before calling: only pass keys where pk.Type() == \"ssh-rsa\".","If the key is intentionally Ed25519, use agessh.NewEd25519Recipient instead.","When parsing a key file, select the ssh-rsa entry from the authorized_keys line rather than the first line.","Generate a dedicated RSA key: ssh-keygen -t rsa -b 4096 -f key_rsa (with -m PEM if it must be an encrypted identity file)."],"exampleFix":"// before\nrec, err := agessh.NewRSARecipient(pubKey) // pubKey is ssh-ed25519\n// after\nif pubKey.Type() == \"ssh-rsa\" {\n    rec, err = agessh.NewRSARecipient(pubKey)\n} else if pubKey.Type() == \"ssh-ed25519\" {\n    rec, err = agessh.NewEd25519Recipient(pubKey)\n}","handlingStrategy":"validation","validationCode":"if pk.Type() != \"ssh-rsa\" {\n    return fmt.Errorf(\"expected ssh-rsa key, got %s\", pk.Type())\n}","typeGuard":"func isSSHRSA(pk ssh.PublicKey) bool { return pk != nil && pk.Type() == \"ssh-rsa\" }","tryCatchPattern":"rec, err := agessh.NewRSARecipient(pk)\nif err != nil {\n    if strings.Contains(err.Error(), \"not an RSA key\") {\n        // fall back to a type-appropriate recipient\n    }\n    return err\n}","preventionTips":["Dispatch recipient construction on pk.Type() rather than hardcoding RSA.","Filter authorized_keys entries by key type before parsing.","Prefer x/crypto/ssh-parsed keys over hand-built ssh.PublicKey values.","Document required key types wherever key files are configured."],"tags":["ssh","key-type","validation","age"],"backgroundTag":"ssh-key-type-mismatch","analyzedSha":"b74dce4cdbe35b5e5f66c06d9612b72f89028758","analyzedAt":"2026-08-31T23:59:31.627Z","contentChangedAt":null,"schemaVersion":2},"datasetVersion":"2026-09-08T05:18:18.240Z"}