Skip to content
体验新版
项目
组织
正在加载...
登录
切换导航
打开侧边栏
MaxKey单点登录官方(MaxKeyTop)
MaxKey
提交
c2537813
MaxKey
项目概览
MaxKey单点登录官方(MaxKeyTop)
/
MaxKey
9 个月 前同步成功
通知
75
Star
3
Fork
1
代码
文件
提交
分支
Tags
贡献者
分支图
Diff
Issue
1
列表
看板
标记
里程碑
合并请求
0
DevOps
流水线
流水线任务
计划
Wiki
0
Wiki
分析
仓库
DevOps
项目成员
Pages
MaxKey
项目概览
项目概览
详情
发布
仓库
仓库
文件
提交
分支
标签
贡献者
分支图
比较
Issue
1
Issue
1
列表
看板
标记
里程碑
合并请求
0
合并请求
0
Pages
DevOps
DevOps
流水线
流水线任务
计划
分析
分析
仓库分析
DevOps
Wiki
0
Wiki
成员
成员
收起侧边栏
关闭侧边栏
动态
分支图
创建新Issue
流水线任务
提交
Issue看板
前往新版Gitcode,体验更适合开发者的 AI 搜索 >>
提交
c2537813
编写于
9月 03, 2020
作者:
MaxKey单点登录官方
浏览文件
操作
浏览文件
下载
电子邮件补丁
差异文件
PasswordEncoders from spring
上级
d5472fa4
变更
8
隐藏空白更改
内联
并排
Showing
8 changed file
with
828 addition
and
6 deletion
+828
-6
gradleSetEnv.bat
gradleSetEnv.bat
+2
-2
maxkey-core/src/main/java/org/maxkey/autoconfigure/ApplicationAutoConfiguration.java
...rg/maxkey/autoconfigure/ApplicationAutoConfiguration.java
+5
-4
maxkey-core/src/main/java/org/maxkey/crypto/password/LdapShaPasswordEncoder.java
...va/org/maxkey/crypto/password/LdapShaPasswordEncoder.java
+211
-0
maxkey-core/src/main/java/org/maxkey/crypto/password/Md4.java
...ey-core/src/main/java/org/maxkey/crypto/password/Md4.java
+184
-0
maxkey-core/src/main/java/org/maxkey/crypto/password/Md4PasswordEncoder.java
...n/java/org/maxkey/crypto/password/Md4PasswordEncoder.java
+154
-0
maxkey-core/src/main/java/org/maxkey/crypto/password/MessageDigestPasswordEncoder.java
.../maxkey/crypto/password/MessageDigestPasswordEncoder.java
+176
-0
maxkey-core/src/main/java/org/maxkey/crypto/password/NoOpPasswordEncoder.java
.../java/org/maxkey/crypto/password/NoOpPasswordEncoder.java
+58
-0
maxkey-core/src/main/java/org/maxkey/crypto/password/PasswordEncoderUtils.java
...java/org/maxkey/crypto/password/PasswordEncoderUtils.java
+38
-0
未找到文件。
gradleSetEnv.bat
浏览文件 @
c2537813
echo
off
echo
set
env
set
JAVA_HOME
=
D
:\
JavaIDE\jdk1.8.0_91
set
GRADLE_HOME
=
D
:\
Java
IDE\gradle
-
6
.5.1
set
JAVA_HOME
=
D
:\
IDE\jdk1.8.0_202
set
GRADLE_HOME
=
D
:\IDE\gradle
-
6
.5.1
call
%JAVA_HOME%
/bin/java -version
call
%GRADLE_HOME%
/bin/gradle -version
maxkey-core/src/main/java/org/maxkey/autoconfigure/ApplicationAutoConfiguration.java
浏览文件 @
c2537813
...
...
@@ -31,6 +31,10 @@ import org.maxkey.authn.support.rememberme.JdbcRemeberMeService;
import
org.maxkey.authn.support.rememberme.RedisRemeberMeService
;
import
org.maxkey.constants.ConstantsProperties
;
import
org.maxkey.crypto.keystore.KeyStoreLoader
;
import
org.maxkey.crypto.password.LdapShaPasswordEncoder
;
import
org.maxkey.crypto.password.Md4PasswordEncoder
;
import
org.maxkey.crypto.password.NoOpPasswordEncoder
;
import
org.maxkey.crypto.password.MessageDigestPasswordEncoder
;
import
org.maxkey.crypto.password.PasswordReciprocal
;
import
org.maxkey.crypto.password.SM3PasswordEncoder
;
import
org.maxkey.crypto.password.StandardPasswordEncoder
;
...
...
@@ -53,10 +57,7 @@ import org.springframework.jdbc.core.JdbcTemplate;
import
org.springframework.jdbc.datasource.DataSourceTransactionManager
;
import
org.springframework.security.crypto.bcrypt.BCryptPasswordEncoder
;
import
org.springframework.security.crypto.password.DelegatingPasswordEncoder
;
import
org.springframework.security.crypto.password.LdapShaPasswordEncoder
;
import
org.springframework.security.crypto.password.Md4PasswordEncoder
;
import
org.springframework.security.crypto.password.MessageDigestPasswordEncoder
;
import
org.springframework.security.crypto.password.NoOpPasswordEncoder
;
import
org.springframework.security.crypto.password.PasswordEncoder
;
import
org.springframework.security.crypto.password.Pbkdf2PasswordEncoder
;
import
org.springframework.security.crypto.scrypt.SCryptPasswordEncoder
;
...
...
maxkey-core/src/main/java/org/maxkey/crypto/password/LdapShaPasswordEncoder.java
0 → 100644
浏览文件 @
c2537813
package
org.maxkey.crypto.password
;
/*
* Copyright 2002-2018 the original author or authors.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* https://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
import
org.springframework.security.crypto.codec.Utf8
;
import
org.springframework.security.crypto.keygen.BytesKeyGenerator
;
import
org.springframework.security.crypto.keygen.KeyGenerators
;
import
org.springframework.security.crypto.password.PasswordEncoder
;
import
java.security.MessageDigest
;
import
java.util.Base64
;
/**
* This {@link PasswordEncoder} is provided for legacy purposes only and is not considered
* secure.
*
* A version of {@link PasswordEncoder} which supports Ldap SHA and SSHA (salted-SHA)
* encodings. The values are base-64 encoded and have the label "{SHA}" (or "{SSHA}")
* prepended to the encoded hash. These can be made lower-case in the encoded password, if
* required, by setting the <tt>forceLowerCasePrefix</tt> property to true.
*
* Also supports plain text passwords, so can safely be used in cases when both encoded
* and non-encoded passwords are in use or when a null implementation is required.
*
* @author Luke Taylor
* deprecated Digest based password encoding is not considered secure. Instead use an
* adaptive one way function like BCryptPasswordEncoder, Pbkdf2PasswordEncoder, or
* SCryptPasswordEncoder. Even better use {@link DelegatingPasswordEncoder} which supports
* password upgrades. There are no plans to remove this support. It is deprecated to indicate
* that this is a legacy implementation and using it is considered insecure.
*/
public
class
LdapShaPasswordEncoder
implements
PasswordEncoder
{
// ~ Static fields/initializers
// =====================================================================================
/** The number of bytes in a SHA hash */
private
static
final
int
SHA_LENGTH
=
20
;
private
static
final
String
SSHA_PREFIX
=
"{SSHA}"
;
private
static
final
String
SSHA_PREFIX_LC
=
SSHA_PREFIX
.
toLowerCase
();
private
static
final
String
SHA_PREFIX
=
"{SHA}"
;
private
static
final
String
SHA_PREFIX_LC
=
SHA_PREFIX
.
toLowerCase
();
// ~ Instance fields
// ================================================================================================
private
BytesKeyGenerator
saltGenerator
;
private
boolean
forceLowerCasePrefix
;
// ~ Constructors
// ===================================================================================================
public
LdapShaPasswordEncoder
()
{
this
(
KeyGenerators
.
secureRandom
());
}
public
LdapShaPasswordEncoder
(
BytesKeyGenerator
saltGenerator
)
{
if
(
saltGenerator
==
null
)
{
throw
new
IllegalArgumentException
(
"saltGenerator cannot be null"
);
}
this
.
saltGenerator
=
saltGenerator
;
}
// ~ Methods
// ========================================================================================================
private
byte
[]
combineHashAndSalt
(
byte
[]
hash
,
byte
[]
salt
)
{
if
(
salt
==
null
)
{
return
hash
;
}
byte
[]
hashAndSalt
=
new
byte
[
hash
.
length
+
salt
.
length
];
System
.
arraycopy
(
hash
,
0
,
hashAndSalt
,
0
,
hash
.
length
);
System
.
arraycopy
(
salt
,
0
,
hashAndSalt
,
hash
.
length
,
salt
.
length
);
return
hashAndSalt
;
}
/**
* Calculates the hash of password (and salt bytes, if supplied) and returns a base64
* encoded concatenation of the hash and salt, prefixed with {SHA} (or {SSHA} if salt
* was used).
*
* @param rawPass the password to be encoded.
*
* @return the encoded password in the specified format
*
*/
public
String
encode
(
CharSequence
rawPass
)
{
byte
[]
salt
=
this
.
saltGenerator
.
generateKey
();
return
encode
(
rawPass
,
salt
);
}
private
String
encode
(
CharSequence
rawPassword
,
byte
[]
salt
)
{
MessageDigest
sha
;
try
{
sha
=
MessageDigest
.
getInstance
(
"SHA"
);
sha
.
update
(
Utf8
.
encode
(
rawPassword
));
}
catch
(
java
.
security
.
NoSuchAlgorithmException
e
)
{
throw
new
IllegalStateException
(
"No SHA implementation available!"
);
}
if
(
salt
!=
null
)
{
sha
.
update
(
salt
);
}
byte
[]
hash
=
combineHashAndSalt
(
sha
.
digest
(),
salt
);
String
prefix
;
if
(
salt
==
null
||
salt
.
length
==
0
)
{
prefix
=
forceLowerCasePrefix
?
SHA_PREFIX_LC
:
SHA_PREFIX
;
}
else
{
prefix
=
forceLowerCasePrefix
?
SSHA_PREFIX_LC
:
SSHA_PREFIX
;
}
return
prefix
+
Utf8
.
decode
(
Base64
.
getEncoder
().
encode
(
hash
));
}
private
byte
[]
extractSalt
(
String
encPass
)
{
String
encPassNoLabel
=
encPass
.
substring
(
6
);
byte
[]
hashAndSalt
=
Base64
.
getDecoder
().
decode
(
encPassNoLabel
.
getBytes
());
int
saltLength
=
hashAndSalt
.
length
-
SHA_LENGTH
;
byte
[]
salt
=
new
byte
[
saltLength
];
System
.
arraycopy
(
hashAndSalt
,
SHA_LENGTH
,
salt
,
0
,
saltLength
);
return
salt
;
}
/**
* Checks the validity of an unencoded password against an encoded one in the form
* "{SSHA}sQuQF8vj8Eg2Y1hPdh3bkQhCKQBgjhQI".
*
* @param rawPassword unencoded password to be verified.
* @param encodedPassword the actual SSHA or SHA encoded password
*
* @return true if they match (independent of the case of the prefix).
*/
public
boolean
matches
(
CharSequence
rawPassword
,
String
encodedPassword
)
{
return
matches
(
rawPassword
==
null
?
null
:
rawPassword
.
toString
(),
encodedPassword
);
}
private
boolean
matches
(
String
rawPassword
,
String
encodedPassword
)
{
String
prefix
=
extractPrefix
(
encodedPassword
);
if
(
prefix
==
null
)
{
return
PasswordEncoderUtils
.
equals
(
encodedPassword
,
rawPassword
);
}
byte
[]
salt
;
if
(
prefix
.
equals
(
SSHA_PREFIX
)
||
prefix
.
equals
(
SSHA_PREFIX_LC
))
{
salt
=
extractSalt
(
encodedPassword
);
}
else
if
(!
prefix
.
equals
(
SHA_PREFIX
)
&&
!
prefix
.
equals
(
SHA_PREFIX_LC
))
{
throw
new
IllegalArgumentException
(
"Unsupported password prefix '"
+
prefix
+
"'"
);
}
else
{
// Standard SHA
salt
=
null
;
}
int
startOfHash
=
prefix
.
length
();
String
encodedRawPass
=
encode
(
rawPassword
,
salt
).
substring
(
startOfHash
);
return
PasswordEncoderUtils
.
equals
(
encodedRawPass
,
encodedPassword
.
substring
(
startOfHash
));
}
/**
* Returns the hash prefix or null if there isn't one.
*/
private
String
extractPrefix
(
String
encPass
)
{
if
(!
encPass
.
startsWith
(
"{"
))
{
return
null
;
}
int
secondBrace
=
encPass
.
lastIndexOf
(
'}'
);
if
(
secondBrace
<
0
)
{
throw
new
IllegalArgumentException
(
"Couldn't find closing brace for SHA prefix"
);
}
return
encPass
.
substring
(
0
,
secondBrace
+
1
);
}
public
void
setForceLowerCasePrefix
(
boolean
forceLowerCasePrefix
)
{
this
.
forceLowerCasePrefix
=
forceLowerCasePrefix
;
}
}
maxkey-core/src/main/java/org/maxkey/crypto/password/Md4.java
0 → 100644
浏览文件 @
c2537813
package
org.maxkey.crypto.password
;
/*
* Copyright 2002-2017 the original author or authors.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* https://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
/**
* Implementation of the MD4 message digest derived from the RSA Data Security, Inc, MD4
* Message-Digest Algorithm.
*
* @author Alan Stewart
*/
class
Md4
{
private
static
final
int
BLOCK_SIZE
=
64
;
private
static
final
int
HASH_SIZE
=
16
;
private
final
byte
[]
buffer
=
new
byte
[
BLOCK_SIZE
];
private
int
bufferOffset
;
private
long
byteCount
;
private
final
int
[]
state
=
new
int
[
4
];
private
final
int
[]
tmp
=
new
int
[
16
];
Md4
()
{
reset
();
}
public
void
reset
()
{
bufferOffset
=
0
;
byteCount
=
0
;
state
[
0
]
=
0x67452301
;
state
[
1
]
=
0xEFCDAB89
;
state
[
2
]
=
0x98BADCFE
;
state
[
3
]
=
0x10325476
;
}
public
byte
[]
digest
()
{
byte
[]
resBuf
=
new
byte
[
HASH_SIZE
];
digest
(
resBuf
,
0
,
HASH_SIZE
);
return
resBuf
;
}
private
void
digest
(
byte
[]
buffer
,
int
off
)
{
for
(
int
i
=
0
;
i
<
4
;
i
++)
{
for
(
int
j
=
0
;
j
<
4
;
j
++)
{
buffer
[
off
+
(
i
*
4
+
j
)]
=
(
byte
)
(
state
[
i
]
>>>
(
8
*
j
));
}
}
}
private
void
digest
(
byte
[]
buffer
,
int
offset
,
int
len
)
{
this
.
buffer
[
this
.
bufferOffset
++]
=
(
byte
)
0x80
;
int
lenOfBitLen
=
8
;
int
C
=
BLOCK_SIZE
-
lenOfBitLen
;
if
(
this
.
bufferOffset
>
C
)
{
while
(
this
.
bufferOffset
<
BLOCK_SIZE
)
{
this
.
buffer
[
this
.
bufferOffset
++]
=
(
byte
)
0x00
;
}
update
(
this
.
buffer
,
0
);
this
.
bufferOffset
=
0
;
}
while
(
this
.
bufferOffset
<
C
)
{
this
.
buffer
[
this
.
bufferOffset
++]
=
(
byte
)
0x00
;
}
long
bitCount
=
byteCount
*
8
;
for
(
int
i
=
0
;
i
<
64
;
i
+=
8
)
{
this
.
buffer
[
this
.
bufferOffset
++]
=
(
byte
)
(
bitCount
>>>
(
i
));
}
update
(
this
.
buffer
,
0
);
digest
(
buffer
,
offset
);
}
public
void
update
(
byte
[]
input
,
int
offset
,
int
length
)
{
byteCount
+=
length
;
int
todo
;
while
(
length
>=
(
todo
=
BLOCK_SIZE
-
this
.
bufferOffset
))
{
System
.
arraycopy
(
input
,
offset
,
this
.
buffer
,
this
.
bufferOffset
,
todo
);
update
(
this
.
buffer
,
0
);
length
-=
todo
;
offset
+=
todo
;
this
.
bufferOffset
=
0
;
}
System
.
arraycopy
(
input
,
offset
,
this
.
buffer
,
this
.
bufferOffset
,
length
);
bufferOffset
+=
length
;
}
private
void
update
(
byte
[]
block
,
int
offset
)
{
for
(
int
i
=
0
;
i
<
16
;
i
++)
{
tmp
[
i
]
=
(
block
[
offset
++]
&
0xFF
)
|
(
block
[
offset
++]
&
0xFF
)
<<
8
|
(
block
[
offset
++]
&
0xFF
)
<<
16
|
(
block
[
offset
++]
&
0xFF
)
<<
24
;
}
int
A
=
state
[
0
];
int
B
=
state
[
1
];
int
C
=
state
[
2
];
int
D
=
state
[
3
];
A
=
FF
(
A
,
B
,
C
,
D
,
tmp
[
0
],
3
);
D
=
FF
(
D
,
A
,
B
,
C
,
tmp
[
1
],
7
);
C
=
FF
(
C
,
D
,
A
,
B
,
tmp
[
2
],
11
);
B
=
FF
(
B
,
C
,
D
,
A
,
tmp
[
3
],
19
);
A
=
FF
(
A
,
B
,
C
,
D
,
tmp
[
4
],
3
);
D
=
FF
(
D
,
A
,
B
,
C
,
tmp
[
5
],
7
);
C
=
FF
(
C
,
D
,
A
,
B
,
tmp
[
6
],
11
);
B
=
FF
(
B
,
C
,
D
,
A
,
tmp
[
7
],
19
);
A
=
FF
(
A
,
B
,
C
,
D
,
tmp
[
8
],
3
);
D
=
FF
(
D
,
A
,
B
,
C
,
tmp
[
9
],
7
);
C
=
FF
(
C
,
D
,
A
,
B
,
tmp
[
10
],
11
);
B
=
FF
(
B
,
C
,
D
,
A
,
tmp
[
11
],
19
);
A
=
FF
(
A
,
B
,
C
,
D
,
tmp
[
12
],
3
);
D
=
FF
(
D
,
A
,
B
,
C
,
tmp
[
13
],
7
);
C
=
FF
(
C
,
D
,
A
,
B
,
tmp
[
14
],
11
);
B
=
FF
(
B
,
C
,
D
,
A
,
tmp
[
15
],
19
);
A
=
GG
(
A
,
B
,
C
,
D
,
tmp
[
0
],
3
);
D
=
GG
(
D
,
A
,
B
,
C
,
tmp
[
4
],
5
);
C
=
GG
(
C
,
D
,
A
,
B
,
tmp
[
8
],
9
);
B
=
GG
(
B
,
C
,
D
,
A
,
tmp
[
12
],
13
);
A
=
GG
(
A
,
B
,
C
,
D
,
tmp
[
1
],
3
);
D
=
GG
(
D
,
A
,
B
,
C
,
tmp
[
5
],
5
);
C
=
GG
(
C
,
D
,
A
,
B
,
tmp
[
9
],
9
);
B
=
GG
(
B
,
C
,
D
,
A
,
tmp
[
13
],
13
);
A
=
GG
(
A
,
B
,
C
,
D
,
tmp
[
2
],
3
);
D
=
GG
(
D
,
A
,
B
,
C
,
tmp
[
6
],
5
);
C
=
GG
(
C
,
D
,
A
,
B
,
tmp
[
10
],
9
);
B
=
GG
(
B
,
C
,
D
,
A
,
tmp
[
14
],
13
);
A
=
GG
(
A
,
B
,
C
,
D
,
tmp
[
3
],
3
);
D
=
GG
(
D
,
A
,
B
,
C
,
tmp
[
7
],
5
);
C
=
GG
(
C
,
D
,
A
,
B
,
tmp
[
11
],
9
);
B
=
GG
(
B
,
C
,
D
,
A
,
tmp
[
15
],
13
);
A
=
HH
(
A
,
B
,
C
,
D
,
tmp
[
0
],
3
);
D
=
HH
(
D
,
A
,
B
,
C
,
tmp
[
8
],
9
);
C
=
HH
(
C
,
D
,
A
,
B
,
tmp
[
4
],
11
);
B
=
HH
(
B
,
C
,
D
,
A
,
tmp
[
12
],
15
);
A
=
HH
(
A
,
B
,
C
,
D
,
tmp
[
2
],
3
);
D
=
HH
(
D
,
A
,
B
,
C
,
tmp
[
10
],
9
);
C
=
HH
(
C
,
D
,
A
,
B
,
tmp
[
6
],
11
);
B
=
HH
(
B
,
C
,
D
,
A
,
tmp
[
14
],
15
);
A
=
HH
(
A
,
B
,
C
,
D
,
tmp
[
1
],
3
);
D
=
HH
(
D
,
A
,
B
,
C
,
tmp
[
9
],
9
);
C
=
HH
(
C
,
D
,
A
,
B
,
tmp
[
5
],
11
);
B
=
HH
(
B
,
C
,
D
,
A
,
tmp
[
13
],
15
);
A
=
HH
(
A
,
B
,
C
,
D
,
tmp
[
3
],
3
);
D
=
HH
(
D
,
A
,
B
,
C
,
tmp
[
11
],
9
);
C
=
HH
(
C
,
D
,
A
,
B
,
tmp
[
7
],
11
);
B
=
HH
(
B
,
C
,
D
,
A
,
tmp
[
15
],
15
);
state
[
0
]
+=
A
;
state
[
1
]
+=
B
;
state
[
2
]
+=
C
;
state
[
3
]
+=
D
;
}
private
int
FF
(
int
a
,
int
b
,
int
c
,
int
d
,
int
x
,
int
s
)
{
int
t
=
a
+
((
b
&
c
)
|
(~
b
&
d
))
+
x
;
return
t
<<
s
|
t
>>>
(
32
-
s
);
}
private
int
GG
(
int
a
,
int
b
,
int
c
,
int
d
,
int
x
,
int
s
)
{
int
t
=
a
+
((
b
&
(
c
|
d
))
|
(
c
&
d
))
+
x
+
0x5A827999
;
return
t
<<
s
|
t
>>>
(
32
-
s
);
}
private
int
HH
(
int
a
,
int
b
,
int
c
,
int
d
,
int
x
,
int
s
)
{
int
t
=
a
+
(
b
^
c
^
d
)
+
x
+
0x6ED9EBA1
;
return
t
<<
s
|
t
>>>
(
32
-
s
);
}
}
maxkey-core/src/main/java/org/maxkey/crypto/password/Md4PasswordEncoder.java
0 → 100644
浏览文件 @
c2537813
package
org.maxkey.crypto.password
;
/*
* Copyright 2002-2018 the original author or authors.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* https://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
import
org.springframework.security.crypto.codec.Hex
;
import
org.springframework.security.crypto.codec.Utf8
;
import
org.springframework.security.crypto.keygen.Base64StringKeyGenerator
;
import
org.springframework.security.crypto.keygen.StringKeyGenerator
;
import
org.springframework.security.crypto.password.PasswordEncoder
;
import
java.util.Base64
;
/**
* This {@link PasswordEncoder} is provided for legacy purposes only and is not considered secure.
*
* Encodes passwords using MD4. The general format of the password is:
*
* <pre>
* s = salt == null ? "" : "{" + salt + "}"
* s + md4(password + s)
* </pre>
*
* Such that "salt" is the salt, md4 is the digest method, and password is the actual
* password. For example with a password of "password", and a salt of
* "thisissalt":
*
* <pre>
* String s = salt == null ? "" : "{" + salt + "}";
* s + md4(password + s)
* "{thisissalt}" + md4(password + "{thisissalt}")
* "{thisissalt}6cc7924dad12ade79dfb99e424f25260"
* </pre>
*
* If the salt does not exist, then omit "{salt}" like this:
*
* <pre>
* md4(password)
* </pre>
*
* If the salt is an empty String, then only use "{}" like this:
*
* <pre>
* "{}" + md4(password + "{}")
* </pre>
*
* The format is intended to work with the Md4PasswordEncoder that was found in the
* Spring Security core module. However, the passwords will need to be migrated to include
* any salt with the password since this API provides Salt internally vs making it the
* responsibility of the user. To migrate passwords from the SaltSource use the following:
*
* <pre>
* String salt = saltSource.getSalt(user);
* String s = salt == null ? null : "{" + salt + "}";
* String migratedPassword = s + user.getPassword();
* </pre>
*
* @author Ray Krueger
* @author Luke Taylor
* @author Rob winch
* @since 5.0
* deprecated Digest based password encoding is not considered secure. Instead use an
* adaptive one way function like BCryptPasswordEncoder, Pbkdf2PasswordEncoder, or
* SCryptPasswordEncoder. Even better use {@link DelegatingPasswordEncoder} which supports
* password upgrades. There are no plans to remove this support. It is deprecated to indicate
* that this is a legacy implementation and using it is considered insecure.
*/
public
class
Md4PasswordEncoder
implements
PasswordEncoder
{
private
static
final
String
PREFIX
=
"{"
;
private
static
final
String
SUFFIX
=
"}"
;
private
StringKeyGenerator
saltGenerator
=
new
Base64StringKeyGenerator
();
private
boolean
encodeHashAsBase64
;
public
void
setEncodeHashAsBase64
(
boolean
encodeHashAsBase64
)
{
this
.
encodeHashAsBase64
=
encodeHashAsBase64
;
}
/**
* Encodes the rawPass using a MessageDigest. If a salt is specified it will be merged
* with the password before encoding.
*
* @param rawPassword The plain text password
* @return Hex string of password digest (or base64 encoded string if
* encodeHashAsBase64 is enabled.
*/
public
String
encode
(
CharSequence
rawPassword
)
{
String
salt
=
PREFIX
+
this
.
saltGenerator
.
generateKey
()
+
SUFFIX
;
return
digest
(
salt
,
rawPassword
);
}
private
String
digest
(
String
salt
,
CharSequence
rawPassword
)
{
if
(
rawPassword
==
null
)
{
rawPassword
=
""
;
}
String
saltedPassword
=
rawPassword
+
salt
;
byte
[]
saltedPasswordBytes
=
Utf8
.
encode
(
saltedPassword
);
Md4
md4
=
new
Md4
();
md4
.
update
(
saltedPasswordBytes
,
0
,
saltedPasswordBytes
.
length
);
byte
[]
digest
=
md4
.
digest
();
String
encoded
=
encode
(
digest
);
return
salt
+
encoded
;
}
private
String
encode
(
byte
[]
digest
)
{
if
(
this
.
encodeHashAsBase64
)
{
return
Utf8
.
decode
(
Base64
.
getEncoder
().
encode
(
digest
));
}
else
{
return
new
String
(
Hex
.
encode
(
digest
));
}
}
/**
* Takes a previously encoded password and compares it with a rawpassword after mixing
* in the salt and encoding that value
*
* @param rawPassword plain text password
* @param encodedPassword previously encoded password
* @return true or false
*/
public
boolean
matches
(
CharSequence
rawPassword
,
String
encodedPassword
)
{
String
salt
=
extractSalt
(
encodedPassword
);
String
rawPasswordEncoded
=
digest
(
salt
,
rawPassword
);
return
PasswordEncoderUtils
.
equals
(
encodedPassword
.
toString
(),
rawPasswordEncoded
);
}
private
String
extractSalt
(
String
prefixEncodedPassword
)
{
int
start
=
prefixEncodedPassword
.
indexOf
(
PREFIX
);
if
(
start
!=
0
)
{
return
""
;
}
int
end
=
prefixEncodedPassword
.
indexOf
(
SUFFIX
,
start
);
if
(
end
<
0
)
{
return
""
;
}
return
prefixEncodedPassword
.
substring
(
start
,
end
+
1
);
}
}
maxkey-core/src/main/java/org/maxkey/crypto/password/MessageDigestPasswordEncoder.java
0 → 100644
浏览文件 @
c2537813
package
org.maxkey.crypto.password
;
/*
* Copyright 2002-2018 the original author or authors.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* https://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
import
org.springframework.security.crypto.codec.Hex
;
import
org.springframework.security.crypto.codec.Utf8
;
import
org.springframework.security.crypto.keygen.Base64StringKeyGenerator
;
import
org.springframework.security.crypto.keygen.StringKeyGenerator
;
import
org.springframework.security.crypto.password.PasswordEncoder
;
import
java.security.MessageDigest
;
import
java.util.Base64
;
/**
* This {@link PasswordEncoder} is provided for legacy purposes only and is not considered secure.
*
* Encodes passwords using the passed in {@link MessageDigest}.
*
* The general format of the password is:
*
* <pre>
* s = salt == null ? "" : "{" + salt + "}"
* s + digest(password + s)
* </pre>
*
* Such that "salt" is the salt, digest is the digest method, and password is the actual
* password. For example when using MD5, a password of "password", and a salt of
* "thisissalt":
*
* <pre>
* String s = salt == null ? "" : "{" + salt + "}";
* s + md5(password + s)
* "{thisissalt}" + md5(password + "{thisissalt}")
* "{thisissalt}2a4e7104c2780098f50ed5a84bb2323d"
* </pre>
*
* If the salt does not exist, then omit "{salt}" like this:
*
* <pre>
* digest(password)
* </pre>
*
* If the salt is an empty String, then only use "{}" like this:
*
* <pre>
* "{}" + digest(password + "{}")
* </pre>
*
* The format is intended to work with the DigestPasswordEncoder that was found in the
* Spring Security core module. However, the passwords will need to be migrated to include
* any salt with the password since this API provides Salt internally vs making it the
* responsibility of the user. To migrate passwords from the SaltSource use the following:
*
* <pre>
* String salt = saltSource.getSalt(user);
* String s = salt == null ? null : "{" + salt + "}";
* String migratedPassword = s + user.getPassword();
* </pre>
*
* @author Ray Krueger
* @author Luke Taylor
* @author Rob Winch
* @since 5.0
* @deprecated Digest based password encoding is not considered secure. Instead use an
* adaptive one way function like BCryptPasswordEncoder, Pbkdf2PasswordEncoder, or
* SCryptPasswordEncoder. Even better use {@link DelegatingPasswordEncoder} which supports
* password upgrades. There are no plans to remove this support. It is deprecated to indicate
* that this is a legacy implementation and using it is considered insecure.
*/
public
class
MessageDigestPasswordEncoder
implements
PasswordEncoder
{
private
static
final
String
PREFIX
=
"{"
;
private
static
final
String
SUFFIX
=
"}"
;
private
StringKeyGenerator
saltGenerator
=
new
Base64StringKeyGenerator
();
private
boolean
encodeHashAsBase64
;
private
Digester
digester
;
/**
* The digest algorithm to use Supports the named
* <a href="https://java.sun.com/j2se/1.4.2/docs/guide/security/CryptoSpec.html#AppA">
* Message Digest Algorithms</a> in the Java environment.
*
* @param algorithm
*/
public
MessageDigestPasswordEncoder
(
String
algorithm
)
{
this
.
digester
=
new
Digester
(
algorithm
,
1
);
}
public
void
setEncodeHashAsBase64
(
boolean
encodeHashAsBase64
)
{
this
.
encodeHashAsBase64
=
encodeHashAsBase64
;
}
/**
* Encodes the rawPass using a MessageDigest. If a salt is specified it will be merged
* with the password before encoding.
*
* @param rawPassword The plain text password
* @return Hex string of password digest (or base64 encoded string if
* encodeHashAsBase64 is enabled.
*/
public
String
encode
(
CharSequence
rawPassword
)
{
String
salt
=
PREFIX
+
this
.
saltGenerator
.
generateKey
()
+
SUFFIX
;
return
digest
(
salt
,
rawPassword
);
}
private
String
digest
(
String
salt
,
CharSequence
rawPassword
)
{
String
saltedPassword
=
rawPassword
+
salt
;
byte
[]
digest
=
this
.
digester
.
digest
(
Utf8
.
encode
(
saltedPassword
));
String
encoded
=
encode
(
digest
);
return
salt
+
encoded
;
}
private
String
encode
(
byte
[]
digest
)
{
if
(
this
.
encodeHashAsBase64
)
{
return
Utf8
.
decode
(
Base64
.
getEncoder
().
encode
(
digest
));
}
else
{
return
new
String
(
Hex
.
encode
(
digest
));
}
}
/**
* Takes a previously encoded password and compares it with a rawpassword after mixing
* in the salt and encoding that value
*
* @param rawPassword plain text password
* @param encodedPassword previously encoded password
* @return true or false
*/
public
boolean
matches
(
CharSequence
rawPassword
,
String
encodedPassword
)
{
String
salt
=
extractSalt
(
encodedPassword
);
String
rawPasswordEncoded
=
digest
(
salt
,
rawPassword
);
return
PasswordEncoderUtils
.
equals
(
encodedPassword
.
toString
(),
rawPasswordEncoded
);
}
/**
* Sets the number of iterations for which the calculated hash value should be
* "stretched". If this is greater than one, the initial digest is calculated, the
* digest function will be called repeatedly on the result for the additional number
* of iterations.
*
* @param iterations the number of iterations which will be executed on the hashed
* password/salt value. Defaults to 1.
*/
public
void
setIterations
(
int
iterations
)
{
this
.
digester
.
setIterations
(
iterations
);
}
private
String
extractSalt
(
String
prefixEncodedPassword
)
{
int
start
=
prefixEncodedPassword
.
indexOf
(
PREFIX
);
if
(
start
!=
0
)
{
return
""
;
}
int
end
=
prefixEncodedPassword
.
indexOf
(
SUFFIX
,
start
);
if
(
end
<
0
)
{
return
""
;
}
return
prefixEncodedPassword
.
substring
(
start
,
end
+
1
);
}
}
maxkey-core/src/main/java/org/maxkey/crypto/password/NoOpPasswordEncoder.java
0 → 100644
浏览文件 @
c2537813
package
org.maxkey.crypto.password
;
import
org.springframework.security.crypto.password.PasswordEncoder
;
/*
* Copyright 2011-2016 the original author or authors.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* https://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
/**
* This {@link PasswordEncoder} is provided for legacy and testing purposes only and is
* not considered secure.
*
* A password encoder that does nothing. Useful for testing where working with plain text
* passwords may be preferred.
*
* @author Keith Donald
* deprecated This PasswordEncoder is not secure. Instead use an
* adaptive one way function like BCryptPasswordEncoder, Pbkdf2PasswordEncoder, or
* SCryptPasswordEncoder. Even better use {@link DelegatingPasswordEncoder} which supports
* password upgrades. There are no plans to remove this support. It is deprecated to indicate that
* this is a legacy implementation and using it is considered insecure.
*/
public
final
class
NoOpPasswordEncoder
implements
PasswordEncoder
{
public
String
encode
(
CharSequence
rawPassword
)
{
return
rawPassword
.
toString
();
}
public
boolean
matches
(
CharSequence
rawPassword
,
String
encodedPassword
)
{
return
rawPassword
.
toString
().
equals
(
encodedPassword
);
}
/**
* Get the singleton {@link NoOpPasswordEncoder}.
*/
public
static
PasswordEncoder
getInstance
()
{
return
INSTANCE
;
}
private
static
final
PasswordEncoder
INSTANCE
=
new
NoOpPasswordEncoder
();
private
NoOpPasswordEncoder
()
{
}
}
maxkey-core/src/main/java/org/maxkey/crypto/password/PasswordEncoderUtils.java
0 → 100644
浏览文件 @
c2537813
package
org.maxkey.crypto.password
;
import
org.springframework.security.crypto.codec.Utf8
;
import
java.security.MessageDigest
;
/**
* Utility for constant time comparison to prevent against timing attacks.
*
* @author Rob Winch
*/
public
class
PasswordEncoderUtils
{
/**
* Constant time comparison to prevent against timing attacks.
* @param expected
* @param actual
* @return
*/
static
boolean
equals
(
String
expected
,
String
actual
)
{
byte
[]
expectedBytes
=
bytesUtf8
(
expected
);
byte
[]
actualBytes
=
bytesUtf8
(
actual
);
return
MessageDigest
.
isEqual
(
expectedBytes
,
actualBytes
);
}
private
static
byte
[]
bytesUtf8
(
String
s
)
{
if
(
s
==
null
)
{
return
null
;
}
return
Utf8
.
encode
(
s
);
// need to check if Utf8.encode() runs in constant time (probably not). This may leak length of string.
}
private
PasswordEncoderUtils
()
{
}
}
编辑
预览
Markdown
is supported
0%
请重试
或
添加新附件
.
添加附件
取消
You are about to add
0
people
to the discussion. Proceed with caution.
先完成此消息的编辑!
取消
想要评论请
注册
或
登录