Convert private base64 encoded key to SecKey - ios

I did generate private key using OpenSSL like this:
openssl req -x509 -newkey rsa:4096 -keyout key.pem -out cert.pem -days 563 -nodes -subj "//C=LT\ST=Vilnius\L=Vilnius\O=Briedis\OU=Org\CN=www.example.com"
The key.pem file contains privte key in base64 format. Removed BEGIN and END marks. As well new lines. After applying these changes private key looks like this:
let privteKeyString = “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“
Conversion from base64 to SecKey:
let privateKeyData = Data(privteKeyString.utf8)
var error: Unmanaged<CFError>?
let secKey = SecKeyCreateWithData(privateKeyData as! NSData, [
kSecAttrKeyType: kSecAttrKeyTypeRSA,
kSecAttrKeySizeInBits: 4096,
kSecAttrKeyClass: kSecAttrKeyClassPrivate,
] as NSDictionary, &error)
After execution getting an -50 error.
What is the way to convert private base64 key to SecKey?

Usually all RSAPrivateKey is not a vanilla RSAPrivateKey, but rather a PrivateKeyInfo (see Section 5 of RFC 5208). IOS do not support PrivateKeyInfo key structure. In this case a converted to RSAPrivateKey is needed. Steps to create iOS SecKey from PrivateKeyInfo:
Using OpenSSL convert private key.pem to DER format.
openssl rsa -in key.pem -outform der -out key.der
Drag and drop key.der file to the Xcode project.
Load and print out private key as vanilla RSAPrivateKey:
let keyUrl = Bundle.main.url(forResource: "key", withExtension: "der")
let keyDerData = try? Data(contentsOf: keyUrl!)
print(keyDerData?.base64EncodedString())
Convert vanilla RSAPrivateKey raw data to SecKey.
var error: Unmanaged<CFError>?
let secKey = SecKeyCreateWithData(keyDerData as! NSData, [
kSecAttrKeyType: kSecAttrKeyTypeRSA,
kSecAttrKeySizeInBits: 4096,
kSecAttrKeyClass: kSecAttrKeyClassPrivate,
] as NSDictionary, &error)

Related

iOS CryptoSwift AES Encryption to Python Decryption works - but not the inverse

