Skip to content

Commit

Permalink
[ELY-2320] Changed main_index into to a concurrenthashmap
Browse files Browse the repository at this point in the history
Temporary: Need to determine best way to store checksum
  • Loading branch information
Ashpan committed May 17, 2022
1 parent 65f6e73 commit 78476ad
Show file tree
Hide file tree
Showing 2 changed files with 46 additions and 249 deletions.
Expand Up @@ -31,11 +31,9 @@
import static org.apache.xml.security.signature.XMLSignature.ALGO_ID_SIGNATURE_DSA_SHA256;

import java.io.BufferedOutputStream;
import java.io.BufferedReader;
import java.io.File;
import java.io.FileInputStream;
import java.io.FileNotFoundException;
import java.io.FileReader;
import java.io.FileWriter;
import java.io.IOException;
import java.io.InputStream;
Expand Down Expand Up @@ -78,7 +76,6 @@
import java.util.Locale;
import java.util.Map;
import java.util.NoSuchElementException;
import java.util.Scanner;
import java.util.concurrent.ConcurrentHashMap;
import java.util.concurrent.ThreadLocalRandom;
import java.util.function.Consumer;
Expand Down Expand Up @@ -210,7 +207,7 @@ boolean isAtLeast(Version version) {
private final SecretKey secretKey;
private final PrivateKey privateKey;
private final PublicKey publicKey;
private final File mainIndex;
private final ConcurrentHashMap<String, String> mainIndex;
private final ConcurrentHashMap<String, IdentitySharedExclusiveLock> realmIdentityLocks = new ConcurrentHashMap<>();

/**
Expand All @@ -221,6 +218,7 @@ boolean isAtLeast(Version version) {
public static FileSystemSecurityRealmBuilder builder() {
return new FileSystemSecurityRealmBuilder();
}

/**
* Construct a new instance.
*
Expand Down Expand Up @@ -399,6 +397,7 @@ public FileSystemSecurityRealm(Path root, int levels, Supplier<Provider[]> provi
public boolean hasIntegrityEnabled() {
return privateKey != null && publicKey != null;
}

private Path pathFor(String name) {
assert name.codePointCount(0, name.length()) > 0;
String normalizedName = name;
Expand Down Expand Up @@ -616,6 +615,7 @@ public void updateRealmKeyPair() throws IOException, GeneralSecurityException {
throw ElytronMessages.log.invalidKeyPairArgument(root.toString());
}
ModifiableRealmIdentityIterator realmIterator = this.getRealmIdentityIterator();
mainIndex.clear();
while (realmIterator.hasNext()) {
Identity identity = (Identity) realmIterator.next();
identity.writeDigitalSignature(String.valueOf(identity.path), identity.name);
Expand Down Expand Up @@ -663,9 +663,9 @@ static class Identity implements ModifiableRealmIdentity {
private final SecretKey secretKey;
private final PrivateKey privateKey;
private final PublicKey publicKey;
private final File mainIndex;
private final ConcurrentHashMap<String, String> mainIndex;

Identity(final String name, final Path path, final IdentityLock lock, final Charset hashCharset, final Encoding hashEncoding, Supplier<Provider[]> providers, final SecretKey secretKey, final PrivateKey privateKey, final PublicKey publicKey, final File mainIndex) {
Identity(final String name, final Path path, final IdentityLock lock, final Charset hashCharset, final Encoding hashEncoding, Supplier<Provider[]> providers, final SecretKey secretKey, final PrivateKey privateKey, final PublicKey publicKey, final ConcurrentHashMap<String, String> mainIndex) {
this.name = name;
this.path = path;
this.lock = lock;
Expand Down Expand Up @@ -766,9 +766,6 @@ public boolean verifyEvidence(final Evidence evidence) throws RealmUnavailableEx
}

List<Credential> loadCredentials() throws RealmUnavailableException {
if (!verifyIntegrity()) {
throw ElytronMessages.log.invalidIdentitySignature(name);
}
final LoadedIdentity loadedIdentity = loadIdentity(false, true);
return loadedIdentity == null ? Collections.emptyList() : loadedIdentity.getCredentials();
}
Expand All @@ -783,13 +780,6 @@ public boolean exists() throws RealmUnavailableException {
public void delete() throws RealmUnavailableException {
if (System.getSecurityManager() == null) {
deletePrivileged();
try {
if(publicKey != null) {
deleteMainIndexEntry(mainIndex, path);
}
} catch (IOException e) {
throw ElytronMessages.log.unableToAccessMainIndex(mainIndex.toString());
}
return;
}
try {
Expand All @@ -805,6 +795,9 @@ public void delete() throws RealmUnavailableException {
private Void deletePrivileged() throws RealmUnavailableException {
try {
Files.delete(path);
if (publicKey != null) {
mainIndex.remove(path.getFileName().toString());
}
return null;
} catch (NoSuchFileException e) {
throw ElytronMessages.log.fileSystemRealmNotFound(name);
Expand Down Expand Up @@ -839,19 +832,6 @@ private Path tempPath() {
public void create() throws RealmUnavailableException {
if (System.getSecurityManager() == null) {
createPrivileged();
if(privateKey != null) {
try {
createDigitalSignature(path.toString(), this.name);
writeToMainIndex(this.mainIndex, this.path, this.secretKey);
} catch (IllegalStateException e) {
throw ElytronMessages.log.unableToGenerateSignature(path.toString());
} catch (IOException | GeneralSecurityException e) {
throw ElytronMessages.log.unableToAccessMainIndex(this.mainIndex.toString());
}
if (!verifyIntegrity()) {
throw ElytronMessages.log.invalidIdentitySignature(this.name);
}
}
return;
}
try {
Expand Down Expand Up @@ -912,7 +892,7 @@ private Void createPrivileged() throws RealmUnavailableException {
} catch (IOException ignored) {
// nothing we can do
}
if(privateKey != null) {
if (privateKey != null) {
try {
writeDigitalSignature(path.toString(), this.name);
writeToMainIndex(this.mainIndex, this.path, this.secretKey);
Expand Down Expand Up @@ -979,10 +959,12 @@ private Void replaceIdentityPrivileged(final LoadedIdentity newIdentity) throws
final Path tempPath = tempPath();
try {
final XMLOutputFactory xmlOutputFactory = XMLOutputFactory.newFactory();
try (OutputStream outputStream = new BufferedOutputStream(Files.newOutputStream(tempPath, WRITE, CREATE_NEW, DSYNC))) {
try (OutputStream outputStream = new BufferedOutputStream(
Files.newOutputStream(tempPath, WRITE, CREATE_NEW, DSYNC))) {
try (AutoCloseableXMLStreamWriterHolder holder = new AutoCloseableXMLStreamWriterHolder(xmlOutputFactory.createXMLStreamWriter(outputStream))) {
writeIdentity(holder.getXmlStreamWriter(), newIdentity);
} catch (XMLStreamException | InvalidKeySpecException | NoSuchAlgorithmException | CertificateEncodingException e) {
} catch (XMLStreamException | InvalidKeySpecException | NoSuchAlgorithmException
| CertificateEncodingException e) {
throw ElytronMessages.log.fileSystemRealmFailedToWrite(tempPath, name, e);
} catch (GeneralSecurityException e) {
throw ElytronMessages.log.fileSystemRealmEncryptionFailed(e);
Expand Down Expand Up @@ -1189,7 +1171,8 @@ private LoadedIdentity parseIdentity(final XMLStreamReader streamReader, final b
final int tag = streamReader.nextTag();
Version version;
if (tag != START_ELEMENT || ((version = identifyVersion(streamReader)) == null) || ! "identity".equals(streamReader.getLocalName())) {
throw ElytronMessages.log.fileSystemRealmInvalidContent(path, streamReader.getLocation().getLineNumber(), name); }
throw ElytronMessages.log.fileSystemRealmInvalidContent(path, streamReader.getLocation().getLineNumber(), name);
}
return parseIdentityContents(streamReader, version, skipCredentials, skipAttributes);
}

Expand All @@ -1209,13 +1192,13 @@ private LoadedIdentity parseIdentityContents(final XMLStreamReader streamReader,
for (;;) {
if (streamReader.isEndElement()) {
if (attributes == Attributes.EMPTY && !skipAttributes) {
// Since this could be a use-case wanting to modify the attributes, make sure that we have a
// modifiable version of Attributes;
//Since this could be a use-case wanting to modify the attributes, make sure that we have a
//modifiable version of Attributes;
attributes = new MapAttributes();
}
return new LoadedIdentity(name, credentials, attributes, hashEncoding);
}
if (! version.getNamespace().equals(streamReader.getNamespaceURI())) {
if (!(version.getNamespace().equals(streamReader.getNamespaceURI())) && !(XMLSignature.XMLNS.equals(streamReader.getNamespaceURI()))) {
// Mixed versions unsupported.
throw ElytronMessages.log.fileSystemRealmInvalidContent(path, streamReader.getLocation().getLineNumber(), name);
}
Expand Down Expand Up @@ -1479,7 +1462,7 @@ private void parseAttribute(final XMLStreamReader streamReader, final Attributes
if (secretKey != null) {
try {
attributes.addLast(CipherUtil.decrypt(name, secretKey), CipherUtil.decrypt(value, secretKey));
} catch (GeneralSecurityException e){
} catch (GeneralSecurityException e) {
throw ElytronMessages.log.fileSystemRealmDecryptionFailed(e);
}
} else {
Expand Down Expand Up @@ -1549,9 +1532,12 @@ private boolean validateDigitalSignature(String path, String name) {
DOMValidateContext valContext = new DOMValidateContext(publicKey, nl.item(0));
XMLSignature signature = fac.unmarshalXMLSignature(valContext);
boolean coreValidity = signature.validate(valContext);
ElytronMessages.log.tracef("FileSystemSecurityRealm - verification against signature for credential [%s] = %b", name, coreValidity);
ElytronMessages.log.tracef(
"FileSystemSecurityRealm - verification against signature for credential [%s] = %b", name,
coreValidity);
return coreValidity;
} catch (ParserConfigurationException | IOException | MarshalException | XMLSignatureException | SAXException e) {
} catch (ParserConfigurationException | IOException | MarshalException | XMLSignatureException
| SAXException e) {
return false;
}
}
Expand All @@ -1570,11 +1556,7 @@ private void writeDigitalSignature(String path, String name) throws RealmUnavail
}
DOMSignContext dsc = new DOMSignContext(this.privateKey, elem);
XMLSignatureFactory fac = XMLSignatureFactory.getInstance("DOM");
Reference ref = fac.newReference
("", fac.newDigestMethod(DigestMethod.SHA256, null),
Collections.singletonList
(fac.newTransform(Transform.ENVELOPED,
(TransformParameterSpec) null)), null, null);
Reference ref = fac.newReference("", fac.newDigestMethod(DigestMethod.SHA256, null), Collections.singletonList(fac.newTransform(Transform.ENVELOPED, (TransformParameterSpec) null)), null, null);
String signatureMethod = "";
switch (this.publicKey.getAlgorithm()) {
case "DSA":
Expand All @@ -1590,12 +1572,7 @@ private void writeDigitalSignature(String path, String name) throws RealmUnavail
signatureMethod = ALGO_ID_SIGNATURE_ECDSA_SHA256;
break;
}
SignedInfo si = fac.newSignedInfo
(fac.newCanonicalizationMethod
(CanonicalizationMethod.INCLUSIVE,
(C14NMethodParameterSpec) null),
fac.newSignatureMethod(signatureMethod, null),
Collections.singletonList(ref));
SignedInfo si = fac.newSignedInfo(fac.newCanonicalizationMethod(CanonicalizationMethod.INCLUSIVE, (C14NMethodParameterSpec) null), fac.newSignatureMethod(signatureMethod, null), Collections.singletonList(ref));
KeyInfoFactory kif = fac.getKeyInfoFactory();
KeyValue kv = kif.newKeyValue(this.publicKey);
KeyInfo ki = kif.newKeyInfo(Collections.singletonList(kv));
Expand All @@ -1610,94 +1587,43 @@ private void writeDigitalSignature(String path, String name) throws RealmUnavail
ElytronMessages.log.tracef("FileSystemSecurityRealm - signature against file updated [%s]", name);
writer.close();

} catch (ParserConfigurationException | IOException | NoSuchAlgorithmException | InvalidAlgorithmParameterException |
KeyException | XMLSignatureException | MarshalException | TransformerException | SAXException e) {
} catch (ParserConfigurationException | IOException | NoSuchAlgorithmException
| InvalidAlgorithmParameterException | KeyException | XMLSignatureException | MarshalException
| TransformerException | SAXException e) {
throw ElytronMessages.log.unableToGenerateSignature(path);
}
}

private static void writeToMainIndex(File mainIndex, Path path, SecretKey secretKey) throws IOException, GeneralSecurityException {
FileWriter mainIndexWriter = new FileWriter(mainIndex, true);
private static void writeToMainIndex(ConcurrentHashMap<String, String> mainIndex, Path path, SecretKey secretKey) throws IOException, GeneralSecurityException {
MessageDigest messageDigest = MessageDigest.getInstance("MD5");
String checksum = getChecksum(messageDigest, new File(path.toString()));
String identityFilename = path.getFileName().toString();
if(secretKey != null) {
if (secretKey != null) {
checksum = CipherUtil.encrypt(checksum, secretKey);
identityFilename = CipherUtil.encrypt(identityFilename, secretKey);
}
mainIndexWriter.append(identityFilename).append(":").append(checksum).append('\n');
mainIndexWriter.close();
mainIndex.put(identityFilename, checksum);
}

private static void updateMainIndex(File mainIndex, Path path, SecretKey secretKey) throws IOException, GeneralSecurityException {
BufferedReader reader = new BufferedReader(new FileReader(mainIndex));
private static boolean validateChecksum(ConcurrentHashMap<String, String> mainIndex, Path path, SecretKey secretKey) throws IOException, GeneralSecurityException {
MessageDigest messageDigest = MessageDigest.getInstance("MD5");
String checksum = getChecksum(messageDigest, new File(path.toString()));
String currentFileChecksum = getChecksum(messageDigest, new File(path.toString()));
String identityFilename = path.getFileName().toString();
String line;
ArrayList<String> fileContents = new ArrayList<String>();
while((line = reader.readLine()) != null) {
fileContents.add(line);
}
reader.close();
String mainIndexChecksum = "";

for (int i = 0; i < fileContents.size(); i++) {
String currentIdentity = fileContents.get(i).split(":")[0];
if (secretKey != null) {
currentIdentity = CipherUtil.decrypt(currentIdentity, secretKey);
checksum = CipherUtil.encrypt(checksum, secretKey);
}
if (currentIdentity.equals(identityFilename)) {
fileContents.set(i, fileContents.get(i).split(":")[0] + ":" + checksum);
break;
}
if (secretKey != null) {
mainIndexChecksum = CipherUtil.decrypt(mainIndex.get(identityFilename), secretKey);
} else {
mainIndexChecksum = mainIndex.get(identityFilename);
}
FileWriter writer = new FileWriter(mainIndex);
writer.write(String.join("\n", fileContents));
writer.close();
}

private static boolean validateChecksum(File mainIndex, Path path, SecretKey secretKey) throws IOException, GeneralSecurityException {
try {
Scanner mainIndexScanner = new Scanner(mainIndex);
MessageDigest messageDigest = MessageDigest.getInstance("MD5");
String currentFileChecksum = getChecksum(messageDigest, new File(path.toString()));
String identityFilename = path.getFileName().toString();
String data, mainIndexFilename, mainIndexChecksum;
while (mainIndexScanner.hasNextLine()) {
data = mainIndexScanner.nextLine();
mainIndexFilename = data.split(":")[0];
mainIndexChecksum = data.split(":")[1];
if(secretKey != null) {
mainIndexFilename = CipherUtil.decrypt(mainIndexFilename, secretKey);
mainIndexChecksum = CipherUtil.decrypt(mainIndexChecksum, secretKey);
}
if(mainIndexFilename.equals(identityFilename)) {
mainIndexScanner.close();
return mainIndexChecksum.equals(currentFileChecksum);
}
}
mainIndexScanner.close();
} catch (FileNotFoundException e) {
throw ElytronMessages.log.unableToAccessMainIndex(path.toString());
// If identity entry not found in main index return false
if (mainIndexChecksum == null) {
return false;
}
return false;
return mainIndexChecksum.equals(currentFileChecksum);
}

private static void deleteMainIndexEntry(File mainIndex, Path path) throws IOException {
BufferedReader reader = new BufferedReader(new FileReader(mainIndex));
String line;
StringBuilder fileContents = new StringBuilder();
while((line = reader.readLine()) != null) {
fileContents.append(line).append("\r\n");
}
reader.close();
int beginningIndex = fileContents.indexOf(path.getFileName().toString());
int endIndex = fileContents.indexOf("\n", beginningIndex);
fileContents.delete(beginningIndex, endIndex+"\n".length());
FileWriter writer = new FileWriter(mainIndex);
writer.write(fileContents.toString());
writer.close();
private static void deleteMainIndexEntry(ConcurrentHashMap<String, String> mainIndex, Path path) throws IOException {
mainIndex.remove(path.getFileName().toString());
}

private static String getChecksum(MessageDigest digest, File file) throws IOException {
Expand Down

0 comments on commit 78476ad

Please sign in to comment.