Skip to content
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

Load cert from file system in key vault #21947

Merged
merged 68 commits into from
Jun 15, 2021
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
68 commits
Select commit Hold shift + click to select a range
0c152c6
Add jre key store certificates to key vault keystore
May 26, 2021
eb31e52
Merge remote-tracking branch into local_jre_with_refresh
May 26, 2021
bb5c756
minor code change
May 26, 2021
4001d9b
fix checkstyle failures
May 26, 2021
b3aa1e7
Minor code change
May 26, 2021
885ab8f
Fix checkstyle failures and some test
May 27, 2021
e45e507
Merge remote-tracking branch into local_jre_with_refresh
May 27, 2021
59449a5
minor code change
May 27, 2021
53a5c3b
minor code change
May 27, 2021
746d0e5
Minor text change
May 27, 2021
81627a1
Merge remote-tracking branch into local_jre_with_refresh
May 27, 2021
29cf190
Minor text change
May 27, 2021
24ad907
Change static fields to be instance fields
May 27, 2021
dbaef66
Merge remote-tracking branch into local_jre_with_refresh
May 27, 2021
ce1853b
Fix checkstyle error
May 27, 2021
2835aec
Merge remote-tracking branch into local_jre_with_refresh
May 27, 2021
9097c26
Fix checkstyle failure
May 27, 2021
09abf23
Merge remote-tracking branch into local_jre_with_refresh
May 27, 2021
1da9298
Use try-with-resource instead of close inputStream manually.
rujche May 28, 2021
ab5daae
remove thread sleep
May 28, 2021
93c3917
remove thread sleep
May 28, 2021
c21201d
remove return null in Collectors.toMap
May 28, 2021
0dd1415
fix checkstyle failures
May 28, 2021
539b906
remove unused function and test
zhichengliu12581 May 28, 2021
b73102a
add file system certificate
zhichengliu12581 May 28, 2021
f7d0bc0
revert unused change and fix build error
zhichengliu12581 May 28, 2021
6042ddd
revert non-related test change
May 28, 2021
1830b0a
add exemption for redundant non-null check for JreCertificates
May 28, 2021
4118c85
Minor code change
May 28, 2021
f0995d4
add test for FileSystemCertificates
zhichengliu12581 May 28, 2021
ba17d25
merge jre certificate
zhichengliu12581 May 28, 2021
3b924ae
Merge remote-tracking branch into local_jre_with_refresh
May 28, 2021
9f86422
try to change FileSystemCertificates to singleton
zhichengliu12581 May 28, 2021
a88acb3
try to fix test error
zhichengliu12581 May 28, 2021
bd9a2da
Minor code change
May 31, 2021
f177a3a
Merge remote-tracking branch into local_jre_with_refresh
May 31, 2021
2896a1b
Merge remote-tracking branch into local_jre_with_refresh
May 31, 2021
86c9c24
Merge remote-tracking branch into local_jre_with_refresh
May 31, 2021
65b9e91
re-organize code
May 31, 2021
033f926
use stream replace duplicate code
zhichengliu12581 May 31, 2021
4a387b6
Merge branch 'local_jre_with_refresh' of https://github.com/michaelqi…
zhichengliu12581 May 31, 2021
c217b08
merge master
zhichengliu12581 May 31, 2021
3f2bc5c
fix build error
zhichengliu12581 May 31, 2021
12a9657
revert error merge
zhichengliu12581 May 31, 2021
339656d
nothing changed ,just format code
zhichengliu12581 May 31, 2021
22a7bb4
remove unused import to fix pipeline error
zhichengliu12581 Jun 1, 2021
e1b7f41
update FileSystemCertificates
zhichengliu12581 Jun 3, 2021
5979221
move load Classpath Certificates code form KeyVaultKeyStore to Classp…
zhichengliu12581 Jun 8, 2021
a282a4b
Merge branch 'master' of https://github.com/Azure/azure-sdk-for-java …
zhichengliu12581 Jun 8, 2021
6aa998f
update default path value and properties name
zhichengliu12581 Jun 8, 2021
ed82c13
add javadocs and change properties names
zhichengliu12581 Jun 8, 2021
8c0f776
remove unused change
zhichengliu12581 Jun 8, 2021
0506a01
fix pipeline error
zhichengliu12581 Jun 9, 2021
1bf319b
change properties name in test
zhichengliu12581 Jun 9, 2021
33a29a6
add disable properties when don't have environment variable
zhichengliu12581 Jun 9, 2021
897374d
update by comments
zhichengliu12581 Jun 10, 2021
5f927b7
update test for file system
zhichengliu12581 Jun 10, 2021
34a17f0
nothing changed, just simplify code using public methods
zhichengliu12581 Jun 10, 2021
e793f04
update sort
zhichengliu12581 Jun 10, 2021
d99f8d2
add log and load all aliases
zhichengliu12581 Jun 10, 2021
58c7ab8
fix error and try to enable test
zhichengliu12581 Jun 11, 2021
845abec
rename test function and add getAllAlias function
zhichengliu12581 Jun 11, 2021
c70bf17
Add check for duplicate alias
zhichengliu12581 Jun 11, 2021
8f2e543
add factory for file system to keep singleton
zhichengliu12581 Jun 11, 2021
394a5dd
fix error
zhichengliu12581 Jun 11, 2021
e37c051
fix error and add test for sort
zhichengliu12581 Jun 11, 2021
25c5ba9
change log level
zhichengliu12581 Jun 11, 2021
2516857
revert change log class
zhichengliu12581 Jun 11, 2021
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
Expand Up @@ -3,17 +3,38 @@