I am using CryptoSwift 1.4.1, iOS 15.2, PyCryptodome 3.12.0, and XCode 13.2.1 to encrypt small string messages that I send to a Raspberry Pi Linux Device over BLE. It works when iOS encrypts the message and sends it to the Raspberry Pi. The Pi can successfully decrypt it. Now I want to do the inverse, encrypt a message on the Pi and have the iOS App read and decrypt it. This, however is not working and the decrypted value is the not the message I encrypted on the Pi.
Working iOS encryption:
func aesEncrypt(stringToEncrypt: String, key: Array<UInt8>, iv: Array<UInt8>) throws -> String {
let data = stringToEncrypt.data(using: String.Encoding.utf8)
let encrypted = try AES(key: key, blockMode: CFB(iv: iv), padding: .noPadding).encrypt((data?.bytes)!)
return encrypted.toHexString()
}
let ivString = "4198816658388141"
let keyString = "9004786896524916"
let key = [UInt8](keyString.utf8)
let iv = [UInt8](ivString.utf8)
let encryptedSsid = try! aesEncrypt(stringToEncrypt: ssid!, key: key, iv: iv)
Working Raspberry Pi decryption in Python:
KEY = b'9004786896524916'
IV = b'4198816658388141'
MODE = AES.MODE_CFB
def decrypt(key, iv, encrypted_text):
logger.info(f"Encrypted: {encrypted_text}")
aes = AES.new(key, MODE, iv, segment_size=128)
encrypted_text_bytes = binascii.a2b_hex(encrypted_text)
decrypted_text = aes.decrypt(encrypted_text_bytes).decode("utf-8")
logger.info(f"Decrypted: {decrypted_text}")
return decrypted_text
I tried to encrypt a message on the Pi with the following code:
KEY = b'9004786896524916'
IV = b'4198816658388141'
MODE = AES.MODE_CFB
def encrypt(key, decrypted_text):
# Create cipher object and encrypt the data
logger.info(f"Decrypted: {decrypted_text}")
cipher = AES.new(key, MODE, segment_size=128) # Create a AES cipher object with the key using the mode CBC
#encrypted_text = cipher.encrypt(pad(decrypted_text, AES.block_size)) # Pad the input data and then encrypt
encrypted_text = cipher.encrypt(decrypted_text) # Pad the input data and then encrypt
logger.info(f"Encrypted: {encrypted_text}")
return encrypted_text
...
encrypt(KEY, returnString('utf-8'))
However, the iOS App fails to decrypt it properly with this method:
func aesDecrypt(stringToDecrypt: String, key: Array<UInt8>, iv: Array<UInt8>) throws -> String {
let data = stringToDecrypt.data(using: String.Encoding.utf8)
let decrypted = try AES(key: key, blockMode: CFB(iv: iv), padding: .noPadding).decrypt((data?.bytes)!)
return decrypted.toHexString()
}
let ivString = "4198816658388141"
let keyString = "9004786896524916"
let key = [UInt8](keyString.utf8)
let iv = [UInt8](ivString.utf8)
var message = try! aesDecrypt(stringToDecrypt: charString, key: key, iv: iv)
How can I get decryption to work properly in the iOS App when a message is encrypted on the Pi?
In the encrypt() method the IV is not considered. As in aesEncrypt(), the IV must be passed and used when creating the AES object.
Furthermore there are bugs in the encoding: The plaintext must be UTF8 encoded and the ciphertext must be hex encoded:
from Crypto.Cipher import AES
import binascii
def encrypt(key, iv, plaintext):
cipher = AES.new(key, MODE, iv, segment_size=128)
plaintext_bytes = plaintext.encode("utf-8")
ciphertext = cipher.encrypt(plaintext_bytes)
ciphertext_hex = binascii.b2a_hex(ciphertext)
return ciphertext_hex
This function is the counterpart to decrypt(), i.e. encrypt() can be used to generate a ciphertext which can be decrypted with decrypt() (or aesDecrypt()).
In the iOS code there are two bugs, both concerning the encoding: The ciphertext must not be UTF8 encoded, but hex decoded. And the decrypted data must not be hex encoded, but UTF-8 decoded.
A possible fix is:
func aesDecrypt(stringToDecrypt: String, key: Array<UInt8>, iv: Array<UInt8>) throws -> String {
let data = Array<UInt8>(hex: stringToDecrypt)
let decrypted = try AES(key: key, blockMode: CFB(iv: iv), padding: .noPadding).decrypt(data)
return String(bytes: decrypted, encoding: .utf8)!
}
This function is the counterpart to aesEncrypt(), i.e. aesDecrypt() can be used to decrypt a ciphertext generated with aesEncrypt() (or encrypt()).
Regarding security: A static IV is insecure. Instead, the IV should be randomly generated for each encryption. Since the (non-secret IV) is needed for decryption, it is passed along with the ciphertext (typically concatenated).

Retrieve issuer name from certificate by SecCertificate

I have a certificate file. SecCertificate object is created correctly.
let data = try? Data(contentsOf: fileUrl!)
let certificate = SecCertificateCreateWithData(nil, data! as CFData)
▿ Optional<SecCertificateRef>
- some : <cert(0x7fec5d608250) s: www.google.com i: Avast trusted CA>
// I can get Subject name by
SecCertificateCopySubjectSummary(certificate!)! as String
// I tried printing SecCertificateCopyNormalizedIssuerSequence
var data1 = SecCertificateCopyNormalizedIssuerSequence(certificate!)! as Data
print(String(data: data1, encoding: .utf8))
Optional("0h1\u{0B}0\t\u{06}\u{03}U\u{04}\u{06}\u{13}\u{02}CZ1\u{0F}0\r\u{06}\u{03}U\u{04}\u{08}\u{0C}\u{06}Prague1\u{0E}0\u{0C}\u{06}\u{03}U\u{04}\n\u{0C}\u{05}AVAST1\u{1D}0\u{1B}\u{06}\u{03}U\u{04}\u{0B}\u{0C}\u{14}Software Development1\u{19}0\u{17}\u{06}\u{03}U\u{04}\u{03}\u{0C}\u{10}Avast trusted CA")
But I need to get issuers name info like common name or organisation(like i from SecCertificateRef in above example) using SecCertificate.h
Thanks.

