2222import java .io .IOException ;
2323import java .io .InputStream ;
2424import java .nio .charset .StandardCharsets ;
25- import java .security .MessageDigest ;
26- import java .security .NoSuchAlgorithmException ;
25+ import java .security .*;
2726
2827import org .apache .logging .log4j .LogManager ;
2928import org .apache .logging .log4j .Logger ;
3332import ro .kuberam .libs .java .crypto .utils .HexString ;
3433
3534import 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