Skip to content

Instantly share code, notes, and snippets.

@takuan-osho
Last active July 12, 2019 05:07
Show Gist options
  • Star 0 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save takuan-osho/4df2db2760d4f870363b4a4e1898122e to your computer and use it in GitHub Desktop.
Save takuan-osho/4df2db2760d4f870363b4a4e1898122e to your computer and use it in GitHub Desktop.
ssh.ParsePrivateKeyWithPassphrase (https://godoc.org/golang.org/x/crypto/ssh#ParsePrivateKeyWithPassphrase) の動作確認用スクリプト
package main
import (
"fmt"
"io/ioutil"
"log"
"os"
"golang.org/x/crypto/ssh"
)
func main() {
sshPrivateKeyPath := os.Args[1]
keyData, err := ioutil.ReadFile(sshPrivateKeyPath)
if err != nil {
log.Fatal(err)
}
signer, err := ssh.ParsePrivateKey([]byte(keyData))
if err == nil {
fmt.Println(signer)
os.Exit(0)
}
passPhrase := os.Getenv("PASSPHRASE")
if passPhrase == "" {
fmt.Println("Please configure `PASSPHRASE` environment variable for your passphrase")
os.Exit(1)
}
signer, err = ssh.ParsePrivateKeyWithPassphrase(keyData, []byte(passPhrase))
if err != nil {
log.Fatal(err)
}
fmt.Println("Parsed private key with passphrase!!")
fmt.Println(signer)
}
@takuan-osho
Copy link
Author

takuan-osho commented Jul 11, 2019

$ ssh -V
OpenSSH_7.9p1, LibreSSL 2.7.3

$ openssl version
OpenSSL 1.0.2s  28 May 2019

$ ssh-keygen -l -f ~/.ssh/id_rsa
2048 SHA256:/<masked> taku@altair.local (RSA)

$ go version
go version go1.12.6 darwin/amd64

@takuan-osho
Copy link
Author

takuan-osho commented Jul 11, 2019

~/.ssh/id_rsa にパスフレーズが無い時はこんなふうにエラーが出ない

$ go run main.go ~/.ssh/id_rsa
&{0xc0000ac120 0xc0000ac120}

@takuan-osho
Copy link
Author

takuan-osho commented Jul 11, 2019

~/.ssh/id_rsa パスフレーズがあるときはこんなふうにエラーが出る

# 事前にファイルを使って export PASSPHRASE="<~/.ssh/id_rsa のパスフレーズ>" と下準備した上で以下のコマンドを実行している

$ go run main.go ~/.ssh/id_rsa
2019/07/11 23:25:03 ssh: cannot decode encrypted private keys
exit status 1

エラーにならず、最後までプログラムが走るのが期待した動作なのだが、途中でエラーになる理由が「ssh private key側の問題」なのか「golang側のコードの問題」なのかの判別をしたい。

@takuan-osho
Copy link
Author

takuan-osho commented Jul 11, 2019

delveを使ってデバッグしてみたところ、パスフレーズがあるときはここでエラーが出ていた
https://github.com/golang/crypto/blob/4def268fd1a49955bfb3dda92fe3db4f924f2285/ssh/keys.go#L990-L992

w.CipherNameaes256-ctr, w.KdfNamebcrypt という値になっていてエラーになっていた。

ここで上記の値になっているのか真っ当な状態なのかどうか、それともgolang/x/crypto/ssh パッケージの上記の処理がよろしくないのかの判別が付かない。

delveをCLIで起動してエラー処理直前の時に w.CipherNameaes256-ctr, w.KdfNamebcrypt という値になっている状況を示したのが以下のデバッグ時のコード。

$ dlv debug ./main.go -- ~/.ssh/id_rsa
Type 'help' for list of commands.
(dlv) b ssh.parseOpenSSHPrivateKey
Breakpoint 1 set at 0x11a0d7b for golang.org/x/crypto/ssh.parseOpenSSHPrivateKey() /Users/taku/dev/src/golang.org/x/crypto/ssh/keys.go:970
(dlv) c
> golang.org/x/crypto/ssh.parseOpenSSHPrivateKey() /Users/taku/dev/src/golang.org/x/crypto/ssh/keys.go:970 (hits goroutine(1):1 total:1) (PC: 0x11a0d7b)
   965:		}, nil
   966:	}
   967:
   968:	// Implemented based on the documentation at
   969:	// https://github.com/openssh/openssh-portable/blob/master/PROTOCOL.key
