Skip to content
Projects
Groups
Snippets
Help
Loading...
Help
Submit feedback
Sign in / Register
Toggle navigation
Y
yzg-util
Project
Project
Details
Activity
Releases
Cycle Analytics
Repository
Repository
Files
Commits
Branches
Tags
Contributors
Graph
Compare
Charts
Issues
0
Issues
0
List
Board
Labels
Milestones
Merge Requests
0
Merge Requests
0
CI / CD
CI / CD
Pipelines
Jobs
Schedules
Charts
Wiki
Wiki
Snippets
Snippets
Members
Members
Collapse sidebar
Close sidebar
Activity
Graph
Charts
Create a new issue
Jobs
Commits
Issue Boards
Open sidebar
YZG
yzg-util
Commits
d048f8a5
Commit
d048f8a5
authored
Jun 27, 2023
by
yanzg
Browse files
Options
Browse Files
Download
Email Patches
Plain Diff
表结构修改
parent
10448834
Changes
2
Hide whitespace changes
Inline
Side-by-side
Showing
2 changed files
with
308 additions
and
94 deletions
+308
-94
RsaHelper.java
.../src/main/java/com/yanzuoguang/util/helper/RsaHelper.java
+291
-89
TestRsa.java
yzg-util-base/src/test/java/helper/TestRsa.java
+17
-5
No files found.
yzg-util-base/src/main/java/com/yanzuoguang/util/helper/RsaHelper.java
View file @
d048f8a5
package
com
.
yanzuoguang
.
util
.
helper
;
import
com.yanzuoguang.util.YzgError
;
import
com.yanzuoguang.util.
contants.SystemContants
;
import
com.yanzuoguang.util.
exception.RuntimeCodeException
;
import
com.yanzuoguang.util.log.Log
;
import
org.springframework.util.Base64Utils
;
import
javax.crypto.Cipher
;
import
java.io.ByteArrayOutputStream
;
import
java.nio.charset.StandardCharsets
;
import
java.security.*
;
import
java.security.interfaces.RSAPrivateKey
;
import
java.security.interfaces.RSAPublicKey
;
...
...
@@ -79,8 +80,6 @@ public final class RsaHelper {
/**
* 生成密钥对
*
* @throws Exception
*/
public
static
void
generatorKeyPair
()
{
try
{
...
...
@@ -94,7 +93,7 @@ public final class RsaHelper {
/**
* 生成密钥对
*
* @
throws Exception
* @
return 返回值
*/
public
static
RsaInfo
generatorRsa
()
{
try
{
...
...
@@ -104,10 +103,7 @@ public final class RsaHelper {
RSAPublicKey
rsaPublicKey
=
(
RSAPublicKey
)
keyPair
.
getPublic
();
RSAPrivateKey
rsaPrivateKey
=
(
RSAPrivateKey
)
keyPair
.
getPrivate
();
String
publicKey
=
encodeBase64
(
rsaPublicKey
.
getEncoded
());
String
privateKey
=
encodeBase64
(
rsaPrivateKey
.
getEncoded
());
return
new
RsaInfo
(
publicKey
,
privateKey
);
return
new
RsaInfo
(
rsaPublicKey
,
rsaPrivateKey
);
}
catch
(
Exception
ex
)
{
throw
new
RuntimeException
(
ex
);
}
...
...
@@ -116,10 +112,9 @@ public final class RsaHelper {
/**
* 获取公钥
*
* @return
* @throws Exception
* @return 公钥
*/
private
static
PublicKey
getPublicKey
(
String
publicKey
)
throws
Exception
{
private
static
PublicKey
getPublicKey
(
String
publicKey
)
{
try
{
X509EncodedKeySpec
publicKeySpec
=
new
X509EncodedKeySpec
(
decodeBase64
(
publicKey
));
KeyFactory
keyFactory
=
KeyFactory
.
getInstance
(
ALGORITHM_RSA
);
...
...
@@ -132,8 +127,7 @@ public final class RsaHelper {
/**
* 获取私钥
*
* @return
* @throws Exception
* @return 私钥
*/
private
static
PrivateKey
getPrivateKey
(
String
privateKey
)
{
try
{
...
...
@@ -146,26 +140,31 @@ public final class RsaHelper {
}
/**
* 公钥加密
* 公钥加密
,需要用私钥解密
*
* @param source
* @param publicKeyStr
* @return
* @throws Exception
* @param source 源代码
* @param publicKeyStr 公钥base64字符串
* @return 返回值
*/
public
static
String
encryptionByPublicKey
(
String
source
,
String
publicKeyStr
)
{
PublicKey
publicKey
=
getPublicKey
(
publicKeyStr
);
return
encryptionByPublicKey
(
source
,
publicKey
);
}
/**
* 公钥加密,需要用私钥解密
*
* @param source 源代码
* @param publicKey 公钥
* @return 返回值
*/
public
static
String
encryptionByPublicKey
(
String
source
,
PublicKey
publicKey
)
{
try
{
PublicKey
publicKey
=
getPublicKey
(
publicKeyStr
);
Cipher
cipher
=
Cipher
.
getInstance
(
publicKey
.
getAlgorithm
());
cipher
.
init
(
Cipher
.
ENCRYPT_MODE
,
publicKey
);
byte
[]
bytes
=
source
.
getBytes
(
SystemContants
.
UTF8
);
byte
[]
to
=
handle
(
bytes
,
MAX_ENCRYPT_BLOCK
,
new
HandleBytes
()
{
@Override
public
byte
[]
handle
(
byte
[]
from
,
int
offset
,
int
len
)
throws
Exception
{
return
cipher
.
doFinal
(
from
,
offset
,
len
);
}
});
byte
[]
bytes
=
source
.
getBytes
(
StandardCharsets
.
UTF_8
);
byte
[]
to
=
handle
(
bytes
,
MAX_ENCRYPT_BLOCK
,
cipher:
:
doFinal
);
return
encodeBase64
(
to
);
}
catch
(
Exception
ex
)
{
throw
new
RuntimeException
(
ex
);
...
...
@@ -173,27 +172,33 @@ public final class RsaHelper {
}
/**
* 公钥解密
* 公钥解密
,解密私钥加密的字符串
*
* @param target
* @throws Exception
* @param target 私钥加密的值
* @param publicKeyStr 公钥
* @return 返回值
*/
public
static
String
decryptionByPublicKey
(
String
target
,
String
publicKeyStr
)
{
PublicKey
publicKey
=
getPublicKey
(
publicKeyStr
);
return
decryptionByPublicKey
(
target
,
publicKey
);
}
/**
* 公钥解密,解密私钥加密的字符串
*
* @param target 私钥加密的值
* @param publicKey 公钥
* @return 返回值
*/
public
static
String
decryptionByPublicKey
(
String
target
,
PublicKey
publicKey
)
{
try
{
byte
[]
bytes
=
decodeBase64
(
target
);
PublicKey
publicKey
=
getPublicKey
(
publicKeyStr
);
Cipher
cipher
=
Cipher
.
getInstance
(
publicKey
.
getAlgorithm
());
cipher
.
init
(
Cipher
.
DECRYPT_MODE
,
publicKey
);
byte
[]
to
=
handle
(
bytes
,
MAX_DECRYPT_BLOCK
,
new
HandleBytes
()
{
@Override
public
byte
[]
handle
(
byte
[]
from
,
int
offset
,
int
len
)
throws
Exception
{
return
cipher
.
doFinal
(
from
,
offset
,
len
);
}
});
return
new
String
(
to
,
SystemContants
.
UTF8
);
byte
[]
to
=
handle
(
bytes
,
MAX_DECRYPT_BLOCK
,
cipher:
:
doFinal
);
return
new
String
(
to
,
StandardCharsets
.
UTF_8
);
}
catch
(
Exception
ex
)
{
throw
new
RuntimeException
(
ex
);
}
...
...
@@ -202,76 +207,97 @@ public final class RsaHelper {
/**
* 公钥验证签名
*
* @return
* @throws Exception
* @param target 私钥加密的值
* @param sign 签名
* @param publicKeyStr 公钥
* @return 返回值
*/
public
static
void
verifyByPublicKey
(
String
target
,
String
sign
,
String
publicKeyStr
)
{
public
static
boolean
verifyByPublicKey
(
String
target
,
String
sign
,
String
publicKeyStr
)
{
PublicKey
publicKey
=
getPublicKey
(
publicKeyStr
);
return
verifyByPublicKey
(
target
,
sign
,
publicKey
);
}
/**
* 公钥验证签名
*
* @param target 私钥加密的值
* @param sign 签名
* @param publicKey 公钥
* @return 返回值
*/
public
static
boolean
verifyByPublicKey
(
String
target
,
String
sign
,
PublicKey
publicKey
)
{
try
{
PublicKey
publicKey
=
getPublicKey
(
publicKeyStr
);
Signature
signature
=
Signature
.
getInstance
(
ALGORITHM_SIGN
);
signature
.
initVerify
(
publicKey
);
signature
.
update
(
target
.
getBytes
(
SystemContants
.
UTF8
));
if
(
signature
.
verify
(
decodeBase64
(
sign
)))
{
Log
.
info
(
RsaHelper
.
class
,
"sign true"
);
}
else
{
Log
.
info
(
RsaHelper
.
class
,
"sign false"
);
}
signature
.
update
(
target
.
getBytes
(
StandardCharsets
.
UTF_8
));
return
signature
.
verify
(
decodeBase64
(
sign
));
}
catch
(
Exception
ex
)
{
throw
new
RuntimeException
(
ex
);
}
}
/**
* 私钥加密
* 私钥加密
,要用公钥解密
*
* @param source
* @
return
* @
throws Exception
* @param source
原字符串
* @
param privateKeyStr 私钥base64字符串
* @
return 返回值
*/
public
static
String
encryptionByPrivateKey
(
String
source
,
String
privateKeyStr
)
{
PrivateKey
privateKey
=
getPrivateKey
(
privateKeyStr
);
return
encryptionByPrivateKey
(
source
,
privateKey
);
}
/**
* 私钥加密,要用公钥解密
*
* @param source 原字符串
* @param privateKey 私钥
* @return 返回值
*/
public
static
String
encryptionByPrivateKey
(
String
source
,
PrivateKey
privateKey
)
{
try
{
byte
[]
bytes
=
source
.
getBytes
(
S
ystemContants
.
UTF
8
);
byte
[]
bytes
=
source
.
getBytes
(
S
tandardCharsets
.
UTF_
8
);
PrivateKey
privateKey
=
getPrivateKey
(
privateKeyStr
);
Cipher
cipher
=
Cipher
.
getInstance
(
privateKey
.
getAlgorithm
());
cipher
.
init
(
Cipher
.
ENCRYPT_MODE
,
privateKey
);
byte
[]
to
=
handle
(
bytes
,
MAX_ENCRYPT_BLOCK
,
new
HandleBytes
()
{
@Override
public
byte
[]
handle
(
byte
[]
from
,
int
offset
,
int
len
)
throws
Exception
{
return
cipher
.
doFinal
(
from
,
offset
,
len
);
}
});
byte
[]
to
=
handle
(
bytes
,
MAX_ENCRYPT_BLOCK
,
cipher:
:
doFinal
);
String
target
=
encodeBase64
(
to
);
return
target
;
return
encodeBase64
(
to
);
}
catch
(
Exception
ex
)
{
throw
new
RuntimeException
(
ex
);
}
}
/**
* 私钥解密
* 私钥解密
,解密公钥加密后的字符串
*
* @param target
* @throws Exception
* @param target 加密字符
* @param privateKeyStr base64位的私钥
* @return 返回值
*/
public
static
String
decryptionByPrivateKey
(
String
target
,
String
privateKeyStr
)
{
PrivateKey
privateKey
=
getPrivateKey
(
privateKeyStr
);
return
decryptionByPrivateKey
(
target
,
privateKey
);
}
/**
* 私钥解密,解密公钥加密后的字符串
*
* @param target 加密字符
* @param privateKey 私钥
* @return 返回值
*/
public
static
String
decryptionByPrivateKey
(
String
target
,
PrivateKey
privateKey
)
{
try
{
byte
[]
bytes
=
decodeBase64
(
target
);
PrivateKey
privateKey
=
getPrivateKey
(
privateKeyStr
);
Cipher
cipher
=
Cipher
.
getInstance
(
privateKey
.
getAlgorithm
());
cipher
.
init
(
Cipher
.
DECRYPT_MODE
,
privateKey
);
byte
[]
to
=
handle
(
bytes
,
MAX_DECRYPT_BLOCK
,
new
HandleBytes
()
{
@Override
public
byte
[]
handle
(
byte
[]
from
,
int
offset
,
int
len
)
throws
Exception
{
return
cipher
.
doFinal
(
from
,
offset
,
len
);
}
});
byte
[]
to
=
handle
(
bytes
,
MAX_DECRYPT_BLOCK
,
cipher:
:
doFinal
);
return
new
String
(
to
,
S
ystemContants
.
UTF
8
);
return
new
String
(
to
,
S
tandardCharsets
.
UTF_
8
);
}
catch
(
Exception
ex
)
{
throw
YzgError
.
getRuntimeException
(
ex
,
"056"
,
ex
.
getMessage
());
}
...
...
@@ -280,18 +306,28 @@ public final class RsaHelper {
/**
* 私钥签名
*
* @param target
* @
return
* @
throws Exception
* @param target
原字符
* @
param privateKeyStr 私钥
* @
return 签名值
*/
public
static
String
signByPrivateKey
(
String
target
,
String
privateKeyStr
)
{
PrivateKey
privateKey
=
getPrivateKey
(
privateKeyStr
);
return
signByPrivateKey
(
target
,
privateKey
);
}
/**
* 私钥签名
*
* @param target 原字符串
* @param privateKey 私钥
* @return 签名值
*/
public
static
String
signByPrivateKey
(
String
target
,
PrivateKey
privateKey
)
{
try
{
PrivateKey
privateKey
=
getPrivateKey
(
privateKeyStr
);
Signature
signature
=
Signature
.
getInstance
(
ALGORITHM_SIGN
);
signature
.
initSign
(
privateKey
);
signature
.
update
(
target
.
getBytes
(
SystemContants
.
UTF8
));
String
sign
=
encodeBase64
(
signature
.
sign
());
return
sign
;
signature
.
update
(
target
.
getBytes
(
StandardCharsets
.
UTF_8
));
return
encodeBase64
(
signature
.
sign
());
}
catch
(
Exception
ex
)
{
throw
new
RuntimeException
(
ex
);
}
...
...
@@ -300,14 +336,13 @@ public final class RsaHelper {
/**
* base64编码
*
* @param source
* @return
* @throws Exception
* @param source 原字符
* @return 编码
*/
public
static
String
encodeBase64
(
byte
[]
source
)
{
try
{
byte
[]
to
=
Base64Utils
.
encode
(
source
);
return
new
String
(
to
,
S
ystemContants
.
UTF
8
);
return
new
String
(
to
,
S
tandardCharsets
.
UTF_
8
);
}
catch
(
Exception
ex
)
{
throw
new
RuntimeException
(
ex
);
}
...
...
@@ -316,20 +351,132 @@ public final class RsaHelper {
/**
* Base64解码
*
* @param target
* @return
* @throws Exception
* @param target 原字符
* @return 解码
*/
public
static
byte
[]
decodeBase64
(
String
target
)
{
try
{
byte
[]
from
=
target
.
getBytes
(
S
ystemContants
.
UTF
8
);
byte
[]
from
=
target
.
getBytes
(
S
tandardCharsets
.
UTF_
8
);
return
Base64Utils
.
decode
(
from
);
}
catch
(
Exception
ex
)
{
throw
new
RuntimeException
(
ex
);
}
}
/**
* 解密信息
*
* @param rsaJsonPwd 加密后的json字符串
* @param publicKey 公钥
* @param timeout 超时时间
* @return 返回值
*/
public
static
String
decodeRsaValueInfo
(
String
rsaJsonPwd
,
PublicKey
publicKey
,
long
timeout
)
{
String
rsaJson
=
decryptionByPublicKey
(
rsaJsonPwd
,
publicKey
);
RsaValueInfo
rsaValueInfo
=
JsonHelper
.
deserialize
(
rsaJson
,
RsaValueInfo
.
class
);
long
splitTime
=
rsaValueInfo
.
getT
()
-
System
.
currentTimeMillis
()
/
1000
;
if
(
splitTime
>
timeout
)
{
throw
new
RuntimeCodeException
(
"056"
,
"解密超时"
);
}
return
rsaValueInfo
.
getV
();
}
/**
* 解密信息
*
* @param rsaJsonPwd 加密后的json字符串
* @param privateKey 私钥
* @param timeout 超时时间
* @return 返回值
*/
public
static
String
decodeRsaValueInfo
(
String
rsaJsonPwd
,
PrivateKey
privateKey
,
long
timeout
)
{
String
rsaJson
=
decryptionByPrivateKey
(
rsaJsonPwd
,
privateKey
);
RsaValueInfo
rsaValueInfo
=
JsonHelper
.
deserialize
(
rsaJson
,
RsaValueInfo
.
class
);
long
splitTime
=
rsaValueInfo
.
getT
()
-
System
.
currentTimeMillis
()
/
1000
;
if
(
splitTime
>
timeout
)
{
throw
new
RuntimeCodeException
(
"056"
,
"解密超时"
);
}
return
rsaValueInfo
.
getV
();
}
/**
* 解密信息
*
* @param value 私钥
* @param publicKey 公钥
* @return 返回值
*/
public
static
String
encodeRsaValueInfo
(
String
value
,
PublicKey
publicKey
)
{
RsaValueInfo
info
=
new
RsaValueInfo
(
value
);
String
json
=
JsonHelper
.
serialize
(
info
);
return
encryptionByPublicKey
(
json
,
publicKey
);
}
/**
* 解密信息
*
* @param value 私钥
* @param privateKey 私钥
* @return 返回值
*/
public
static
String
encodeRsaValueInfo
(
String
value
,
PrivateKey
privateKey
)
{
RsaValueInfo
info
=
new
RsaValueInfo
(
value
);
String
json
=
JsonHelper
.
serialize
(
info
);
return
encryptionByPrivateKey
(
json
,
privateKey
);
}
/**
* RSA加密值信息
*/
public
static
class
RsaValueInfo
{
/**
* 加密值
*/
private
String
v
;
/**
* Linux时间错(秒)
*/
private
long
t
=
System
.
currentTimeMillis
()
/
1000
;
public
RsaValueInfo
(
String
v
)
{
this
.
v
=
v
;
}
public
RsaValueInfo
(
String
v
,
long
t
)
{
this
.
v
=
v
;
this
.
t
=
t
;
}
public
String
getV
()
{
return
v
;
}
public
void
setV
(
String
v
)
{
this
.
v
=
v
;
}
public
long
getT
()
{
return
t
;
}
public
void
setT
(
long
t
)
{
this
.
t
=
t
;
}
}
/**
* RSA公钥信息
*/
public
static
class
RsaInfo
{
/**
* 公钥
*/
private
PublicKey
publicKeyFrom
;
/**
* 私钥
*/
private
PrivateKey
privateKeyFrom
;
/**
* 公钥
*/
...
...
@@ -338,13 +485,60 @@ public final class RsaHelper {
* 私钥
*/
private
String
privateKey
;
/**
* 加密超时时间
*/
private
long
timeout
;
public
RsaInfo
()
{
}
public
RsaInfo
(
PublicKey
publicKeyFrom
,
PrivateKey
privateKeyFrom
)
{
this
(
publicKeyFrom
,
privateKeyFrom
,
0
);
}
public
RsaInfo
(
PublicKey
publicKeyFrom
,
PrivateKey
privateKeyFrom
,
long
timeout
)
{
this
.
publicKeyFrom
=
publicKeyFrom
;
this
.
privateKeyFrom
=
privateKeyFrom
;
this
.
timeout
=
timeout
;
this
.
initToWithFrom
();
}
public
RsaInfo
(
String
publicKey
,
String
privateKey
)
{
this
(
publicKey
,
privateKey
,
0
);
}
public
RsaInfo
(
String
publicKey
,
String
privateKey
,
long
timeout
)
{
this
.
publicKey
=
publicKey
;
this
.
privateKey
=
privateKey
;
this
.
timeout
=
timeout
;
this
.
initFromWithTo
();
}
private
void
initToWithFrom
()
{
this
.
publicKey
=
encodeBase64
(
this
.
publicKeyFrom
.
getEncoded
());
this
.
privateKey
=
encodeBase64
(
this
.
privateKeyFrom
.
getEncoded
());
}
private
void
initFromWithTo
()
{
this
.
publicKeyFrom
=
RsaHelper
.
getPublicKey
(
this
.
publicKey
);
this
.
privateKeyFrom
=
RsaHelper
.
getPrivateKey
(
this
.
privateKey
);
}
public
PublicKey
getPublicKeyFrom
()
{
return
publicKeyFrom
;
}
public
void
setPublicKeyFrom
(
PublicKey
publicKeyFrom
)
{
this
.
publicKeyFrom
=
publicKeyFrom
;
}
public
PrivateKey
getPrivateKeyFrom
()
{
return
privateKeyFrom
;
}
public
void
setPrivateKeyFrom
(
PrivateKey
privateKeyFrom
)
{
this
.
privateKeyFrom
=
privateKeyFrom
;
}
public
String
getPublicKey
()
{
...
...
@@ -362,5 +556,13 @@ public final class RsaHelper {
public
void
setPrivateKey
(
String
privateKey
)
{
this
.
privateKey
=
privateKey
;
}
public
long
getTimeout
()
{
return
timeout
;
}
public
void
setTimeout
(
long
timeout
)
{
this
.
timeout
=
timeout
;
}
}
}
\ No newline at end of file
yzg-util-base/src/test/java/helper/TestRsa.java
View file @
d048f8a5
...
...
@@ -12,7 +12,7 @@ public class TestRsa {
}
@Test
public
void
testRsa
()
throws
Exception
{
public
void
testRsa
()
{
RsaHelper
.
RsaInfo
rsaInfo
=
RsaHelper
.
generatorRsa
();
testRsaPublicEncode
(
rsaInfo
);
testRsaPrivateEncode
(
rsaInfo
);
...
...
@@ -37,9 +37,15 @@ public class TestRsa {
String
temp
=
RsaHelper
.
encryptionByPublicKey
(
from
,
rsaInfo
.
getPublicKey
());
String
result
=
RsaHelper
.
decryptionByPrivateKey
(
temp
,
rsaInfo
.
getPrivateKey
());
System
.
out
.
println
(
"原字段:"
+
from
);
System
.
out
.
println
(
"公钥加密后字段"
+
temp
);
System
.
out
.
println
(
"私钥解密后字段"
+
result
);
System
.
out
.
println
(
"公钥加密后字段
:
"
+
temp
);
System
.
out
.
println
(
"私钥解密后字段
:
"
+
result
);
Assert
.
assertEquals
(
from
,
result
);
String
rsaTemp
=
RsaHelper
.
encodeRsaValueInfo
(
from
,
rsaInfo
.
getPublicKeyFrom
());
String
rsaResult
=
RsaHelper
.
decodeRsaValueInfo
(
rsaTemp
,
rsaInfo
.
getPrivateKeyFrom
(),
10
*
1000
);
System
.
out
.
println
(
"Json时间公钥加密后字段:"
+
rsaTemp
);
System
.
out
.
println
(
"Json时间私钥解密后字段:"
+
rsaResult
);
Assert
.
assertEquals
(
from
,
rsaResult
);
}
...
...
@@ -53,8 +59,14 @@ public class TestRsa {
String
temp
=
RsaHelper
.
encryptionByPrivateKey
(
from
,
rsaInfo
.
getPrivateKey
());
String
result
=
RsaHelper
.
decryptionByPublicKey
(
temp
,
rsaInfo
.
getPublicKey
());
System
.
out
.
println
(
"原字段:"
+
from
);
System
.
out
.
println
(
"公钥加密后字段"
+
temp
);
System
.
out
.
println
(
"私钥解密后字段"
+
result
);
System
.
out
.
println
(
"公钥加密后字段
:
"
+
temp
);
System
.
out
.
println
(
"私钥解密后字段
:
"
+
result
);
Assert
.
assertEquals
(
from
,
result
);
String
rsaTemp
=
RsaHelper
.
encodeRsaValueInfo
(
from
,
rsaInfo
.
getPrivateKeyFrom
());
String
rsaResult
=
RsaHelper
.
decodeRsaValueInfo
(
rsaTemp
,
rsaInfo
.
getPublicKeyFrom
(),
10
*
1000
);
System
.
out
.
println
(
"Json时间公钥加密后字段:"
+
rsaTemp
);
System
.
out
.
println
(
"Json时间私钥解密后字段:"
+
rsaResult
);
Assert
.
assertEquals
(
from
,
rsaResult
);
}
}
Write
Preview
Markdown
is supported
0%
Try again
or
attach a new file
Attach a file
Cancel
You are about to add
0
people
to the discussion. Proceed with caution.
Finish editing this message first!
Cancel
Please
register
or
sign in
to comment