View Javadoc
1   /*
2    * Licensed to the Apache Software Foundation (ASF) under one
3    * or more contributor license agreements.  See the NOTICE file
4    * distributed with this work for additional information
5    * regarding copyright ownership.  The ASF licenses this file
6    * to you under the Apache License, Version 2.0 (the
7    * "License"); you may not use this file except in compliance
8    * with the License.  You may obtain a copy of the License at
9    *
10   *   http://www.apache.org/licenses/LICENSE-2.0
11   *
12   * Unless required by applicable law or agreed to in writing,
13   * software distributed under the License is distributed on an
14   * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
15   * KIND, either express or implied.  See the License for the
16   * specific language governing permissions and limitations
17   * under the License.
18   */
19  package org.eclipse.aether.util;
20  
21  import java.nio.charset.StandardCharsets;
22  import java.security.MessageDigest;
23  import java.security.NoSuchAlgorithmException;
24  
25  /**
26   * A simple digester utility for Strings. Uses {@link MessageDigest} for requested algorithm. Supports one-pass or
27   * several rounds of updates, and as result emits hex encoded String.
28   *
29   * @since 1.9.0
30   */
31  public final class StringDigestUtil {
32      private final MessageDigest digest;
33  
34      /**
35       * Constructs instance with given algorithm.
36       *
37       * @see #sha1()
38       * @see #sha1(String)
39       */
40      public StringDigestUtil(final String alg) {
41          try {
42              this.digest = MessageDigest.getInstance(alg);
43          } catch (NoSuchAlgorithmException e) {
44              throw new IllegalStateException("Not supported digest algorithm: " + alg);
45          }
46      }
47  
48      /**
49       * Updates instance with passed in string.
50       */
51      public StringDigestUtil update(String data) {
52          if (data != null && !data.isEmpty()) {
53              digest.update(data.getBytes(StandardCharsets.UTF_8));
54          }
55          return this;
56      }
57  
58      /**
59       * Returns the digest of all strings passed via {@link #update(String)} as hex string. There is no state preserved
60       * and due implementation of {@link MessageDigest#digest()}, same applies here: this instance "resets" itself.
61       * Hence, the digest hex encoded string is returned only once.
62       *
63       * @see MessageDigest#digest()
64       */
65      public String digest() {
66          return toHexString(digest.digest());
67      }
68  
69      /**
70       * Helper method to create {@link StringDigestUtil} using SHA-1 digest algorithm.
71       */
72      public static StringDigestUtil sha1() {
73          return new StringDigestUtil("SHA-1");
74      }
75  
76      /**
77       * Helper method to calculate SHA-1 digest and hex encode it.
78       */
79      public static String sha1(final String string) {
80          return sha1().update(string).digest();
81      }
82  
83      /**
84       * Creates a hexadecimal representation of the specified bytes. Each byte is converted into a two-digit hex number
85       * and appended to the result with no separator between consecutive bytes.
86       *
87       * @param bytes The bytes to represent in hex notation, may be {@code null}.
88       * @return The hexadecimal representation of the input or {@code null} if the input was {@code null}.
89       * @since 2.0.0
90       */
91      @SuppressWarnings("checkstyle:magicnumber")
92      public static String toHexString(byte[] bytes) {
93          if (bytes == null) {
94              return null;
95          }
96  
97          StringBuilder buffer = new StringBuilder(bytes.length * 2);
98  
99          for (byte aByte : bytes) {
100             int b = aByte & 0xFF;
101             if (b < 0x10) {
102                 buffer.append('0');
103             }
104             buffer.append(Integer.toHexString(b));
105         }
106 
107         return buffer.toString();
108     }
109 
110     /**
111      * Creates a byte array out of hexadecimal representation of the specified bytes. If input string is {@code null},
112      * {@code null} is returned. Input value must have even length (due hex encoding = 2 chars one byte).
113      *
114      * @param hexString The hexString to convert to byte array, may be {@code null}.
115      * @return The byte array of the input or {@code null} if the input was {@code null}.
116      * @since 2.0.0
117      */
118     @SuppressWarnings("checkstyle:magicnumber")
119     public static byte[] fromHexString(String hexString) {
120         if (hexString == null) {
121             return null;
122         }
123         if (hexString.isEmpty()) {
124             return new byte[] {};
125         }
126         int len = hexString.length();
127         if (len % 2 != 0) {
128             throw new IllegalArgumentException("hexString length not even");
129         }
130         byte[] data = new byte[len / 2];
131         for (int i = 0; i < len; i += 2) {
132             data[i / 2] = (byte)
133                     ((Character.digit(hexString.charAt(i), 16) << 4) + Character.digit(hexString.charAt(i + 1), 16));
134         }
135         return data;
136     }
137 }