how to encrypt string using AES 256, ECB mode with padding pkcs5?

I'm new to iOS and I don't have idea about how to encrypt string using AES 256 with ECB mode and padding I take look of cryptoswift but I get error of key length I have 64 character key and I'm not able to encrypt
func aes_Encrypt(AES_KEY: String) -> String {
var result = ""
do {
let key: [UInt8] = Array(AES_KEY.utf8) as [UInt8]
let aes = try! AES(key: key, blockMode: ECB() as BlockMode, padding: .pkcs5)
let encrypted = try aes.encrypt(Array(self.utf8))
result = encrypted.toHexString()
print("AES Encryption Result: \(result)")
} catch {
print("Error: \(error)")
}
return result
}
64 characters × 8 bits = 512 bits, not the 256 required for AES256.
If the string you are passing in is a hexadecimal representation of a key (e.g. "1234abcd…"), you will need to break it into a sequence of two-character substrings and use UInt8(…, radix:16) to parse each one as hexadecimal.

How to get SecIdentity after SecCertificateCreateWithData and SecItemAdd?

I was given a .cer file which needs to be used to connect with the server properly. At first SecCertificateCreateWithData crashed, so after research, I ended up converting .cer file to der format (openssl x509 -in cert.cer -outform der -out certder.der), and then iOS seemed to accept it.
I need to get SecIdentity object, however, there seems to be a problem:
let cerPath = Bundle.main.path(forResource: "certder", ofType: "der")
let data: NSData = try! Data(contentsOf: URL(fileURLWithPath: cerPath!)) as! NSData
let cert: SecCertificate = SecCertificateCreateWithData(nil, data)!
let params : [String: Any] = [
kSecClass as String : kSecClassCertificate,
kSecValueRef as String : cert,
kSecAttrLabel as String: "label1",
]
SecItemDelete(params as CFDictionary)
let status = SecItemAdd(params as CFDictionary, nil)
print("status1: \(status)")
let query: [String: Any] = [
kSecClass as String : kSecClassIdentity,
kSecReturnRef as String: true,
kSecAttrLabel as String: "label1"
]
var secIdentity: SecIdentity?
var extractedData: AnyObject?
let status2 = SecItemCopyMatching(query as NSDictionary, &extractedData)
print("status2: \(status2)")
if (status2 == errSecSuccess) {
print("success")
secIdentity = extractedData as! SecIdentity?
}
Log:
status1: 0
status2: -25300. Error -25300 is errSecItemNotFound
Here is how info of that given certificate looks like:
openssl x509 -in cert.cer -text
Certificate:
Data:
Version: 3 (0x2)
Serial Number: 1465898828 (0x575fd74c)
Signature Algorithm: sha256WithRSAEncryption
Issuer: C=LT, L=NamePurposelyHidden, O=NamePurposelyHidden, OU=NamePurposelyHidden, CN=NamePurposelyHidden
Validity
Not Before: Aug 9 08:21:03 2014 GMT
Not After : Jul 27 08:21:03 2064 GMT
Subject: C=LT, L=NamePurposelyHidden, O=NamePurposelyHidden, OU=NamePurposelyHidden, CN=NamePurposelyHidden
Subject Public Key Info:
Public Key Algorithm: rsaEncryption
Public-Key: (2048 bit)
Modulus:
00:b6:26:54:85:05:15:bd:2f:af:39:c0:91:41:05:
d2:35:74:bf:d2:e5:35:2d:3d:21:60:15:6b:d9:f6:
66:2b:02:29:c9:94:8b:3c:36:af:f8:5a:05:0f:21:
25:7d:90:5b:ab:70:f5:85:e8:ce:8a:60:2b:a4:61:
36:a8:b9:16:f6:79:7b:e1:1f:37:83:5b:fd:18:db:
70:7a:dd:79:3b:74:0d:4b:4f:4d:49:8c:79:0b:8c:
c1:2e:7c:4c:62:ae:b3:e4:b5:cf:4b:20:2c:98:20:
4a:4c:dd:49:67:99:7f:c1:dd:39:4d:be:8c:b7:6a:
b8:e6:c3:e3:a8:03:21:ec:a3:c8:e4:46:d7:e6:d4:
83:4a:5a:d7:a1:35:6f:54:72:96:b2:52:54:37:d4:
b4:62:f8:07:eb:27:d5:f0:42:0f:5a:3a:b8:ae:78:
38:73:e4:b5:7c:d1:6b:e4:61:6e:fd:df:c8:03:a4:
8d:fe:d3:92:98:df:30:fb:e1:03:45:cb:dd:6a:ca:
50:25:b2:a6:4b:4a:64:e0:79:a6:ef:35:53:df:d9:
48:16:f8:39:08:94:9b:8e:f0:74:01:ae:76:46:9c:
9c:a3:ec:70:45:24:44:96:3d:b4:06:51:2e:1e:dd:
4d:72:09:0d:9d:f1:11:08:9d:24:4b:5c:3e:79:0b:
b9:c7
Exponent: 65537 (0x10001)
X509v3 extensions:
X509v3 Subject Key Identifier:
AE:95:DF:CD:E0:D9:BB:D1:8E:CD:15:86:98:9B:04:35:D7:E6:96:44
Signature Algorithm: sha256WithRSAEncryption
61:d3:98:46:a9:53:db:fc:a9:3b:db:40:40:63:55:7c:66:74:
35:82:83:c8:19:4a:89:ae:bd:1b:5c:26:8e:20:f9:5b:db:aa:
0c:4c:99:4e:28:3b:51:00:33:96:5f:22:40:c1:c1:c8:ed:70:
d7:f7:3e:aa:ec:f9:0e:bb:ec:f8:17:12:c1:49:f6:02:0d:73:
57:39:48:5d:ad:8f:e5:e9:3b:b1:f8:1d:d4:7e:cf:58:d5:78:
73:9c:56:3b:bb:3a:25:36:c8:3b:b8:ba:90:97:6e:28:51:5d:
63:23:3e:90:7d:87:61:13:03:c5:27:1b:51:55:c0:17:76:18:
1a:74:6d:f9:2c:c8:f1:7a:89:64:41:fc:3b:7b:82:4f:e3:e8:
5d:02:40:3a:d5:81:f3:38:de:aa:79:53:1a:d2:a3:60:59:94:
8c:bd:62:bb:6d:70:da:0d:22:3e:9d:e0:fa:5d:20:87:ce:16:
f0:53:f4:b7:1d:c7:b3:59:64:ba:8a:73:75:bc:7f:61:cb:14:
d4:9f:34:3f:bd:b1:06:0c:62:f6:1f:b4:d2:15:38:61:bd:2c:
44:94:1c:e1:88:f4:d2:fc:42:d7:6d:ed:d6:4e:a2:b7:67:20:
01:7a:87:39:3b:4c:2b:3c:ef:3c:15:54:1d:cc:00:30:9c:cc:
e2:4f:71:98

