-
Notifications
You must be signed in to change notification settings - Fork 112
Make use of key store its own key manager #3606
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Changes from all commits
bc6541e
93cfbf2
23e81e3
3342add
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,231 @@ | ||
/* | ||
* KeyStoreSerializationKeyManager.java | ||
* | ||
* This source file is part of the FoundationDB open source project | ||
* | ||
* Copyright 2015-2025 Apple Inc. and the FoundationDB project authors | ||
* | ||
* Licensed under the Apache License, Version 2.0 (the "License"); | ||
* you may not use this file except in compliance with the License. | ||
* You may obtain a copy of the License at | ||
* | ||
* http://www.apache.org/licenses/LICENSE-2.0 | ||
* | ||
* Unless required by applicable law or agreed to in writing, software | ||
* distributed under the License is distributed on an "AS IS" BASIS, | ||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. | ||
* See the License for the specific language governing permissions and | ||
* limitations under the License. | ||
*/ | ||
|
||
package com.apple.foundationdb.record.provider.common; | ||
|
||
import com.apple.foundationdb.annotation.API; | ||
import com.apple.foundationdb.record.RecordCoreArgumentException; | ||
|
||
import javax.annotation.Nonnull; | ||
import javax.annotation.Nullable; | ||
import java.io.File; | ||
import java.io.FileNotFoundException; | ||
import java.io.IOException; | ||
import java.security.GeneralSecurityException; | ||
import java.security.Key; | ||
import java.security.KeyStore; | ||
import java.security.SecureRandom; | ||
import java.util.List; | ||
import java.util.Random; | ||
|
||
|
||
/** | ||
* A {@link SerializationKeyManager} that uses a {@link KeyStore}. | ||
* <p> | ||
* Key numbers are mapped to and from key names, which are looked up in the store. | ||
*/ | ||
@API(API.Status.EXPERIMENTAL) | ||
public class KeyStoreSerializationKeyManager implements SerializationKeyManager { | ||
@Nonnull | ||
private final KeyStore keyStore; | ||
@Nonnull | ||
private final KeyStore.ProtectionParameter keyEntryPassword; | ||
@Nonnull | ||
private final List<String> keyEntryAliases; | ||
private final int defaultKeyNumber; | ||
@Nonnull | ||
private final String cipherName; | ||
@Nonnull | ||
private final SecureRandom secureRandom; | ||
|
||
protected KeyStoreSerializationKeyManager(@Nonnull KeyStore keyStore, @Nonnull KeyStore.ProtectionParameter keyEntryPassword, | ||
@Nonnull List<String> keyEntryAliases, int defaultKeyNumber, | ||
@Nonnull String cipherName, @Nonnull SecureRandom secureRandom) { | ||
this.keyStore = keyStore; | ||
this.keyEntryPassword = keyEntryPassword; | ||
this.keyEntryAliases = List.copyOf(keyEntryAliases); | ||
this.defaultKeyNumber = defaultKeyNumber; | ||
this.cipherName = cipherName; | ||
this.secureRandom = secureRandom; | ||
} | ||
|
||
@Override | ||
public int getSerializationKey() { | ||
return defaultKeyNumber; | ||
} | ||
|
||
@Override | ||
public Key getKey(int keyNumber) { | ||
if (keyNumber < 0 || keyNumber >= keyEntryAliases.size()) { | ||
throw new RecordSerializationException("key number out of range"); | ||
} | ||
final String keyEntryAlias = keyEntryAliases.get(keyNumber); | ||
final KeyStore.SecretKeyEntry entry; | ||
try { | ||
entry = (KeyStore.SecretKeyEntry)keyStore.getEntry(keyEntryAlias, keyEntryPassword); | ||
} catch (GeneralSecurityException ex) { | ||
throw new RecordSerializationException("cannot load key", ex); | ||
} | ||
return entry.getSecretKey(); | ||
} | ||
|
||
@Override | ||
public String getCipher(int keyNumber) { | ||
return cipherName; | ||
} | ||
|
||
@Override | ||
public Random getRandom(int keyNumber) { | ||
return secureRandom; | ||
} | ||
|
||
@Nonnull | ||
public static Builder newBuilder() { | ||
return new Builder(); | ||
} | ||
|
||
public static class Builder { | ||
Check warning on line 104 in fdb-record-layer-core/src/main/java/com/apple/foundationdb/record/provider/common/KeyStoreSerializationKeyManager.java
|
||
@Nullable | ||
String keyStoreFileName; | ||
@Nullable | ||
String keyStorePassword; | ||
@Nullable | ||
String keyEntryPassword; | ||
@Nullable | ||
String defaultKeyEntryAlias; | ||
@Nullable | ||
List<String> keyEntryAliases; | ||
@Nonnull | ||
String cipherName = CipherPool.DEFAULT_CIPHER; | ||
@Nullable | ||
SecureRandom secureRandom; | ||
|
||
protected Builder() { | ||
} | ||
|
||
@Nullable | ||
public String getKeyStoreFileName() { | ||
return keyStoreFileName; | ||
} | ||
|
||
public void setKeyStoreFileName(@Nonnull String keyStoreFileName) { | ||
this.keyStoreFileName = keyStoreFileName; | ||
} | ||
|
||
@Nullable | ||
public String getKeyStorePassword() { | ||
return keyStorePassword; | ||
} | ||
|
||
public void setKeyStorePassword(@Nonnull String keyStorePassword) { | ||
this.keyStorePassword = keyStorePassword; | ||
} | ||
|
||
@Nullable | ||
public String getKeyEntryPassword() { | ||
return keyEntryPassword; | ||
} | ||
|
||
public void setKeyEntryPassword(@Nonnull String keyEntryPassword) { | ||
this.keyEntryPassword = keyEntryPassword; | ||
} | ||
|
||
@Nullable | ||
public String getDefaultKeyEntryAlias() { | ||
return defaultKeyEntryAlias; | ||
} | ||
|
||
public void setDefaultKeyEntryAlias(@Nonnull String defaultKeyEntryAlias) { | ||
this.defaultKeyEntryAlias = defaultKeyEntryAlias; | ||
} | ||
|
||
@Nullable | ||
public List<String> getKeyEntryAliases() { | ||
return keyEntryAliases; | ||
} | ||
|
||
public void setKeyEntryAliases(@Nonnull List<String> keyEntryAliases) { | ||
this.keyEntryAliases = keyEntryAliases; | ||
} | ||
|
||
@Nonnull | ||
public String getCipherName() { | ||
return cipherName; | ||
} | ||
|
||
public void setCipherName(@Nonnull String cipherName) { | ||
this.cipherName = cipherName; | ||
} | ||
|
||
@Nullable | ||
public SecureRandom getSecureRandom() { | ||
return secureRandom; | ||
} | ||
|
||
public void setSecureRandom(@Nonnull SecureRandom secureRandom) { | ||
this.secureRandom = secureRandom; | ||
} | ||
|
||
@Nonnull | ||
public KeyStoreSerializationKeyManager build() { | ||
if (keyStoreFileName == null) { | ||
throw new RecordCoreArgumentException("must specify key store file name"); | ||
} | ||
if (keyStorePassword == null) { | ||
keyStorePassword = ""; | ||
} | ||
final KeyStore keyStore; | ||
try { | ||
keyStore = KeyStore.getInstance(new File(keyStoreFileName), keyStorePassword.toCharArray()); | ||
} catch (FileNotFoundException ex) { | ||
throw new RecordCoreArgumentException("Key store not found", ex); | ||
} catch (GeneralSecurityException | IOException ex) { | ||
throw new RecordCoreArgumentException("Key store loading failed", ex); | ||
} | ||
if (keyEntryPassword == null) { | ||
keyEntryPassword = keyStorePassword; | ||
} | ||
final KeyStore.ProtectionParameter keyEntryProtection = new KeyStore.PasswordProtection(keyEntryPassword.toCharArray()); | ||
Check warning on line 205 in fdb-record-layer-core/src/main/java/com/apple/foundationdb/record/provider/common/KeyStoreSerializationKeyManager.java
|
||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Yeah, probably good to move this closer There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. But then something else is too far away, right? Right now, it is constructing the arguments in the order in which they are passed to the constructor. |
||
if (keyEntryAliases == null && defaultKeyEntryAlias == null) { | ||
throw new RecordCoreArgumentException("must specify key alias list or single default alias"); | ||
} | ||
final int defaultKeyNumber; | ||
if (keyEntryAliases == null) { | ||
keyEntryAliases = List.of(defaultKeyEntryAlias); | ||
defaultKeyNumber = 0; | ||
} else if (defaultKeyEntryAlias == null) { | ||
ScottDugas marked this conversation as resolved.
Show resolved
Hide resolved
|
||
if (keyEntryAliases.isEmpty()) { | ||
throw new RecordCoreArgumentException("need at least one key alias"); | ||
} | ||
defaultKeyNumber = keyEntryAliases.size() - 1; | ||
} else { | ||
defaultKeyNumber = keyEntryAliases.indexOf(defaultKeyEntryAlias); | ||
if (defaultKeyNumber < 0) { | ||
throw new RecordCoreArgumentException("default key alias not in key alias list"); | ||
} | ||
} | ||
if (secureRandom == null) { | ||
secureRandom = new SecureRandom(); | ||
} | ||
return new KeyStoreSerializationKeyManager(keyStore, keyEntryProtection, keyEntryAliases, defaultKeyNumber, | ||
cipherName, secureRandom); | ||
} | ||
} | ||
} |
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,48 @@ | ||
/* | ||
* OrderedCollectionTypeContract.java | ||
* | ||
* This source file is part of the FoundationDB open source project | ||
* | ||
* Copyright 2025 Apple Inc. and the FoundationDB project authors | ||
* | ||
* Licensed under the Apache License, Version 2.0 (the "License"); | ||
* you may not use this file except in compliance with the License. | ||
* You may obtain a copy of the License at | ||
* | ||
* http://www.apache.org/licenses/LICENSE-2.0 | ||
* | ||
* Unless required by applicable law or agreed to in writing, software | ||
* distributed under the License is distributed on an "AS IS" BASIS, | ||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. | ||
* See the License for the specific language governing permissions and | ||
* limitations under the License. | ||
*/ | ||
|
||
package com.apple.foundationdb.relational.api.options; | ||
|
||
import com.apple.foundationdb.relational.api.Options; | ||
import com.apple.foundationdb.relational.api.exceptions.ErrorCode; | ||
|
||
import javax.annotation.Nonnull; | ||
import java.sql.SQLException; | ||
import java.util.List; | ||
|
||
/** | ||
* Ordered ({@code List}) version of {@link CollectionContract}. | ||
* @param <T> the type parameter of the collection | ||
*/ | ||
public class OrderedCollectionContract<T> extends CollectionContract<T> { | ||
public OrderedCollectionContract(@Nonnull TypeContract<T> elementContract) { | ||
super(elementContract); | ||
} | ||
|
||
@Override | ||
public void validate(final Options.Name name, final Object value) throws SQLException { | ||
if (!(value instanceof List<?>)) { | ||
throw new SQLException("Option " + name + " should be of a list type instead of " + value.getClass().getName(), ErrorCode.INVALID_PARAMETER.getErrorCode()); | ||
} | ||
super.validate(name, value); | ||
} | ||
|
||
// No need to override fromString, as that already returns a list in value element order. | ||
} |
Uh oh!
There was an error while loading. Please reload this page.