AES encrypted secret key in JAVA decrypt in Swift

For implementing Security while communicating , we are trying to implement encryption in Swift ...
For this first trying to implement a demo where I am trying to decrypt the RSA private key , secret key and encrypted message i have got from Java end.

The private key i got is : MIICdgIBADANBgkqhkiG9w0BAQEFAASCAmAwggJcAgEAAoGBAJitrKyHsfJtNHcr0dXvv0hV8iq0/2cefgXlgVg4HGj1Z5036ZuKCZONx3pFJ9fsKM2kDovEWsUA2hGHQvaGfzjkt19p6PmN8r56VnTX
DrxHQOLXPAE3T7eQSA/g7IqfqT1ILkGX/80J2jqNRGJhDKsZ3IcWa1T4lVsqLAoiNNPNAgMBAAEC
gYAKYFNJ7nbziOR17O534bFYUy8AJAjvkyzxbaWav0V/BJ6kGravsXPxKUOTVbvdetlTEIFEknWw
ydwIMO8mHgHrWtprRI7COkV1l46UgObgXTQp/Nwfqukki2TenbbAo/Ja72+CMycOy3IzvW2+Qd0Z
wFHheUmLE4lF7fHssSMJwQJBAOAc8ph+hR64jvJIydjYzoZUm7K0HMnPZQA/bDpRJ6CeR8EPZ7/x
IXQxaWTN5RzNEb/RY96KkyZ0xWDpAmiBb8UCQQCuZs3lpWFodos0lnVW/WoDVdng7qE8Xqwx68kX
hueclgBYbJR9SYd14AqKbrDJvt+8dtw5qKOkIEVdNuxrqcxpAkEAmd+mPUeZFNe45edOF0H8wsRy
xobdwT5RZZMmNwAjiidCsu5l2Kaxxnpql5i6d0Thq+cTf+d7UwsXvgsd6Sz91QJABGYZaWJre4wJ
5NCqsv//TYg7z52VOYWVyEiPMOW5L8zkw1YxxJs3LHTzLxytntkOoZ1J3rZvMjOSLFC3U9vbiQJA
HTbMYvxt3yKnYvksUINkOB+3sMHuUDdWZlgZ2POnWA+gP93gPogB0/547aR6f/WSBGDA0nWnE4uU
U2I1Eu08rQ==

encryptedSecretKey : bjgZTpZD7DvrjTRXJ6LCA9GpkDnLJLgIVAmFWFXNUVqVWdjXOiOGeRHJx8owW6Pp/a5ECe8Cljlw
ZIAFhRUEPfTks22yntUOhrCUbDzzqYOMiQYfEV/h7/zahl4PV1t4rHXDsQPaf7Rrl8k5jKj0Cf7E
aKA4U/pyAjZlvVhMjH8=

encryptedText : 9pKpAq2HorRsG1IGX9+6Qg==

This is what I have got from server end.

I am now stuck to decrypt the encrypted Secret key.

Java Code for creating secret key is