=> 970:	func parseOpenSSHPrivateKey(key []byte) (crypto.PrivateKey, error) {
   971:		const magic = "openssh-key-v1\x00"
   972:		if len(key) < len(magic) || string(key[:len(magic)]) != magic {
   973:			return nil, errors.New("ssh: invalid openssh private key format")
   974:		}
   975:		remaining := key[len(magic):]
(dlv) n
> golang.org/x/crypto/ssh.parseOpenSSHPrivateKey() /Users/taku/dev/src/golang.org/x/crypto/ssh/keys.go:972 (PC: 0x11a0da8)
   967:
   968:	// Implemented based on the documentation at
   969:	// https://github.com/openssh/openssh-portable/blob/master/PROTOCOL.key
   970:	func parseOpenSSHPrivateKey(key []byte) (crypto.PrivateKey, error) {
   971:		const magic = "openssh-key-v1\x00"
=> 972:		if len(key) < len(magic) || string(key[:len(magic)]) != magic {
   973:			return nil, errors.New("ssh: invalid openssh private key format")
   974:		}
   975:		remaining := key[len(magic):]
   976:
   977:		var w struct {
(dlv) n
> golang.org/x/crypto/ssh.parseOpenSSHPrivateKey() /Users/taku/dev/src/golang.org/x/crypto/ssh/keys.go:975 (PC: 0x11a0e43)
   970:	func parseOpenSSHPrivateKey(key []byte) (crypto.PrivateKey, error) {
   971:		const magic = "openssh-key-v1\x00"
   972:		if len(key) < len(magic) || string(key[:len(magic)]) != magic {
   973:			return nil, errors.New("ssh: invalid openssh private key format")
   974:		}
=> 975:		remaining := key[len(magic):]
   976:
   977:		var w struct {
   978:			CipherName   string
   979:			KdfName      string
   980:			KdfOpts      string
(dlv) n
> golang.org/x/crypto/ssh.parseOpenSSHPrivateKey() /Users/taku/dev/src/golang.org/x/crypto/ssh/keys.go:977 (PC: 0x11a0e9b)
   972:		if len(key) < len(magic) || string(key[:len(magic)]) != magic {
   973:			return nil, errors.New("ssh: invalid openssh private key format")
   974:		}
   975:		remaining := key[len(magic):]
   976:
=> 977:		var w struct {
   978:			CipherName   string
   979:			KdfName      string
   980:			KdfOpts      string
   981:			NumKeys      uint32
   982:			PubKey       []byte
(dlv) n
> golang.org/x/crypto/ssh.parseOpenSSHPrivateKey() /Users/taku/dev/src/golang.org/x/crypto/ssh/keys.go:986 (PC: 0x11a0eed)
   981:			NumKeys      uint32
   982:			PubKey       []byte
   983:			PrivKeyBlock []byte
   984:		}
   985:
=> 986:		if err := Unmarshal(remaining, &w); err != nil {
   987:			return nil, err
   988:		}
   989:
   990:		if w.KdfName != "none" || w.CipherName != "none" {
   991:			return nil, errors.New("ssh: cannot decode encrypted private keys")
(dlv) n
> golang.org/x/crypto/ssh.parseOpenSSHPrivateKey() /Users/taku/dev/src/golang.org/x/crypto/ssh/keys.go:990 (PC: 0x11a0f9b)
   985:
   986:		if err := Unmarshal(remaining, &w); err != nil {
   987:			return nil, err
   988:		}
   989:
=> 990:		if w.KdfName != "none" || w.CipherName != "none" {
   991:			return nil, errors.New("ssh: cannot decode encrypted private keys")
   992:		}
   993:
   994:		pk1 := struct {
   995:			Check1  uint32
(dlv) n
> golang.org/x/crypto/ssh.parseOpenSSHPrivateKey() /Users/taku/dev/src/golang.org/x/crypto/ssh/keys.go:991 (PC: 0x11a0fcc)
   986:		if err := Unmarshal(remaining, &w); err != nil {
   987:			return nil, err
   988:		}
   989:
   990:		if w.KdfName != "none" || w.CipherName != "none" {
=> 991:			return nil, errors.New("ssh: cannot decode encrypted private keys")
   992:		}
   993:
   994:		pk1 := struct {
   995:			Check1  uint32
   996:			Check2  uint32
(dlv) p w.CipherName
"aes256-ctr"
(dlv) p w.KdfName
"bcrypt"

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment