Skip to content

Commit b885ea2

Browse files
committed
[feature] Facility for listing providers and algorithms
1 parent eff0e97 commit b885ea2

File tree

2 files changed

+97
-12
lines changed

2 files changed

+97
-12
lines changed

src/main/java/ro/kuberam/libs/java/crypto/CryptoError.java

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -22,6 +22,7 @@
2222
public enum CryptoError {
2323

2424
UNKNOWN_ALGORITH("crypto:unknown-algorithm", "The specified algorithm is not supported."),
25+
UNKNOWN_PROVIDER("crypto:unknown-provider", "The specified provider is not available."),
2526
SIGNATURE_TYPE("crypto:signature-type", "The specified signature type is not supported."),
2627
UNREADABLE_KEYSTORE("crypto:unreadable-keystore", "I/O error while reading keystore, or the password is incorrect."),
2728
DENIED_KEYSTORE("crypto:denied-keystore", "Permission denied to read keystore."),

src/main/java/ro/kuberam/libs/java/crypto/digest/Hash.java

Lines changed: 96 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -22,8 +22,7 @@
2222
import java.io.IOException;
2323
import java.io.InputStream;
2424
import java.nio.charset.StandardCharsets;
25-
import java.security.MessageDigest;
26-
import java.security.NoSuchAlgorithmException;
25+
import java.security.*;
2726

2827
import org.apache.logging.log4j.LogManager;
2928
import org.apache.logging.log4j.Logger;
@@ -33,8 +32,7 @@
3332
import ro.kuberam.libs.java.crypto.utils.HexString;
3433

3534
import javax.annotation.Nullable;
36-
import java.util.Base64;
37-
import java.util.Optional;
35+
import java.util.*;
3836

3937
/**
4038
* Implements the crypto:hash() function.
@@ -47,16 +45,20 @@ public class Hash {
4745
private static final Logger LOG = LogManager.getLogger(Hash.class);
4846

4947
public static String hashString(final String data, final String algorithm) throws CryptoException {
50-
return hashString(data, algorithm, null);
48+
return hashString(data, algorithm, null, null);
5149
}
5250

53-
public static String hashString(final String data, final String algorithm, final @Nullable String format)
51+
public static String hashString(final String data, final String algorithm, @Nullable final String provider) throws CryptoException {
52+
return hashString(data, algorithm, provider, null);
53+
}
54+
55+
public static String hashString(final String data, final String algorithm, @Nullable final String provider, final @Nullable String format)
5456
throws CryptoException {
5557

5658
// TODO: validate the format
5759
final String actualFormat = Optional.ofNullable(format).filter(str -> !str.isEmpty()).orElse("base64");
5860

59-
final MessageDigest messageDigester = getMessageDigester(algorithm);
61+
final MessageDigest messageDigester = getMessageDigester(algorithm, provider);
6062
messageDigester.update(data.getBytes(StandardCharsets.UTF_8));
6163

6264
final byte[] resultBytes = messageDigester.digest();
@@ -70,17 +72,22 @@ public static String hashString(final String data, final String algorithm, final
7072

7173
public static String hashBinary(final InputStream data, final String algorithm)
7274
throws CryptoException, IOException {
73-
return hashBinary(data, algorithm, null);
75+
return hashBinary(data, algorithm, null, null);
76+
}
77+
78+
public static String hashBinary(final InputStream data, final String algorithm, @Nullable final String provider)
79+
throws CryptoException, IOException {
80+
return hashBinary(data, algorithm, provider, null);
7481
}
7582

76-
public static String hashBinary(final InputStream data, final String algorithm, @Nullable final String format)
83+
public static String hashBinary(final InputStream data, final String algorithm, @Nullable final String provider, @Nullable final String format)
7784
throws CryptoException, IOException {
7885

7986
// TODO: validate the format
8087
final String actualFormat = Optional.ofNullable(format).filter(str -> !str.isEmpty()).orElse("base64");
8188

8289
final byte[] resultBytes;
83-
final MessageDigest messageDigester = getMessageDigester(algorithm);
90+
final MessageDigest messageDigester = getMessageDigester(algorithm, provider);
8491

8592
final byte[] buf = new byte[Buffer.TRANSFER_SIZE];
8693
int read = -1;
@@ -100,11 +107,88 @@ public static String hashBinary(final InputStream data, final String algorithm,
100107
return result;
101108
}
102109

103-
private static MessageDigest getMessageDigester(final String algorithm) throws CryptoException {
110+
private static MessageDigest getMessageDigester(final String algorithm, @Nullable final String provider) throws CryptoException {
104111
try {
105-
return MessageDigest.getInstance(algorithm);
112+
if (provider != null) {
113+
return MessageDigest.getInstance(algorithm, provider);
114+
} else {
115+
return MessageDigest.getInstance(algorithm);
116+
}
106117
} catch (final NoSuchAlgorithmException e) {
107118
throw new CryptoException(CryptoError.UNKNOWN_ALGORITH, e);
119+
} catch (final NoSuchProviderException e) {
120+
throw new CryptoException(CryptoError.UNKNOWN_PROVIDER, e);
121+
}
122+
}
123+
124+
125+
/**
126+
* Returns a list of security providers which
127+
* offer hash services.
128+
*
129+
* @return the names of the security providers which
130+
* offer hash services.
131+
*/
132+
public static List<String> listProviders() {
133+
final List<String> hashProviders = new ArrayList<>();
134+
135+
final Provider[] providers = Security.getProviders();
136+
for (final Provider provider : providers) {
137+
final Set<Provider.Service> services = provider.getServices();
138+
for (final Provider.Service service : services) {
139+
if (service.getType().equals("MessageDigest")) {
140+
hashProviders.add(provider.getName());
141+
break;
142+
}
143+
}
108144
}
145+
146+
return hashProviders;
147+
}
148+
149+
/**
150+
* Returns a Map of all hash services from each
151+
* security provider.
152+
*
153+
* @return a map from key: `service provider name` to value: `algorithm name(s)`.
154+
*/
155+
public static Map<String, Set<String>> listAlgorithms() {
156+
final Map<String, Set<String>> algorithms = new HashMap<>();
157+
158+
final Provider[] providers = Security.getProviders();
159+
for (final Provider provider : providers) {
160+
final Set<Provider.Service> services = provider.getServices();
161+
for (final Provider.Service service : services) {
162+
if (service.getType().equals("MessageDigest")) {
163+
164+
final Set<String> providerAlgs = algorithms.computeIfAbsent(provider.getName(), k -> new HashSet<>());
165+
providerAlgs.add(service.getAlgorithm());
166+
}
167+
}
168+
}
169+
170+
return algorithms;
171+
}
172+
173+
/**
174+
* Returns a Map of all hash services from a
175+
* security provider.
176+
*
177+
* @param providerName the name of the security provider.
178+
*
179+
* @return the names of the algorithms provided by the security provider.
180+
*/
181+
public static Set<String> listAlgorithms(final String providerName) {
182+
final Set<String> algorithms = new HashSet<>();
183+
184+
final Provider provider = Security.getProvider(providerName);
185+
final Set<Provider.Service> services = provider.getServices();
186+
for (final Provider.Service service : services) {
187+
if (service.getType().equals("MessageDigest")) {
188+
algorithms.add(service.getAlgorithm());
189+
}
190+
}
191+
192+
return algorithms;
109193
}
110194
}

0 commit comments

Comments
 (0)