|
|
|
@ -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") |
|
|
|
|