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 ;
2928
3029import javax .annotation .Nullable ;
3130
3231import org .apache .logging .log4j .LogManager ;
3332import org .apache .logging .log4j .Logger ;
34-
33+ import ro . kuberam . libs . java . crypto . CryptoError ;
3534import ro .kuberam .libs .java .crypto .CryptoException ;
3635import ro .kuberam .libs .java .crypto .utils .Buffer ;
3736import ro .kuberam .libs .java .crypto .utils .HexString ;
4443 */
4544public class Hash {
4645
47- private static Logger LOG = LogManager .getLogger (Hash .class );
46+ private static final Logger LOG = LogManager .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 );
50+ }
51+
52+ public static String hashString (final String data , final String algorithm , @ Nullable final String provider ) throws CryptoException {
53+ return hashString (data , algorithm , provider , null );
5154 }
5255
53- public static String hashString (final String data , final String algorithm , final @ Nullable String format )
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,88 @@ 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 ) {
107- throw new CryptoException (e );
113+ if (provider != null ) {
114+ return MessageDigest .getInstance (algorithm , provider );
115+ } else {
116+ return MessageDigest .getInstance (algorithm );
117+ }
118+ } catch (final NoSuchAlgorithmException e ) {
119+ throw new CryptoException (CryptoError .UNKNOWN_ALGORITH , e );
120+ } catch (final NoSuchProviderException e ) {
121+ throw new CryptoException (CryptoError .UNKNOWN_PROVIDER , e );
122+ }
123+ }
124+
125+
126+ /**
127+ * Returns a list of security providers which
128+ * offer hash services.
129+ *
130+ * @return the names of the security providers which
131+ * offer hash services.
132+ */
133+ public static List <String > listProviders () {
134+ final List <String > hashProviders = new ArrayList <>();
135+
136+ final Provider [] providers = Security .getProviders ();
137+ for (final Provider provider : providers ) {
138+ final Set <Provider .Service > services = provider .getServices ();
139+ for (final Provider .Service service : services ) {
140+ if (service .getType ().equals ("MessageDigest" )) {
141+ hashProviders .add (provider .getName ());
142+ break ;
143+ }
144+ }
108145 }
146+
147+ return hashProviders ;
148+ }
149+
150+ /**
151+ * Returns a Map of all hash services from each
152+ * security provider.
153+ *
154+ * @return a map from key: `service provider name` to value: `algorithm name(s)`.
155+ */
156+ public static Map <String , Set <String >> listAlgorithms () {
157+ final Map <String , Set <String >> algorithms = new HashMap <>();
158+
159+ final Provider [] providers = Security .getProviders ();
160+ for (final Provider provider : providers ) {
161+ final Set <Provider .Service > services = provider .getServices ();
162+ for (final Provider .Service service : services ) {
163+ if (service .getType ().equals ("MessageDigest" )) {
164+
165+ final Set <String > providerAlgs = algorithms .computeIfAbsent (provider .getName (), k -> new HashSet <>());
166+ providerAlgs .add (service .getAlgorithm ());
167+ }
168+ }
169+ }
170+
171+ return algorithms ;
172+ }
173+
174+ /**
175+ * Returns a Map of all hash services from a
176+ * security provider.
177+ *
178+ * @param providerName the name of the security provider.
179+ *
180+ * @return the names of the algorithms provided by the security provider.
181+ */
182+ public static Set <String > listAlgorithms (final String providerName ) {
183+ final Set <String > algorithms = new HashSet <>();
184+
185+ final Provider provider = Security .getProvider (providerName );
186+ final Set <Provider .Service > services = provider .getServices ();
187+ for (final Provider .Service service : services ) {
188+ if (service .getType ().equals ("MessageDigest" )) {
189+ algorithms .add (service .getAlgorithm ());
190+ }
191+ }
192+
193+ return algorithms ;
109194 }
110195}
0 commit comments