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 .*;
2726import java .util .Base64 ;
2827import java .util .Optional ;
2928import javax .annotation .Nullable ;
@@ -47,16 +46,20 @@ public class Hash {
4746 private static final Logger LOG = LoggerFactory .getLogger (Hash .class );
4847
4948 public static String hashString (final String data , final String algorithm ) throws CryptoException {
50- return hashString (data , algorithm , null );
49+ return hashString (data , algorithm , null , null );
5150 }
5251
53- public static String hashString (final String data , final String algorithm , final @ Nullable String format )
52+ public static String hashString (final String data , final String algorithm , @ Nullable final String provider ) throws CryptoException {
53+ return hashString (data , algorithm , provider , null );
54+ }
55+
56+ public static String hashString (final String data , final String algorithm , @ Nullable final String provider , final @ Nullable String format )
5457 throws CryptoException {
5558
5659 // TODO: validate the format
5760 final String actualFormat = Optional .ofNullable (format ).filter (str -> !str .isEmpty ()).orElse ("base64" );
5861
59- final MessageDigest messageDigester = getMessageDigester (algorithm );
62+ final MessageDigest messageDigester = getMessageDigester (algorithm , provider );
6063 messageDigester .update (data .getBytes (StandardCharsets .UTF_8 ));
6164
6265 final byte [] resultBytes = messageDigester .digest ();
@@ -70,17 +73,22 @@ public static String hashString(final String data, final String algorithm, final
7073
7174 public static String hashBinary (final InputStream data , final String algorithm )
7275 throws CryptoException , IOException {
73- return hashBinary (data , algorithm , null );
76+ return hashBinary (data , algorithm , null , null );
77+ }
78+
79+ public static String hashBinary (final InputStream data , final String algorithm , @ Nullable final String provider )
80+ throws CryptoException , IOException {
81+ return hashBinary (data , algorithm , provider , null );
7482 }
7583
76- public static String hashBinary (final InputStream data , final String algorithm , @ Nullable final String format )
84+ public static String hashBinary (final InputStream data , final String algorithm , @ Nullable final String provider , @ Nullable final String format )
7785 throws CryptoException , IOException {
7886
7987 // TODO: validate the format
8088 final String actualFormat = Optional .ofNullable (format ).filter (str -> !str .isEmpty ()).orElse ("base64" );
8189
8290 final byte [] resultBytes ;
83- final MessageDigest messageDigester = getMessageDigester (algorithm );
91+ final MessageDigest messageDigester = getMessageDigester (algorithm , provider );
8492
8593 final byte [] buf = new byte [Buffer .TRANSFER_SIZE ];
8694 int read = -1 ;
@@ -100,11 +108,87 @@ public static String hashBinary(final InputStream data, final String algorithm,
100108 return result ;
101109 }
102110
103- private static MessageDigest getMessageDigester (final String algorithm ) throws CryptoException {
111+ private static MessageDigest getMessageDigester (final String algorithm , @ Nullable final String provider ) throws CryptoException {
104112 try {
105- return MessageDigest .getInstance (algorithm );
106- } catch (NoSuchAlgorithmException e ) {
113+ if (provider != null ) {
114+ return MessageDigest .getInstance (algorithm , provider );
115+ } else {
116+ return MessageDigest .getInstance (algorithm );
117+ }
118+ } catch (final NoSuchAlgorithmException e ) {
107119 throw new CryptoException (CryptoError .NoSuchAlgorithmException , e );
120+ } catch (final NoSuchProviderException e ) {
121+ throw new CryptoException (CryptoError .UNKNOWN_PROVIDER , e );
108122 }
109123 }
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+ }
144+ }
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 ;
193+ }
110194}
0 commit comments