package com.azure.security.keyvault.jca;

import java.io.InputStream;
import java.io.ByteArrayInputStream;
import java.io.IOException;
import java.io.ByteArrayOutputStream;
import java.io.BufferedReader;
import java.io.InputStreamReader;
import java.nio.charset.StandardCharsets;
import java.security.Key;
import java.security.cert.Certificate;
import java.security.cert.CertificateException;
import java.security.cert.CertificateFactory;
import java.security.cert.X509Certificate;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.Objects;
import java.util.logging.Logger;

import static java.util.logging.Level.INFO;
import static java.util.logging.Level.WARNING;

/**
* Store certificates loaded from classpath.
*/
public class ClasspathCertificates implements AzureCertificates {
public final class ClasspathCertificates implements AzureCertificates {


/**
* Stores the logger.
*/
private static final Logger LOGGER = Logger.getLogger(ClasspathCertificates.class.getName());

/**
* Store certificates' alias.
Expand Down Expand Up @@ -57,22 +78,6 @@ public Map<String, Key> getCertificateKeys() {
return certificateKeys;
}

/**
* Remove alias if exist.
* @param alias certificate alias
*/
public void removeAlias(String alias) {
aliases.remove(alias);
}

/**
* Remove certificate if exist.
* @param alias certificate alias
*/
public void removeCertificate(String alias) {
certificates.remove(alias);
}

/**
* Add certificate.
* @param alias certificate alias
Expand All @@ -96,4 +101,81 @@ public void deleteEntry(String alias) {
certificateKeys.remove(alias);
}

/**
* Side-load certificate from classpath.
*/
void loadCertificatesFromClasspath() {
try {
String[] filenames = getFilenames("/keyvault");
for (String filename : filenames) {
try (InputStream inputStream = getClass().getResourceAsStream("/keyvault/" + filename)) {
String alias = filename;
if (alias != null) {
if (alias.lastIndexOf('.') != -1) {
alias = alias.substring(0, alias.lastIndexOf('.'));
}
byte[] bytes = readAllBytes(inputStream);
try {
CertificateFactory cf = CertificateFactory.getInstance("X.509");
X509Certificate certificate = (X509Certificate) cf.generateCertificate(
new ByteArrayInputStream(bytes));
setCertificateEntry(alias, certificate);
LOGGER.log(INFO, "Side loaded certificate: {0} from: {1}",
new Object[]{alias, filename});
} catch (CertificateException e) {
LOGGER.log(WARNING, "Unable to side-load certificate from: " + filename, e);
}
}
}
}
} catch (IOException ioe) {
LOGGER.log(WARNING, "Unable to determine certificates to side-load", ioe);
}
}


/**
* Read all the bytes for a given input stream.
*
* @param inputStream the input stream.
* @return the byte-array.
* @throws IOException when an I/O error occurs.
*/
private byte[] readAllBytes(InputStream inputStream) throws IOException {
byte[] bytes;
try (ByteArrayOutputStream byteOutput = new ByteArrayOutputStream()) {
byte[] buffer = new byte[1024];
while (true) {
int r = inputStream.read(buffer);
if (r == -1) {
break;
}
byteOutput.write(buffer, 0, r);
}
bytes = byteOutput.toByteArray();
}
return bytes;
}

/**
* Get the filenames.
*
* @param path the path.
* @return the filenames.
* @throws IOException when an I/O error occurs.
*/
private String[] getFilenames(String path) throws IOException {
List<String> filenames = new ArrayList<>();
try (InputStream in = getClass().getResourceAsStream(path)) {
if (!Objects.isNull(in)) {
try (BufferedReader br = new BufferedReader(new InputStreamReader(in, StandardCharsets.UTF_8))) {
String resource;
while ((resource = br.readLine()) != null) {
filenames.add(resource);
}
}
}
}
return filenames.toArray(new String[0]);
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,214 @@
// Copyright (c) Microsoft Corporation. All rights reserved.
// Licensed under the MIT License.

package com.azure.security.keyvault.jca;

import java.io.InputStream;
import java.io.BufferedInputStream;
import java.io.IOException;
import java.io.FileInputStream;
import java.io.File;
import java.security.Key;
import java.security.cert.Certificate;
import java.security.cert.CertificateException;
import java.security.cert.CertificateFactory;
import java.security.cert.X509Certificate;
import java.util.List;
import java.util.Objects;
import java.util.HashMap;
import java.util.Map;
import java.util.ArrayList;
import java.util.Optional;
import java.util.Arrays;
import java.util.logging.Logger;
import java.util.stream.Stream;

import static java.util.logging.Level.INFO;
import static java.util.logging.Level.WARNING;

/**
* Store certificates loaded from file system.
*/
public final class FileSystemCertificates implements AzureCertificates {

/**
* Stores the logger.
*/
private static final Logger LOGGER = Logger.getLogger(FileSystemCertificates.class.getName());

/**
* Stores the jre key store aliases.
*/
private final List<String> aliases = new ArrayList<>();

/**
* Stores the file system certificates by alias.
*/
private final Map<String, Certificate> certificates = new HashMap<>();

/**
* Stores the file system certificate keys by alias.
*/
private final Map<String, Key> certificateKeys = new HashMap<>();
chenrujun marked this conversation as resolved.
Show resolved Hide resolved

private final String certificatePath;

@Override
public List<String> getAliases() {
return aliases;
}

@Override
public Map<String, Certificate> getCertificates() {
return certificates;
}

@Override
public Map<String, Key> getCertificateKeys() {
return certificateKeys;
}

@Override
public void deleteEntry(String alias) {
aliases.remove(alias);
certificates.remove(alias);
certificateKeys.remove(alias);
}

/**
* Constructor.
*
* @param certificatePath Store the file path where certificates are placed
*/
private FileSystemCertificates(String certificatePath) {
this.certificatePath = certificatePath;
}

/**
* Add alias and certificate
*
* @param alias certificate alias
* @param certificate certificate value
*/
public void setCertificateEntry(String alias, Certificate certificate) {
//Add verification to avoid certificate files with the same file name but different suffixes
if (aliases.contains(alias)) {
LOGGER.log(WARNING, "Cannot load certificates with the same alias in file system", alias);
return;
}
aliases.add(alias);
certificates.put(alias, certificate);
}

/**
* If the file can be parsed into a certificate, add it to the list
*
* @param file file which try to parsed into a certificate
* @throws IOException Exception thrown when there is an error in reading all the bytes from the File.
*/
private void setCertificateByFile(File file) throws IOException {
X509Certificate certificate;
try (InputStream inputStream = new FileInputStream(file);
BufferedInputStream bytes = new BufferedInputStream(inputStream)) {
String alias = toCertificateAlias(file);
CertificateFactory cf = CertificateFactory.getInstance("X.509");
certificate = (X509Certificate) cf.generateCertificate(bytes);
if (certificate != null) {
setCertificateEntry(alias, certificate);
LOGGER.log(INFO, "Load file system certificate: {0} from: {1}",
new Object[]{alias, file.getName()});
}
} catch (CertificateException e) {
LOGGER.log(WARNING, "Unable to load file system certificate from: " + file.getName(), e);
}
}

/**
* Load certificates in the file directory
*/
void loadCertificatesFromFileSystem() {
chenrujun marked this conversation as resolved.
Show resolved Hide resolved
try {
List<File> files = getFiles();
for (File file : files) {
setCertificateByFile(file);
}
} catch (IOException ioe) {
LOGGER.log(WARNING, "Unable to determine certificates to file system", ioe);
}
}

/**
* Get alias from file
* @param file File containing certificate information
* @return certificate alias
*/
public static String toCertificateAlias(File file) {
String fileName = file.getName();
int lastIndexOfDot = fileName.lastIndexOf('.');
if (lastIndexOfDot == -1) {
return fileName;
}
return fileName.substring(0, lastIndexOfDot);
}

/**
* Load all certificates in the folder, to avoid alias duplication, do not read files in subdirectories.
*/
private List<File> getFiles() {
List<File> files = new ArrayList<>();
File filePackage = new File(certificatePath);
File[] array = filePackage.listFiles();
Optional.ofNullable(array)
.map(Arrays::stream)
.orElseGet(Stream::empty)
.filter(Objects::nonNull)
.filter(File::isFile)
.filter(File::exists)
.filter(File::canRead)
.forEach(files::add);
return files;
}

/**
* Factory of FileSystemCertificate, to avoid loading files multiple times
*/
public static class FileSystemCertificatesFactory {

private static volatile FileSystemCertificates customFileSystemCertificates;

/**
* Get Singleton custom file system certificates
* @param path custom certificate path, which works only in first time
* @return custom file certificate
*/
public static FileSystemCertificates getCustomFileSystemCertificates(String path) {
if (customFileSystemCertificates == null) {
synchronized (FileSystemCertificatesFactory.class) {
if (customFileSystemCertificates == null) {
customFileSystemCertificates = new FileSystemCertificates(path);
}
}
}
return customFileSystemCertificates;
}

private static volatile FileSystemCertificates wellKnownFileSystemCertificates;

/**
* Get Singleton well known file system certificates
* @param path well known certificate path, which works only in first time
* @return well known file certificate
*/
public static FileSystemCertificates getWellKnownFileSystemCertificates(String path) {
if (wellKnownFileSystemCertificates == null) {
synchronized (FileSystemCertificatesFactory.class) {
if (wellKnownFileSystemCertificates == null) {
wellKnownFileSystemCertificates = new FileSystemCertificates(path);
}
}
}
return wellKnownFileSystemCertificates;
}
}

}
Loading