Code Block
public class ClientEncrypt {
// private static String publicKeyString = "<your_public_key_here>";
// private static String publicKeyString = "MIICdQIBADANBgkqhkiG9w0BAQEFAASCAl8wggJbAgEAAoGBAJwdPPeUl9Jknt6gcB98xcsUGlp1ISopEfHL4WFPvYB04UKf9aJOzUpAynl0ZNgI2wBejL6+Ap6pGkqmn2AamEdg24G0G8u//0nDIQYdtDpwoxmADEG2UaEz4+DxFTjH2JICD2WQRx2bjd3h6GN/BimJRVYhs8CLtvhr0f6iJZ9VAgMBAAECgYAu8sgL+L7zwdjZQMyXW5GsMf5+56yynilMWzELb8ygngXEYJChYCtywith/TP6mCcsZELVdhUZm+dRPey+HSFgFa3XDQV+IQZG2f3RK/L78cUFnzXjIwa8INHI7dkfZVBBW6fY1pdzjcEVl0lVAkeLvMowb00wMMlrFSVGnFBBYQJBAMg3anp33/GxuUTV8al6UwEVYwXuOCV5R+vc1M0MW4fEXceFX+GUPL4yF/k6h/AT/Qx90jS6BpUs4OfE1L4/KGkCQQDHnDJptJwp5WTzZLErUSSKBotWar7uo+kCpDer8BCrBZUqqff8f8B3rCGohwrvLJkY1H0I/2VuV4pYGuIXqcINAkBAu5IEmJ9iIIj7FYT1u6P8lLKYZ0Xl78luG879oMIzKiz75kZyw5/ECqQvo1nyKDRHhqKhA4g/UOxCYyQ5gmgJAkAeYzgFgZpYgnSY3RZfqs2uOYrS0BNbDxb08P0MrtJKxuHLie6XwPcTAOBq4IwYlyiDcC3MxdyyLJ+CBC2xPoWtAkAOB7PvjFeFcL5JZe/MpM0NW+CkKOAu8Xs4L9qihnMlZE6zZDwbbDFONyxJeDlcRmGlvlCPPxL1blZGgVeQW/M1";
private static String publicKeyString = "MIGfMA0GCSqGSIb3DQEBAQUAA4GNADCBiQKBgQDFzfAPe5h40OHut+gT5JEm5MEqw4SVbsWKdusLXPawqBNPPn6TpWhbHgFJfe3HsqpmpdXBslSWM7T/8Pu9+XzKiPoGTzG91a59kl2kMYNQE0GeIJmzHrqPSiw3QG64fU10mzR/Ys9Yh2FyTcbaekDSUqkL7KpDTPV35eAWs6vHmQIDAQAB";
public static void main(String args[]) {
try {
// 1. generate secret key using AES
KeyGenerator keyGenerator = KeyGenerator.getInstance("AES");
keyGenerator.init(128); // AES is currently available in three key sizes: 128, 192 and 256 bits.The
// design and strength of all key lengths of the AES algorithm are sufficient to
// protect classified information up to the SECRET level
SecretKey secretKey = keyGenerator.generateKey();
// 2. get string which needs to be encrypted
String text = "<your_string_which_needs_to_be_encrypted_here>";
// 3. encrypt string using secret key
byte[] raw = secretKey.getEncoded();
SecretKeySpec skeySpec = new SecretKeySpec(raw, "AES");
Cipher cipher = Cipher.getInstance("AES/CBC/PKCS5Padding");
cipher.init(Cipher.ENCRYPT_MODE, skeySpec, new IvParameterSpec(new byte[16]));
String cipherTextString = Base64.encodeToString(cipher.doFinal(text.getBytes(Charset.forName("UTF-8"))),
Base64.DEFAULT);
System.out.println("cipherTextString : "+cipherTextString);
// 4. get public key
X509EncodedKeySpec publicSpec = new X509EncodedKeySpec(Base64.decode(publicKeyString, Base64.DEFAULT));
KeyFactory keyFactory = KeyFactory.getInstance("RSA");
PublicKey publicKey = keyFactory.generatePublic(publicSpec);
// 6. encrypt secret key using public key
Cipher cipher2 = Cipher.getInstance("RSA/ECB/OAEPWithSHA1AndMGF1Padding");
cipher2.init(Cipher.ENCRYPT_MODE, publicKey);
String encryptedSecretKey = Base64.encodeToString(cipher2.doFinal(secretKey.getEncoded()), Base64.DEFAULT);
//System.out.println(encryptedSecretKey);
System.out.println("encryptedSecretKey : "+encryptedSecretKey);
// 7. pass cipherTextString (encypted sensitive data) and encryptedSecretKey to
// your server via your preferred way.
// Tips:
// You may use JSON to combine both the strings under 1 object.
// You may use a volley call to send this data to your server.
} catch (NoSuchAlgorithmException | InvalidKeyException | NoSuchPaddingException | IllegalBlockSizeException
| BadPaddingException | InvalidKeySpecException | InvalidAlgorithmParameterException e) {
e.printStackTrace();
}
}
}


Please help in this regard!!
Answered by OOPer in 622182022

All the keys and the encrypted text you have shown are Base-64 encoded.
You need to decode them to binary data. In Swift, they are represented by type Data.

You know how to convert Base-64 encoded String to Data, as shown in other threads of yours.

Keep all the Data as Data, do not try to convert them to String. Are you OK with this?
If OK, then step 1 cleared.

Now you have 3 Data,
  • privateKeyData (634 bytes)

  • encryptedSecretKeyData (128 bytes)

  • encryptedTextData (16 bytes)


The second thing you need to do is to retrieve the right private key content to pass to SecKeyCreateWithData as suggested by @eskimo in another thread of yours.

