i've a problem..when i decrypt the data that is returned from my php page,
if the length of the string is less than 16, the char \0 is append to string.
Original string is: 100000065912248
I decrypt the encrypted string with this function:
#define FBENCRYPT_ALGORITHM kCCAlgorithmAES128
#define FBENCRYPT_BLOCK_SIZE kCCBlockSizeAES128
#define FBENCRYPT_KEY_SIZE kCCKeySizeAES256
+ (NSData*)decryptData:(NSData*)data key:(NSData*)key iv:(NSData*)iv;
{
NSData* result = nil;
// setup key
unsigned char cKey[FBENCRYPT_KEY_SIZE];
bzero(cKey, sizeof(cKey));
[key getBytes:cKey length:FBENCRYPT_KEY_SIZE];
// setup iv
char cIv[FBENCRYPT_BLOCK_SIZE];
bzero(cIv, FBENCRYPT_BLOCK_SIZE);
if (iv) {
[iv getBytes:cIv length:FBENCRYPT_BLOCK_SIZE];
}
// setup output buffer
size_t bufferSize = [data length] + FBENCRYPT_BLOCK_SIZE;
void *buffer = malloc(bufferSize);
int length = [data length];
// do decrypt
size_t decryptedSize = 0;
CCCryptorStatus cryptStatus = CCCrypt(kCCDecrypt,
FBENCRYPT_ALGORITHM,
0,
cKey,
FBENCRYPT_KEY_SIZE,
cIv,
[data bytes],
[data length],
buffer,
bufferSize,
&decryptedSize);
if (cryptStatus == kCCSuccess) {
result = [NSData dataWithBytesNoCopy:buffer length:decryptedSize];
} else {
free(buffer);
NSLog(#"[ERROR] failed to decrypt| CCCryptoStatus: %d", cryptStatus);
}
return result;
}
I send a nil "iv" parameter to the function and after i use "cIv" in function, and it contain this:
The result is exactly, but the length of string is 16 instead of 15 (string: 100000065912248). In fact, the last character is \0.
Why? how can i solve?
EDIT:
PHP encrypt function:
function encrypt($plaintext) {
$key = 'a16byteslongkey!a16byteslongkey!';
$base64encoded_ciphertext = base64_encode(mcrypt_encrypt(MCRYPT_RIJNDAEL_128, $key, $plaintext, MCRYPT_MODE_CBC));
$base64encoded_ciphertext = trim($base64encoded_ciphertext);
return $base64encoded_ciphertext;
}
AES is a block cypher and encrypts/decrypts blocks of length 128 bits (16 bytes). So if the data is not a block size some padding must be added. The most popular and supported by Apple is PKCS7.
Interfacing with PHP one must consider padding and possible base64 encoding.
The solution is to use the same padding on both sides, PHP and iOS.
AES always operates on 16 bytes, there is no option--so, if you have 15 bytes a byte is going to have to be added, that is padding. From what I understand (not much about PHP encryption) PHP does not do true PCKS7padding and it is best to pad yourself. Lookup PKCS7 in Wikipedia.
You should be OK with zero padding (the default) if you only operate on strings, but I would recommend PKCS#7 padding, if only for interoperability reasons.
With zero padding the plaintext is padded with 00 valued bytes, but only if required. This is different from PKCS#7 padding, which is always deployed. After decryption you can use the trim function on the resulting plaintext after decryption. You should then get the original string.
This obviously wont work on binary data because it may end with a character that is removed by the trim function. Beware that trim in PHP seems to strip off 00 bytes. This is not a given, officially 00 is not whitespace, even though it is treated that way by many runtimes.
You have to remove padding from the decrypted data
function removePadding($decryptedText){
$strPad = ord($decryptedText[strlen($decryptedText)-1]);
$decryptedText= substr($decryptedText, 0, -$strPad);
return $decryptedText;
}
Related
I am implementing the AES/CBC encryption using the CommonCrypto library for Objective C code base and i found a code as answered by Zaph in which he recommends to prefix the IV with the encrypted text which we receive at the end of CommonCrypto's kCCEncrypt operation method zaph's answer. The bunch of code for AES encryption as suggested by him in one of his answer looks like this :
if (key.length != 16 && key.length != 24 && key.length != 32) {
*error = [NSError errorWithDomain:#"keyLengthError" code:-1 userInfo:nil];
return nil;
}
CCCryptorStatus ccStatus = kCCSuccess;
int ivLength = kCCBlockSizeAES128;
size_t cryptBytes = 0;
NSMutableData *dataOut = [NSMutableData dataWithLength:ivLength + data.length + kCCBlockSizeAES128];
int status = SecRandomCopyBytes(kSecRandomDefault, ivLength, dataOut.mutableBytes);
if (status != 0) {
*error = [NSError errorWithDomain:#"ivError" code:status userInfo:nil];
return nil;
}
ccStatus = CCCrypt(kCCEncrypt,
kCCAlgorithmAES,
kCCOptionPKCS7Padding,
key.bytes, key.length,
dataOut.bytes,
data.bytes,
data.length,
dataOut.mutableBytes + ivLength,
dataOut.length,
&cryptBytes);
if (ccStatus == kCCSuccess) {
dataOut.length = cryptBytes + ivLength;
}
else {
if (error) {
*error = [NSError errorWithDomain:#"kEncryptionError" code:ccStatus userInfo:nil];
}
dataOut = nil;
}
return dataOut;
My doubt is at which line and how the IV is getting prefixed with the Encrypted text in the above code? Any help in understanding the code will help me a lot.
Dear all after much head scratching and slacking with the help of a wonderful person i am able to decode what the above code is doing .
There are four parts to it.
1) The NSMutableData is initialized to size that's big enough to
hold the ciphertext and the IV (IV length + data length + block
size)
2) The random IV is generated in place, at the beginning of the
data, using SecRandomCopyBytes.
3) Using a tiny bit of pointer arithmetics, the ciphertext is also
written into the same Data, but the starting pointer is moved after
the IV (dataOut.mutableBytes + ivLength). Technically it means that the dataOut.mutableBytes pointer is moved forward by ivLength
bytes (so it points at immediately after the IV in the large buffer)
4) And finally, any excess noise is cut off by setting the length of
the Data to the ivLength + the length of the ciphertext that CCCrypt
reported
Final interpretation : When dataOut.bytes is passed as IV in CCCrypt then it means the CCCRypt gets the IV from the dataOut by its pointer as it has already the random IV in it which is generated by SecRandomCopyBytes . Also dataOut.mutableBytes + ivLength means the pointer is moved forward by ivLength bytes (so it points at immediately after the IV in the large buffer) of dataOut for storing cipher text as it has already the IV till that position.
cryptBytes which is returned just contains the length of the ciphered text. Thus the ciphered text as explained above is stored from the pointer position which was moved. Thus in short the NSMutableData was initialised with enough space by the code NSMutableData *dataOut = [NSMutableData dataWithLength:ivLength + data.length + kCCBlockSizeAES128];for writing IV and Ciphered text, in the next line of code int status = SecRandomCopyBytes(kSecRandomDefault, ivLength, dataOut.mutableBytes);the random IV of length 16 is written to dataOut.mutableBytes buffer. Now the NSMutableData has IV bytes in it. Next in CCCrypt dataOut.bytes can be passed as IV parameter as NSMutableData already has IV bytes. Moving next we have to give the buffer position for writing the crypted text , thus we are writing to NSMutableData by moving the pointer position defined by dataOut.mutableBytes + ivLength so this is actually the buffer position from where we have to write ciphered text after IV. At last cryptoBytes returns the length of the ciphered text(it does not has length of IV included as starting pointer was moved by +ivLength )
So finally NSMutableData has IV(length of 16 bytes) and Ciphered text(length given by cryptBytes) in it and thus the extra buffer space in NSMutableData is trimmed of by giving the length of dataOut as dataOut.length = cryptBytes + ivLength;So final dataOut variable has IV plus ciphered text and it is returned in return dataOut;
Note : it's important to consider that CCCrypt is a C function, it doesn't work on objects like NSData or NSStrings, but pointers to memory addresses. in other words, it doesn't care where you feed it inputs from and where it will write its output, as long as the pointed addresses are valid and there's enough space starting from that address allocated. (and of course, for inputs, the actual data is already there).
i'm using external library OpenSSL-Universal for encrypting password using RSA_PKCS1_PADDING. Unfortunately the output of char encoded result have an inconsistent length. Let say i have 2048 bit modulus the length result i expect is 128 otherwise it will be failed to decrypt back to plain text.
BIGNUM *xponent = BN_new();
BIGNUM *modulus = BN_new();
BN_hex2bn(&xponent,xponentInHex);
BN_hex2bn(&modulus,modInHex);
RSA *rsa = RSA_new();
rsa->e = xponent;
rsa->n = modulus;
char encoded[1024] = {0};
RSA_public_encrypt(
(int)strlen(charString),// from len
(const unsigned char *)charString, // from
(unsigned char *)encoded, // to
rsa,
RSA_PKCS1_PADDING
);
RSA_free(rsa);
NSLog(#"%lu", strlen(encoded));
if there is anything wrong with my implements or if you have some explanation about inconsistent length result please let me know
Encrypted Output from RSA_public_encrypt is not a string, so you cannot check it's length using strlen. I hope you do realize that strlen will consider any '\0' seen in your encrypted output as end of string and will return the numbers of bytes till that point as length of the string. But, encrypted buffer can have valid bytes which can be '\0'.
Also, please note that RSA_public_encrypt returns the length of the encrypted data, which you should be using.
You can try using Objective-C-RSA third-party libraries,Or read the source code and then realize by yourself.
I am encrypting a string in objective-c and also encrypting the same string in Java using AES and am seeing some strange issues. The first part of the result matches up to a certain point but then it is different, hence when i go to decode the result from Java onto the iPhone it cant decrypt it.
I am using a source string of "Now then and what is this nonsense all about. Do you know?"
Using a key of "1234567890123456"
The objective-c code to encrypt is the following: NOTE: it is a NSData category so assume that the method is called on an NSData object so 'self' contains the byte data to encrypt.
- (NSData *)AESEncryptWithKey:(NSString *)key {
char keyPtr[kCCKeySizeAES128+1]; // room for terminator (unused)
bzero(keyPtr, sizeof(keyPtr)); // fill with zeroes (for padding)
// fetch key data
[key getCString:keyPtr maxLength:sizeof(keyPtr) encoding:NSUTF8StringEncoding];
NSUInteger dataLength = [self length];
//See the doc: For block ciphers, the output size will always be less than or
//equal to the input size plus the size of one block.
//That's why we need to add the size of one block here
size_t bufferSize = dataLength + kCCBlockSizeAES128;
void *buffer = malloc(bufferSize);
size_t numBytesEncrypted = 0;
CCCryptorStatus cryptStatus = CCCrypt(kCCEncrypt, kCCAlgorithmAES128, kCCOptionPKCS7Padding,
keyPtr, kCCKeySizeAES128,
NULL /* initialization vector (optional) */,
[self bytes], dataLength, /* input */
buffer, bufferSize, /* output */
&numBytesEncrypted);
if (cryptStatus == kCCSuccess) {
//the returned NSData takes ownership of the buffer and will free it on deallocation
return [NSData dataWithBytesNoCopy:buffer length:numBytesEncrypted];
}
free(buffer); //free the buffer;
return nil;
}
And the java encryption code is...
public byte[] encryptData(byte[] data, String key) {
byte[] encrypted = null;
Security.addProvider(new org.bouncycastle.jce.provider.BouncyCastleProvider());
byte[] keyBytes = key.getBytes();
SecretKeySpec keySpec = new SecretKeySpec(keyBytes, "AES");
try {
Cipher cipher = Cipher.getInstance("AES/ECB/PKCS7Padding", "BC");
cipher.init(Cipher.ENCRYPT_MODE, keySpec);
encrypted = new byte[cipher.getOutputSize(data.length)];
int ctLength = cipher.update(data, 0, data.length, encrypted, 0);
ctLength += cipher.doFinal(encrypted, ctLength);
} catch (Exception e) {
logger.log(Level.SEVERE, e.getMessage());
} finally {
return encrypted;
}
}
The hex output of the objective-c code is -
7a68ea36 8288c73d f7c45d8d 22432577 9693920a 4fae38b2 2e4bdcef 9aeb8afe 69394f3e 1eb62fa7 74da2b5c 8d7b3c89 a295d306 f1f90349 6899ac34 63a6efa0
and the java output is -
7a68ea36 8288c73d f7c45d8d 22432577 e66b32f9 772b6679 d7c0cb69 037b8740 883f8211 748229f4 723984beb 50b5aea1 f17594c9 fad2d05e e0926805 572156d
As you can see everything is fine up to -
7a68ea36 8288c73d f7c45d8d 22432577
I am guessing I have some of the settings different but can't work out what, I tried changing between ECB and CBC on the java side and it had no effect.
Can anyone help!? please....
Since the CCCrypt takes an IV, does it not use a chaining block cipher method (such as CBC)? This would be consistant with what you see: the first block is identical, but in the second block the Java version applies the original key to encrypt, but the OSX version seems to use something else.
EDIT:
From here I saw an example. Seems like you need to pass the kCCOptionECBMode to CCCrypt:
ccStatus = CCCrypt(encryptOrDecrypt,
kCCAlgorithm3DES,
kCCOptionECBMode, <-- this could help
vkey, //"123456789012345678901234", //key
kCCKeySize3DES,
nil, //"init Vec", //iv,
vplainText, //"Your Name", //plainText,
plainTextBufferSize,
(void *)bufferPtr,
bufferPtrSize,
&movedBytes);
EDIT 2:
I played around with some command line to see which one was right. I thought I could contribute it:
$ echo "Now then and what is this nonsense all about. Do you know?" | openssl enc -aes-128-ecb -K $(echo 1234567890123456 | xxd -p) -iv 0 | xxd
0000000: 7a68 ea36 8288 c73d f7c4 5d8d 2243 2577 zh.6...=..]."C%w
0000010: e66b 32f9 772b 6679 d7c0 cb69 037b 8740 .k2.w+fy...i.{.#
0000020: 883f 8211 7482 29f4 7239 84be b50b 5aea .?..t.).r9....Z.
0000030: eaa7 519b 65e8 fa26 a1bb de52 083b 478f ..Q.e..&...R.;G.
I spent a few weeks decrypting a base64 encoded, AES256 encrypted string. Encryption was done by CCCrypt (Objective-C)on an iPad. The decryption was to be done in Java (using Bouncy Castle).
I finally succeeded and learnt quite a lot in the process. The encryption code was exactly the same as above (I guess it's taken from the Objective-C sample in the iPhone developer documentation).
What CCCrypt() documentation does NOT mention is that it uses CBC mode by default (if you don't specify an option like kCCOptionECBMode). It does mention that the IV, if not specified, defaults to all zeros (so IV will be a byte array of 0x00, 16 members in length).
Using these two pieces of information, you can create a functionally identical encryption module using CBC (and avoid using ECB which is less secure) on both Java and OSx/iphone/ipad(CCCrypt).
The Cipher init function will take the IV byte array as a third argument:
cipher.init(Cipher.ENCRYPT_MODE, keySpec, IV).
For anyone else who needs this, disown was absolutely spot on... the revised call to create the crypt in objective-c is as follows (note you need the ECB mode AND the padding)...
CCCryptorStatus cryptStatus = CCCrypt(kCCEncrypt, kCCAlgorithmAES128, kCCOptionECBMode + kCCOptionPKCS7Padding,
keyPtr, kCCKeySizeAES128,
NULL /* initialization vector (optional) */,
[self bytes], dataLength, /* input */
buffer, bufferSize, /* output */
&numBytesEncrypted);
Just to add to the first post: in your objective C/cocoa code you used CBC mode and in your java code you used EBC and an IV initialization vector wasn't used in either.
EBC cipher is block by block and CBC chains upon the preceding block, so if your text is smaller than 1 block (=16 bytes in your example), the cipher text produced by both are decryptable by the other (the same).
If you are looking for a way to standardize your use of the ciphers, NIST Special Publication 800-38A, 2001 Edition has test vectors. I can post code for the AES CBC and EBC vectors if it's helpful to anyone.
I have a RSA key (pair) represented as big integeger modulus and exponent and need to encrypt/decrypt with those.
I figured out how to handle keys as needed in iOS using swift.
To my question: Is there any way to convert the modulus/exponent representation to a standard SecKeyRef?
Both is formatted as big int (coming from android),
a modulus for example looks like this:
23986589886077318012326064844037831693417390067186403792990846282531380456965701688980194375481519508455379138899060072530724598302129656976140458275478340281694599774176865257462922861492999970413042311221914141827738166785420817621605554859384423695247859963064446809695729281306530681131568503935369097838468173777374667631401317163094053418212485192857751897040859007584244053136110895205839896478287122804119514727484734998762296502939823974188856604771622873660784676915716476754048257418841069214486772931445697194023455179601077893872576165858771367831752886749210944303260745331014786145738511592470796648651
I had exactly the same task - given a modulus and an exponent I had to create a public key and encrypt a message using that key. After a long time spent in reading and trying various libraries, I was able to accomplish this with OpenSSL. I'm posting my way of doing it below. Although it's written in Objective-C, it might be helpful.
NSData* message, modulus, exponent;
BIGNUM* mod = BN_bin2bn((unsigned char *)[modulus bytes], (int)modulus.length, NULL);
if (mod == NULL) {
NSLog(#"Error creating modulus BIGNUM");
}
BIGNUM* exp = BN_bin2bn((unsigned char *)[exponent bytes], (int)exponent.length, NULL);
if (exp == NULL) {
NSLog(#"Error creating exponent BIGNUM");
}
RSA* rsa = RSA_new();
rsa->pad = 0;
rsa->e = exp;
rsa->n = mod;
int keylen = RSA_size(rsa);
unsigned char* enc = malloc(keylen);
char* err = malloc(130);
int status = RSA_public_encrypt((int)message.length, (const unsigned char*)[message bytes], enc, rsa, RSA_NO_PADDING);
if (status != -1) {
NSData* encryptedMessage = [NSData dataWithBytes:enc length:keylen];
NSLog(#"Encryption SUCCESSFUL: %#", encryptedMessage);
}
else {
ERR_load_crypto_strings();
ERR_error_string(ERR_get_error(), err);
NSLog(#"Encryption failed with error: %s", err);
}
free(enc);
free(err);
So first I'm creating big integers out of my NSData modulus and exponent. You already have them as big integers, but if they're not represented as OpenSSL's BIGNUM type, you'll have to convert them. BIGNUM has other useful functions for creating big integers like BN_hex2bn and BN_dec2bn - these create big integers out of C strings containing hexadecimal or decimal numbers. In my case the modulus and exponent are stored as a byte array in an NSData and BN_bin2bn creates a BIGNUM directly from that.
Moving on, I create an RSA structure which represents a key and holds the modulus and exponent, and the enc buffer, which will hold the raw encrypted bytes. The length of enc is the same as the size of the key, because RSA can not encrypt messages longer that the key.
The main work is done by the RSA_public_encrypt() function. It takes five arguments - the size of the message that you're going to encrypt, the actual message bytes, an output buffer to store the encrypted message in, the RSA key and a padding scheme. I'm using no padding here, because my message is exactly the same size as the key, but in the rsa.h there are macros that represent the most common padding schemes.
Lastly I check the status which holds the number of encrypted bytes and print an error message if something went wrong.
I hope this will help you and somebody else. Tell me if you managed to do it in Swift. Cheers ;-)
P.S. Adding OpenSSL to your iOS project is easy using CocoaPods. Just add
pod 'OpenSSL-Universal', '1.0.1.k'
to your podfile.
I have been using UUIDString as an encrption key for the files stored on my iPAD, but the security review done on my app by a third party suggested the following.
With the launch of the application, a global database key is generated and stored in the keychain. During generation, the method UUIDString of the class NSUUID provided by the iOS is used. This function generates a random string composed of letters A to F, numbers and hyphens and unnecessarily restricts the key space, resulting in a weakening of the entropy.
Since the key is used only by application logic and does not have to be read, understood or processed by an individual, there is no need to restrict the key space to readable characters. Therefore, a random 256-bit key generated via SecRandomCopyBytes () should be used as the master key.
Now I have searched a lot and tried some code implementation but havent found the exact thing.
What I have tried:
NSMutableData* data = [NSMutableData dataWithLength:32];
int result = SecRandomCopyBytes(kSecRandomDefault, 32, data.mutableBytes);
NSLog(#"Description %d",result);
My understanding is that this should give me an integer and I should convert it to an NSString and use this as my key, but I am pretty sure that this is not what is required here and also the above method always gives the result as 0. I am completely lost here and any help is appreciated.
Thanks.
The result of SecRandomCopyBytes should always be 0, unless there is some error (which I can't imagine why that might happen) and then the result would be -1. You're not going to convert that into a NSString.
The thing you're trying to get are the random bytes which are being written into the mutable bytes section, and that's what you'll be using as your "master key" instead of the UUID string.
The way I would do it would be:
uint8_t randomBytes[16];
int result = SecRandomCopyBytes(kSecRandomDefault, 16, randomBytes);
if(result == 0) {
NSMutableString *uuidStringReplacement = [[NSMutableString alloc] initWithCapacity:16*2];
for(NSInteger index = 0; index < 16; index++)
{
[uuidStringReplacement appendFormat: #"%02x", randomBytes[index]];
}
NSLog(#"uuidStringReplacement is %#", uuidStringReplacement);
} else {
NSLog(#"SecRandomCopyBytes failed for some reason");
}
Using a UUIDString feels secure enough to me, but it sounds like your third party security audit firm is trying really hard to justify their fees.
EDITED: since I'm now starting to collect downvotes because of Vlad's alternative answer and I can't delete mine (as it still has the accepted checkmark), here's another version of my code. I'm doing it with 16 random bytes (which gets doubled in converting to Hex).
The NSData generated does not guarantee UTF16 chars.
This method will generate 32byte UTF string which is equivalent to 256bit. (Advantage is this is plain text and can be sent in GET requests ext.)
Since the length of Base64 hash is = (3/4) x (length of input string) we can work out input length required to generate 32byte hash is 24 bytes long. Note: Base64 may pad end with one, two or no '=' chars if not divisible.
With OSX 10.9 & iOS 7 you can use:
-[NSData base64EncodedDataWithOptions:]
This method can be used to generate your UUID:
+ (NSString*)generateSecureUUID {
NSMutableData *data = [NSMutableData dataWithLength:24];
int result = SecRandomCopyBytes(NULL, 24, data.mutableBytes);
NSAssert(result == 0, #"Error generating random bytes: %d", result);
NSString *base64EncodedData = [data base64EncodedStringWithOptions:0];
return base64EncodedData;
}
A UUID is a 16 bytes (128 bits) unique identifier, so you aren't using a 256 bits key here. Also, as #zaph pointed out, UUIDs use hardware identifiers and other inputs to guarantee uniqueness. These factors being predictable are definitely not cryptographically secure.
You don't have to use a UUID as an encryption key, instead I would go for a base 64 or hexadecimal encoded data of 32 bytes, so you'll have your 256 bit cryptographically secure key:
/** Generates a 256 bits cryptographically secure key.
* The output will be a 44 characters base 64 string (32 bytes data
* before the base 64 encoding).
* #return A base 64 encoded 256 bits secure key.
*/
+ (NSString*)generateSecureKey
{
NSMutableData *data = [NSMutableData dataWithLength:32];
int result = SecRandomCopyBytes(kSecRandomDefault, 32, data.mutableBytes);
if (result != noErr) {
return nil;
}
return [data base64EncodedStringWithOptions:kNilOptions];
}
To answer the part about generate UUID-like (secure) random numbers, here's a good way, but remember these will be 128 bits only keys:
/** Generates a 128 bits cryptographically secure key, formatted as a UUID.
* Keep that you won't have the same guarantee for uniqueness
* as you have with regular UUIDs.
* #return A cryptographically secure UUID.
*/
+ (NSString*)generateCryptoSecureUUID
{
unsigned char bytes[16];
int result = SecRandomCopyBytes(kSecRandomDefault, 16, bytes);
if (result != noErr) {
return nil;
}
return [[NSUUID alloc] initWithUUIDBytes:bytes].UUIDString;
}
Cryptography is great, but doing it right is really hard (it's easy to leave security breaches). I cannot recommend you more the use of RNCryptor, which will push you through the use of good encryption standards, will make sure you're not unsafely reusing the same keys, will derivate encryption keys from passwords correctly, etc.
And i try this code for length 16 and bytes 16 :
uint8_t randomBytes[16];
NSMutableString *ivStr;
int result = SecRandomCopyBytes(kSecRandomDefault, 16, randomBytes);
if(result == 0) {
ivStr = [[NSMutableString alloc] initWithCapacity:16];
for(NSInteger index = 0; index < 8; index++)
{
[ivStr appendFormat: #"%02x", randomBytes[index]];
}
NSLog(#"uuidStringReplacement is %#", ivStr);
} else {
NSLog(#"SecRandomCopyBytes failed for some reason");
}
Successful
Since the Key usually needs to be UTF-8 encoded and "readable" - i.e. with no UTF-8 control characters- I decided to filter the randomly generated bytes generated using SecRandomCopyBytes so it'd only have characters from the Basic Latin Unicode block.
/*!
* #brief Generates NSData from a randomly generated byte array with a specific number of bits
* #param numberOfBits the number of bits the generated data must have
* #return the randomly generated NSData
*/
+ (NSData *)randomKeyDataGeneratorWithNumberBits:(int)numberOfBits {
int numberOfBytes = numberOfBits/8;
uint8_t randomBytes[numberOfBytes];
int result = SecRandomCopyBytes(kSecRandomDefault, numberOfBytes, randomBytes);
if(result == 0) {
return [NSData dataWithBytes:randomBytes length:numberOfBytes];
} else {
return nil;
}
}
/*!
* #brief Generates UTF-8 NSData from a randomly generated byte array with a specific number of bits
* #param numberOfBits the number of bits the generated data must have
* #return the randomly generated NSData
*/
+ (NSData *)randomKeyUTF8DataGeneratorWithNumberBits:(int)numberOfBits {
NSMutableData *result = [[NSMutableData alloc] init];
int numberOfBytes = numberOfBits/8;
while (result.length < numberOfBytes) {
// Creates a random byte
NSData *byte = [self randomKeyDataGeneratorWithNumberBits:8];
int asciiValue = [[[NSString alloc] initWithData:byte encoding:NSUTF8StringEncoding] characterAtIndex:0];
// Checks if the byte is UTF-8
if (asciiValue > 32 && asciiValue < 127) {
[result appendData:byte];
}
}
return result;
}
If you want to make your key a little more "readable" you can try and make it Base64 URL Safe
/*!
* #brief Encodes a String Base 64 with URL and Filename Safe Alphabet
* #discussion Base64url Encoding The URL- and filename-safe Base64 encoding described in RFC 4648 [RFC4648] (https://tools.ietf.org/html/rfc4648)
* #discussion Section 5 (https://tools.ietf.org/html/rfc4648#section-5)
* #param string the string to be enconded
* #return the encoded string
*/
+ (NSString *)base64URLandFilenameSafeString:(NSString *)string {
NSString *base64String = string;
base64String = [base64String stringByReplacingOccurrencesOfString:#"/"
withString:#"_"];
base64String = [base64String stringByReplacingOccurrencesOfString:#"+"
withString:#"-"];
return base64String;
}
Generate a UTF-8 256 bits key:
NSData *key = [self randomKeyUTF8DataGeneratorWithNumberBits:256];
NSString *UTF8String = [[NSString alloc] initWithBytes:[key bytes] length:data.length encoding:NSUTF8StringEncoding];
NSString *base64URLSafeString = [self base64URLandFilenameSafeString:UTF8String];