// CheckPublicKeyString checks if the given public key string is recognized by SSH. func CheckPublicKeyString(content string) (_ string, err error) { content, err = parseKeyString(content) if err != nil { return "", err } content = strings.TrimRight(content, "\n\r") if strings.ContainsAny(content, "\n\r") { return "", errors.New("only a single line with a single key please") } // remove any unnecessary whitespace now content = strings.TrimSpace(content) fields := strings.Fields(content) if len(fields) < 2 { return "", errors.New("too less fields") } key, err := base64.StdEncoding.DecodeString(fields[1]) if err != nil { return "", fmt.Errorf("StdEncoding.DecodeString: %v", err) } pkey, err := ssh.ParsePublicKey([]byte(key)) if err != nil { return "", fmt.Errorf("ParsePublicKey: %v", err) } log.Trace("Key type: %s", pkey.Type()) return content, nil }
// Converts a public key to authorized keys format. func PublicKeyToAuthorizedKeysFormat(in string) (string, error) { s := strings.Replace(strings.Replace(strings.TrimSpace(in), "\r\n", "\n", -1), "\r", "\n", -1) lines := strings.Split(s, "\n") if len(lines) == 1 { publicKey, comment, _, _, err := ssh.ParseAuthorizedKey([]byte(lines[0])) if err != nil { return "", fmt.Errorf("failed to parse public key from authorized keys format: %v", err) } encodedKey := base64.StdEncoding.EncodeToString(publicKey.Marshal()) return fmt.Sprintf("%s %s %s", publicKey.Type(), encodedKey, comment), nil } else { var encodedKey string continuationLine := false for _, line := range lines { if continuationLine || strings.ContainsAny(line, ":-") { continuationLine = strings.HasSuffix(line, "\\") } else { encodedKey += line } } rawKey, err := base64.StdEncoding.DecodeString(encodedKey) fmt.Println(encodedKey) if err != nil { return "", fmt.Errorf("detected SSH2 format, but contains invalid base64 content: %v", err) } fmt.Println(string(base64.StdEncoding.EncodeToString(rawKey))) publicKey, err := ssh.ParsePublicKey(rawKey) if err != nil { return "", fmt.Errorf("failed to parse public key from SSH2 format: %v", err) } newEncodedKey := base64.StdEncoding.EncodeToString(publicKey.Marshal()) return fmt.Sprintf("%s %s cloug@cloug", publicKey.Type(), newEncodedKey), nil } }
//export c_ParsePublicKey func c_ParsePublicKey(in []byte) (uint64, int, *C.char) { pkey, err := ssh.ParsePublicKey(in) if err != nil { return IH, ErrorCodeInternal, C.CString(err.Error()) } return uint64(RegisterObject(&pkey)), ErrorCodeSuccess, nil }
func parseECDSACert(req []byte) (*AddedKey, error) { var k ecdsaCertMsg if err := ssh.Unmarshal(req, &k); err != nil { return nil, err } pubKey, err := ssh.ParsePublicKey(k.CertBytes) if err != nil { return nil, err } cert, ok := pubKey.(*ssh.Certificate) if !ok { return nil, errors.New("agent: bad ECDSA certificate") } // An ECDSA publickey as marshaled by ecdsaPublicKey.Marshal() in keys.go var ecdsaPub struct { Name string ID string Key []byte } if err := ssh.Unmarshal(cert.Key.Marshal(), &ecdsaPub); err != nil { return nil, err } priv, err := unmarshalECDSA(ecdsaPub.ID, ecdsaPub.Key, k.D) if err != nil { return nil, err } return &AddedKey{PrivateKey: priv, Certificate: cert, Comment: k.Comments}, nil }
// Verify satisfies the ssh.PublicKey interface. func (k *Key) Verify(data []byte, sig *ssh.Signature) error { pubKey, err := ssh.ParsePublicKey(k.Blob) if err != nil { return fmt.Errorf("agent: bad public key: %v", err) } return pubKey.Verify(data, sig) }
// SSHNativeParsePublicKey extracts the key type and length using the golang SSH library. // NOTE: ed25519 is not supported. func SSHNativeParsePublicKey(keyLine string) (string, int, error) { fields := strings.Fields(keyLine) if len(fields) < 2 { return "", 0, fmt.Errorf("not enough fields in public key line: %s", string(keyLine)) } raw, err := base64.StdEncoding.DecodeString(fields[1]) if err != nil { return "", 0, err } pkey, err := ssh.ParsePublicKey(raw) if err != nil { if strings.Contains(err.Error(), "ssh: unknown key algorithm") { return "", 0, ErrKeyUnableVerify{err.Error()} } return "", 0, fmt.Errorf("ssh.ParsePublicKey: %v", err) } // The ssh library can parse the key, so next we find out what key exactly we have. switch pkey.Type() { case ssh.KeyAlgoDSA: rawPub := struct { Name string P, Q, G, Y *big.Int }{} if err := ssh.Unmarshal(pkey.Marshal(), &rawPub); err != nil { return "", 0, err } // as per https://bugzilla.mindrot.org/show_bug.cgi?id=1647 we should never // see dsa keys != 1024 bit, but as it seems to work, we will not check here return "dsa", rawPub.P.BitLen(), nil // use P as per crypto/dsa/dsa.go (is L) case ssh.KeyAlgoRSA: rawPub := struct { Name string E *big.Int N *big.Int }{} if err := ssh.Unmarshal(pkey.Marshal(), &rawPub); err != nil { return "", 0, err } return "rsa", rawPub.N.BitLen(), nil // use N as per crypto/rsa/rsa.go (is bits) case ssh.KeyAlgoECDSA256: return "ecdsa", 256, nil case ssh.KeyAlgoECDSA384: return "ecdsa", 384, nil case ssh.KeyAlgoECDSA521: return "ecdsa", 521, nil case "ssh-ed25519": // TODO replace with ssh constant when available return "ed25519", 256, nil } return "", 0, fmt.Errorf("Unsupported key length detection for type: %s", pkey.Type()) }
func parseEd25519Cert(req []byte) (*AddedKey, error) { var k ed25519CertMsg if err := ssh.Unmarshal(req, &k); err != nil { return nil, err } pubKey, err := ssh.ParsePublicKey(k.CertBytes) if err != nil { return nil, err } priv := ed25519.PrivateKey(k.Priv) cert, ok := pubKey.(*ssh.Certificate) if !ok { return nil, errors.New("agent: bad ED25519 certificate") } return &AddedKey{PrivateKey: &priv, Certificate: cert, Comment: k.Comments}, nil }
func (c sshConn) PublicKey() ssh.PublicKey { if c.Permissions == nil { return nil } s, ok := c.Permissions.Extensions["pubkey"] if !ok { return nil } key, err := ssh.ParsePublicKey([]byte(s)) if err != nil { return nil } return key }
/* Update() searches LDAP for the current user set that supports the necessary properties for Hologram. TODO: call this at some point during verification failure so that keys that have been recently added to LDAP work, instead of requiring a server restart. */ func (luc *ldapUserCache) Update() error { start := time.Now() filter := "(sshPublicKey=*)" searchRequest := ldap.NewSearchRequest( luc.baseDN, ldap.ScopeWholeSubtree, ldap.NeverDerefAliases, 0, 0, false, filter, []string{"sshPublicKey", luc.userAttr}, nil, ) searchResult, err := luc.server.Search(searchRequest) if err != nil { return err } for _, entry := range searchResult.Entries { username := entry.GetAttributeValue(luc.userAttr) userKeys := []ssh.PublicKey{} for _, eachKey := range entry.GetAttributeValues("sshPublicKey") { sshKeyBytes, _ := base64.StdEncoding.DecodeString(eachKey) userSSHKey, err := ssh.ParsePublicKey(sshKeyBytes) if err != nil { userSSHKey, _, _, _, err = ssh.ParseAuthorizedKey([]byte(eachKey)) if err != nil { log.Warning("SSH key parsing for user %s failed (key was '%s')! This key will not be added into LDAP.", username, eachKey) continue } } userKeys = append(userKeys, userSSHKey) } luc.users[username] = &User{ SSHKeys: userKeys, Username: username, } log.Debug("Information on %s (re-)generated.", username) } log.Debug("LDAP information re-cached.") luc.stats.Timing(1.0, "ldapCacheUpdate", time.Since(start)) return nil }
func parseRSACert(req []byte) (*AddedKey, error) { var k rsaCertMsg if err := ssh.Unmarshal(req, &k); err != nil { return nil, err } pubKey, err := ssh.ParsePublicKey(k.CertBytes) if err != nil { return nil, err } cert, ok := pubKey.(*ssh.Certificate) if !ok { return nil, errors.New("agent: bad RSA certificate") } // An RSA publickey as marshaled by rsaPublicKey.Marshal() in keys.go var rsaPub struct { Name string E *big.Int N *big.Int } if err := ssh.Unmarshal(cert.Key.Marshal(), &rsaPub); err != nil { return nil, fmt.Errorf("agent: Unmarshal failed to parse public key: %v", err) } if rsaPub.E.BitLen() > 30 { return nil, errors.New("agent: RSA public exponent too large") } priv := rsa.PrivateKey{ PublicKey: rsa.PublicKey{ E: int(rsaPub.E.Int64()), N: rsaPub.N, }, D: k.D, Primes: []*big.Int{k.Q, k.P}, } priv.Precompute() return &AddedKey{PrivateKey: &priv, Certificate: cert, Comment: k.Comments}, nil }
func (h *certRequestHandler) extractCertFromRequest(req *http.Request) (*ssh.Certificate, error) { if req.Form["cert"] == nil || len(req.Form["cert"]) == 0 { err := errors.New("Please specify exactly one cert request") return nil, err } rawCertRequest, err := base64.StdEncoding.DecodeString(req.Form["cert"][0]) if err != nil { err := errors.New("Unable to base64 decode cert request") return nil, err } pubKey, err := ssh.ParsePublicKey(rawCertRequest) if err != nil { err := errors.New("Unable to parse cert request") return nil, err } return pubKey.(*ssh.Certificate), nil }
func verify(a agent.Agent, b string, sFormat string, s string) { keys, err := a.List() if err != nil { fmt.Println(err) os.Exit(1) } data, err := base64.StdEncoding.DecodeString(b) if err != nil { fmt.Println(err) os.Exit(1) } sigData, err := base64.StdEncoding.DecodeString(s) if err != nil { fmt.Println(err) os.Exit(1) } sig := &ssh.Signature{sFormat, sigData} w := new(tabwriter.Writer) // Format in tab-separated columns with a tab stop of 8. w.Init(os.Stdout, 0, 8, 0, '\t', 0) for i, k := range keys { mKey := k.Marshal() verifyKey, err := ssh.ParsePublicKey(mKey) if err != nil { fmt.Println(err.Error()) os.Exit(1) } ok := true err = verifyKey.Verify(data, sig) if err != nil { ok = false } fmt.Fprintf(w, "%02d: verified: %t\n", i, ok) w.Flush() } }
func GetFingerPrint(key OpenSSHPublicKey, algo string) (fp string, err error) { pubkey, err := ssh.ParsePublicKey(key.key) if err != nil { return "", errors.New("Cannot Get Fingerprint: Cannot Parse") } var cs []byte switch algo { case "md5": tmp := md5.Sum(pubkey.Marshal()) cs = tmp[:] case "sha224": tmp := sha256.Sum224(pubkey.Marshal()) cs = tmp[:] case "sha256": tmp := sha256.Sum256(pubkey.Marshal()) cs = tmp[:] case "sha384": tmp := sha512.Sum384(pubkey.Marshal()) cs = tmp[:] case "sha512": tmp := sha512.Sum512(pubkey.Marshal()) cs = tmp[:] default: tmp := sha256.Sum256(pubkey.Marshal()) cs = tmp[:] } switch algo { case "md5": for i := 0; i < len(cs); i++ { fp = fmt.Sprintf("%s%0.2x", fp, cs[i]) if i != len(cs)-1 { fp = fp + ":" } } default: fp = base64.StdEncoding.EncodeToString(cs) } return }
func computeAwsKeyFingerprint(publicKey *fi.ResourceHolder) (string, error) { publicKeyString, err := publicKey.AsString() if err != nil { return "", fmt.Errorf("error reading SSH public key: %v", err) } tokens := strings.Split(publicKeyString, " ") if len(tokens) < 2 { return "", fmt.Errorf("error parsing SSH public key: %s", publicKeyString) } sshPublicKeyBytes, err := base64.StdEncoding.DecodeString(tokens[1]) if len(tokens) < 2 { return "", fmt.Errorf("error decoding SSH public key: %s", publicKeyString) } sshPublicKey, err := ssh.ParsePublicKey(sshPublicKeyBytes) if err != nil { return "", fmt.Errorf("error parsing SSH public key: %v", err) } der, err := toDER(sshPublicKey) if err != nil { return "", fmt.Errorf("error computing fingerprint for SSH public key: %v", err) } h := md5.Sum(der) sshKeyFingerprint := fmt.Sprintf("%x", h) var colonSeparated bytes.Buffer for i := 0; i < len(sshKeyFingerprint); i++ { if (i%2) == 0 && i != 0 { colonSeparated.WriteByte(':') } colonSeparated.WriteByte(sshKeyFingerprint[i]) } return colonSeparated.String(), nil }
func parseDSACert(req []byte) (*AddedKey, error) { var k dsaCertMsg if err := ssh.Unmarshal(req, &k); err != nil { return nil, err } pubKey, err := ssh.ParsePublicKey(k.CertBytes) if err != nil { return nil, err } cert, ok := pubKey.(*ssh.Certificate) if !ok { return nil, errors.New("agent: bad DSA certificate") } // A DSA publickey as marshaled by dsaPublicKey.Marshal() in keys.go var w struct { Name string P, Q, G, Y *big.Int } if err := ssh.Unmarshal(cert.Key.Marshal(), &w); err != nil { return nil, fmt.Errorf("agent: Unmarshal failed to parse public key: %v", err) } priv := &dsa.PrivateKey{ PublicKey: dsa.PublicKey{ Parameters: dsa.Parameters{ P: w.P, Q: w.Q, G: w.G, }, Y: w.Y, }, X: k.X, } return &AddedKey{PrivateKey: priv, Certificate: cert, Comment: k.Comments}, nil }
func listCerts(c *cli.Context) { configPath := c.String("config-file") environment := c.String("environment") showAll := c.Bool("show-all") allConfig := make(map[string]ssh_ca_util.RequesterConfig) err := ssh_ca_util.LoadConfig(configPath, &allConfig) wrongTypeConfig, err := ssh_ca_util.GetConfigForEnv(environment, &allConfig) if err != nil { fmt.Println(err) os.Exit(1) } config := wrongTypeConfig.(ssh_ca_util.RequesterConfig) getResp, err := http.Get(config.SignerUrl + "cert/requests") if err != nil { fmt.Println("Didn't get a valid response", err) os.Exit(1) } getRespBuf, err := ioutil.ReadAll(getResp.Body) if err != nil { fmt.Println("Error reading response body", err) os.Exit(1) } getResp.Body.Close() if getResp.StatusCode != 200 { fmt.Println("Error getting listing of certs", string(getRespBuf)) os.Exit(1) } certs := make(certRequestResponse) json.Unmarshal(getRespBuf, &certs) for requestID, respElement := range certs { if showAll || !respElement.Signed { rawCert, err := base64.StdEncoding.DecodeString(respElement.CertBlob) if err != nil { fmt.Println("Trouble base64 decoding response:", err, respElement.CertBlob) os.Exit(1) } pubKey, err := ssh.ParsePublicKey(rawCert) if err != nil { fmt.Println("Trouble parsing response:", err) os.Exit(1) } cert := *pubKey.(*ssh.Certificate) env, ok := cert.Extensions["*****@*****.**"] if !ok { env = "unknown env" } expired := int64(cert.ValidBefore)-int64(time.Now().Unix()) < 1 if !expired || showAll { expiredMsg := "" if expired { expiredMsg = ", \033[91mexpired\033[0m" } fmt.Printf("%d %s[%s, %d/%d%s]: %s - %s\n", respElement.Serial, requestID, env, respElement.NumSignatures, respElement.SignaturesRequired, expiredMsg, cert.KeyId, cert.Extensions["*****@*****.**"], ) } } } }
func ValidateOpenSSHPublicKey(key OpenSSHPublicKey) error { _, err := ssh.ParsePublicKey(key.key) return err }
func ClonePublicKey(key ssh.PublicKey) (ssh.PublicKey, error) { return ssh.ParsePublicKey(key.Marshal()) }
func signCert(c *cli.Context) { configPath := c.String("config-file") allConfig := make(map[string]ssh_ca_util.SignerConfig) err := ssh_ca_util.LoadConfig(configPath, &allConfig) if err != nil { fmt.Println("Load Config failed:", err) os.Exit(1) } certRequestID := c.String("cert-request-id") if certRequestID == "" { certRequestID = c.Args().First() if certRequestID == "" { fmt.Println("Specify a cert-request-id") os.Exit(1) } } environment := c.String("environment") wrongTypeConfig, err := ssh_ca_util.GetConfigForEnv(environment, &allConfig) if err != nil { fmt.Println(err) os.Exit(1) } config := wrongTypeConfig.(ssh_ca_util.SignerConfig) conn, err := net.Dial("unix", os.Getenv("SSH_AUTH_SOCK")) if err != nil { fmt.Println("Dial failed:", err) os.Exit(1) } signer, err := ssh_ca_util.GetSignerForFingerprint(config.KeyFingerprint, conn) if err != nil { fmt.Println(err) os.Exit(1) } requestParameters := make(url.Values) requestParameters["certRequestId"] = make([]string, 1) requestParameters["certRequestId"][0] = certRequestID getResp, err := http.Get(config.SignerUrl + "cert/requests?" + requestParameters.Encode()) if err != nil { fmt.Println("Didn't get a valid response", err) os.Exit(1) } getRespBuf, err := ioutil.ReadAll(getResp.Body) if err != nil { fmt.Println("Error reading response body", err) os.Exit(1) } getResp.Body.Close() if getResp.StatusCode != 200 { fmt.Println("Error getting that request id:", string(getRespBuf)) os.Exit(1) } getResponse := make(certRequestResponse) err = json.Unmarshal(getRespBuf, &getResponse) if err != nil { fmt.Println("Unable to unmarshall response", err) os.Exit(1) } if getResponse[certRequestID].Signed { fmt.Println("Certificate already signed. Thanks for trying.") os.Exit(0) } rawCert, err := base64.StdEncoding.DecodeString(getResponse[certRequestID].CertBlob) if err != nil { fmt.Println("Trouble base64 decoding response", err) os.Exit(1) } pubKey, err := ssh.ParsePublicKey(rawCert) if err != nil { fmt.Println("Trouble parsing response", err) os.Exit(1) } cert := *pubKey.(*ssh.Certificate) ssh_ca_util.PrintForInspection(cert) fmt.Printf("Type 'yes' if you'd like to sign this cert request ") reader := bufio.NewReader(os.Stdin) text, _ := reader.ReadString('\n') text = strings.TrimSpace(text) if text != "yes" && text != "YES" { os.Exit(0) } err = cert.SignCert(rand.Reader, signer) if err != nil { fmt.Println("Error signing:", err) os.Exit(1) } request := ssh_ca_client.MakeSigningRequest(cert, certRequestID, config) requestWebParameters := request.BuildWebRequest() err = request.PostToWeb(requestWebParameters) if err != nil { fmt.Println("Error sending in +1:", err) os.Exit(1) } else { fmt.Println("Signature accepted by server.") } }
/* Update() searches LDAP for the current user set that supports the necessary properties for Hologram. TODO: call this at some point during verification failure so that keys that have been recently added to LDAP work, instead of requiring a server restart. */ func (luc *ldapUserCache) Update() error { start := time.Now() if luc.enableLDAPRoles { groupSearchRequest := ldap.NewSearchRequest( luc.baseDN, ldap.ScopeWholeSubtree, ldap.NeverDerefAliases, 0, 0, false, "(objectClass=groupOfNames)", []string{luc.roleAttribute}, nil, ) groupSearchResult, err := luc.server.Search(groupSearchRequest) if err != nil { return err } for _, entry := range groupSearchResult.Entries { dn := entry.DN arns := entry.GetAttributeValues(luc.roleAttribute) log.Debug("Adding %s to %s", arns, dn) luc.groups[dn] = arns } } filter := "(sshPublicKey=*)" searchRequest := ldap.NewSearchRequest( luc.baseDN, ldap.ScopeWholeSubtree, ldap.NeverDerefAliases, 0, 0, false, filter, []string{"sshPublicKey", luc.userAttr, "memberOf"}, nil, ) searchResult, err := luc.server.Search(searchRequest) if err != nil { return err } for _, entry := range searchResult.Entries { username := entry.GetAttributeValue(luc.userAttr) userKeys := []ssh.PublicKey{} for _, eachKey := range entry.GetAttributeValues("sshPublicKey") { sshKeyBytes, _ := base64.StdEncoding.DecodeString(eachKey) userSSHKey, err := ssh.ParsePublicKey(sshKeyBytes) if err != nil { userSSHKey, _, _, _, err = ssh.ParseAuthorizedKey([]byte(eachKey)) if err != nil { log.Warning("SSH key parsing for user %s failed (key was '%s')! This key will not be added into LDAP.", username, eachKey) continue } } userKeys = append(userKeys, userSSHKey) } arns := []string{} if luc.enableLDAPRoles { for _, groupDN := range entry.GetAttributeValues("memberOf") { log.Debug(groupDN) arns = append(arns, luc.groups[groupDN]...) } } luc.users[username] = &User{ SSHKeys: userKeys, Username: username, ARNs: arns, } log.Debug("Information on %s (re-)generated.", username) } log.Debug("LDAP information re-cached.") luc.stats.Timing(1.0, "ldapCacheUpdate", time.Since(start)) return nil }