mirror of
https://codeberg.org/davrot/forgejo.git
synced 2025-05-16 11:00:02 +02:00
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
This commit is contained in:
parent
6485962dd5
commit
18c0e9c2a9
2 changed files with 125 additions and 27 deletions
|
@ -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
|
||||
}
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue