diff options
author | zeripath <art27@cantab.net> | 2019-07-25 20:33:38 +0100 |
---|---|---|
committer | GitHub <noreply@github.com> | 2019-07-25 20:33:38 +0100 |
commit | 18c0e9c2a95f3051bc1615a6cda0dd88b5489117 (patch) | |
tree | 63c91f10cc37456e2615cc35ae2ef1c054d22640 /models/ssh_key.go | |
parent | 6485962dd53d9704f0a2b438257f722a306beedd (diff) | |
download | gitea-18c0e9c2a95f3051bc1615a6cda0dd88b5489117.tar.gz gitea-18c0e9c2a95f3051bc1615a6cda0dd88b5489117.zip |
Make PKCS8, PEM and SSH2 keys work (#7600)
* Make PEM and SSH2 keys work
* add ssh2 testcases and PEM cases - and fix PEM
* Add final test to parse the proposed key
Diffstat (limited to 'models/ssh_key.go')
-rw-r--r-- | models/ssh_key.go | 91 |
1 files changed, 64 insertions, 27 deletions
diff --git a/models/ssh_key.go b/models/ssh_key.go index cbd68a307e..b2a905305a 100644 --- a/models/ssh_key.go +++ b/models/ssh_key.go @@ -7,8 +7,12 @@ package models import ( "bufio" + "crypto/rsa" + "crypto/x509" + "encoding/asn1" "encoding/base64" "encoding/binary" + "encoding/pem" "errors" "fmt" "io/ioutil" @@ -94,6 +98,8 @@ func extractTypeFromBase64Key(key string) (string, error) { return string(b[4 : 4+keyLength]), nil } +const ssh2keyStart = "---- BEGIN SSH2 PUBLIC KEY ----" + // parseKeyString parses any key string in OpenSSH or SSH2 format to clean OpenSSH string (RFC4253). func parseKeyString(content string) (string, error) { // remove whitespace at start and end @@ -101,7 +107,59 @@ func parseKeyString(content string) (string, error) { var keyType, keyContent, keyComment string - if !strings.Contains(content, "-----BEGIN") { + if content[:len(ssh2keyStart)] == ssh2keyStart { + // Parse SSH2 file format. + + // Transform all legal line endings to a single "\n". + content = strings.NewReplacer("\r\n", "\n", "\r", "\n").Replace(content) + + lines := strings.Split(content, "\n") + 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 += line + } + } + + t, err := extractTypeFromBase64Key(keyContent) + if err != nil { + return "", fmt.Errorf("extractTypeFromBase64Key: %v", err) + } + keyType = t + } else { + if strings.Contains(content, "-----BEGIN") { + // Convert PEM Keys to OpenSSH format + // Transform all legal line endings to a single "\n". + content = strings.NewReplacer("\r\n", "\n", "\r", "\n").Replace(content) + + block, _ := pem.Decode([]byte(content)) + if block == nil { + return "", fmt.Errorf("failed to parse PEM block containing the public key") + } + + pub, err := x509.ParsePKIXPublicKey(block.Bytes) + if err != nil { + var pk rsa.PublicKey + _, err2 := asn1.Unmarshal(block.Bytes, &pk) + if err2 != nil { + return "", fmt.Errorf("failed to parse DER encoded public key as either PKIX or PEM RSA Key: %v %v", err, err2) + } + pub = &pk + } + + sshKey, err := ssh.NewPublicKey(pub) + if err != nil { + return "", fmt.Errorf("unable to convert to ssh public key: %v", err) + } + content = string(ssh.MarshalAuthorizedKey(sshKey)) + } // Parse OpenSSH format. // Remove all newlines @@ -132,32 +190,11 @@ func parseKeyString(content string) (string, error) { } else if keyType != t { return "", fmt.Errorf("key type and content does not match: %s - %s", keyType, t) } - } else { - // Parse SSH2 file format. - - // Transform all legal line endings to a single "\n". - content = strings.NewReplacer("\r\n", "\n", "\r", "\n").Replace(content) - - lines := strings.Split(content, "\n") - 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 += line - } - } - - t, err := extractTypeFromBase64Key(keyContent) - if err != nil { - return "", fmt.Errorf("extractTypeFromBase64Key: %v", err) - } - keyType = t + } + // Finally we need to check whether we can actually read the proposed key: + _, _, _, _, err := ssh.ParseAuthorizedKey([]byte(keyType + " " + keyContent + " " + keyComment)) + if err != nil { + return "", fmt.Errorf("invalid ssh public key: %v", err) } return keyType + " " + keyContent + " " + keyComment, nil } |