Java 到 swift 密码学转换
Java to swift conversion in cryptography
我有一个 java 代码,我在其中使用了密码学(加密和解密)。我想在 swift 代码上使用相同的过程。
//我的 java 加密密码 class
class Crypto {
private SecretKeySpec skc;
private final static char[] hexArray = "0123456789abcdef".toCharArray();
String TAG = "TAG";
@RequiresApi(api = Build.VERSION_CODES.O)
Crypto(String token) {
try {
MessageDigest md = MessageDigest.getInstance("MD5");
byte[] thedigest = md.digest(("YourStrongKey" + token).getBytes("UTF-8"));
skc = new SecretKeySpec(thedigest, "AES/ECB/PKCS5Padding");
} catch (NoSuchAlgorithmException | UnsupportedEncodingException e) {
e.printStackTrace();
}
}
private byte[] hexStringToByteArray(String s) {
int len = s.length();
byte[] data = new byte[len/2];
for(int i = 0; i < len; i+=2){
data[i/2] = (byte) ((Character.digit(s.charAt(i), 16) << 4) + Character.digit(s.charAt(i+1), 16));
}
return data;
}
public byte[] encrypt(String clear) throws Exception {
Cipher cipher = Cipher.getInstance("AES/ECB/PKCS5Padding");
cipher.init(Cipher.ENCRYPT_MODE, skc);
byte[] input = clear.getBytes("utf-8");
byte[] cipherText = new byte[cipher.getOutputSize(input.length)];
int ctLength = cipher.update(input, 0, input.length, cipherText, 0);
cipher.doFinal(cipherText, ctLength);
Log.d(TAG, "encrypt: ciper: "+cipher.toString());
return cipherText;
}
public byte[] decrypt(byte[] buf, int offset, int len) throws Exception {
Cipher cipher = Cipher.getInstance("AES/ECB/PKCS5Padding");
cipher.init(Cipher.DECRYPT_MODE, skc);
byte[] dec = new byte[cipher.getOutputSize(len - offset)];
int ctLength = cipher.update(buf, offset, len - offset, dec, 0);
cipher.doFinal(dec, ctLength);
Log.d(TAG, "encrypt: dec: "+dec.toString());
return dec;
}
}
//使用 java 形式主要 activity class
Crypto crypto;
String token = "Jpc3MiOiJodHRwczovL3NlY3VyZXRva2";
crypto = new Crypto(token);
JSONObject msg = new JSONObject();
msg.put("phoneData", "data1");
msg.put("countryData", "data2");
Log.d(TAG, "startHttpApiRequest: msg: "+msg);
String ss = msg.toString();
byte[] msgD = crypto.encrypt(ss);
//我在 playground 上的 swift 代码
class SecretSpec {
var algorithm: String = "AES/ECB/PKCS7padding"
var key = [UInt8]()
func secretSpec(key: [UInt8], algorithm: String){
self.key = key
self.algorithm = algorithm
}
func getAlgorithm() -> String {
return self.algorithm
}
func getFormat() -> String {
return "RAW"
}
func getEncoded() -> [UInt8] {
return self.key
}
func hashCode() -> Int {
var retval: Int = 0
for i in 1...key.count-1 {
retval = retval + Int(key[i]) * Int(i)
}
if (algorithm.lowercased() == "tripledes"){
retval = retval ^ Int("desede".hashValue)
return retval
}else{
retval = retval ^ Int(algorithm.lowercased().hashValue)
return retval
}
}
}
extension String {
func aesEncrypt(key: String) -> String? {
if let keyData = key.data(using: String.Encoding.utf8),
let data = self.data(using: String.Encoding.utf8),
let cryptData = NSMutableData(length: Int((data.count)) + kCCBlockSizeAES128) {
let keyLength = size_t(kCCKeySizeAES128)
let operation: CCOperation = UInt32(kCCEncrypt)
let algoritm: CCAlgorithm = UInt32(kCCAlgorithmAES128)
let options: CCOptions = UInt32(kCCOptionECBMode + kCCOptionPKCS7Padding)
var numBytesEncrypted :size_t = 0
let cryptStatus = CCCrypt(operation, algoritm, options,
(keyData as NSData).bytes,keyLength,nil, (data as NSData).bytes, data.count,cryptData.mutableBytes, cryptData.length,&numBytesEncrypted)
if UInt32(cryptStatus) == UInt32(kCCSuccess) {
cryptData.length = Int(numBytesEncrypted)
var bytes = [UInt8](repeating: 0, count: cryptData.length)
cryptData.getBytes(&bytes, length: cryptData.length)
var hexString = ""
for byte in bytes {
hexString += String(format:"%02x", UInt8(byte))
}
return hexString
}
else {
return nil
}
}
return nil
}
}
func MD5(_ string: String) -> String? {
let length = Int(CC_MD5_DIGEST_LENGTH)
var digest = [UInt8](repeating: 0, count: length)
if let d = string.data(using: String.Encoding.utf8) {
_ = d.withUnsafeBytes { (body: UnsafePointer<UInt8>) in
CC_MD5(body, CC_LONG(d.count), &digest)
}
}
return (0..<length).reduce("") {
[=14=] + String(format: "%02x", digest[])
}
}
var token = "YourStrongKeyJpc3MiOiJodHRwczovL3NlY3VyZXRva2"
var algorithm: String = "AES/ECB/PKCS5padding"
var tokenRes = MD5(token)
let digest = hexstringToBytes(tokenRes!)
var skcSpec = SecretSpec()
skcSpec.secretSpec(key: digest!, algorithm: algorithm)
print("hello: \(skcSpec)")
let msg: NSMutableDictionary = NSMutableDictionary()
msg.setValue("data1", forKey: "phoneData")
msg.setValue("data2", forKey: "countryData")
let msgData: NSData
var msgStr: String = ""
var requestUrl: String = ""
do {
msgData = try JSONSerialization.data(withJSONObject: msg, options: JSONSerialization.WritingOptions()) as NSData
msgStr = NSString(data: msgData as Data, encoding: String.Encoding.utf8.rawValue)! as String
} catch _ {
print ("JSON Failure")
}
var str = skcSpec.getEncoded()
print(str)
let skc = NSString(bytes: str, length: str.count, encoding: String.Encoding.ascii.rawValue)! as String
let eneMsg = msgStr.aesEncrypt(key: skc)!
print("encoded: \(eneMsg)")
它没有给我相同的输出。请帮我找到相同的输出。 nb: java加密和解密的密码是固定的。
输出:
in java:
ffe957f00bdd93cfe1ef1133993cc9d2d8682310c648633660b448d92098e7fa07ae25f600467894ac94ccdcbe4039b8
in swift:
2e130be30aa3d8ff7fdc31dc8ffe1c39afe987ccbf8481caed9c78b49624a31c68df63a899df130128af6852c82d9aea
我试过将你的 Crypto
class 直接转换成 Swift,结果是:
import Foundation
import CommonCrypto
func MD5(_ data: Data) -> Data {
let length = Int(CC_MD5_DIGEST_LENGTH)
var digest = Data(count: length)
data.withUnsafeBytes {bytes in
_ = digest.withUnsafeMutableBytes {mutableBytes in
CC_MD5(bytes.baseAddress, CC_LONG(bytes.count), mutableBytes.bindMemory(to: UInt8.self).baseAddress)
}
}
return digest
}
func hexStringToData(_ s: String) -> Data {
let len = s.count
var data = Data(count: len/2)
var start = s.startIndex
for i in 0..<len/2 {
let end = s.index(start, offsetBy: 2)
data[i] = UInt8(s[start..<end], radix: 16)!
start = end
}
return data
}
class Crypto {
private var key: Data
init(_ token: String) {
let theDigest = MD5(("YourStrongKey" + token).data(using: .utf8)!)
key = theDigest
}
public func encrypt(_ clear: String) -> Data? {
let input = clear.data(using: .utf8)!
var cipherText = Data(count: input.count + kCCBlockSizeAES128)
let keyLength = size_t(kCCKeySizeAES128)
assert(key.count >= keyLength)
let operation: CCOperation = UInt32(kCCEncrypt)
let algoritm: CCAlgorithm = UInt32(kCCAlgorithmAES128)
let options: CCOptions = UInt32(kCCOptionECBMode + kCCOptionPKCS7Padding)
var ctLength: size_t = 0
let cryptStatus = key.withUnsafeBytes {keyBytes in
input.withUnsafeBytes {inputBytes in
cipherText.withUnsafeMutableBytes {mutableBytes in
CCCrypt(operation, algoritm, options,
keyBytes.baseAddress, keyLength, nil,
inputBytes.baseAddress, inputBytes.count,
mutableBytes.baseAddress, mutableBytes.count,
&ctLength)
}
}
}
if cryptStatus == CCCryptorStatus(kCCSuccess) {
cipherText.count = Int(ctLength)
return cipherText
} else {
return nil
}
}
public func decrypt(_ buf: Data, _ offset: Int, _ len: Int) -> Data? {
var dec = Data(count: len)
let keyLength = size_t(kCCKeySizeAES128)
let operation: CCOperation = UInt32(kCCDecrypt)
let algoritm: CCAlgorithm = UInt32(kCCAlgorithmAES128)
let options: CCOptions = UInt32(kCCOptionECBMode + kCCOptionPKCS7Padding)
var ctLength :size_t = 0
let cryptStatus = key.withUnsafeBytes {keyBytes in
buf.withUnsafeBytes {inputBytes in
dec.withUnsafeMutableBytes {mutableBytes in
CCCrypt(operation, algoritm, options,
keyBytes.baseAddress, keyLength, nil,
inputBytes.baseAddress, inputBytes.count,
mutableBytes.baseAddress, mutableBytes.count,
&ctLength)
}
}
}
if cryptStatus == CCCryptorStatus(kCCSuccess) {
dec.count = Int(ctLength)
return dec
} else {
return nil
}
}
}
let token = "Jpc3MiOiJodHRwczovL3NlY3VyZXRva2"
let crypto = Crypto(token)
let msg = [
"phoneData": "data1",
"countryData": "data2",
]
let msgData = try! JSONSerialization.data(withJSONObject: msg)
let ss = String(data: msgData, encoding: .utf8)!
print(ss) //->{"phoneData":"data1","countryData":"data2"}
//### Be careful, this may be different than the result of JSONObject#toString() in Java.
//### Sometimes, outputs->{"countryData":"data2","phoneData":"data1"}
let msgD = crypto.encrypt(ss)!
print(msgD as NSData) //-><ffe957f0 0bdd93cf e1ef1133 993cc9d2 24e8b9a1 162520e5 54a3d8af 8f478db7 07ae25f6 00467894 ac94ccdc be4039b8>
//### When `ss` is {"phoneData":"data1","countryData":"data2"}
let decrypted = crypto.decrypt(msgD, 0, msgD.count)!
print(String(data: decrypted, encoding: .utf8)!) //->{"phoneData":"data1","countryData":"data2"}
在您的 Java 代码中,您的密钥字符串 ("YourStrongKey" + token
) 得到两次转换,直到用作 AES 的二进制密钥数据:
key: "YourStrongKey" + token
↓
UTF-8 bytes
↓
MD5 digest
但是,在您的 Swift 代码中,您转换密钥的次数更多:
token: "YourStrongKeyJpc3MiOiJodHRwczovL3NlY3VyZXRva2"
↓
UTF-8 bytes (`d` in `MD5(_:)`)
↓
MD5 digest
↓
binary to HEX String conversion (return value from `MD5(_:)`)
↓
UTF-8 bytes (`keyData` in `aesEncrypt(key:)`)
您的 Java 代码中没有二进制到十六进制字符串的转换,您不应该这样做。
顺便说一下,ECB 不被认为是一种安全的 加密方式,您最好更新您的Java 代码。
我有一个 java 代码,我在其中使用了密码学(加密和解密)。我想在 swift 代码上使用相同的过程。 //我的 java 加密密码 class
class Crypto {
private SecretKeySpec skc;
private final static char[] hexArray = "0123456789abcdef".toCharArray();
String TAG = "TAG";
@RequiresApi(api = Build.VERSION_CODES.O)
Crypto(String token) {
try {
MessageDigest md = MessageDigest.getInstance("MD5");
byte[] thedigest = md.digest(("YourStrongKey" + token).getBytes("UTF-8"));
skc = new SecretKeySpec(thedigest, "AES/ECB/PKCS5Padding");
} catch (NoSuchAlgorithmException | UnsupportedEncodingException e) {
e.printStackTrace();
}
}
private byte[] hexStringToByteArray(String s) {
int len = s.length();
byte[] data = new byte[len/2];
for(int i = 0; i < len; i+=2){
data[i/2] = (byte) ((Character.digit(s.charAt(i), 16) << 4) + Character.digit(s.charAt(i+1), 16));
}
return data;
}
public byte[] encrypt(String clear) throws Exception {
Cipher cipher = Cipher.getInstance("AES/ECB/PKCS5Padding");
cipher.init(Cipher.ENCRYPT_MODE, skc);
byte[] input = clear.getBytes("utf-8");
byte[] cipherText = new byte[cipher.getOutputSize(input.length)];
int ctLength = cipher.update(input, 0, input.length, cipherText, 0);
cipher.doFinal(cipherText, ctLength);
Log.d(TAG, "encrypt: ciper: "+cipher.toString());
return cipherText;
}
public byte[] decrypt(byte[] buf, int offset, int len) throws Exception {
Cipher cipher = Cipher.getInstance("AES/ECB/PKCS5Padding");
cipher.init(Cipher.DECRYPT_MODE, skc);
byte[] dec = new byte[cipher.getOutputSize(len - offset)];
int ctLength = cipher.update(buf, offset, len - offset, dec, 0);
cipher.doFinal(dec, ctLength);
Log.d(TAG, "encrypt: dec: "+dec.toString());
return dec;
}
}
//使用 java 形式主要 activity class
Crypto crypto;
String token = "Jpc3MiOiJodHRwczovL3NlY3VyZXRva2";
crypto = new Crypto(token);
JSONObject msg = new JSONObject();
msg.put("phoneData", "data1");
msg.put("countryData", "data2");
Log.d(TAG, "startHttpApiRequest: msg: "+msg);
String ss = msg.toString();
byte[] msgD = crypto.encrypt(ss);
//我在 playground 上的 swift 代码
class SecretSpec {
var algorithm: String = "AES/ECB/PKCS7padding"
var key = [UInt8]()
func secretSpec(key: [UInt8], algorithm: String){
self.key = key
self.algorithm = algorithm
}
func getAlgorithm() -> String {
return self.algorithm
}
func getFormat() -> String {
return "RAW"
}
func getEncoded() -> [UInt8] {
return self.key
}
func hashCode() -> Int {
var retval: Int = 0
for i in 1...key.count-1 {
retval = retval + Int(key[i]) * Int(i)
}
if (algorithm.lowercased() == "tripledes"){
retval = retval ^ Int("desede".hashValue)
return retval
}else{
retval = retval ^ Int(algorithm.lowercased().hashValue)
return retval
}
}
}
extension String {
func aesEncrypt(key: String) -> String? {
if let keyData = key.data(using: String.Encoding.utf8),
let data = self.data(using: String.Encoding.utf8),
let cryptData = NSMutableData(length: Int((data.count)) + kCCBlockSizeAES128) {
let keyLength = size_t(kCCKeySizeAES128)
let operation: CCOperation = UInt32(kCCEncrypt)
let algoritm: CCAlgorithm = UInt32(kCCAlgorithmAES128)
let options: CCOptions = UInt32(kCCOptionECBMode + kCCOptionPKCS7Padding)
var numBytesEncrypted :size_t = 0
let cryptStatus = CCCrypt(operation, algoritm, options,
(keyData as NSData).bytes,keyLength,nil, (data as NSData).bytes, data.count,cryptData.mutableBytes, cryptData.length,&numBytesEncrypted)
if UInt32(cryptStatus) == UInt32(kCCSuccess) {
cryptData.length = Int(numBytesEncrypted)
var bytes = [UInt8](repeating: 0, count: cryptData.length)
cryptData.getBytes(&bytes, length: cryptData.length)
var hexString = ""
for byte in bytes {
hexString += String(format:"%02x", UInt8(byte))
}
return hexString
}
else {
return nil
}
}
return nil
}
}
func MD5(_ string: String) -> String? {
let length = Int(CC_MD5_DIGEST_LENGTH)
var digest = [UInt8](repeating: 0, count: length)
if let d = string.data(using: String.Encoding.utf8) {
_ = d.withUnsafeBytes { (body: UnsafePointer<UInt8>) in
CC_MD5(body, CC_LONG(d.count), &digest)
}
}
return (0..<length).reduce("") {
[=14=] + String(format: "%02x", digest[])
}
}
var token = "YourStrongKeyJpc3MiOiJodHRwczovL3NlY3VyZXRva2"
var algorithm: String = "AES/ECB/PKCS5padding"
var tokenRes = MD5(token)
let digest = hexstringToBytes(tokenRes!)
var skcSpec = SecretSpec()
skcSpec.secretSpec(key: digest!, algorithm: algorithm)
print("hello: \(skcSpec)")
let msg: NSMutableDictionary = NSMutableDictionary()
msg.setValue("data1", forKey: "phoneData")
msg.setValue("data2", forKey: "countryData")
let msgData: NSData
var msgStr: String = ""
var requestUrl: String = ""
do {
msgData = try JSONSerialization.data(withJSONObject: msg, options: JSONSerialization.WritingOptions()) as NSData
msgStr = NSString(data: msgData as Data, encoding: String.Encoding.utf8.rawValue)! as String
} catch _ {
print ("JSON Failure")
}
var str = skcSpec.getEncoded()
print(str)
let skc = NSString(bytes: str, length: str.count, encoding: String.Encoding.ascii.rawValue)! as String
let eneMsg = msgStr.aesEncrypt(key: skc)!
print("encoded: \(eneMsg)")
它没有给我相同的输出。请帮我找到相同的输出。 nb: java加密和解密的密码是固定的。
输出:
in java: ffe957f00bdd93cfe1ef1133993cc9d2d8682310c648633660b448d92098e7fa07ae25f600467894ac94ccdcbe4039b8
in swift: 2e130be30aa3d8ff7fdc31dc8ffe1c39afe987ccbf8481caed9c78b49624a31c68df63a899df130128af6852c82d9aea
我试过将你的 Crypto
class 直接转换成 Swift,结果是:
import Foundation
import CommonCrypto
func MD5(_ data: Data) -> Data {
let length = Int(CC_MD5_DIGEST_LENGTH)
var digest = Data(count: length)
data.withUnsafeBytes {bytes in
_ = digest.withUnsafeMutableBytes {mutableBytes in
CC_MD5(bytes.baseAddress, CC_LONG(bytes.count), mutableBytes.bindMemory(to: UInt8.self).baseAddress)
}
}
return digest
}
func hexStringToData(_ s: String) -> Data {
let len = s.count
var data = Data(count: len/2)
var start = s.startIndex
for i in 0..<len/2 {
let end = s.index(start, offsetBy: 2)
data[i] = UInt8(s[start..<end], radix: 16)!
start = end
}
return data
}
class Crypto {
private var key: Data
init(_ token: String) {
let theDigest = MD5(("YourStrongKey" + token).data(using: .utf8)!)
key = theDigest
}
public func encrypt(_ clear: String) -> Data? {
let input = clear.data(using: .utf8)!
var cipherText = Data(count: input.count + kCCBlockSizeAES128)
let keyLength = size_t(kCCKeySizeAES128)
assert(key.count >= keyLength)
let operation: CCOperation = UInt32(kCCEncrypt)
let algoritm: CCAlgorithm = UInt32(kCCAlgorithmAES128)
let options: CCOptions = UInt32(kCCOptionECBMode + kCCOptionPKCS7Padding)
var ctLength: size_t = 0
let cryptStatus = key.withUnsafeBytes {keyBytes in
input.withUnsafeBytes {inputBytes in
cipherText.withUnsafeMutableBytes {mutableBytes in
CCCrypt(operation, algoritm, options,
keyBytes.baseAddress, keyLength, nil,
inputBytes.baseAddress, inputBytes.count,
mutableBytes.baseAddress, mutableBytes.count,
&ctLength)
}
}
}
if cryptStatus == CCCryptorStatus(kCCSuccess) {
cipherText.count = Int(ctLength)
return cipherText
} else {
return nil
}
}
public func decrypt(_ buf: Data, _ offset: Int, _ len: Int) -> Data? {
var dec = Data(count: len)
let keyLength = size_t(kCCKeySizeAES128)
let operation: CCOperation = UInt32(kCCDecrypt)
let algoritm: CCAlgorithm = UInt32(kCCAlgorithmAES128)
let options: CCOptions = UInt32(kCCOptionECBMode + kCCOptionPKCS7Padding)
var ctLength :size_t = 0
let cryptStatus = key.withUnsafeBytes {keyBytes in
buf.withUnsafeBytes {inputBytes in
dec.withUnsafeMutableBytes {mutableBytes in
CCCrypt(operation, algoritm, options,
keyBytes.baseAddress, keyLength, nil,
inputBytes.baseAddress, inputBytes.count,
mutableBytes.baseAddress, mutableBytes.count,
&ctLength)
}
}
}
if cryptStatus == CCCryptorStatus(kCCSuccess) {
dec.count = Int(ctLength)
return dec
} else {
return nil
}
}
}
let token = "Jpc3MiOiJodHRwczovL3NlY3VyZXRva2"
let crypto = Crypto(token)
let msg = [
"phoneData": "data1",
"countryData": "data2",
]
let msgData = try! JSONSerialization.data(withJSONObject: msg)
let ss = String(data: msgData, encoding: .utf8)!
print(ss) //->{"phoneData":"data1","countryData":"data2"}
//### Be careful, this may be different than the result of JSONObject#toString() in Java.
//### Sometimes, outputs->{"countryData":"data2","phoneData":"data1"}
let msgD = crypto.encrypt(ss)!
print(msgD as NSData) //-><ffe957f0 0bdd93cf e1ef1133 993cc9d2 24e8b9a1 162520e5 54a3d8af 8f478db7 07ae25f6 00467894 ac94ccdc be4039b8>
//### When `ss` is {"phoneData":"data1","countryData":"data2"}
let decrypted = crypto.decrypt(msgD, 0, msgD.count)!
print(String(data: decrypted, encoding: .utf8)!) //->{"phoneData":"data1","countryData":"data2"}
在您的 Java 代码中,您的密钥字符串 ("YourStrongKey" + token
) 得到两次转换,直到用作 AES 的二进制密钥数据:
key: "YourStrongKey" + token
↓
UTF-8 bytes
↓
MD5 digest
但是,在您的 Swift 代码中,您转换密钥的次数更多:
token: "YourStrongKeyJpc3MiOiJodHRwczovL3NlY3VyZXRva2"
↓
UTF-8 bytes (`d` in `MD5(_:)`)
↓
MD5 digest
↓
binary to HEX String conversion (return value from `MD5(_:)`)
↓
UTF-8 bytes (`keyData` in `aesEncrypt(key:)`)
您的 Java 代码中没有二进制到十六进制字符串的转换,您不应该这样做。
顺便说一下,ECB 不被认为是一种安全的 加密方式,您最好更新您的Java 代码。