Skip to content

Commit 70f1e9e

Browse files
committed
✨ feat: add byte4j, encryption4j, os4j, string4j and uniqueid4j unify functions #4
1 parent 6b8aadb commit 70f1e9e

6 files changed

Lines changed: 957 additions & 0 deletions

File tree

Lines changed: 67 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,67 @@
1+
package org.unify4j.common;
2+
3+
public class Byte4j {
4+
protected static final char[] hexes = {'0', '1', '2', '3', '4', '5', '6', '7', '8', '9', 'A', 'B', 'C', 'D', 'E', 'F'}; // Array of hexadecimal characters used for encoding bytes
5+
6+
/**
7+
* Decodes a hexadecimal string into a byte array.
8+
*
9+
* @param s the hexadecimal string to decode
10+
* @return the decoded byte array, or null if the input string length is not even
11+
*/
12+
public static byte[] decode(final String s) {
13+
final int len = s.length();
14+
// Check if the length of the string is even
15+
if (len % 2 != 0) {
16+
return null;
17+
}
18+
byte[] bytes = new byte[len / 2];
19+
int pos = 0;
20+
// Loop through the string two characters at a time
21+
for (int i = 0; i < len; i += 2) {
22+
byte hi = (byte) Character.digit(s.charAt(i), 16);
23+
byte lo = (byte) Character.digit(s.charAt(i + 1), 16);
24+
// Combine the high and low nibbles into a single byte
25+
bytes[pos++] = (byte) (hi * 16 + lo);
26+
}
27+
return bytes;
28+
}
29+
30+
/**
31+
* Converts a byte array into a string of hexadecimal digits.
32+
*
33+
* @param bytes the byte array to encode
34+
* @return a string containing the hexadecimal representation of the byte array
35+
*/
36+
public static String encode(final byte[] bytes) {
37+
// Use StringBuilder to construct the hexadecimal string
38+
StringBuilder sb = new StringBuilder(bytes.length << 1);
39+
// Loop through each byte and convert it to two hexadecimal characters
40+
for (byte aByte : bytes) {
41+
sb.append(convertDigit(aByte >> 4));
42+
sb.append(convertDigit(aByte & 0x0f));
43+
}
44+
return sb.toString();
45+
}
46+
47+
/**
48+
* Converts a value (0 .. 15) to the corresponding hexadecimal digit.
49+
*
50+
* @param value the value to convert
51+
* @return the corresponding hexadecimal character ('0'..'F')
52+
*/
53+
private static char convertDigit(final int value) {
54+
return hexes[value & 0x0f];
55+
}
56+
57+
/**
58+
* Checks if a byte array is gzip compressed.
59+
*
60+
* @param bytes the byte array to check
61+
* @return true if the byte array is gzip compressed, false otherwise
62+
*/
63+
public static boolean isGzipped(byte[] bytes) {
64+
// Gzip files start with the magic number 0x1f8b
65+
return bytes[0] == (byte) 0x1f && bytes[1] == (byte) 0x8b;
66+
}
67+
}
Lines changed: 330 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,330 @@
1+
package org.unify4j.common;
2+
3+
import javax.crypto.Cipher;
4+
import javax.crypto.spec.IvParameterSpec;
5+
import javax.crypto.spec.SecretKeySpec;
6+
import java.io.File;
7+
import java.io.FileInputStream;
8+
import java.io.IOException;
9+
import java.nio.ByteBuffer;
10+
import java.nio.channels.FileChannel;
11+
import java.nio.charset.StandardCharsets;
12+
import java.security.Key;
13+
import java.security.MessageDigest;
14+
import java.security.NoSuchAlgorithmException;
15+
import java.security.spec.AlgorithmParameterSpec;
16+
import java.util.Objects;
17+
18+
public class Encryption4j {
19+
20+
/**
21+
* Computes the MD5 hash of a file using a FileChannel and a direct ByteBuffer.
22+
*
23+
* @param file the file for which to compute the MD5 hash
24+
* @return the MD5 hash of the file as a hexadecimal string, or null if an error occurs
25+
*/
26+
public static String fastMD5(File file) {
27+
try (FileInputStream in = new FileInputStream(file)) {
28+
return calculateFileHash(in.getChannel(), getMD5Digest());
29+
} catch (IOException e) {
30+
return null;
31+
}
32+
}
33+
34+
/**
35+
* Computes the SHA-1 hash of a file using a FileChannel and a direct ByteBuffer.
36+
*
37+
* @param file the file for which to compute the SHA-1 hash
38+
* @return the SHA-1 hash of the file as a hexadecimal string, or null if an error occurs
39+
*/
40+
public static String fastSHA1(File file) {
41+
try (FileInputStream in = new FileInputStream(file)) {
42+
return calculateFileHash(in.getChannel(), getSHA1Digest());
43+
} catch (IOException e) {
44+
return null;
45+
}
46+
}
47+
48+
/**
49+
* Computes the SHA-256 hash of a file using a FileChannel and a direct ByteBuffer.
50+
*
51+
* @param file the file for which to compute the SHA-256 hash
52+
* @return the SHA-256 hash of the file as a hexadecimal string, or null if an error occurs
53+
*/
54+
public static String fastSHA256(File file) {
55+
try (FileInputStream in = new FileInputStream(file)) {
56+
return calculateFileHash(in.getChannel(), getSHA256Digest());
57+
} catch (IOException e) {
58+
return null;
59+
}
60+
}
61+
62+
/**
63+
* Computes the SHA-512 hash of a file using a FileChannel and a direct ByteBuffer.
64+
*
65+
* @param file the file for which to compute the SHA-512 hash
66+
* @return the SHA-512 hash of the file as a hexadecimal string, or null if an error occurs
67+
*/
68+
public static String fastSHA512(File file) {
69+
try (FileInputStream in = new FileInputStream(file)) {
70+
return calculateFileHash(in.getChannel(), getSHA512Digest());
71+
} catch (IOException e) {
72+
return null;
73+
}
74+
}
75+
76+
/**
77+
* Calculates the hash of a file using a given MessageDigest.
78+
*
79+
* @param ch the FileChannel to read from
80+
* @param d the MessageDigest to update with the file data
81+
* @return the hash of the file as a hexadecimal string
82+
* @throws IOException if an I/O error occurs
83+
*/
84+
public static String calculateFileHash(FileChannel ch, MessageDigest d) throws IOException {
85+
ByteBuffer bb = ByteBuffer.allocateDirect(65536);
86+
int nRead;
87+
while ((nRead = ch.read(bb)) != -1) {
88+
if (nRead == 0) {
89+
continue;
90+
}
91+
bb.position(0);
92+
bb.limit(nRead);
93+
d.update(bb);
94+
bb.clear();
95+
}
96+
return Byte4j.encode(d.digest());
97+
}
98+
99+
/**
100+
* Calculates the MD5 hash of a byte array.
101+
*
102+
* @param bytes the byte array to hash
103+
* @return the MD5 hash as a hexadecimal string
104+
*/
105+
public static String calculateMD5Hash(byte[] bytes) {
106+
return calculateHash(getMD5Digest(), bytes);
107+
}
108+
109+
/**
110+
* Returns a MessageDigest instance for the specified algorithm.
111+
*
112+
* @param digest the name of the algorithm
113+
* @return the MessageDigest instance
114+
* @throws IllegalArgumentException if the algorithm is not available
115+
*/
116+
public static MessageDigest getDigest(String digest) {
117+
try {
118+
return MessageDigest.getInstance(digest);
119+
} catch (NoSuchAlgorithmException e) {
120+
throw new IllegalArgumentException(String.format("The requested MessageDigest (%s) does not exist", digest), e);
121+
}
122+
123+
}
124+
125+
/**
126+
* Returns a MessageDigest instance for MD5.
127+
*
128+
* @return the MD5 MessageDigest instance
129+
*/
130+
public static MessageDigest getMD5Digest() {
131+
return getDigest("MD5");
132+
}
133+
134+
/**
135+
* Calculates the SHA-1 hash of a byte array.
136+
*
137+
* @param bytes the byte array to hash
138+
* @return the SHA-1 hash as a hexadecimal string
139+
*/
140+
public static String calculateSHA1Hash(byte[] bytes) {
141+
return calculateHash(getSHA1Digest(), bytes);
142+
}
143+
144+
/**
145+
* Returns a MessageDigest instance for SHA-1.
146+
*
147+
* @return the SHA-1 MessageDigest instance
148+
*/
149+
public static MessageDigest getSHA1Digest() {
150+
return getDigest("SHA-1");
151+
}
152+
153+
/**
154+
* Calculates the SHA-256 hash of a byte array.
155+
*
156+
* @param bytes the byte array to hash
157+
* @return the SHA-256 hash as a hexadecimal string
158+
*/
159+
public static String calculateSHA256Hash(byte[] bytes) {
160+
return calculateHash(getSHA256Digest(), bytes);
161+
}
162+
163+
/**
164+
* Returns a MessageDigest instance for SHA-256.
165+
*
166+
* @return the SHA-256 MessageDigest instance
167+
*/
168+
public static MessageDigest getSHA256Digest() {
169+
return getDigest("SHA-256");
170+
}
171+
172+
/**
173+
* Calculates the SHA-512 hash of a byte array.
174+
*
175+
* @param bytes the byte array to hash
176+
* @return the SHA-512 hash as a hexadecimal string
177+
*/
178+
public static String calculateSHA512Hash(byte[] bytes) {
179+
return calculateHash(getSHA512Digest(), bytes);
180+
}
181+
182+
/**
183+
* Returns a MessageDigest instance for SHA-512.
184+
*
185+
* @return the SHA-512 MessageDigest instance
186+
*/
187+
public static MessageDigest getSHA512Digest() {
188+
return getDigest("SHA-512");
189+
}
190+
191+
/**
192+
* Creates a byte array suitable for use as an encryption key from a string key.
193+
*
194+
* @param key the string key
195+
* @param bitsNeeded the number of bits needed for the encryption key
196+
* @return the byte array of the encryption key
197+
*/
198+
public static byte[] createCipherBytes(String key, int bitsNeeded) {
199+
String word = calculateMD5Hash(key.getBytes(StandardCharsets.UTF_8));
200+
return word.substring(0, bitsNeeded / 8).getBytes(StandardCharsets.UTF_8);
201+
}
202+
203+
/**
204+
* Creates an AES encryption cipher using a given key.
205+
*
206+
* @param key the encryption key
207+
* @return the AES encryption cipher
208+
* @throws Exception if an error occurs creating the cipher
209+
*/
210+
public static Cipher createAesEncryptionCipher(String key) throws Exception {
211+
return createAesCipher(key, Cipher.ENCRYPT_MODE);
212+
}
213+
214+
/**
215+
* Creates an AES decryption cipher using a given key.
216+
*
217+
* @param key the decryption key
218+
* @return the AES decryption cipher
219+
* @throws Exception if an error occurs creating the cipher
220+
*/
221+
public static Cipher createAesDecryptionCipher(String key) throws Exception {
222+
return createAesCipher(key, Cipher.DECRYPT_MODE);
223+
}
224+
225+
/**
226+
* Creates an AES cipher using a given key and mode.
227+
*
228+
* @param key the encryption/decryption key
229+
* @param mode the cipher mode (Cipher.ENCRYPT_MODE or Cipher.DECRYPT_MODE)
230+
* @return the AES cipher
231+
* @throws Exception if an error occurs creating the cipher
232+
*/
233+
public static Cipher createAesCipher(String key, int mode) throws Exception {
234+
Key sKey = new SecretKeySpec(createCipherBytes(key, 128), "AES");
235+
return createAesCipher(sKey, mode);
236+
}
237+
238+
/**
239+
* Creates a Cipher from the passed in key, using the passed in mode.
240+
*
241+
* @param key the SecretKeySpec
242+
* @param mode the cipher mode (Cipher.ENCRYPT_MODE or Cipher.DECRYPT_MODE)
243+
* @return the Cipher instance
244+
* @throws Exception if an error occurs creating the cipher
245+
*/
246+
public static Cipher createAesCipher(Key key, int mode) throws Exception {
247+
MessageDigest d = getMD5Digest(); // Use password key as seed for IV (must be 16 bytes)
248+
d.update(key.getEncoded());
249+
byte[] iv = d.digest();
250+
AlgorithmParameterSpec paramSpec = new IvParameterSpec(iv);
251+
Cipher cipher = Cipher.getInstance("AES/CBC/PKCS5Padding"); // CBC faster than CFB8/NoPadding (but file length changes)
252+
cipher.init(mode, key, paramSpec);
253+
return cipher;
254+
}
255+
256+
/**
257+
* Encrypts a string using AES-128 and returns the encrypted content as a hexadecimal string.
258+
*
259+
* @param key the encryption key
260+
* @param content the content to be encrypted
261+
* @return the encrypted content as a hexadecimal string
262+
*/
263+
public static String encrypt(String key, String content) {
264+
try {
265+
return Byte4j.encode(createAesEncryptionCipher(key).doFinal(content.getBytes(StandardCharsets.UTF_8)));
266+
} catch (Exception e) {
267+
throw new IllegalStateException("Error occurred encrypting data", e);
268+
}
269+
}
270+
271+
/**
272+
* Encrypts a byte array using AES-128 and returns the encrypted content as a hexadecimal string.
273+
*
274+
* @param key the encryption key
275+
* @param content the byte array to be encrypted
276+
* @return the encrypted content as a hexadecimal string
277+
*/
278+
public static String encryptBytes(String key, byte[] content) {
279+
try {
280+
return Byte4j.encode(createAesEncryptionCipher(key).doFinal(content));
281+
} catch (Exception e) {
282+
throw new IllegalStateException("Error occurred encrypting data", e);
283+
}
284+
}
285+
286+
/**
287+
* Decrypts a hexadecimal string using AES-128 and returns the original content as a string.
288+
*
289+
* @param key the decryption key
290+
* @param hexStr the encrypted content as a hexadecimal string
291+
* @return the original content as a string
292+
*/
293+
public static String decrypt(String key, String hexStr) {
294+
try {
295+
return new String(createAesDecryptionCipher(key).doFinal(Objects.requireNonNull(Byte4j.decode(hexStr))));
296+
} catch (Exception e) {
297+
throw new IllegalStateException("Error occurred decrypting data", e);
298+
}
299+
}
300+
301+
/**
302+
* Decrypts a hexadecimal string using AES-128 and returns the original content as a byte array.
303+
*
304+
* @param key the decryption key
305+
* @param hexStr the encrypted content as a hexadecimal string
306+
* @return the original content as a byte array
307+
*/
308+
public static byte[] decryptBytes(String key, String hexStr) {
309+
try {
310+
return createAesDecryptionCipher(key).doFinal(Objects.requireNonNull(Byte4j.decode(hexStr)));
311+
} catch (Exception e) {
312+
throw new IllegalStateException("Error occurred decrypting data", e);
313+
}
314+
}
315+
316+
/**
317+
* Calculates a hash from a byte array using a given MessageDigest.
318+
*
319+
* @param d the MessageDigest to update
320+
* @param bytes the byte array to hash
321+
* @return the hash as a hexadecimal string
322+
*/
323+
public static String calculateHash(MessageDigest d, byte[] bytes) {
324+
if (bytes == null) {
325+
return null;
326+
}
327+
d.update(bytes);
328+
return Byte4j.encode(d.digest());
329+
}
330+
}

0 commit comments

Comments
 (0)