|
|
|
@ -6,6 +6,8 @@ package models
|
|
|
|
|
|
|
|
|
|
import (
|
|
|
|
|
"bufio"
|
|
|
|
|
"encoding/base64"
|
|
|
|
|
"encoding/binary"
|
|
|
|
|
"errors"
|
|
|
|
|
"fmt"
|
|
|
|
|
"io"
|
|
|
|
@ -111,6 +113,85 @@ var (
|
|
|
|
|
}
|
|
|
|
|
)
|
|
|
|
|
|
|
|
|
|
func extractTypeFromBase64Key(key string) (string, error) {
|
|
|
|
|
b, err := base64.StdEncoding.DecodeString(key)
|
|
|
|
|
if err != nil || len(b) < 4 {
|
|
|
|
|
return "", errors.New("Invalid key format")
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
keyLength := int(binary.BigEndian.Uint32(b))
|
|
|
|
|
|
|
|
|
|
if len(b) < 4+keyLength {
|
|
|
|
|
return "", errors.New("Invalid key format")
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
return string(b[4 : 4+keyLength]), nil
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// Parse any key string in openssh or ssh2 format to clean openssh string (rfc4253)
|
|
|
|
|
func ParseKeyString(content string) (string, error) {
|
|
|
|
|
|
|
|
|
|
// Transform all legal line endings to a single "\n"
|
|
|
|
|
s := strings.Replace(strings.Replace(strings.TrimSpace(content), "\r\n", "\n", -1), "\r", "\n", -1)
|
|
|
|
|
|
|
|
|
|
lines := strings.Split(s, "\n")
|
|
|
|
|
|
|
|
|
|
var keyType, keyContent, keyComment string
|
|
|
|
|
|
|
|
|
|
if len(lines) == 1 {
|
|
|
|
|
// Parse openssh format
|
|
|
|
|
parts := strings.Fields(lines[0])
|
|
|
|
|
switch len(parts) {
|
|
|
|
|
case 0:
|
|
|
|
|
return "", errors.New("Empty key")
|
|
|
|
|
case 1:
|
|
|
|
|
keyContent = parts[0]
|
|
|
|
|
case 2:
|
|
|
|
|
keyType = parts[0]
|
|
|
|
|
keyContent = parts[1]
|
|
|
|
|
default:
|
|
|
|
|
keyType = parts[0]
|
|
|
|
|
keyContent = parts[1]
|
|
|
|
|
keyComment = parts[2]
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// If keyType is not given, extract it from content. If given, validate it
|
|
|
|
|
if len(keyType) == 0 {
|
|
|
|
|
if t, err := extractTypeFromBase64Key(keyContent); err == nil {
|
|
|
|
|
keyType = t
|
|
|
|
|
} else {
|
|
|
|
|
return "", err
|
|
|
|
|
}
|
|
|
|
|
} else {
|
|
|
|
|
if t, err := extractTypeFromBase64Key(keyContent); err != nil || keyType != t {
|
|
|
|
|
return "", err
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
} else {
|
|
|
|
|
// Parse SSH2 file format.
|
|
|
|
|
continuationLine := false
|
|
|
|
|
|
|
|
|
|
for _, line := range lines {
|
|
|
|
|
// Skip lines that:
|
|
|
|
|
// 1) are a continuation of the previous line,
|
|
|
|
|
// 2) contain ":" as that are comment lines
|
|
|
|
|
// 3) contain "-" as that are begin and end tags
|
|
|
|
|
if continuationLine || strings.ContainsAny(line, ":-") {
|
|
|
|
|
continuationLine = strings.HasSuffix(line, "\\")
|
|
|
|
|
} else {
|
|
|
|
|
keyContent = keyContent + line
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
if t, err := extractTypeFromBase64Key(keyContent); err == nil {
|
|
|
|
|
keyType = t
|
|
|
|
|
} else {
|
|
|
|
|
return "", err
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
return keyType + " " + keyContent + " " + keyComment, nil
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// CheckPublicKeyString checks if the given public key string is recognized by SSH.
|
|
|
|
|
func CheckPublicKeyString(content string) (bool, error) {
|
|
|
|
|
content = strings.TrimRight(content, "\n\r")
|
|
|
|
|