【问题标题】:What's the proper way to extract unexported fields from a Golang type?从 Golang 类型中提取未导出字段的正确方法是什么?
【发布时间】:2015-07-23 16:41:13
【问题描述】:

我正在尝试获取一个 SSH 公钥(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() 函数,但这给了我有线格式的表示,如果我通过那里,好吧,我想它可以避免滥用类型系统,但我只是最终写我自己的 byte-stream-to-big-Int-to-RSA 转换器在那里,这似乎并没有真正帮助。那时我还不如从authorized_keys 文件开始并自己解析它,这似乎也很愚蠢。

这样做的正确方法是什么?

【问题讨论】:

    标签: reflection ssh go type-conversion rsa


    【解决方案1】:

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

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

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

    【讨论】:

    • 只有在包main 中的所有内容都有效,否则它会在运行时出现恐慌。
    • @evanmcdonnal:除非您尝试过这些,否则请停止评论。这有效,并且大致相当于我的答案。我已经直接使用 ssh 包尝试了这两种方法。 Ainar-G,Convert 上的好主意,我在反射包中忘记了。
    • 这当然可以解决问题,并且不会弄乱各个字段。不过,这很可怕。谢谢!
    • @JimB 其实有办法检查。 reflect.TypeConvertibleTo(u Type) bool 方法。
    • @Ainar-G,哦,当然!我将删除其他评论;)
    【解决方案2】:

    您可以做的最好的事情是使用反射从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,
    }
    

    【讨论】:

    • 但是这个范围内没有pub,它没有被导出。即使那样会在运行时出现恐慌。
    • @evanmcdonnal:不,如果它是正确的密钥类型,它不会恐慌。 pub 只是解析后的 ssh.PublicKeyplay.golang.org/p/bhsdKWGj_u
    • @evanmcdonnal:问题是避免重新实现解析器或解释字符串表示。虽然不漂亮,但这避免了两者,并且对反射进行了充分检查,不会惊慌。
    • @evanmcdonnal:请在编译并运行示例代码之前停止评论。这行得通。 Ainar-G 的作品(顺便说一句,您可以使用反射 play.golang.org/p/xFjUBUU5a8读取未导出的字段)
    • 这个版本和使用 Convert 的版本在我的用例中都有效。这更像是我最初的方法,因为它取决于了解有关 RSA 密钥内部结构的一些东西——但它优于我最初的方法,因为它避免使用 String 作为一种真正低预算的序列化机制。鉴于我知道我有一个 RSA SSH 密钥,我正在从一个 authorized_keys 条目中获得,我将采用基于 Convert() 的方法,但这种方法更容易让我理解。谢谢!
    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2021-08-25
    • 1970-01-01
    • 1970-01-01
    • 2019-01-12
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多