{"record":{"id":"0581c21993622ec7","repo":"FiloSottile/age","slug":"pk-does-not-implement-ssh-cryptopublickey","errorCode":null,"errorMessage":"pk does not implement ssh.CryptoPublicKey","messagePattern":"pk does not implement ssh\\.CryptoPublicKey","errorType":"exception","errorClass":null,"httpStatus":null,"severity":"error","filePath":"agessh/agessh.go","lineNumber":64,"sourceCode":"\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\n}\n\nfunc (r *RSARecipient) Wrap(fileKey []byte) ([]*age.Stanza, error) {\n\tif r.pubKey.N.BitLen() < 2048 {\n\t\treturn nil, errors.New(\"RSA key size is too small\")\n\t}\n\tl := &age.Stanza{\n\t\tType: \"ssh-rsa\",\n\t\tArgs: []string{sshFingerprint(r.sshKey)},\n\t}\n\n\twrappedKey, err := rsa.EncryptOAEP(sha256.New(), rand.Reader,\n\t\tr.pubKey, fileKey, []byte(oaepLabel))","sourceCodeStart":46,"sourceCodeEnd":82,"githubUrl":"https://github.com/FiloSottile/age/blob/b74dce4cdbe35b5e5f66c06d9612b72f89028758/agessh/agessh.go#L46-L82","documentation":"NewRSARecipient requires the ssh.PublicKey to implement the ssh.CryptoPublicKey interface so the raw *rsa.PublicKey can be extracted. This error means the value passed does not implement that interface, so age cannot perform RSA-OAEP encryption. With keys from the standard x/crypto/ssh parsers this essentially never happens; it indicates a hand-rolled ssh.PublicKey type.","triggerScenarios":"Calling agessh.NewRSARecipient(pk) where pk does not implement ssh.CryptoPublicKey() crypto.PublicKey — only realistic with a custom ssh.PublicKey implementation or a nil/malformed wrapper.","commonSituations":"Mock ssh.PublicKey values in tests; keys constructed by third-party SSH libraries that do not implement CryptoPublicKey.","solutions":["Use keys from golang.org/x/crypto/ssh (ParsePublicKey, NewPublicKey, ParseAuthorizedKey), which implement CryptoPublicKey.","Add a CryptoPublicKey() crypto.PublicKey method returning the *rsa.PublicKey to your custom key type.","Guard with a type assertion to ssh.CryptoPublicKey before calling."],"exampleFix":"// before\nrec, err := agessh.NewRSARecipient(pk) // pk is a custom type\n// after\nif _, ok := pk.(ssh.CryptoPublicKey); !ok {\n    return fmt.Errorf(\"unsupported ssh public key type %T\", pk)\n}\nrec, err = agessh.NewRSARecipient(pk)","handlingStrategy":"type-guard","validationCode":"if _, ok := pk.(ssh.CryptoPublicKey); !ok {\n    return fmt.Errorf(\"key type %T does not implement ssh.CryptoPublicKey\", pk)\n}","typeGuard":"func supportsCrypto(pk ssh.PublicKey) bool {\n    _, ok := pk.(ssh.CryptoPublicKey)\n    return ok\n}","tryCatchPattern":"rec, err := agessh.NewRSARecipient(pk)\nif err != nil {\n    if strings.Contains(err.Error(), \"ssh.CryptoPublicKey\") {\n        return fmt.Errorf(\"unsupported key implementation %T\", pk)\n    }\n    return err\n}","preventionTips":["Obtain ssh.PublicKey values exclusively from golang.org/x/crypto/ssh parsers.","Avoid mocks/fakes for ssh.PublicKey in production code paths.","Add compile-time assertions: var _ ssh.CryptoPublicKey = (*myKey)(nil)."],"tags":["ssh","interface","rsa","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"}