CryptoSwift - Converting UInt8 array to String resolves as nil

(Xcode 8, Swift 3)
Using the CryptoSwift library, I am wanting to encrypt a string and store it in coredata, for some reason, cipherstring results as nil, despite ciphertext having 128 values:
let aes = try AES(key: pw, iv: nil, blockMode: .CBC, padding: PKCS7())
let ciphertext = try aes.encrypt(token.utf8.map({$0}))
let cipherstring = String(bytes:ciphertext, encoding: String.Encoding.utf8) // always nil
I have also tried using the data: overload of string, convering the byte array to a data object. This also results as nil.
EDIT/SOLUTION (per Rob Napier's answer)
// encode/convert to string
let aes = try AES(key: pw, iv: nil, blockMode: .CBC, padding: PKCS7())
let ciphertext = try aes.encrypt(token.utf8.map({$0}))
let cipherstring = Data(bytes: ciphertext).base64EncodedString()
// decode
let aes = try AES(key: pw, iv: nil, blockMode: .CBC, padding: PKCS7())
let cipherdata = Data(base64Encoded: cipherstring)
let ciphertext = try aes.decrypt(cipherdata!.bytes)
let token = String(bytes:ciphertext, encoding:String.Encoding.utf8)
AES encrypted data is a random bunch of bytes. If you pick a random bunch of bytes, it is very unlikely to be valid UTF-8. If you have data and you want a string, you need to encode the data somehow. The most popular way to do that is Base-64. See Data.base64EncodedString() for a tool to do that. You'll also need Data(base64Encoded:) to reconstruct your Data from the String later.

Resources