Disclaimer: This post was written entirely with GPT-4 model
Version: 0.1
Greetings,
The absence of an RSA library in B4i, particularly for generating RSA key pairs and adjusting encryption key size, has been a personal challenge for me. Despite not being well-versed in Objective C, I was committed to discovering a viable solution.
With the assistance of GPT-4, inline Objective C and native objects were utilized to interact with iOS's native cryptography functions. It was a challenging process that involved not only feeding GPT-4 with Apple's developer documentation but also providing it with relevant StackOverflow answers. Numerous iterations were required, but we eventually achieved success.
Bear in mind, I'm not a cryptography expert. This solution functions, but I encourage those more knowledgeable in cryptography to review and provide feedback.
Multiple encryption types have been added. The available algorithms include:
Here's the inline Objective C code:
Then, to generate key pairs, encrypt, and decrypt text, use the following B4i code. Modifying it to encrypt and decrypt bytes should be straightforward:
It's also important to highlight: my familiarity with key formats is somewhat limited. Apple's public key isn't in X509 and needs conversion by adding a specific header to the key. I've included a function to handle this, and the converted key should be accepted in B4j and B4A. If anyone can provide more insight on this matter, it would be greatly appreciated.
Looking forward to your feedback and implementations!
Happy coding!
Version: 0.1
Greetings,
The absence of an RSA library in B4i, particularly for generating RSA key pairs and adjusting encryption key size, has been a personal challenge for me. Despite not being well-versed in Objective C, I was committed to discovering a viable solution.
With the assistance of GPT-4, inline Objective C and native objects were utilized to interact with iOS's native cryptography functions. It was a challenging process that involved not only feeding GPT-4 with Apple's developer documentation but also providing it with relevant StackOverflow answers. Numerous iterations were required, but we eventually achieved success.
Bear in mind, I'm not a cryptography expert. This solution functions, but I encourage those more knowledgeable in cryptography to review and provide feedback.
Multiple encryption types have been added. The available algorithms include:
- kSecKeyAlgorithmRSAEncryptionPKCS1
- kSecKeyAlgorithmRSAEncryptionRaw
- kSecKeyAlgorithmRSAEncryptionOAEPSHA1
- kSecKeyAlgorithmRSAEncryptionOAEPSHA256
- kSecKeyAlgorithmRSAEncryptionOAEPSHA512
- kSecKeyAlgorithmRSAEncryptionOAEPSHA1AESGCM
- kSecKeyAlgorithmRSAEncryptionOAEPSHA256AESGCM
- kSecKeyAlgorithmRSAEncryptionOAEPSHA512AESGCM
Here's the inline Objective C code:
Inline Objective C Code:
#If OBJC
#import <Security/Security.h>
#import <CommonCrypto/CommonCrypto.h>
- (NSDictionary *)generateRSAKeyPairWithSize:(int)keySize {
NSData* tag = [@"com.example.keys.mykey" dataUsingEncoding:NSUTF8StringEncoding];
NSDictionary* attributes =
@{ (id)kSecAttrKeyType: (id)kSecAttrKeyTypeRSA,
(id)kSecAttrKeySizeInBits: @(keySize),
(id)kSecPrivateKeyAttrs:
@{ (id)kSecAttrIsPermanent: @YES,
(id)kSecAttrApplicationTag: tag,
},
};
CFErrorRef error = NULL;
SecKeyRef privateKey = SecKeyCreateRandomKey((__bridge CFDictionaryRef)attributes, &error);
if (!privateKey) {
NSError *err = CFBridgingRelease(error); // ARC takes ownership
// Handle the error. . .
}
SecKeyRef publicKey = SecKeyCopyPublicKey(privateKey);
NSData *publicKeyData = (__bridge_transfer NSData*)SecKeyCopyExternalRepresentation(publicKey, &error);
NSData *privateKeyData = (__bridge_transfer NSData*)SecKeyCopyExternalRepresentation(privateKey, &error);
if (!publicKeyData || !privateKeyData) {
// Handle the error
}
NSDictionary *result = @{@"publicKey": publicKeyData, @"privateKey": privateKeyData};
if (publicKey) { CFRelease(publicKey); }
if (privateKey) { CFRelease(privateKey); }
return result;
}
- (NSData *)encryptData:(NSData *)data withPublicKey:(NSData *)publicKeyData keySize:(int)keySize algorithmStr:(NSString *)algorithmStr {
CFErrorRef error = NULL;
SecKeyRef publicKey = SecKeyCreateWithData((__bridge CFDataRef)publicKeyData,
(__bridge CFDictionaryRef)@{(id)kSecAttrKeyType: (id)kSecAttrKeyTypeRSA,
(id)kSecAttrKeyClass: (id)kSecAttrKeyClassPublic,
(id)kSecAttrKeySizeInBits: @(keySize)}, &error);
SecKeyAlgorithm algorithm ;
if ([algorithmStr isEqualToString:@"kSecKeyAlgorithmRSAEncryptionOAEPSHA512"]) {
algorithm = kSecKeyAlgorithmRSAEncryptionOAEPSHA512;
} else if ([algorithmStr isEqualToString:@"kSecKeyAlgorithmRSAEncryptionRaw"]) {
algorithm = kSecKeyAlgorithmRSAEncryptionRaw;
} else if ([algorithmStr isEqualToString:@"kSecKeyAlgorithmRSAEncryptionOAEPSHA256"]) {
algorithm = kSecKeyAlgorithmRSAEncryptionOAEPSHA256;
} else if ([algorithmStr isEqualToString:@"kSecKeyAlgorithmRSAEncryptionOAEPSHA1"]) {
algorithm = kSecKeyAlgorithmRSAEncryptionOAEPSHA1;
} else if ([algorithmStr isEqualToString:@"kSecKeyAlgorithmRSAEncryptionOAEPSHA512AESGCM"]) {
algorithm = kSecKeyAlgorithmRSAEncryptionOAEPSHA512AESGCM;
} else if ([algorithmStr isEqualToString:@"kSecKeyAlgorithmRSAEncryptionOAEPSHA256AESGCM"]) {
algorithm = kSecKeyAlgorithmRSAEncryptionOAEPSHA256AESGCM;
} else if ([algorithmStr isEqualToString:@"kSecKeyAlgorithmRSAEncryptionOAEPSHA1AESGCM"]) {
algorithm = kSecKeyAlgorithmRSAEncryptionOAEPSHA1AESGCM;
} else {
// Default to kSecKeyAlgorithmRSAEncryptionPKCS1
algorithm = kSecKeyAlgorithmRSAEncryptionPKCS1;
}
BOOL canEncrypt = SecKeyIsAlgorithmSupported(publicKey, kSecKeyOperationTypeEncrypt, algorithm);
canEncrypt &= ([data length] < (SecKeyGetBlockSize(publicKey) - 130));
NSData* encryptedData = nil;
if (canEncrypt) {
encryptedData = (NSData*)CFBridgingRelease(SecKeyCreateEncryptedData(publicKey, algorithm, (__bridge CFDataRef)data, &error));
if (!encryptedData) {
NSError *err = CFBridgingRelease(error);
// Handle the error. . .
}
}
return encryptedData;
}
- (NSData *)decryptData:(NSData *)data withPrivateKey:(NSData *)privateKeyData keySize:(int)keySize algorithmStr:(NSString *)algorithmStr {
CFErrorRef error = NULL;
SecKeyRef privateKey = SecKeyCreateWithData((__bridge CFDataRef)privateKeyData,
(__bridge CFDictionaryRef)@{(id)kSecAttrKeyType: (id)kSecAttrKeyTypeRSA,
(id)kSecAttrKeyClass: (id)kSecAttrKeyClassPrivate,
(id)kSecAttrKeySizeInBits: @(keySize)}, &error);
SecKeyAlgorithm algorithm ;
if ([algorithmStr isEqualToString:@"kSecKeyAlgorithmRSAEncryptionOAEPSHA512"]) {
algorithm = kSecKeyAlgorithmRSAEncryptionOAEPSHA512;
} else if ([algorithmStr isEqualToString:@"kSecKeyAlgorithmRSAEncryptionRaw"]) {
algorithm = kSecKeyAlgorithmRSAEncryptionRaw;
} else if ([algorithmStr isEqualToString:@"kSecKeyAlgorithmRSAEncryptionOAEPSHA256"]) {
algorithm = kSecKeyAlgorithmRSAEncryptionOAEPSHA256;
} else if ([algorithmStr isEqualToString:@"kSecKeyAlgorithmRSAEncryptionOAEPSHA1"]) {
algorithm = kSecKeyAlgorithmRSAEncryptionOAEPSHA1;
} else if ([algorithmStr isEqualToString:@"kSecKeyAlgorithmRSAEncryptionOAEPSHA512AESGCM"]) {
algorithm = kSecKeyAlgorithmRSAEncryptionOAEPSHA512AESGCM;
} else if ([algorithmStr isEqualToString:@"kSecKeyAlgorithmRSAEncryptionOAEPSHA256AESGCM"]) {
algorithm = kSecKeyAlgorithmRSAEncryptionOAEPSHA256AESGCM;
} else if ([algorithmStr isEqualToString:@"kSecKeyAlgorithmRSAEncryptionOAEPSHA1AESGCM"]) {
algorithm = kSecKeyAlgorithmRSAEncryptionOAEPSHA1AESGCM;
} else {
// Default to kSecKeyAlgorithmRSAEncryptionPKCS1
algorithm = kSecKeyAlgorithmRSAEncryptionPKCS1;
}
BOOL canDecrypt = SecKeyIsAlgorithmSupported(privateKey, kSecKeyOperationTypeDecrypt, algorithm);
canDecrypt &= ([data length] == SecKeyGetBlockSize(privateKey));
NSData* decryptedData = nil;
if (canDecrypt) {
decryptedData = (NSData*)CFBridgingRelease(SecKeyCreateDecryptedData(privateKey, algorithm, (__bridge CFDataRef)data, &error));
if (!decryptedData) {
NSError *err = CFBridgingRelease(error);
// Handle the error. . .
}
}
return decryptedData;
}
- (NSData *)convertToX509:(NSData *)publicKeyData keySize:(int)keySize {
CFErrorRef error = NULL;
SecKeyRef publicKey = SecKeyCreateWithData((__bridge CFDataRef)publicKeyData,
(__bridge CFDictionaryRef)@{(id)kSecAttrKeyType: (id)kSecAttrKeyTypeRSA,
(id)kSecAttrKeyClass: (id)kSecAttrKeyClassPublic,
(id)kSecAttrKeySizeInBits: @(keySize)}, &error);
if (!publicKey || error) {
return nil;
}
// Get the public key data
CFDataRef keyData = SecKeyCopyExternalRepresentation(publicKey, NULL);
if (!keyData) {
CFRelease(publicKey);
return nil;
}
// Create the ASN.1 header for an RSA public key in X.509 format
static uint8_t const header[] = {
0x30, 0x82, 0x01, 0x22, 0x30, 0x0D, 0x06, 0x09, 0x2A, 0x86, 0x48, 0x86, 0xF7, 0x0D, 0x01, 0x01, 0x01, 0x05, 0x00, 0x03, 0x82, 0x01, 0x0F, 0x00
};
// Add the header to the public key data
NSMutableData *result = [NSMutableData dataWithBytes:header length:sizeof(header)];
[result appendData:(__bridge NSData *)keyData];
CFRelease(keyData);
CFRelease(publicKey);
return result;
}
#End If
Then, to generate key pairs, encrypt, and decrypt text, use the following B4i code. Modifying it to encrypt and decrypt bytes should be straightforward:
B4i Code:
Private Sub Application_Start (Nav As NavigationController)
'...
Private keySize As Int = 2048
' Generate RSA key pair
Dim keyPair As Map = GenerateRSAKeyPair(keySize)
Log(keyPair)
Dim publicKey As String = keyPair.Get("publicKey")
Dim privateKey As String = keyPair.Get("privateKey")
' Encrypt data
Dim data As String = "Hello, World!"
' Available algorithms:
' kSecKeyAlgorithmRSAEncryptionPKCS1
' kSecKeyAlgorithmRSAEncryptionRaw
' kSecKeyAlgorithmRSAEncryptionOAEPSHA1
' kSecKeyAlgorithmRSAEncryptionOAEPSHA256
' kSecKeyAlgorithmRSAEncryptionOAEPSHA512
' kSecKeyAlgorithmRSAEncryptionOAEPSHA1AESGCM
' kSecKeyAlgorithmRSAEncryptionOAEPSHA256AESGCM
' kSecKeyAlgorithmRSAEncryptionOAEPSHA512AESGCM
Dim encryptedData As String = EncryptData(data, publicKey, keySize, "kSecKeyAlgorithmRSAEncryptionPKCS1")
Log("Encrypted data: " & encryptedData)
' Decrypt data
Dim decryptedData As String = DecryptData(encryptedData, privateKey, keySize, "kSecKeyAlgorithmRSAEncryptionPKCS1")
Log("Decrypted data: " & decryptedData)
Dim x509PublicKey As String = ConvertToX509(publicKey, keySize) 'Share this key with B4j and B4A
Log(x509PublicKey)
End Sub
Sub GenerateRSAKeyPair(keySize As Int) As Map
Dim no As NativeObject = Me
Dim keyPair As NativeObject = no.RunMethod("generateRSAKeyPairWithSize:", Array(keySize))
Dim publicKey As Object = keyPair.GetField("publicKey")
Dim privateKey As Object = keyPair.GetField("privateKey")
' Convert NSData to byte arrays
Dim publicKeyBytes() As Byte = no.NSDataToArray(publicKey)
Dim privateKeyBytes() As Byte = no.NSDataToArray(privateKey)
' Dim bc As ByteConverter
Dim su As StringUtils
' Convert byte arrays to base64 strings
Dim publicKeyB64 As String = su.EncodeBase64(publicKeyBytes)
Dim privateKeyB64 As String = su.EncodeBase64(privateKeyBytes)
Dim result As Map = CreateMap("publicKey": publicKeyB64, "privateKey": privateKeyB64)
Return result
End Sub
Sub EncryptData(data As String, publicKeyBase64 As String, keySize As Int, algorithm As String) As String
Dim no As NativeObject = Me
Dim bc As ByteConverter
Dim su As StringUtils
' Convert the public key from Base64 string to byte array
Dim publicKeyBytes() As Byte = su.DecodeBase64(publicKeyBase64)
' Convert the byte array to NSData object
Dim publicKey As Object = no.ArrayToNSData(publicKeyBytes)
Dim dataBytes() As Byte = bc.StringToBytes(data, "UTF8")
Dim dataBytesNSdata As Object = no.ArrayToNSData(dataBytes)
Dim encryptedString As String = ""
Try
Dim encryptedData As Object = no.RunMethod("encryptData:withPublicKey:keySize:algorithmStr:", Array As Object(dataBytesNSdata, publicKey, keySize, algorithm))
encryptedString = su.EncodeBase64(no.NSDataToArray(encryptedData))
Catch
Log(LastException)
End Try
Return encryptedString
End Sub
Sub DecryptData(encryptedData As String, privateKey As String, keySize As Int, algorithm As String) As String
Dim no As NativeObject = Me
Dim bc As ByteConverter
Dim su As StringUtils
Dim encryptedBytes() As Byte = su.DecodeBase64(encryptedData)
Dim encryptedBytesNSdata As Object = no.ArrayToNSData(encryptedBytes)
' Convert the private key from Base64 string to byte array
Dim privateKeyBytes() As Byte = su.DecodeBase64(privateKey)
' Convert the byte array to NSData object
Dim privateKeyData As Object = no.ArrayToNSData(privateKeyBytes)
Dim decryptedString As String = ""
Try
Dim decryptedData As Object = no.RunMethod("decryptData:withPrivateKey:keySize:algorithmStr:", Array(encryptedBytesNSdata, privateKeyData, keySize, algorithm))
Dim decryptedBytes() As Byte = no.NSDataToArray(decryptedData)
decryptedString = bc.StringFromBytes(decryptedBytes, "UTF8")
Catch
Log(LastException)
End Try
Return decryptedString
End Sub
Sub ConvertToX509(publicKey As String, keySize As Int) As String
Dim no As NativeObject = Me
Dim su As StringUtils
Dim privateKeyBytes() As Byte = su.DecodeBase64(publicKey)
Dim data As Object = no.ArrayToNSData(privateKeyBytes)
Dim result As Object = no.RunMethod("convertToX509:keySize:", Array(data, keySize))
Dim publicKeyBytes() As Byte = no.NSDataToArray(result)
Dim publicKeyB64 As String = su.EncodeBase64(publicKeyBytes)
Return publicKeyB64
End Sub
It's also important to highlight: my familiarity with key formats is somewhat limited. Apple's public key isn't in X509 and needs conversion by adding a specific header to the key. I've included a function to handle this, and the converted key should be accepted in B4j and B4A. If anyone can provide more insight on this matter, it would be greatly appreciated.
Looking forward to your feedback and implementations!
Happy coding!