You once said in the same thread:

I somehow managed to strip header from the privateKey and get privateKey

But you have never shown how have you done it, nor never shown the actual privateKey.

Thus, your step 2 is not cleared yet.

Please show how have you got the privateKey. Is it really the right data to use with Apple's frameworks?

Accepted Answer

All the keys and the encrypted text you have shown are Base-64 encoded.
You need to decode them to binary data. In Swift, they are represented by type Data.

You know how to convert Base-64 encoded String to Data, as shown in other threads of yours.

Keep all the Data as Data, do not try to convert them to String. Are you OK with this?
If OK, then step 1 cleared.

Now you have 3 Data,
  • privateKeyData (634 bytes)

  • encryptedSecretKeyData (128 bytes)

  • encryptedTextData (16 bytes)


The second thing you need to do is to retrieve the right private key content to pass to SecKeyCreateWithData as suggested by @eskimo in another thread of yours.

You once said in the same thread:

I somehow managed to strip header from the privateKey and get privateKey

But you have never shown how have you done it, nor never shown the actual privateKey.

Thus, your step 2 is not cleared yet.

Please show how have you got the privateKey. Is it really the right data to use with Apple's frameworks?

Thanks OOPer !!
Step 1 :
Code Block
let privatekeyData : Data = Data(base64Encoded: privateKeyString)!
let SecretKeyData : Data =  Data(base64Encoded: EncrytedsecretKey)!
let textData : Data = Data(base64Encoded: encryptedText)!

conversion from Base64 to Data : Done

Step 2: private key strip the header through this method and created the SECKEY
Code Block
let privateKeyStripped  =  try! stripHeaderIfAny(keyData: privatekeyData)
        let attributes: [String: Any] = [
                         kSecAttrKeyType as String: kSecAttrKeyTypeRSA,
                         kSecAttrKeyClass as String: kSecAttrKeyClassPrivate
                     ]
   var error: Unmanaged<CFError>?
 let privateSecKey : SecKey = SecKeyCreateWithData(privateKeyStripped as CFData, attributes as CFDictionary, &error)
private func stripHeaderIfAny(keyData: Data) throws -> NSData {
let bytes = keyData.arrayOfBytes()
var offset = 0
 guard bytes[offset] == 0x30 else {
        throw SwKeyConvert.SwError.invalidKey
    }
    offset += 1
    if bytes[offset] > 0x80 {
        offset += Int(bytes[offset]) - 0x80
    }
    offset += 1
    guard bytes[offset] == 0x02 else {
        throw SwKeyConvert.SwError.invalidKey
    }
    offset += 3
    /*without PKCS8 header*/
    if bytes[offset] == 0x02 {
        return keyData as NSData
    }
    let OID: [UInt8] = [0x30, 0x0d, 0x06, 0x09, 0x2a, 0x86, 0x48, 0x86,
    0xf7, 0x0d, 0x01, 0x01, 0x01, 0x05, 0x00]
    let slice: [UInt8] = Array(bytes[offset..<(offset + OID.count)])
    guard slice == OID else {
        throw SwKeyConvert.SwError.invalidKey
    }
    offset += OID.count
    guard bytes[offset] == 0x04 else {
    throw SwKeyConvert.SwError.invalidKey
    }
    offset += 1
    if bytes[offset] > 0x80 {
    offset += Int(bytes[offset]) - 0x80
    }
    offset += 1
    guard bytes[offset] == 0x30 else {
        throw SwKeyConvert.SwError.invalidKey
    }
        let newKeyData = keyData as NSData
        return newKeyData.subdata(with: NSRange(location: offset, length: keyData.count - offset)) as NSData
}

used a library SwCrypt

This way I able to get key without the header and then created a SECKEY private key.
Is this way correct..?Please help!!
Thanks for showing your code.
The method stripHeaderIfAny(keyData:) is far from efficient and can be simplified in various ways and should return Data instead of NSData... But every such thing is just another thing.

The most important thing is that I could retrieve the same Data as I tried in a hacky simplified way.

Step 2 cleared!



Now you have the right private key of type SecKey in privateSecKey. (I needed to unwrap the result, but OK, again another thing.)

So, your step 3: Decrypt the SecretKeyData using the private key privateSecKey.

As far as I can find in Apple's docs, SecKeyCreateDecryptedData seems to be the right function for it.

