mirror of https://github.com/apache/cloudstack.git
Apple base416 passphrase enc (#240)
* Move PassphraseVO to use String instead of byte[] to support Encrypt annotation * Check for unencrypted passphrases before migrating passphrase table --------- Co-authored-by: Marcus Sorensen <mls@apple.com>
This commit is contained in:
parent
5bca36f258
commit
2316e800ba
|
|
@ -314,8 +314,7 @@ public class VolumeOrchestrator extends ManagerBase implements VolumeOrchestrati
|
|||
newVol.setFormat(oldVol.getFormat());
|
||||
|
||||
if (oldVol.getPassphraseId() != null) {
|
||||
PassphraseVO passphrase =_passphraseDao.persist(new PassphraseVO());
|
||||
passphrase.clearPassphrase();
|
||||
PassphraseVO passphrase =_passphraseDao.persist(new PassphraseVO(true));
|
||||
newVol.setPassphraseId(passphrase.getId());
|
||||
}
|
||||
|
||||
|
|
@ -1689,8 +1688,7 @@ public class VolumeOrchestrator extends ManagerBase implements VolumeOrchestrati
|
|||
}
|
||||
s_logger.debug("Creating passphrase for the volume: " + volume.getName());
|
||||
long startTime = System.currentTimeMillis();
|
||||
PassphraseVO passphrase = _passphraseDao.persist(new PassphraseVO());
|
||||
passphrase.clearPassphrase();
|
||||
PassphraseVO passphrase = _passphraseDao.persist(new PassphraseVO(true));
|
||||
volume.setPassphraseId(passphrase.getId());
|
||||
long finishTime = System.currentTimeMillis();
|
||||
s_logger.debug("Creating and persisting passphrase took: " + (finishTime - startTime) + " ms for the volume: " + volume.toString());
|
||||
|
|
|
|||
|
|
@ -2,6 +2,7 @@ package org.apache.cloudstack.secret;
|
|||
|
||||
import com.cloud.utils.db.Encrypt;
|
||||
import com.cloud.utils.exception.CloudRuntimeException;
|
||||
import org.apache.commons.lang3.StringUtils;
|
||||
|
||||
import javax.persistence.Column;
|
||||
import javax.persistence.Entity;
|
||||
|
|
@ -9,6 +10,7 @@ import javax.persistence.GeneratedValue;
|
|||
import javax.persistence.GenerationType;
|
||||
import javax.persistence.Id;
|
||||
import javax.persistence.Table;
|
||||
|
||||
import java.security.NoSuchAlgorithmException;
|
||||
import java.security.SecureRandom;
|
||||
import java.util.Arrays;
|
||||
|
|
@ -24,32 +26,39 @@ public class PassphraseVO {
|
|||
|
||||
@Column(name = "passphrase")
|
||||
@Encrypt
|
||||
private byte[] passphrase;
|
||||
private String passphrase;
|
||||
|
||||
public PassphraseVO() {
|
||||
try {
|
||||
SecureRandom random = SecureRandom.getInstanceStrong();
|
||||
byte[] temporary = new byte[48]; // 48 byte random passphrase buffer
|
||||
this.passphrase = new byte[64]; // 48 byte random passphrase as base64 for usability
|
||||
random.nextBytes(temporary);
|
||||
Base64.getEncoder().encode(temporary, this.passphrase);
|
||||
Arrays.fill(temporary, (byte) 0); // clear passphrase from buffer
|
||||
} catch (NoSuchAlgorithmException ex ) {
|
||||
throw new CloudRuntimeException("Volume encryption requested but system is missing specified algorithm to generate passphrase");
|
||||
}
|
||||
|
||||
public PassphraseVO(boolean initialize) {
|
||||
if (initialize) {
|
||||
try {
|
||||
SecureRandom random = SecureRandom.getInstanceStrong();
|
||||
byte[] temporary = new byte[48]; // 48 byte random passphrase buffer
|
||||
random.nextBytes(temporary);
|
||||
this.passphrase = Base64.getEncoder().encodeToString(temporary);
|
||||
Arrays.fill(temporary, (byte) 0); // clear passphrase from buffer
|
||||
} catch (NoSuchAlgorithmException ex ) {
|
||||
throw new CloudRuntimeException("Volume encryption requested but system is missing specified algorithm to generate passphrase");
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public PassphraseVO(PassphraseVO existing) {
|
||||
this.passphrase = existing.getPassphrase();
|
||||
this.passphrase = existing.getPassphraseString();
|
||||
}
|
||||
|
||||
public void clearPassphrase() {
|
||||
if (this.passphrase != null) {
|
||||
Arrays.fill(this.passphrase, (byte) 0);
|
||||
public byte[] getPassphrase() {
|
||||
if (StringUtils.isBlank(this.passphrase)) {
|
||||
return new byte[]{};
|
||||
}
|
||||
return this.passphrase.getBytes();
|
||||
}
|
||||
|
||||
public byte[] getPassphrase() { return this.passphrase; }
|
||||
public String getPassphraseString() {
|
||||
return this.passphrase;
|
||||
}
|
||||
|
||||
public Long getId() { return this.id; }
|
||||
}
|
||||
|
|
|
|||
|
|
@ -486,6 +486,8 @@ public class EncryptionSecretKeyChanger {
|
|||
migrateImageStoreUrlForCifs(conn);
|
||||
migrateStoragePoolPathForSMB(conn);
|
||||
|
||||
preparePassphraseTableForMigration(conn);
|
||||
|
||||
// migrate columns with annotation @Encrypt
|
||||
migrateEncryptedTableColumns(conn);
|
||||
|
||||
|
|
@ -665,6 +667,56 @@ public class EncryptionSecretKeyChanger {
|
|||
System.out.println("End migrate user vm deploy_as_is details");
|
||||
}
|
||||
|
||||
// encrypt any unencrypted passphrases using old style encryptor before we migrate
|
||||
private void preparePassphraseTableForMigration(Connection conn) throws SQLException {
|
||||
System.out.println("Preparing passphrase table by checking for unencrypted passphrases");
|
||||
|
||||
try(PreparedStatement selectPstmt = conn.prepareStatement("SELECT id, passphrase FROM passphrase");
|
||||
ResultSet rs = selectPstmt.executeQuery();
|
||||
PreparedStatement updatePstmt = conn.prepareStatement("UPDATE passphrase SET passphrase=? WHERE id=?")
|
||||
) {
|
||||
while(rs.next()) {
|
||||
long id = rs.getLong(1);
|
||||
String value = rs.getString(2);
|
||||
if (StringUtils.isBlank(value)) {
|
||||
continue;
|
||||
}
|
||||
|
||||
// passphrases are 64 bytes long when unencrypted, longer when encrypted
|
||||
if (value.length() == 64) {
|
||||
// just confirm it won't decrypt, to be safe, before assuming raw value and encrypting
|
||||
try {
|
||||
oldEncryptor.decrypt(value);
|
||||
System.out.printf("Passphrase table entry db id %d was already encrypted with old encryption\n", id);
|
||||
} catch(EncryptionException | CloudRuntimeException ex) {
|
||||
String message = null;
|
||||
if (ex instanceof CloudRuntimeException && ex.getCause() != null) {
|
||||
if ((ex.getCause() instanceof EncryptionException)) {
|
||||
message = ex.getCause().getMessage();
|
||||
}
|
||||
} else if (ex instanceof EncryptionException) {
|
||||
message = ex.getMessage();
|
||||
}
|
||||
|
||||
if (message != null && message.contains("Failed to decrypt")) {
|
||||
System.out.printf("Encrypting unencrypted passphrase table entry db id %d before migration using old encryption\n", id);
|
||||
String encrypted = oldEncryptor.encrypt(value);
|
||||
updatePstmt.setBytes(1, encrypted.getBytes(StandardCharsets.UTF_8));
|
||||
updatePstmt.setLong(2, id);
|
||||
updatePstmt.executeUpdate();
|
||||
} else {
|
||||
throwCloudRuntimeException("Unhandled EncryptionException", ex);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
} catch (SQLException e) {
|
||||
throwCloudRuntimeException("Unable to prepare passphrase table", e);
|
||||
}
|
||||
|
||||
System.out.println("End preparing passphrase table");
|
||||
}
|
||||
|
||||
private void migrateImageStoreUrlForCifs(Connection conn) {
|
||||
System.out.println("Begin migrate image store url if protocol is cifs");
|
||||
|
||||
|
|
|
|||
|
|
@ -33,7 +33,7 @@ public class CryptSetupTest {
|
|||
file.close();
|
||||
|
||||
String filePath = path.toAbsolutePath().toString();
|
||||
PassphraseVO passphrase = new PassphraseVO();
|
||||
PassphraseVO passphrase = new PassphraseVO(true);
|
||||
|
||||
cryptSetup.luksFormat(passphrase.getPassphrase(), CryptSetup.LuksType.LUKS, filePath);
|
||||
|
||||
|
|
|
|||
Loading…
Reference in New Issue