package ssh import ( "encoding/pem" "fmt" "io/ioutil" "os" "time" "golang.org/x/crypto/ssh" ) func parseKeyFile(path string) ([]byte, error) { f, err := os.Open(path) if err != nil { return nil, err } defer f.Close() keyBytes, err := ioutil.ReadAll(f) if err != nil { return nil, err } // We parse the private key on our own first so that we can // show a nicer error if the private key has a password. block, _ := pem.Decode(keyBytes) if block == nil { return nil, fmt.Errorf( "Failed to read key '%s': no key found", path) } if block.Headers["Proc-Type"] == "4,ENCRYPTED" { return nil, fmt.Errorf( "Failed to read key '%s': password protected keys are\n"+ "not supported. Please decrypt the key prior to use.", path) } return keyBytes, nil } // FileSigner returns an ssh.Signer for a key file. func FileSigner(path string) (ssh.Signer, error) { keyBytes, err := parseKeyFile(path) if err != nil { return nil, fmt.Errorf("Error setting up SSH config: %s", err) } signer, err := ssh.ParsePrivateKey(keyBytes) if err != nil { return nil, fmt.Errorf("Error setting up SSH config: %s", err) } return signer, nil } func ReadCertificate(certificatePath string, keySigner ssh.Signer) (ssh.Signer, error) { if certificatePath == "" { return keySigner, fmt.Errorf("no certificate file provided") } // Load the certificate cert, err := ioutil.ReadFile(certificatePath) if err != nil { return nil, fmt.Errorf("unable to read certificate file: %v", err) } pk, _, _, _, err := ssh.ParseAuthorizedKey(cert) if err != nil { return nil, fmt.Errorf("unable to parse public key: %v", err) } certificate, ok := pk.(*ssh.Certificate) if !ok { return nil, fmt.Errorf("Error loading certificate") } err = checkValidCert(certificate) if err != nil { return nil, fmt.Errorf("%s not a valid cert: %v", certificatePath, err) } certSigner, err := ssh.NewCertSigner(certificate, keySigner) if err != nil { return nil, fmt.Errorf("failed to create cert signer: %v", err) } return certSigner, nil } // FileSigner returns an ssh.Signer for a key file. func FileSignerWithCert(path string, certificatePath string) (ssh.Signer, error) { keySigner, err := FileSigner(path) if err != nil { return nil, err } return ReadCertificate(certificatePath, keySigner) } func checkValidCert(cert *ssh.Certificate) error { const CertTimeInfinity = 1<<64 - 1 unixNow := time.Now().Unix() if after := int64(cert.ValidAfter); after < 0 || unixNow < int64(cert.ValidAfter) { return fmt.Errorf("ssh: cert is not yet valid") } if before := int64(cert.ValidBefore); cert.ValidBefore != uint64(CertTimeInfinity) && (unixNow >= before || before < 0) { return fmt.Errorf("ssh: cert has expired") } return nil }