doutongwei4380 2018-08-31 21:35
浏览 148
已采纳

如何将id_ed25519-cert.pub合并到go ssh客户端中?

I can SSH (using openssh client) to my server using two files: ~/.ssh/id_ed25519{,-cert.pub}

debug1: Trying private key: /home/xavier/.ssh/id_ed25519                        
debug1: Authentications that can continue: publickey,keyboard-interactive      
debug1: Offering ED25519-CERT public key: /home/xavier/.ssh/id_ed25519          
debug1: Server accepts key: pkalg ssh-ed25519-cert-v01@openssh.com blen 441    
debug1: sign_and_send_pubkey: no separate private key for certificate "/home/xavier/.ssh/id_ed25519"
debug1: Authentication succeeded (publickey).

I would like a go client that does the same thing, but I don't know how to incorporate the id_ed25519-cert.pub file into the example at https://godoc.org/golang.org/x/crypto/ssh#example-PublicKeys

key, err := ioutil.ReadFile("/home/xavier/.ssh/id_ed25519")
if err != nil {
    log.Fatalf("unable to read private key: %v", err)
}

// Create the Signer for this private key.
signer, err := ssh.ParsePrivateKey(key)
if err != nil {
    log.Fatalf("unable to parse private key: %v", err)
}

config := &ssh.ClientConfig{
    User: "user",
    Auth: []ssh.AuthMethod{
        // Use the PublicKeys method for remote authentication.
        ssh.PublicKeys(signer),
    },
}

// Connect to the remote server and perform the SSH handshake.
client, err := ssh.Dial("tcp", "host.com:22", config)
if err != nil {
    log.Fatalf("unable to connect: %v", err)
}
defer client.Close()

Part of the problem is I don't know what this file is (PublicKey? Certificate?), part of the problem is even if I did know I don't understand what purpose it is playing in this exchange.

I have confirmed that this file is required: removing it causes the ssh CLI to fail.

  • 写回答

1条回答 默认 最新

  • dongyan2469 2018-09-02 17:44
    关注

    That's an SSH certificate file, used to implement SSH certificate-based user authentication. This verifies the authenticity of a user on login by checking for a valid signature from a trusted certificate authority in a public key hierarchy. This approach offers various benefits over standard SSH key-based auth (with authorized_keys files), such as:

    • control over the issuing of key files (someone with access to the CA's master key must sign new certificates, rather than users issuing their own with ssh-keygen)
    • automated key file expiry
    • reduced administration overhead when adding or rotating certificates, as only the CA's public key is required to verify a certificate; it is no longer necessary to populate an authorized_keys file for each user on each host
    • providing easier support for certificate revocation when the relationship with a user changes

    Assuming you're using the built-in golang.org/x/crypto/ssh library, you can implement this by:

    • reading in and parsing your signed public key certificate alongside the private key
    • creating a signer from the private key
    • creating a certificate signer using the read in public key and the corresponding private key signer

    The specified format of the OpenSSH public key certificates is similar to an authorized_keys file. The ParseAuthorizedKeys function of the Go library will parse this file and return the corresponding key as an instance of the ssh.PublicKey interface; for certificates, this is concretely an instance of the ssh.Certificate struct.

    See the code example (note: I added a HostKeyCallback to your ClientConfig to make this connect against a test box – however, it uses the InsecureIgnoreHostKey checker, which I do not recommend in production!).

    package main
    
    import (
        "bytes"
        "io/ioutil"
        "log"
    
        "golang.org/x/crypto/ssh"
    )
    
    func main() {
        key, err := ioutil.ReadFile("/tmp/mycert")
        if err != nil {
            log.Fatalf("unable to read private key: %v", err)
        }
    
        // Create the Signer for this private key.
        signer, err := ssh.ParsePrivateKey(key)
        if err != nil {
            log.Fatalf("unable to parse private key: %v", err)
        }
    
        // Load the certificate
        cert, err := ioutil.ReadFile("/tmp/mycert-cert.pub")
        if err != nil {
            log.Fatalf("unable to read certificate file: %v", err)
        }
    
        pk, _, _, _, err := ssh.ParseAuthorizedKey(cert)
        if err != nil {
            log.Fatalf("unable to parse public key: %v", err)
        }
    
        certSigner, err := ssh.NewCertSigner(pk.(*ssh.Certificate), signer)
        if err != nil {
            log.Fatalf("failed to create cert signer: %v", err)
        }
    
        config := &ssh.ClientConfig{
            User: "user",
            Auth: []ssh.AuthMethod{
                // Use the PublicKeys method for remote authentication.
                ssh.PublicKeys(certSigner),
            },
            HostKeyCallback: ssh.InsecureIgnoreHostKey(),
        }
    
        // Connect to the remote server and perform the SSH handshake.
        client, err := ssh.Dial("tcp", "host.com:22", config)
        if err != nil {
            log.Fatalf("unable to connect: %v", err)
        }
        defer client.Close()
    }
    

    If you want to write a more generic connection client which supports certificates and non-certificates, you would obviously require additional logic to handle other types of public key. As written, I would expect the type assertion pk.(*ssh.Certificate) to fail for non-certificate public key files! (Indeed, for non-certificate connections, you probably don't need to read the public key at all.)

    本回答被题主选为最佳回答 , 对您是否有帮助呢?
    评论

报告相同问题?

悬赏问题

  • ¥15 微信会员卡等级和折扣规则
  • ¥15 微信公众平台自制会员卡可以通过收款码收款码收款进行自动积分吗
  • ¥15 随身WiFi网络灯亮但是没有网络,如何解决?
  • ¥15 gdf格式的脑电数据如何处理matlab
  • ¥20 重新写的代码替换了之后运行hbuliderx就这样了
  • ¥100 监控抖音用户作品更新可以微信公众号提醒
  • ¥15 UE5 如何可以不渲染HDRIBackdrop背景
  • ¥70 2048小游戏毕设项目
  • ¥20 mysql架构,按照姓名分表
  • ¥15 MATLAB实现区间[a,b]上的Gauss-Legendre积分