summaryrefslogtreecommitdiffstats
path: root/models/ssh_key.go
diff options
context:
space:
mode:
authorzeripath <art27@cantab.net>2019-07-25 20:33:38 +0100
committerGitHub <noreply@github.com>2019-07-25 20:33:38 +0100
commit18c0e9c2a95f3051bc1615a6cda0dd88b5489117 (patch)
tree63c91f10cc37456e2615cc35ae2ef1c054d22640 /models/ssh_key.go
parent6485962dd53d9704f0a2b438257f722a306beedd (diff)
downloadgitea-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.go91
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
}