You should better try it by yourself, and please tell me the result.
Thanks OOPer ...
Step 3 :  SecKeyCreateDecryptedData as I think is regarding getting the encrypted text from private key,I read in the article,
In my case though I need to decrypt cipher text using the secret key for which first I need to get the secret key from the Private Key seckey,
so as per my understanding our Step 3 : should be to decrypt the Secret key with private sec key and secret key is AES - 128 encrypted key which I tried to decrypt through following code :
Code Block
        var error:Unmanaged<CFError>?
        if let cfdata = SecKeyCopyExternalRepresentation(privateSecKey, &error) {
           let privateSecyKeyData:Data = cfdata as Data
           var secKey =  try! aesCBCDecrypt(data: SecretKeyData, keyData: privateSecyKeyData)
        }
 func aesCBCEncrypt(data:Data, keyData:Data) throws -> Data {
        let keyLength = keyData.count
        let validKeyLengths = [kCCKeySizeAES128, kCCKeySizeAES192, kCCKeySizeAES256]
        if (validKeyLengths.contains(keyLength) == false) {
            throw AESError.KeyError(("Invalid key length", keyLength))
        }
        let ivSize = kCCBlockSizeAES128;
        let cryptLength = size_t(ivSize + data.count + kCCBlockSizeAES128)
        var cryptData = Data(count:cryptLength)
        let status = cryptData.withUnsafeMutableBytes {ivBytes in
        SecRandomCopyBytes(kSecRandomDefault, kCCBlockSizeAES128, ivBytes)
        }
        if (status != 0) {
            throw AESError.IVError(("IV generation failed", Int(status)))
        }
        var numBytesEncrypted :size_t = 0
        let options   = CCOptions(kCCOptionPKCS7Padding)
        let cryptStatus = cryptData.withUnsafeMutableBytes {cryptBytes in
            data.withUnsafeBytes {dataBytes in
                keyData.withUnsafeBytes {keyBytes in
                    CCCrypt(CCOperation(kCCEncrypt),
                            CCAlgorithm(kCCAlgorithmAES),
                            options,
                            keyBytes, keyLength,
                            cryptBytes,
                            dataBytes, data.count,
                            cryptBytes+kCCBlockSizeAES128, cryptLength,
                            &numBytesEncrypted)
                }
            }
        }
        if UInt32(cryptStatus) == UInt32(kCCSuccess) {
            cryptData.count = numBytesEncrypted + ivSize
        }
        else {
            throw AESError.CryptorError(("Encryption failed", Int(cryptStatus)))
        }
        return cryptData;
    }

But getting the error "Invalid key Length" for privateSecKey as the length is too big
Seems you are skipping some steps.

step 3: Decrypt the SecretKeyData using the private key privateSecKey.

No AES decryption yet. No SecKeyCopyExternalRepresentation.

When you successfully decrypt SecretKeyData with SecKeyCreateDecryptedData, you can get a 32-byte Data.
And then you can use it as the key of AES decryption (as you know, 32-bytes == 128-bits, the key size of AES-128.)
But it is your step4.

First, you need to find how to use SecKeyCreateDecryptedData correctly. Forget anything about AES until then.
Thanks OOPer for your Help!
Step 3 : Decrypt SecretKeyData with SecKeyCreateDecryptedData , I am doing this through following function , getting one error..

Code Block
let algorithm: SecKeyAlgorithm = .rsaEncryptionPKCS1
   guard SecKeyIsAlgorithmSupported(privateSecKey, .decrypt, algorithm) else {
   print("can not decrypt using this algorithm")
    return
   }
    var errorSec: Unmanaged<CFError>?
    let SecKeyDecryptedData = SecKeyCreateDecryptedData(privateSecKey,
                                                  algorithm,
                                                  SecretKeyData as CFData,
                                                  &errorSec) as Data?
        guard SecKeyDecryptedData != nil else {
          print("can not decrypt")
            return
        }
        let clearText = String(decoding: clearTextData!, as: UTF8.self)
}

Algorithm is correct , Error is in SecKeyCreateDecryptedData "Error Domain=NSOSStatusErrorDomain Code=-50 "RSAdecrypt wrong input (err -27)"
Please Help !!!

Algorithm is correct

