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
					
				|  | @ -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 | ||||
| } | ||||
|  |  | |||
|  | @ -66,6 +66,67 @@ func Test_CheckPublicKeyString(t *testing.T) { | |||
| 		{"ssh-rsa AAAAB3NzaC1yc2EA\r\nAAADAQABAAAAgQDAu7tvI\nvX6ZHrRXuZNfkR3XLHSsuCK9Zn3X58lxBcQzuo5xZgB6vRwwm/QtJuF+zZPtY5hsQILBLmF+\r\nBZ5WpKZp1jBeSjH2G7lxet9kbcH+kIVj0tPFEoyKI9wvW\nqIwC4prx/WVk2wLTJjzBAhyNx\r\nfEq7C9CeiX9pQEbEqJfkKCQ== nocomment\r\n\r\n"}, | ||||
| 		{"ssh-ed25519 AAAAC3NzaC1lZDI1NTE5AAAAICV0MGX/W9IvLA4FXpIuUcdDcbj5KX4syHgsTy7soVgf"}, | ||||
| 		{"\r\nssh-ed25519 \r\nAAAAC3NzaC1lZDI1NTE5AAAAICV0MGX/W9IvLA4FXpIuUcdDcbj5KX4syHgsTy7soVgf\r\n\r\n"}, | ||||
| 		{`---- BEGIN SSH2 PUBLIC KEY ---- | ||||
| Comment: "1024-bit DSA, converted by andrew@phaedra from OpenSSH" | ||||
| AAAAB3NzaC1kc3MAAACBAOChCC7lf6Uo9n7BmZ6M8St19PZf4Tn59NriyboW2x/DZuYAz3 | ||||
| ibZ2OkQ3S0SqDIa0HXSEJ1zaExQdmbO+Ux/wsytWZmCczWOVsaszBZSl90q8UnWlSH6P+/ | ||||
| YA+RWJm5SFtuV9PtGIhyZgoNuz5kBQ7K139wuQsecdKktISwTakzAAAAFQCzKsO2JhNKlL | ||||
| +wwwLGOcLffoAmkwAAAIBpK7/3xvduajLBD/9vASqBQIHrgK2J+wiQnIb/Wzy0UsVmvfn8 | ||||
| A+udRbBo+csM8xrSnlnlJnjkJS3qiM5g+eTwsLIV1IdKPEwmwB+VcP53Cw6lSyWyJcvhFb | ||||
| 0N6s08NZysLzvj0N+ZC/FnhKTLzIyMtkHf/IrPCwlM+pV/M/96YgAAAIEAqQcGn9CKgzgP | ||||
| aguIZooTAOQdvBLMI5y0bQjOW6734XOpqQGf/Kra90wpoasLKZjSYKNPjE+FRUOrStLrxc | ||||
| Ns4BeVKhy2PYTRnybfYVk1/dmKgH6P1YSRONsGKvTsH6c5IyCRG0ncCgYeF8tXppyd6429 | ||||
| 82daopE7zQ/NPAnJfag= | ||||
| ---- END SSH2 PUBLIC KEY ---- | ||||
| `}, | ||||
| 		{`---- BEGIN SSH2 PUBLIC KEY ---- | ||||
| Comment: "1024-bit RSA, converted by andrew@phaedra from OpenSSH" | ||||
| AAAAB3NzaC1yc2EAAAADAQABAAAAgQDAu7tvIvX6ZHrRXuZNfkR3XLHSsuCK9Zn3X58lxB | ||||
| cQzuo5xZgB6vRwwm/QtJuF+zZPtY5hsQILBLmF+BZ5WpKZp1jBeSjH2G7lxet9kbcH+kIV | ||||
| j0tPFEoyKI9wvWqIwC4prx/WVk2wLTJjzBAhyNxfEq7C9CeiX9pQEbEqJfkKCQ== | ||||
| ---- END SSH2 PUBLIC KEY ---- | ||||
| `}, | ||||
| 		{`-----BEGIN RSA PUBLIC KEY----- | ||||
| MIGJAoGBAMC7u28i9fpketFe5k1+RHdcsdKy4Ir1mfdfnyXEFxDO6jnFmAHq9HDC | ||||
| b9C0m4X7Nk+1jmGxAgsEuYX4FnlakpmnWMF5KMfYbuXF632Rtwf6QhWPS08USjIo | ||||
| j3C9aojALimvH9ZWTbAtMmPMECHI3F8SrsL0J6Jf2lARsSol+QoJAgMBAAE= | ||||
| -----END RSA PUBLIC KEY----- | ||||
| `}, | ||||
| 		{`-----BEGIN PUBLIC KEY----- | ||||
| MIIBtzCCASsGByqGSM44BAEwggEeAoGBAOChCC7lf6Uo9n7BmZ6M8St19PZf4Tn5 | ||||
| 9NriyboW2x/DZuYAz3ibZ2OkQ3S0SqDIa0HXSEJ1zaExQdmbO+Ux/wsytWZmCczW | ||||
| OVsaszBZSl90q8UnWlSH6P+/YA+RWJm5SFtuV9PtGIhyZgoNuz5kBQ7K139wuQse | ||||
| cdKktISwTakzAhUAsyrDtiYTSpS/sMMCxjnC336AJpMCgYBpK7/3xvduajLBD/9v | ||||
| ASqBQIHrgK2J+wiQnIb/Wzy0UsVmvfn8A+udRbBo+csM8xrSnlnlJnjkJS3qiM5g | ||||
| +eTwsLIV1IdKPEwmwB+VcP53Cw6lSyWyJcvhFb0N6s08NZysLzvj0N+ZC/FnhKTL | ||||
| zIyMtkHf/IrPCwlM+pV/M/96YgOBhQACgYEAqQcGn9CKgzgPaguIZooTAOQdvBLM | ||||
| I5y0bQjOW6734XOpqQGf/Kra90wpoasLKZjSYKNPjE+FRUOrStLrxcNs4BeVKhy2 | ||||
| PYTRnybfYVk1/dmKgH6P1YSRONsGKvTsH6c5IyCRG0ncCgYeF8tXppyd642982da | ||||
| opE7zQ/NPAnJfag= | ||||
| -----END PUBLIC KEY----- | ||||
| `}, | ||||
| 		{`-----BEGIN PUBLIC KEY----- | ||||
| MIGfMA0GCSqGSIb3DQEBAQUAA4GNADCBiQKBgQDAu7tvIvX6ZHrRXuZNfkR3XLHS | ||||
| suCK9Zn3X58lxBcQzuo5xZgB6vRwwm/QtJuF+zZPtY5hsQILBLmF+BZ5WpKZp1jB | ||||
| eSjH2G7lxet9kbcH+kIVj0tPFEoyKI9wvWqIwC4prx/WVk2wLTJjzBAhyNxfEq7C | ||||
| 9CeiX9pQEbEqJfkKCQIDAQAB | ||||
| -----END PUBLIC KEY----- | ||||
| `}, | ||||
| 		{`-----BEGIN PUBLIC KEY----- | ||||
| MIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEAzGV4ftTgVMEh/Q+OcE2s | ||||
| RK0CDfSKAvcZezCiZKr077+juUUfWFvyCvRW3414F7KaWBobAmaNYRTjrFxzJ3zj | ||||
| karv8TA8eMj7sryqcOC3jxHIOEw4qWgxbsW1jqnPwVGUWXF7uNUAFnwy6yJ8LJbV | ||||
| mR0nhu4Y4aWnJeBa1b/VdaUujnOUNTccRM087jS0v/HYma05v2AEEP/gfps1iN8x | ||||
| LReJomY4wJY1ndS0wT71Nt3dvQ3AZphWoXGeONV2bE3gMBsRv0Oo/DYDV4/VsTHl | ||||
| sMV1do3gF/xAUqWawlZQkNcibME+sQqfE7gZ04hlmDATU2zmbzwuHtFiNv8mVv7O | ||||
| RQIDAQAB | ||||
| -----END PUBLIC KEY----- | ||||
| `}, | ||||
| 		{`---- BEGIN SSH2 PUBLIC KEY ---- | ||||
| Comment: "256-bit ED25519, converted by andrew@phaedra from OpenSSH" | ||||
| AAAAC3NzaC1lZDI1NTE5AAAAICV0MGX/W9IvLA4FXpIuUcdDcbj5KX4syHgsTy7soVgf | ||||
| ---- END SSH2 PUBLIC KEY ---- | ||||
| `}, | ||||
| 	} { | ||||
| 		_, err := CheckPublicKeyString(test.content) | ||||
| 		assert.NoError(t, err) | ||||
|  |  | |||
		Loading…
	
		Reference in New Issue