@@ -7,6 +7,8 @@ package ssh
7
7
import (
8
8
"bytes"
9
9
"crypto"
10
+ "crypto/aes"
11
+ "crypto/cipher"
10
12
"crypto/dsa"
11
13
"crypto/ecdsa"
12
14
"crypto/elliptic"
@@ -25,6 +27,7 @@ import (
25
27
"strings"
26
28
27
29
"golang.org/x/crypto/ed25519"
30
+ "golang.org/x/crypto/ssh/internal/bcrypt_pbkdf"
28
31
)
29
32
30
33
// These constants represent the algorithm names for key types supported by this
@@ -1122,21 +1125,25 @@ func ParseRawPrivateKey(pemBytes []byte) (interface{}, error) {
1122
1125
case "DSA PRIVATE KEY" :
1123
1126
return ParseDSAPrivateKey (block .Bytes )
1124
1127
case "OPENSSH PRIVATE KEY" :
1125
- return parseOpenSSHPrivateKey (block .Bytes )
1128
+ return parseOpenSSHPrivateKey (block .Bytes , unencryptedOpenSSHKey )
1126
1129
default :
1127
1130
return nil , fmt .Errorf ("ssh: unsupported key type %q" , block .Type )
1128
1131
}
1129
1132
}
1130
1133
1131
1134
// ParseRawPrivateKeyWithPassphrase returns a private key decrypted with
1132
- // passphrase from a PEM encoded private key. If wrong passphrase, return
1133
- // x509.IncorrectPasswordError.
1135
+ // passphrase from a PEM encoded private key. If the passphrase is wrong, it
1136
+ // will return x509.IncorrectPasswordError.
1134
1137
func ParseRawPrivateKeyWithPassphrase (pemBytes , passphrase []byte ) (interface {}, error ) {
1135
1138
block , _ := pem .Decode (pemBytes )
1136
1139
if block == nil {
1137
1140
return nil , errors .New ("ssh: no key found" )
1138
1141
}
1139
1142
1143
+ if block .Type == "OPENSSH PRIVATE KEY" {
1144
+ return parseOpenSSHPrivateKey (block .Bytes , passphraseProtectedOpenSSHKey (passphrase ))
1145
+ }
1146
+
1140
1147
if ! encryptedBlock (block ) || ! x509 .IsEncryptedPEMBlock (block ) {
1141
1148
return nil , errors .New ("ssh: not an encrypted key" )
1142
1149
}
@@ -1193,9 +1200,60 @@ func ParseDSAPrivateKey(der []byte) (*dsa.PrivateKey, error) {
1193
1200
}, nil
1194
1201
}
1195
1202
1196
- // Implemented based on the documentation at
1197
- // https://github.com/openssh/openssh-portable/blob/master/PROTOCOL.key
1198
- func parseOpenSSHPrivateKey (key []byte ) (crypto.PrivateKey , error ) {
1203
+ func unencryptedOpenSSHKey (cipherName , kdfName , kdfOpts string , privKeyBlock []byte ) ([]byte , error ) {
1204
+ if kdfName != "none" || cipherName != "none" {
1205
+ return nil , & PassphraseMissingError {}
1206
+ }
1207
+ if kdfOpts != "" {
1208
+ return nil , errors .New ("ssh: invalid openssh private key" )
1209
+ }
1210
+ return privKeyBlock , nil
1211
+ }
1212
+
1213
+ func passphraseProtectedOpenSSHKey (passphrase []byte ) openSSHDecryptFunc {
1214
+ return func (cipherName , kdfName , kdfOpts string , privKeyBlock []byte ) ([]byte , error ) {
1215
+ if kdfName == "none" || cipherName == "none" {
1216
+ return nil , errors .New ("ssh: key is not password protected" )
1217
+ }
1218
+ if kdfName != "bcrypt" {
1219
+ return nil , fmt .Errorf ("ssh: unknown KDF %q, only supports %q" , kdfName , "bcrypt" )
1220
+ }
1221
+
1222
+ var opts struct {
1223
+ Salt string
1224
+ Rounds uint32
1225
+ }
1226
+ if err := Unmarshal ([]byte (kdfOpts ), & opts ); err != nil {
1227
+ return nil , err
1228
+ }
1229
+
1230
+ k , err := bcrypt_pbkdf .Key (passphrase , []byte (opts .Salt ), int (opts .Rounds ), 32 + 16 )
1231
+ if err != nil {
1232
+ return nil , err
1233
+ }
1234
+ key , iv := k [:32 ], k [32 :]
1235
+
1236
+ if cipherName != "aes256-ctr" {
1237
+ return nil , fmt .Errorf ("ssh: unknown cipher %q, only supports %q" , cipherName , "aes256-ctr" )
1238
+ }
1239
+ c , err := aes .NewCipher (key )
1240
+ if err != nil {
1241
+ return nil , err
1242
+ }
1243
+ ctr := cipher .NewCTR (c , iv )
1244
+ ctr .XORKeyStream (privKeyBlock , privKeyBlock )
1245
+
1246
+ return privKeyBlock , nil
1247
+ }
1248
+ }
1249
+
1250
+ type openSSHDecryptFunc func (CipherName , KdfName , KdfOpts string , PrivKeyBlock []byte ) ([]byte , error )
1251
+
1252
+ // parseOpenSSHPrivateKey parses an OpenSSH private key, using the decrypt
1253
+ // function to unwrap the encrypted portion. unencryptedOpenSSHKey can be used
1254
+ // as the decrypt function to parse an unencrypted private key. See
1255
+ // https://github.com/openssh/openssh-portable/blob/master/PROTOCOL.key.
1256
+ func parseOpenSSHPrivateKey (key []byte , decrypt openSSHDecryptFunc ) (crypto.PrivateKey , error ) {
1199
1257
const magic = "openssh-key-v1\x00 "
1200
1258
if len (key ) < len (magic ) || string (key [:len (magic )]) != magic {
1201
1259
return nil , errors .New ("ssh: invalid openssh private key format" )
@@ -1214,9 +1272,22 @@ func parseOpenSSHPrivateKey(key []byte) (crypto.PrivateKey, error) {
1214
1272
if err := Unmarshal (remaining , & w ); err != nil {
1215
1273
return nil , err
1216
1274
}
1275
+ if w .NumKeys != 1 {
1276
+ // We only support single key files, and so does OpenSSH.
1277
+ // https://github.com/openssh/openssh-portable/blob/4103a3ec7/sshkey.c#L4171
1278
+ return nil , errors .New ("ssh: multi-key files are not supported" )
1279
+ }
1217
1280
1218
- if w .KdfName != "none" || w .CipherName != "none" {
1219
- return nil , errors .New ("ssh: cannot decode encrypted private keys" )
1281
+ privKeyBlock , err := decrypt (w .CipherName , w .KdfName , w .KdfOpts , w .PrivKeyBlock )
1282
+ if err != nil {
1283
+ if err , ok := err .(* PassphraseMissingError ); ok {
1284
+ pub , errPub := ParsePublicKey (w .PubKey )
1285
+ if errPub != nil {
1286
+ return nil , fmt .Errorf ("ssh: failed to parse embedded public key: %v" , errPub )
1287
+ }
1288
+ err .PublicKey = pub
1289
+ }
1290
+ return nil , err
1220
1291
}
1221
1292
1222
1293
pk1 := struct {
@@ -1226,12 +1297,11 @@ func parseOpenSSHPrivateKey(key []byte) (crypto.PrivateKey, error) {
1226
1297
Rest []byte `ssh:"rest"`
1227
1298
}{}
1228
1299
1229
- if err := Unmarshal (w .PrivKeyBlock , & pk1 ); err != nil {
1230
- return nil , err
1231
- }
1232
-
1233
- if pk1 .Check1 != pk1 .Check2 {
1234
- return nil , errors .New ("ssh: checkint mismatch" )
1300
+ if err := Unmarshal (privKeyBlock , & pk1 ); err != nil || pk1 .Check1 != pk1 .Check2 {
1301
+ if w .CipherName != "none" {
1302
+ return nil , x509 .IncorrectPasswordError
1303
+ }
1304
+ return nil , errors .New ("ssh: malformed OpenSSH key" )
1235
1305
}
1236
1306
1237
1307
// we only handle ed25519 and rsa keys currently
@@ -1253,10 +1323,8 @@ func parseOpenSSHPrivateKey(key []byte) (crypto.PrivateKey, error) {
1253
1323
return nil , err
1254
1324
}
1255
1325
1256
- for i , b := range key .Pad {
1257
- if int (b ) != i + 1 {
1258
- return nil , errors .New ("ssh: padding not as expected" )
1259
- }
1326
+ if err := checkOpenSSHKeyPadding (key .Pad ); err != nil {
1327
+ return nil , err
1260
1328
}
1261
1329
1262
1330
pk := & rsa.PrivateKey {
@@ -1291,10 +1359,8 @@ func parseOpenSSHPrivateKey(key []byte) (crypto.PrivateKey, error) {
1291
1359
return nil , errors .New ("ssh: private key unexpected length" )
1292
1360
}
1293
1361
1294
- for i , b := range key .Pad {
1295
- if int (b ) != i + 1 {
1296
- return nil , errors .New ("ssh: padding not as expected" )
1297
- }
1362
+ if err := checkOpenSSHKeyPadding (key .Pad ); err != nil {
1363
+ return nil , err
1298
1364
}
1299
1365
1300
1366
pk := ed25519 .PrivateKey (make ([]byte , ed25519 .PrivateKeySize ))
@@ -1305,6 +1371,15 @@ func parseOpenSSHPrivateKey(key []byte) (crypto.PrivateKey, error) {
1305
1371
}
1306
1372
}
1307
1373
1374
+ func checkOpenSSHKeyPadding (pad []byte ) error {
1375
+ for i , b := range pad {
1376
+ if int (b ) != i + 1 {
1377
+ return errors .New ("ssh: padding not as expected" )
1378
+ }
1379
+ }
1380
+ return nil
1381
+ }
1382
+
1308
1383
// FingerprintLegacyMD5 returns the user presentation of the key's
1309
1384
// fingerprint as described by RFC 4716 section 4.
1310
1385
func FingerprintLegacyMD5 (pubKey PublicKey ) string {
0 commit comments