Are you sure? Better try other algorithms.
In your Java code, you have specified "RSA/ECB/OAEPWithSHA1AndMGF1Padding".
In fact, I have tried almost all the algorithms starting with rsa, and only one worked for your privateSecKey, it was something like OAEPWithSHA1AndMGF1Padding.


Thanks OOPer !!
Yes I tried with numerous other algorithms but could not find which your are pointing to MGF1PaddingwithOAEPSHA1 from the list of algorithms .
But there is one that worked for me is "rsaEncryptionOAEPSHA1"
Code Block
let algorithm: SecKeyAlgorithm = .rsaEncryptionOAEPSHA1

but now issue is I am getting resulted decrypted Data with 16 bytes only and when converted to string giving me Chinese characters

,蘵*UUrX7\u{10}

but now issue is I am getting resulted decrypted Data with 16 bytes only and when converted to string giving me Chinese characters

Seems you are trying to skip steps again. As already noted, 16 bytes is the right size for AES-128.

Please remember.

Keep all the Data as Data, do not try to convert them to String. Are you OK with this?

This applies to this step 3. You need to keep the Data as Data, do not try to convert it to String.
(You need conversion to String in the last step, step 5. You have one more step, step 4, in which you need to work with Data.)

Please tell me what you get wth this line.
Code Block
print(SecKeyDecryptedData as NSData) //<- or `as NSData?` here


Thanks OOPer,
I removed that line for converting into string.
Using this :
Code Block
print(SecKeyDecryptedData as NSData)

Result :
{length = 16, bytes = 0x2cb5d1e898b52a55fa5572583710d4cb}

Is that result is OK?


Is that result is OK?

Yes, it's OK.

Step 3 cleared.



Step 4: Decrypt (AES) textData with key SecKeyDecryptedData.

You can choose any of the AES decryption libraries, but remember:
  • Both the input to decrypt, and the key to decrypt with, are of type Data.

You tend to use methods with parameters of type String, but those are not the right ones.

Just for example, I have modified a method you have shown before, in order to make the parameters Data.
Code Block
import CommonCrypto
func aesOperation(_ operation: CCOperation, on data: Data, keyData: Data) -> Data? {
let dataLength = data.count
let keySize = keyData.count
let bufferSize = dataLength + keySize
var buffer = Data(count: bufferSize)
var numBytesDecrypted: size_t = 0
let cryptStatus = data.withUnsafeBytes {dataBP in
keyData.withUnsafeBytes {keyBP in
buffer.withUnsafeMutableBytes {bufferBP in
CCCrypt(operation,
CCAlgorithm(kCCAlgorithmAES),
CCOptions(kCCOptionPKCS7Padding),
keyBP.baseAddress!, keySize,
nil,
dataBP.baseAddress!, dataLength,
bufferBP.baseAddress!, bufferSize,
&numBytesDecrypted)
}
}
}
guard cryptStatus == kCCSuccess else {
print("fail: \(cryptStatus)")
return nil
}
print("Success")
buffer.count = numBytesDecrypted
return buffer
}


Anyway, with using the right method, you can get a 14-byte Data.

When you get the Data successfully, step 4 cleared and the last step: Convert the Data to String.
You usually use String.init(data:encoding:) to make it.
Thanks OOPer deeply ..Grateful for your guidance ..
Finally I Could Decrypt the Message Correctly : "Ashish Sharma"
Code Block
var textMessageData =  aesOperation(CCOperation(kCCDecrypt), on: textData, keyData: SecKeyDecryptedData!)
  /* Step 5 */
 let datastring = NSString(data: textMessageData!, encoding: String.Encoding.utf8.rawValue)


Got the message correctly decrypted .Thank You ! Thank You ! Thank You ! so much!!!
deeply grateful...!!

Thanks for reporting.

But the last line should be something like:
Code Block
let datastring = String(data: textMessageData!, encoding: .utf8)

In your Swift code, you should better use String rather than NSString as far as you can.

And I usually do not use so many forced-unwrappings (!) and would use more guard-lets. Hope such things may be improved in your production code.

Anyway, I am very happy to hear you have made it.
Thanks OOPer , will keep that in mind to use Strings in Swift instead of NSString , an will use guard-let to avoid force unwrapping.


Hey OOPer , can you just help me with last thing , I have opened a new Thread, Please check..
Thanks for all your Help!!!
AES encrypted secret key in JAVA decrypt in Swift
 
 
Q