MD5Util.java
7.38 KB
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
package com.topdraw.platform.util;
/*
MessageDigest md = MessageDigest.getInstance("SHA");
try {
md.update(toChapter1);
MessageDigest tc1 = md.clone();
byte[] toChapter1Digest = tc1.digest();
md.update(toChapter2);
...etc.
} catch (CloneNotSupportedException cnse) {
throw new DigestException("couldn't make digest of partial content");
}
*/
import org.apache.commons.codec.binary.Hex;
import org.apache.tomcat.util.codec.binary.Base64;
import java.io.UnsupportedEncodingException;
import java.security.MessageDigest;
import java.security.NoSuchAlgorithmException;
/**
* @author zehui.zeng
* @date 13-3-17 下午9:23
*/
public class MD5Util {
private final String algorithm;
private boolean encodeHashAsBase64 = false;
private static MD5Util md5 = null;
/**
* The digest algorithm to use
* Supports the named <a href="http://java.sun.com/j2se/1.4.2/docs/guide/security/CryptoSpec.html#AppA">
* Message Digest Algorithms</a> in the Java environment.
*
* @param algorithm
*/
public MD5Util(String algorithm) {
this(algorithm, false);
}
public MD5Util(){
this.algorithm = "MD5";
}
/**
* Convenience constructor for specifying the algorithm and whether or not to enable base64 encoding
*
* @param algorithm
* @param encodeHashAsBase64
* @throws IllegalArgumentException if an unknown
*/
public MD5Util(String algorithm, boolean encodeHashAsBase64) throws IllegalArgumentException {
this.algorithm = algorithm;
setEncodeHashAsBase64(encodeHashAsBase64);
//Validity Check
getMessageDigest();
}
public static String encodePassword(String passwd){
if(md5 == null){
md5 = new MD5Util();
}
return md5.encodePassword(passwd, "");
}
/**
* Encodes the rawPass using a MessageDigest.
* If a salt is specified it will be merged with the password before encoding.
*
* @param rawPass The plain text password
* @param salt The salt to sprinkle
* @return Hex string of password digest (or base64 encoded string if encodeHashAsBase64 is enabled.
*/
public String encodePassword(String rawPass, Object salt) {
String saltedPass = mergePasswordAndSalt(rawPass, salt, false);
MessageDigest messageDigest = getMessageDigest();
byte[] digest;
try {
digest = messageDigest.digest(saltedPass.getBytes("UTF-8"));
} catch (UnsupportedEncodingException e) {
throw new IllegalStateException("UTF-8 not supported!");
}
if (getEncodeHashAsBase64()) {
return new String(Base64.encodeBase64(digest));
} else {
return new String(Hex.encodeHex(digest));
}
}
/**
* Get a MessageDigest instance for the given algorithm.
* Throws an IllegalArgumentException if <i>algorithm</i> is unknown
*
* @return MessageDigest instance
* @throws IllegalArgumentException if NoSuchAlgorithmException is thrown
*/
protected final MessageDigest getMessageDigest() throws IllegalArgumentException {
try {
return MessageDigest.getInstance(algorithm);
} catch (NoSuchAlgorithmException e) {
throw new IllegalArgumentException("No such algorithm [" + algorithm + "]");
}
}
/**
* Takes a previously encoded password and compares it with a rawpassword after mixing in the salt and
* encoding that value
*
* @param encPass previously encoded password
* @param rawPass plain text password
* @param salt salt to mix into password
* @return true or false
*/
public boolean isPasswordValid(String encPass, String rawPass, Object salt) {
String pass1 = "" + encPass;
String pass2 = encodePassword(rawPass, salt);
return pass1.equals(pass2);
}
/**
* Used by subclasses to extract the password and salt from a merged <code>String</code> created using
* {@link #mergePasswordAndSalt(String,Object,boolean)}.<p>The first element in the returned array is the
* password. The second element is the salt. The salt array element will always be present, even if no salt was
* found in the <code>mergedPasswordSalt</code> argument.</p>
*
* @param mergedPasswordSalt as generated by <code>mergePasswordAndSalt</code>
*
* @return an array, in which the first element is the password and the second the salt
*
* @throws IllegalArgumentException if mergedPasswordSalt is null or empty.
*/
protected String[] demergePasswordAndSalt(String mergedPasswordSalt) {
if ((mergedPasswordSalt == null) || "".equals(mergedPasswordSalt)) {
throw new IllegalArgumentException("Cannot pass a null or empty String");
}
String password = mergedPasswordSalt;
String salt = "";
int saltBegins = mergedPasswordSalt.lastIndexOf("{");
if ((saltBegins != -1) && ((saltBegins + 1) < mergedPasswordSalt.length())) {
salt = mergedPasswordSalt.substring(saltBegins + 1, mergedPasswordSalt.length() - 1);
password = mergedPasswordSalt.substring(0, saltBegins);
}
return new String[] {password, salt};
}
/**
* Used by subclasses to generate a merged password and salt <code>String</code>.<P>The generated password
* will be in the form of <code>password{salt}</code>.</p>
* <p>A <code>null</code> can be passed to either method, and will be handled correctly. If the
* <code>salt</code> is <code>null</code> or empty, the resulting generated password will simply be the passed
* <code>password</code>. The <code>toString</code> method of the <code>salt</code> will be used to represent the
* salt.</p>
*
* @param password the password to be used (can be <code>null</code>)
* @param salt the salt to be used (can be <code>null</code>)
* @param strict ensures salt doesn't contain the delimiters
*
* @return a merged password and salt <code>String</code>
*
* @throws IllegalArgumentException if the salt contains '{' or '}' characters.
*/
protected String mergePasswordAndSalt(String password, Object salt, boolean strict) {
if (password == null) {
password = "";
}
if (strict && (salt != null)) {
if ((salt.toString().lastIndexOf("{") != -1) || (salt.toString().lastIndexOf("}") != -1)) {
throw new IllegalArgumentException("Cannot use { or } in salt.toString()");
}
}
if ((salt == null) || "".equals(salt)) {
return password;
} else {
return password + "{" + salt.toString() + "}";
}
}
public String getAlgorithm() {
return algorithm;
}
public boolean getEncodeHashAsBase64() {
return encodeHashAsBase64;
}
/**
* The encoded password is normally returned as Hex (32 char) version of the hash bytes. Setting this
* property to true will cause the encoded pass to be returned as Base64 text, which will consume 24 characters.
*
* @param encodeHashAsBase64 set to true for Base64 output
*/
public void setEncodeHashAsBase64(boolean encodeHashAsBase64) {
this.encodeHashAsBase64 = encodeHashAsBase64;
}
public static void main(String[] args){
System.out.println(MD5Util.encodePassword("system1"));
}
}