从 Golang 类型中提取未导出字段的正确方法是什么?

What's the proper way to extract unexported fields from a Golang type?

我正在尝试使用 SSH public 密钥(x.crypto.ssh.PublicKey,通过解析 authorized_keys 生成)并从中提取一个普通的旧 crypto.rsa.PublicKey(我知道键的类型为 ssh-rsa)。这意味着,在 ssh-land 中,密钥是(未导出的)类型 rsaPublicKey.

通过使用 reflect.Interface() 或普通的 fmt.Sprintf("%v") 我可以获得如下所示的值:

&{133141753607255377833524803712241410600224583899447743985716492314976605735038858392516407436607315136967439536840885454950028631410155683051419836325912444338003188332463816050354540934271243064035037856360864190161298769948076508355604915775510460445701536855931828420791030023079646791573156484306628882221 35}

我知道,通过阅读 ssh 包源,ssh.rsaPublickey 实际上被定义为我想要的类型:

type rsaPublicKey rsa.PublicKey

因此这个值已经是我想要的东西了。 (实际上,我可以使用 "%+v",甚至看到它的字段(当然)被命名为 NE。)

我的问题是,我不知道有什么更好的方法让 Go 相信这实际上是一个 rsa.PublicKey,而不是用 fmt.Sscan 将这些数字中的每一个提取到字符串中,然后分别转换成big.Intint,然后用我刚刚得到的这两个值作为模数和指数来创建一个新的rsa.PublicKey.

这行得通,但似乎令人难以置信。我明白了,因为它没有被导出,可能没有直接的路径,但肯定有比重新转换它们的可打印表示更好的方法来获取字段。

ssh.PublicKey 确实有一个 Marshal() 函数,但这给了我线格式表示,如果我通过那里,我想它避免了滥用类型系统,但我最终只是在那里编写了我自己的字节流到大 Int 到 RSA 转换器,这似乎并没有真正的帮助。到那时,我不妨从 authorized_keys 文件开始并自己解析它,这看起来也很愚蠢。

正确的做法是什么?

最好的方法是使用反射从 ssh.PublicKey 界面中的 rsaPublicKey 中提取 NE 值:

// pub, _, _, _, err := ssh.ParseAuthorizedKey(authKeyData)
val := reflect.ValueOf(pub).Elem()
n := val.FieldByName("N").Interface().(*big.Int)
e := val.FieldByName("E").Interface().(int)

rsaKey := &rsa.PublicKey{
    N: n,
    E: e,
}

您可以使用 reflect 将该接口值转换回原始 rsa.PublicKey:

rsaKey := reflect.ValueOf(sshKey).
    Convert(reflect.TypeOf(&rsa.PublicKey{})).Interface().(*rsa.PublicKey)

具有类似示例的游乐场:http://play.golang.org/p/bcYhYHrIl_