Clear passphrase bytes after use (#609)

Mimics the behavior of `decrypt()` in `PKCS5KeyFile.java`.
This commit is contained in:
Fabian Henneke
2020-07-01 21:34:31 +02:00
committed by GitHub
parent 3c85b86915
commit 7bde5c15c1
2 changed files with 45 additions and 23 deletions

View File

@@ -143,9 +143,12 @@ public class OpenSSHKeyV1KeyFile extends BaseFileKeyProvider {
CharBuffer charBuffer = CharBuffer.wrap(pwdf.reqPassword(null)); CharBuffer charBuffer = CharBuffer.wrap(pwdf.reqPassword(null));
ByteBuffer byteBuffer = Charset.forName("UTF-8").encode(charBuffer); ByteBuffer byteBuffer = Charset.forName("UTF-8").encode(charBuffer);
passphrase = Arrays.copyOfRange(byteBuffer.array(), byteBuffer.position(), byteBuffer.limit()); passphrase = Arrays.copyOfRange(byteBuffer.array(), byteBuffer.position(), byteBuffer.limit());
Arrays.fill(charBuffer.array(), '\u0000');
Arrays.fill(byteBuffer.array(), (byte) 0);
} }
byte[] keyiv = new byte[48]; byte[] keyiv = new byte[48];
new BCrypt().pbkdf(passphrase, opts.readBytes(), opts.readUInt32AsInt(), keyiv); new BCrypt().pbkdf(passphrase, opts.readBytes(), opts.readUInt32AsInt(), keyiv);
Arrays.fill(passphrase, (byte) 0);
byte[] key = Arrays.copyOfRange(keyiv, 0, 32); byte[] key = Arrays.copyOfRange(keyiv, 0, 32);
byte[] iv = Arrays.copyOfRange(keyiv, 32, 48); byte[] iv = Arrays.copyOfRange(keyiv, 32, 48);
cipher.init(Cipher.Mode.Decrypt, key, iv); cipher.init(Cipher.Mode.Decrypt, key, iv);

View File

@@ -47,10 +47,7 @@ import java.security.interfaces.RSAPublicKey;
import java.text.DateFormat; import java.text.DateFormat;
import java.text.ParseException; import java.text.ParseException;
import java.text.SimpleDateFormat; import java.text.SimpleDateFormat;
import java.util.Arrays; import java.util.*;
import java.util.Date;
import java.util.Map;
import java.util.Scanner;
import static org.hamcrest.CoreMatchers.containsString; import static org.hamcrest.CoreMatchers.containsString;
import static org.hamcrest.CoreMatchers.equalTo; import static org.hamcrest.CoreMatchers.equalTo;
@@ -70,6 +67,44 @@ public class OpenSSHKeyFileTest {
final char[] correctPassphrase = "test_passphrase".toCharArray(); final char[] correctPassphrase = "test_passphrase".toCharArray();
final char[] incorrectPassphrase = new char[]{' '}; final char[] incorrectPassphrase = new char[]{' '};
private static class WipeTrackingPasswordFinder implements PasswordFinder {
private int reqCounter = 0;
final private String password;
final private boolean withRetry;
final private ArrayList<char[]> toWipe = new ArrayList<>();
WipeTrackingPasswordFinder(String password, Boolean withRetry) {
this.password = password;
this.withRetry = withRetry;
}
@Override
public char[] reqPassword(Resource<?> resource) {
char[] passwordChars;
if (withRetry && reqCounter < 3) {
reqCounter++;
// Return an incorrect password three times before returning the correct one.
passwordChars = (password + "incorrect").toCharArray();
} else {
passwordChars = password.toCharArray();
}
toWipe.add(passwordChars);
return passwordChars;
}
@Override
public boolean shouldRetry(Resource<?> resource) {
return withRetry && reqCounter <= 3;
}
public void assertWiped() {
for (char[] passwordChars : toWipe) {
assertArrayEquals(new char[passwordChars.length], passwordChars);
}
}
};
final PasswordFinder onlyGivesWhenReady = new PasswordFinder() { final PasswordFinder onlyGivesWhenReady = new PasswordFinder() {
@Override @Override
public char[] reqPassword(Resource resource) { public char[] reqPassword(Resource resource) {
@@ -249,27 +284,11 @@ public class OpenSSHKeyFileTest {
private void checkOpenSSHKeyV1(String key, final String password, boolean withRetry) throws IOException { private void checkOpenSSHKeyV1(String key, final String password, boolean withRetry) throws IOException {
OpenSSHKeyV1KeyFile keyFile = new OpenSSHKeyV1KeyFile(); OpenSSHKeyV1KeyFile keyFile = new OpenSSHKeyV1KeyFile();
keyFile.init(new File(key), new PasswordFinder() { WipeTrackingPasswordFinder pwf = new WipeTrackingPasswordFinder(password, withRetry);
private int reqCounter = 0; keyFile.init(new File(key), pwf);
@Override
public char[] reqPassword(Resource<?> resource) {
if (withRetry && reqCounter < 3) {
reqCounter++;
// Return an incorrect password three times before returning the correct one.
return (password + "incorrect").toCharArray();
} else {
return password.toCharArray();
}
}
@Override
public boolean shouldRetry(Resource<?> resource) {
return withRetry && reqCounter <= 3;
}
});
PrivateKey aPrivate = keyFile.getPrivate(); PrivateKey aPrivate = keyFile.getPrivate();
assertThat(aPrivate.getAlgorithm(), equalTo("EdDSA")); assertThat(aPrivate.getAlgorithm(), equalTo("EdDSA"));
pwf.assertWiped();
} }
@Test @Test