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 代码。