@@ -100,9 +100,16 @@ public KeyType getType() throws IOException {
100
100
return KeyType .UNKNOWN ;
101
101
}
102
102
103
- public boolean isEncrypted () {
104
- // Currently the only supported encryption types are "aes256-cbc" and "none".
105
- return "aes256-cbc" .equals (headers .get ("Encryption" ));
103
+ public boolean isEncrypted () throws IOException {
104
+ // Currently, the only supported encryption types are "aes256-cbc" and "none".
105
+ String encryption = headers .get ("Encryption" );
106
+ if ("none" .equals (encryption )) {
107
+ return false ;
108
+ }
109
+ if ("aes256-cbc" .equals (encryption )) {
110
+ return true ;
111
+ }
112
+ throw new IOException (String .format ("Unsupported encryption: %s" , encryption ));
106
113
}
107
114
108
115
private Map <String , String > payload = new HashMap <String , String >();
@@ -116,8 +123,9 @@ protected KeyPair readKeyPair() throws IOException {
116
123
this .parseKeyPair ();
117
124
final Buffer .PlainBuffer publicKeyReader = new Buffer .PlainBuffer (publicKey );
118
125
final Buffer .PlainBuffer privateKeyReader = new Buffer .PlainBuffer (privateKey );
126
+ final KeyType keyType = this .getType ();
119
127
publicKeyReader .readBytes (); // The first part of the payload is a human-readable key format name.
120
- if (KeyType .RSA .equals (this . getType () )) {
128
+ if (KeyType .RSA .equals (keyType )) {
121
129
// public key exponent
122
130
BigInteger e = publicKeyReader .readMPInt ();
123
131
// modulus
@@ -139,7 +147,7 @@ protected KeyPair readKeyPair() throws IOException {
139
147
throw new IOException (i .getMessage (), i );
140
148
}
141
149
}
142
- if (KeyType .DSA .equals (this . getType () )) {
150
+ if (KeyType .DSA .equals (keyType )) {
143
151
BigInteger p = publicKeyReader .readMPInt ();
144
152
BigInteger q = publicKeyReader .readMPInt ();
145
153
BigInteger g = publicKeyReader .readMPInt ();
@@ -161,14 +169,14 @@ protected KeyPair readKeyPair() throws IOException {
161
169
throw new IOException (e .getMessage (), e );
162
170
}
163
171
}
164
- if (KeyType .ED25519 .equals (this . getType () )) {
172
+ if (KeyType .ED25519 .equals (keyType )) {
165
173
EdDSANamedCurveSpec ed25519 = EdDSANamedCurveTable .getByName ("Ed25519" );
166
174
EdDSAPublicKeySpec publicSpec = new EdDSAPublicKeySpec (publicKeyReader .readBytes (), ed25519 );
167
175
EdDSAPrivateKeySpec privateSpec = new EdDSAPrivateKeySpec (privateKeyReader .readBytes (), ed25519 );
168
176
return new KeyPair (new EdDSAPublicKey (publicSpec ), new EdDSAPrivateKey (privateSpec ));
169
177
}
170
178
final String ecdsaCurve ;
171
- switch (this . getType () ) {
179
+ switch (keyType ) {
172
180
case ECDSA256 :
173
181
ecdsaCurve = "P-256" ;
174
182
break ;
@@ -190,7 +198,7 @@ protected KeyPair readKeyPair() throws IOException {
190
198
ECPrivateKeySpec pks = new ECPrivateKeySpec (s , ecCurveSpec );
191
199
try {
192
200
PrivateKey privateKey = SecurityUtils .getKeyFactory (KeyAlgorithm .ECDSA ).generatePrivate (pks );
193
- return new KeyPair (getType () .readPubKeyFromBuffer (publicKeyReader ), privateKey );
201
+ return new KeyPair (keyType .readPubKeyFromBuffer (publicKeyReader ), privateKey );
194
202
} catch (GeneralSecurityException e ) {
195
203
throw new IOException (e .getMessage (), e );
196
204
}
@@ -252,6 +260,12 @@ protected void parseKeyPair() throws IOException {
252
260
* This is used to decrypt the private key when it's encrypted.
253
261
*/
254
262
private byte [] toKey (final String passphrase ) throws IOException {
263
+ // The field Key-Derivation has been introduced with Putty v3 key file format
264
+ // The only available formats are "Argon2i" "Argon2d" and "Argon2id"
265
+ String keyDerivation = headers .get ("Key-Derivation" );
266
+ if (keyDerivation != null ) {
267
+ throw new IOException (String .format ("Unsupported key derivation function: %s" , keyDerivation ));
268
+ }
255
269
try {
256
270
MessageDigest digest = MessageDigest .getInstance ("SHA-1" );
257
271
@@ -283,7 +297,7 @@ private byte[] toKey(final String passphrase) throws IOException {
283
297
*/
284
298
private void verify (final String passphrase ) throws IOException {
285
299
try {
286
- // The key to the MAC is itself a SHA-1 hash of:
300
+ // The key to the MAC is itself a SHA-1 hash of (v1/v2 key only) :
287
301
MessageDigest digest = MessageDigest .getInstance ("SHA-1" );
288
302
digest .update ("putty-private-key-file-mac-key" .getBytes ());
289
303
if (passphrase != null ) {
@@ -297,8 +311,9 @@ private void verify(final String passphrase) throws IOException {
297
311
final ByteArrayOutputStream out = new ByteArrayOutputStream ();
298
312
final DataOutputStream data = new DataOutputStream (out );
299
313
// name of algorithm
300
- data .writeInt (this .getType ().toString ().length ());
301
- data .writeBytes (this .getType ().toString ());
314
+ String keyType = this .getType ().toString ();
315
+ data .writeInt (keyType .length ());
316
+ data .writeBytes (keyType );
302
317
303
318
data .writeInt (headers .get ("Encryption" ).length ());
304
319
data .writeBytes (headers .get ("Encryption" ));
0 commit comments