I tried to generate SHA256 in iOS using Arcane library with following data:
String: Amount=50&BillerID=59&ChannelID=2&Context=34|check|test&ReturnURL=https://uat.myfatoora.com/ReceiptPOC.aspx&TxnRefNum=000000000020003&UserName=DCS
Key: 71DD0F73AFFBB47825FF9864DDE95F3B
Result was 409dc622b3bef5c9fc46e45c3210111fcb4536d3a55833316fe0dc8154b3ea34
which I thought to be correct. However, the Windows counterpart is generating SHA256 using following code:
Windows Phone Source Code:
public static string HmacSha256(string secretKey, string value)
{
var msg = CryptographicBuffer.ConvertStringToBinary(value, BinaryStringEncoding.Utf8);
byte[] convertedHash = new byte[secretKey.Length / 2];
for (int i = 0; i < secretKey.Length / 2; i++)
{
convertedHash[i] = (byte)Int32.Parse(secretKey.Substring(i * 2, 2), System.Globalization.NumberStyles.HexNumber);
}
// Create HMAC.
var objMacProv = MacAlgorithmProvider.OpenAlgorithm(MacAlgorithmNames.HmacSha256);
CryptographicHash hash = objMacProv.CreateHash(convertedHash.AsBuffer());
hash.Append(msg);
return CryptographicBuffer.EncodeToHexString(hash.GetValueAndReset());
}
and the result is: 94a20ca39c8487c7763823ec9c918d9e38ae83cb741439f6d129bcdef9edba73 which is different from what I got. Can somebody help me with this and let me know what the above code is doing and how can I replicate it in iOS.
Edit:
iOS Source code
let key = self.md5(string: "71DD0F73AFFBB47825FF9864DDE95F3B")
let hash = HMAC.SHA256(str, key: key)
The key here is you need to convert your secret, which is a hex string, into NSData. In other words, NSData byte stream would "look" like the secret.
This should do what you want:
// Hex string to NSData conversion from here http://stackoverflow.com/questions/7317860/converting-hex-nsstring-to-nsdata
NSString *secret = #"71DD0F73AFFBB47825FF9864DDE95F3B";
NSData *dataIn = [#"Amount=50&BillerID=59&ChannelID=2&Context=34|check|test&ReturnURL=https://uat.myfatoora.com/ReceiptPOC.aspx&TxnRefNum=000000000020003&UserName=DCS" dataUsingEncoding:NSUTF8StringEncoding];
NSMutableData *macOut = [NSMutableData dataWithLength:CC_SHA256_DIGEST_LENGTH];
secret = [secret stringByReplacingOccurrencesOfString:#" " withString:#""];
NSMutableData *secretData = [[NSMutableData alloc] init];
unsigned char whole_byte;
char byte_chars[3] = {'\0','\0','\0'};
int i;
for (i=0; i < [secret length]/2; i++) {
byte_chars[0] = [secret characterAtIndex:i*2];
byte_chars[1] = [secret characterAtIndex:i*2+1];
whole_byte = strtol(byte_chars, NULL, 16);
[secretData appendBytes:&whole_byte length:1];
}
CCHmac(kCCHmacAlgSHA256, secretData.bytes, secretData.length, dataIn.bytes, dataIn.length, macOut.mutableBytes);
NSMutableString *stringOut = [NSMutableString stringWithCapacity:macOut.length];
const unsigned char *macOutBytes = macOut.bytes;
for (NSInteger i=0; i<macOut.length; ++i) {
[stringOut appendFormat:#"%02x", macOutBytes[i]];
}
NSLog(#"dataIn: %#", dataIn);
NSLog(#"macOut: %#", macOut);
NSLog(#"stringOut: %#", stringOut);
Output:
2016-09-27 20:18:54.181 JKS[27562:5321334] dataIn: <416d6f75 6e743d35 30264269 6c6c6572 49443d35 39264368 616e6e65 6c49443d 3226436f 6e746578 743d3334 7c636865 636b7c74 65737426 52657475 726e5552 4c3d6874 7470733a 2f2f7561 742e6d79 6661746f 6f72612e 636f6d2f 52656365 69707450 4f432e61 73707826 54786e52 65664e75 6d3d3030 30303030 30303030 32303030 33265573 65724e61 6d653d44 4353>
2016-09-27 20:18:54.181 JKS[27562:5321334] macOut: <94a20ca3 9c8487c7 763823ec 9c918d9e 38ae83cb 741439f6 d129bcde f9edba73>
2016-09-27 20:18:54.181 JKS[27562:5321334] stringOut: 94a20ca39c8487c7763823ec9c918d9e38ae83cb741439f6d129bcdef9edba73
Updated with Swift (code should be cleaned up)
// http://stackoverflow.com/questions/29799361/generate-a-hmac-swift-sdk8-3-using-cchmac
func generateHMAC(key: String, data: String) -> String {
let keyData = key.dataFromHexadecimalString()! as NSData
let dataIn = data.data(using: .utf8)! as NSData
var result: [CUnsignedChar]
result = Array(repeating: 0, count: Int(CC_SHA256_DIGEST_LENGTH))
CCHmac(CCHmacAlgorithm(kCCHmacAlgSHA256), keyData.bytes, keyData.length, dataIn.bytes, dataIn.length, &result)
let hash = NSMutableString()
for val in result {
hash.appendFormat("%02hhx", val)
}
return hash as String
}
You can use this extension to convert the hex string to Data
// Modified slightly http://stackoverflow.com/questions/26501276/converting-hex-string-to-nsdata-in-swift
extension String {
func dataFromHexadecimalString() -> Data? {
var data = Data(capacity: characters.count / 2)
let regex = try! NSRegularExpression(pattern: "[0-9a-f]{1,2}", options: .caseInsensitive)
regex.enumerateMatches(in: self, options: [], range: NSMakeRange(0, characters.count)) { match, flags, stop in
let byteString = (self as NSString).substring(with: match!.range)
var num = UInt8(byteString, radix: 16)
data.append(&num!, count: 1)
}
return data
}
}
And to use do something like:
let secret = "71DD0F73AFFBB47825FF9864DDE95F3B"
let value = "Amount=50&BillerID=59&ChannelID=2&Context=34|check|test&ReturnURL=https://uat.myfatoora.com/ReceiptPOC.aspx&TxnRefNum=000000000020003&UserName=DCS"
print("\(generateHMAC(key: secret, data: value))")
Your output should be 94a20ca39c8487c7763823ec9c918d9e38ae83cb741439f6d129bcdef9edba73
You will need #import <CommonCrypto/CommonCrypto.h> in your bridging header.
The Windows code takes the string, interprets it as a hexadecimal number, and converts two characters a time into one byte.
Your Mac code most like takes the string as it is. Since the key starts with "71", your windows code takes that as a single byte with value 0x71 = 129, your Mac code takes it as two bytes with values '7' = 55 and '1' = 49.
All you need to do is convert the bytes on the Mac exactly as you do it on Windows. You might have to do the unthinkable and look at the source code of the Mac library to see how it does the actual hash calculation.
#import <CommonCrypto/CommonHMAC.h>
+ (NSString *)hmacSHA256EncryptString{
NSString * parameterSecret = #"input secret key";
NSString *plainString = #"input encrypt content string";
const char *secretKey = [parameterSecret cStringUsingEncoding:NSUTF8StringEncoding];
const char *plainData = [plainString cStringUsingEncoding:NSUTF8StringEncoding];
unsigned char cHMAC[CC_SHA256_DIGEST_LENGTH];
CCHmac(kCCHmacAlgSHA256, secretKey, strlen(secretKey), plainData, strlen(plainData), cHMAC);
NSData *HMACData = [NSData dataWithBytes:cHMAC length:sizeof(cHMAC)];
const unsigned char *bufferChar = (const unsigned char *)[HMACData bytes];
NSMutableString *hmacString = [NSMutableString stringWithCapacity:HMACData.length * 2];
for (int i = 0; i < HMACData.length; ++i){
[hmacString appendFormat:#"%02x", bufferChar[i]];
}
return hmacString;
}
Related
This is my code. Any time items return is null.I have tried in swift and objective c,but nothing.
let certName : String = "private_key"//name of the certificate//
//get p12 file path
let resourcePath: String = NSBundle.mainBundle().pathForResource(certName, ofType: "p12")!
let p12Data: NSData = NSData(contentsOfFile: resourcePath)!
//create key dictionary for reading p12 file
let key : NSString = kSecImportExportPassphrase as NSString
let options : NSDictionary = [key : "password"]
//create variable for holding security information
var privateKeyRef: SecKeyRef? = nil
var items : CFArray?
let securityError: OSStatus = SecPKCS12Import(p12Data, options, &items)
print(items)
I'm using pkcs8. And you need to include
#include <CommonCrypto/CommonDigest.h>
#include <openssl/engine.h>
openssl download from Github
+ (NSString*) getSignatureData:(NSString*) signableData {
// get private key path
NSString* path = [[NSBundle mainBundle] pathForResource:#"private"
ofType:#"der"];
NSData* datasss = [signableData dataUsingEncoding:NSNonLossyASCIIStringEncoding];
char* text = (char*) [signableData UTF8String];
unsigned char *data;
data = (unsigned char *) text;
//creates a new file BIO with mode, mode the meaning of mode is the same as the stdio function fopen()
BIO *in = BIO_new_file([path cStringUsingEncoding:NSUTF8StringEncoding], "rb");
//PKCS#8 private key info structure
PKCS8_PRIV_KEY_INFO *p8inf = d2i_PKCS8_PRIV_KEY_INFO_bio(in, NULL);
EVP_PKEY *pkey = EVP_PKCS82PKEY(p8inf);
PKCS8_PRIV_KEY_INFO_free(p8inf);
BIO_free(in);
uint8_t * cipherBuffer = NULL;
// Calculate the buffer sizes.
unsigned int cipherBufferSize = RSA_size(pkey->pkey.rsa);
unsigned int signatureLength;
// Allocate some buffer space.
cipherBuffer = malloc(cipherBufferSize);
memset((void *)cipherBuffer, 0x0, cipherBufferSize);
unsigned char hashedChars[32];
//return a pointer to the hash value
unsigned char *openSSLHash = CC_SHA256(datasss.bytes, (CC_LONG)signableData.length, hashedChars);
//unsigned char *openSSLHash1 = SHA256(data, signableData.length, NULL);
/*
* The following function sign and verify a X509_SIG ASN1 object inside
* PKCS#8 padded RSA encryption
*/
RSA_sign(NID_sha256, openSSLHash, SHA256_DIGEST_LENGTH, cipherBuffer, &signatureLength, pkey->pkey.rsa);
NSData *signedData = [NSData dataWithBytes:(const void*)cipherBuffer length:signatureLength];
EVP_PKEY_free(pkey);
NSString *base64String = [signedData base64EncodedStringWithOptions:0];
return base64String;
}
I have two methods that create a sha1 hash from a string. Using the same input data this algorithms create different hashes, however they should create the same hashes.
In swift (creates 617fb90f14f2eacecc333d558237bf8bb9fc85f7):
static func sha1FromMessage(message: String) -> String {
let cKey = RestUtils.API_KEY.cStringUsingEncoding(NSASCIIStringEncoding)!
let cData = message.cStringUsingEncoding(NSUTF8StringEncoding)!
var cHMAC = [CUnsignedChar](count: Int(CC_SHA1_DIGEST_LENGTH), repeatedValue: 0)
CCHmac(CCHmacAlgorithm(kCCHmacAlgSHA1), cKey, UInt(cKey.count), cData, UInt(cData.count), &cHMAC)
let output = NSMutableString(capacity: Int(CC_SHA1_DIGEST_LENGTH))
for byte in cHMAC {
output.appendFormat("%02hhx", byte)
}
return output
}
and obj-c (creates d80b816f0b46d5211b6d9487089597e181717ea6)
+(NSString *)sha1FromMessage:(NSString *)message{
const char *cKey = [API_KEY cStringUsingEncoding:NSASCIIStringEncoding];
const char *cData = [message cStringUsingEncoding:NSUTF8StringEncoding];
unsigned char cHMAC[CC_SHA1_DIGEST_LENGTH];
CCHmac(kCCHmacAlgSHA1, cKey, strlen(cKey), cData, strlen(cData), cHMAC);
NSData *HMACData = [NSData dataWithBytes:cHMAC length:sizeof(cHMAC)];
const unsigned char *buffer = (const unsigned char *)[HMACData bytes];
NSMutableString *HMAC = [NSMutableString stringWithCapacity:HMACData.length * 2];
for (int i = 0; i < HMACData.length; ++i){
[HMAC appendFormat:#"%02hhx", buffer[i]];
}
return HMAC;
}
I would like the swift method to return the same hash as the obj-c method. Any ideas where the problem is?
The reason is that cData created by
let cData = message.cStringUsingEncoding(NSUTF8StringEncoding)!
includes the terminating NUL-character of the message string and that is counted in
UInt(cData.count) as well. You could fix that by using UInt(strlen(cData)) instead,
as in your Objective-C code.
But a better solution is to convert the input strings
to NSData objects instead:
let cKey = RestUtils.API_KEY.dataUsingEncoding(NSASCIIStringEncoding)!
let cData = message.dataUsingEncoding(NSUTF8StringEncoding)!
var cHMAC = [CUnsignedChar](count: Int(CC_SHA1_DIGEST_LENGTH), repeatedValue: 0)
CCHmac(CCHmacAlgorithm(kCCHmacAlgSHA1), cKey.bytes, UInt(cKey.length), cData.bytes, UInt(cData.length), &cHMAC)
With this modification, Swift and Objective-C code produce the same message digest.
I want to create an SHA1 hmac hash of a string using a key in swift. In obj-c I used this and it worked great:
+(NSString *)sha1FromMessage:(NSString *)message{
const char *cKey = [API_KEY cStringUsingEncoding:NSASCIIStringEncoding];
const char *cData = [message cStringUsingEncoding:NSUTF8StringEncoding];
NSLog(#"%s", cData);
unsigned char cHMAC[CC_SHA1_DIGEST_LENGTH];
CCHmac(kCCHmacAlgSHA1, cKey, strlen(cKey), cData, strlen(cData), cHMAC);
NSData *HMACData = [NSData dataWithBytes:cHMAC length:sizeof(cHMAC)];
const unsigned char *buffer = (const unsigned char *)[HMACData bytes];
NSMutableString *HMAC = [NSMutableString stringWithCapacity:HMACData.length * 2];
for (int i = 0; i < HMACData.length; ++i){
[HMAC appendFormat:#"%02hhx", buffer[i]];
}
return HMAC;
}
However now I am having a hard time to translate this into swift. This is what I have so far:
static func sha1FromMessage(message: String){
let cKey = RestUtils.apiKey.cStringUsingEncoding(NSASCIIStringEncoding)!
let cData = message.cStringUsingEncoding(NSUTF8StringEncoding)!
let cHMAC = [CUnsignedChar](count: Int(CC_SHA1_DIGEST_LENGTH), repeatedValue: 0)
CCHmac(kCCHmacAlgSHA1, cKey, cKey.count, cData, cData.count, cHMAC)
...
}
and this line
CCHmac(kCCHmacAlgSHA1, cKey, cKey.count, cData, cData.count, cHMAC)
already gives me an error Int is not convertible to CCHmacAlgorithm. Any ideas how to translate the obj-c code to swift?
The last parameter of the CCHmac() function has the type UnsafeMutablePointer<Void> because
that's where the result is written to. You have to declare cHMAC as variable
and pass it as an in-out expression with &. In addition, some type conversions
are necessary:
var cHMAC = [CUnsignedChar](count: Int(CC_SHA1_DIGEST_LENGTH), repeatedValue: 0)
CCHmac(CCHmacAlgorithm(kCCHmacAlgSHA1), cKey, UInt(cKey.count), cData, UInt(cData.count), &cHMAC)
Apple enum values are defined differently in Swift. Instead of kCCHmacAlgSHA1, it's probably defined like CCHmacAlgorithm.SHA1.
This is answered here:
https://stackoverflow.com/a/24411522/2708650
I have used the following Objective C routines for years, to convert a NSData push token into a NSString (for use by a web side push service), and the inverse, to take a known NSString version of the token and recreate the NSData representation. Now, I find the need for the exact same capabilities, but in Swift.
The dataToHex Objective C code essentially uses printf formatting:
- (NSString *)dataToHex:(NSData *)data
{
NSMutableString *str = [NSMutableString stringWithCapacity:100];
const unsigned char *p = [data bytes];
NSUInteger len = [data length];
for(int i=0; i<len; ++i) {
[str appendFormat:#"%02.2X", p[i]];
}
return str;
}
The inverse translation is:
- (NSData *)hexToData:(NSString *)str
{
const char *ptr = [str cStringUsingEncoding:NSASCIIStringEncoding];
NSUInteger len = [str length]/2;
NSMutableData *data = [NSMutableData dataWithCapacity:len];
while(len--) {
char num[5] = (char[]){ '0', 'x', 0, 0, 0 };
num[2] = *ptr++;
num[3] = *ptr++;
uint8_t n = (uint8_t)strtol(num, NULL, 0);
[data appendBytes:&n length:1];
}
return data;
}
By "cleverly" overwriting a two byes in an ASCII array, the "0xXX" string is converted into a byte, which is then appended to the mutable data object.
Now that I'm coding in Swift, I need the same capabilities but have not found any posts with anything like the code above in Swift.
Converting from the NSData representation as supplied by iOS matches the Objective C code almost line for line:
func dataToHex(data: NSData) -> String
{
var str: String = String()
let p = UnsafePointer<UInt8>(data.bytes)
let len = data.length
for var i=0; i<len; ++i {
str += String(format: "%02.2X", p[i])
}
return str
}
However, given an NSString object, the conversion back to a NSData object is a bit harder. You might need to do this if you are testing in the Simulator, have a string token from a real device, and need it to say register with a service.
The first approach I took tried to replicate code I used before, by creating a string with character pairs, and calling strtol:
func hexToData0(str: NSString) -> NSData {
let len = str.length/2
var data = NSMutableData(capacity:len)!
var num: [Int8] = [ 0, 0, 0 ]
let ptr = str.cStringUsingEncoding(NSUTF8StringEncoding)
for var i=0; i<len; ++i {
num[0] = ptr[i*2+0]
num[1] = ptr[i*2+1]
var n = UInt8 ( strtol(&num, nil, 16) )
data.appendBytes(&n, length:1)
}
return data;
}
I just felt the strtol was a bit of a hack, so I did the same using NSScanner that about the same code size, while most likely less efficient:
func hexToData1(str: NSString) -> NSData {
var data = NSMutableData(capacity: str.length/2)!
for var i = 0; i<str.length; i+=2 {
let r = NSRange(location: i, length: 2)
let s = str.substringWithRange(r)
let sc = NSScanner(string: s)
var val: UInt32 = 0
let ret = sc.scanHexInt(&val)
if ret {
var b = UInt8(val)
data.appendBytes(&b, length: 1)
} else {
assert(false, "Yikes!")
}
}
return data
}
Then, it occurred to me that I could do it all in Swift, no Darwin or Foundation needed, at the expense of a few more lines of code:
// Swift 4
func hexToData(str: String) -> Data {
let len = str.count/2
var data = Data(capacity:len)
let ptr = str.cString(using: String.Encoding.utf8)!
for i in 0..<len {
var num: UInt8 = 0
var multi: UInt8 = 16;
for j in 0..<2 {
let c: UInt8 = UInt8(ptr[i*2+j])
var offset: UInt8 = 0
switch c {
case 48...57: // '0'-'9'
offset = 48
case 65...70: // 'A'-'F'
offset = 65 - 10 // 10 since 'A' is 10, not 0
case 97...102: // 'a'-'f'
offset = 97 - 10 // 10 since 'a' is 10, not 0
default:
assert(false)
}
num += (c - offset)*multi
multi = 1
}
data.append(num)
}
return data;
}
I'm using the final hexToData in my code.
I am trying to encrypt a string "HELLO" using RSA encryption with PKCS1 Padding.
I am using openssl for generating the keypair and perform encryption.
Problem: I am getting the generated encypted text with variable length.
Private Key: LS0tLS1CRUdJTiBSU0EgUFJJVkFURSBLRVktLS0tLQpNSUlFb3dJQkFBS0NBUUVBbzdOdmUwZlg3L2NjVExibWhQOWNYUVI5WWFrMi9Oa3Q2bVkwNkpjNEJjWmswemdiCnZ4ek5mTC9sNVQzd1pQcDBWa204RGozcjNVUStMMXlyRjF6bGpBSXE5MlJQcnVWRFpacTJOcXJFelhyTUZxcUMKaWhEMU04dW1EVTNNUTNCMnYxdzdsVU1uWE1Ec21LMDVxbWJRQnRzckp1ck93Mm95a2VrT0dRdndEQWRjZmIydQpVcW5NNVc2OUp3eXNQaTVkc3VhOFBXM0FIK3o0b1lRZXhrOFh4KzRWSllTNkxvVGpaeGFPNysvNXRHMVJhUUc1CklHbFQrZUFjTmtXK1pTKzlzWldXdWRGVldNeGVQaE9jSVdmL1lsNW9HSm5LaUVKRU12bkdzRmpKRlpicTVCajgKaCtyb0xjdWZvWnNPYnVRM3draFpIakNKakFQRmlhcktIR3VjQXdJREFRQUJBb0lCQUVObGp0SnN3OFN5TWw2TApOdUV1MlhJYWRSeU9OR3RwWnZRSVhHUWVUaHhpa0FRK0ozNUpkM2lTOFMydWtKaWlYTjlod0NFRThXVEp3ZWh4CitLYkh3bUlnU2x4TVdsb2hYZHdwRkRmeG10RVdhNHd4YTBrNHJodEREdnlQT1RmU3g3RUZZaXFUMlZMZ3d3Z2cKMllzRWhVVXRXdGpNOTQrUTIzMGpQSUNuWDQ4emwvZnk2YStldzZFbmNpRzNpcEppczdPc3ZxVmc4OGluSDNPWgp3Z3R4ZmpHa2RIL0NzUGk3eVYzMHdmMTZWNThFcmtlamxtOHMrRGtvcTNpRXhBekMvL3Q2TnY0Vlh5VEJYNjRHClV3RjVHdDcyL0pLTldMdytpU1dKdWtkR21wcCtxQlBROHc3QU0xeHUyalc4SFZKbUhUZGRLazBSby9KbVNwNWgKelhic0w4RUNnWUVBMVFEcVFaU0x0SElsVU1ibC9SOWZLWWJVV3JhbE9wTDdjM0EwVnplUWlya2tEV1VnSE9MTgp5bk9NYlUxcitCUFRiN1FoMytsWWlod2tHVHZsaWR6cC9BVDdDSHlpYWVnZVh2RWY1YVFNVGlhajU2UCtkNFh4CmtnZ3ZOZmI5R0VEMkovdi82SS91cTlZb1N6aUY0R0M3R09rYWxtamNSNmJ2U2trdC9QRitjWk1DZ1lFQXhMN0cKMk8vQTVQaGoraHpGbFVhNDFLb1F6WTJ4dUkrTnNFZUZSbStsejJ4M00weHIrRlBGelhnQjFIOHBNcGNxMTlFMAp6VGNiODYvck40eFZmMkJwNnNCOUFaSG42NWM3cy9rZUllNTRnMVNsRlN2NEJ1SDgyVGZnUzAwKzUrZ0hMN0I0CmY0R2ZXVm1jQkp6Sng1Njl2Ynh5TUpnSUJkRlMranIySzhFVmNkRUNnWUJsZlF3Z3R5QmhsOG1EMVdFYjIrakIKcG9aanVvYTdnL0RmTGVVaWNVMGphOWRyc25VOGppd3pmQjY2clpyMkZxdzFtd1JRNWFQVUdsaHcxWU1tenVXRgp1UEhMTjJkRFF0L0ZxT25qeHVnK0RGY29OWkUzYmRDT0hld3dzRUFqWUVCcENMOEVGaUJJSzhBREp5bFNvWVJECitEMDgwVFd6eGl4Wm5zaVVycFBxbFFLQmdRQ21EdXZnNElPci91YkxqUXRMa25TZ3NxWWFDTFZ2TkdMLzNXM2sKNUlsTHlKM1hnM0pDRjlzRk1IeEM4YzFBOFF4dE9odlo5ZUMyenMyMmhkRUlBTVdGS1ZDWlNhQTVYZDA3YTFGKwpqRHZWaFZVTXlKN0FtRytFbTcvSmJ2cGdYSnZzS1lPYkF6b08vUzVOU2xTWkV5Zk5CRWhSTVpEYjhEQ2NHYlg3CmVpM3ljUUtCZ0dJcDF2VWpRYVFaM3hhUGJ3eGpKcHlkSWQ1bk0vTllDb2doMTl6Z0tIZlNVOGxRenFsWHB0ZEEKenloOEYyLzQ5cWNuVEVnZlh0bmJ4bTRKbTV5ck5xdHUyVDFpdWt5NStST2Vud1Y1VjFqRVZETDgrZ0VsOGt2cApvVUlNSFg3ZThWYW5FTGptSWo4WU1DcGtEOFQwOVFGajhzSU5DaVZNMjk3QUs4REwyWjhFCi0tLS0tRU5EIFJTQSBQUklWQVRFIEtFWS0tLS0tCg==
Public Key: LS0tLS1CRUdJTiBSU0EgUFVCTElDIEtFWS0tLS0tTUlJQkNnS0NBUUVBNFdXSE5obm1nUkVISVk3aVlLamVraDBmUEtxbGVlUnVVNjdnanljbllLYlJKaEpaeXF4c09BUmpubWZaaGI1ZXg5K3gxTzVBWXNPVmo5MEowanRBZk82MXhMamhHZ2F1b3NnKy9KbUhUaFp3U2tQc25HalhCLy9HNHNoWU92bUY3QndITjFNK0RUQmFGcm96NXh0RU5nMDB5YzM4cmtFYlJzNWpJSXUrTkxZRXZCZVd0eXpkK1ZqWjBuUm1nekJ5bkMxQ1VZL0lvc2lLUEZEbHRhMkhBdlFZaGxFazBGalBReW1pbEMwT1hKNXZqTVdJU2hYU2NMWklmOEhhekZySVNlUjBFNzkyb0xJL04ydzdrRkd6SmdjRFUxbFMxcWRJUFRGcTR5NFVHejB0NnExbWFxNmdOMGRIVUc2cVZWQmVEUUdSaDdpMUM2aEFLakFiV2t4Vm9RSURBUUFCLS0tLS1FTkQgUlNBIFBVQkxJQyBLRVktLS0tLQ==
Encrypted Text for HELLO in Run1: XuKAosKxw6BWXcK/w7cgMBww4oCZxLHDgO+sggXigJQR4oC5w7o3SAFbTTHiiJrDsuKJoM6p4oC5YMOf4oCwC8ODYsOa76O/MQ5BAUF2w5xMw7vDrkzigJPDq30Bw4bCoRnDgWrLnHHDukNvdcOuKkLCtsOAw6rigJnDseKAucO6G1EcwqfFkzPDiyLDnAjigKHDmUbDoxHCu8O8WU9LwrXigKDDssOvw7zDrhNfw7gCw7bvo78Jw4hp4oiGc8OrxbhTwrfiiI9mw5xQGgg6wrco76yCw6V1fg7DlDbDknPLnWY7KuKAmuKAnU3DqsOcwqzEsRg7y5g7O3RmdMOcD8OZw4TiiaXDoU7LmcWSw6fDmeKAucWTGBDFkiTiiYjiiJ5JXA3DksOuw4ldd8uH76yCUQwm4oKsw7nDhe+sgR0RUA/iiJ4vH2gTO+KAoW7DlcK/w7o8y5tFZHrCsGfigJrDocKwy5zDjMK7w4pKCibDguKInnkbUuKImsuYy5rigKDCrsO/C1HDo3LCrMOqwrXigJzigJ7DqeKAmuKIj3LDjgnDscOlwrZbDzVZw5PDmXlYO8ORFA==
Encrypted Text for HELLO in Run2: E0dedDrigJxBw5jCqhwLRA4yHcOzwqgVa1HCrmkWKcua4omlD+KEoi85G1csw7k=
NOTE: All data above is Base64Encoded.
I am using the below code to generate KeyPair and extract Private and Public Keys:
RSA *rsaKeyPair = NULL;
EVP_PKEY *PrivateKey = NULL;
rsaKeyPair = RSA_new();
BIGNUM *e = NULL;
e = BN_new();
BN_set_word(e, 65537);
//Generating KeyPair
RSA_generate_key_ex(rsaKeyPair, 2048, e, NULL);
PrivateKey = EVP_PKEY_new();
BIO *pri = BIO_new(BIO_s_mem());
BIO *pub = BIO_new(BIO_s_mem());
PEM_write_bio_RSAPrivateKey(pri, rsaKeyPair, NULL, NULL, 0, NULL, NULL);
PEM_write_bio_RSAPublicKey(pub, rsaKeyPair);
size_t pri_len = BIO_pending(pri);
size_t pub_len = BIO_pending(pub);
char *pri_key = malloc(pri_len + 1);
char *pub_key = malloc(pub_len + 1);
BIO_read(pri, pri_key, pri_len);
BIO_read(pub, pub_key, pub_len);
pri_key[pri_len] = '\0';
pub_key[pub_len] = '\0';
NSString *priK = [[[NSString stringWithFormat:#"%s",pri_key] dataUsingEncoding:NSUTF8StringEncoding] base64EncodedString];
NSString *privateKey = [[priK componentsSeparatedByCharactersInSet:[NSCharacterSet newlineCharacterSet]] componentsJoinedByString:#""];
NSString *PKK = [[[NSString stringWithFormat:#"%s",pub_key] componentsSeparatedByCharactersInSet:[NSCharacterSet newlineCharacterSet]] componentsJoinedByString:#""];
NSString *pubK = [[PKK dataUsingEncoding:NSUTF8StringEncoding] base64EncodedString];
NSString *publicKey = [[pubK componentsSeparatedByCharactersInSet:[NSCharacterSet newlineCharacterSet]] componentsJoinedByString:#""];
//IMP: publicKey is shared along with encrypted text(HELLO string encrypted with PrivateKey)
I am encrypting HELLO with PrivateKey using the below:
NSString *myString = #"HELLO";
const char *msg = (const char *)[myString cStringUsingEncoding:NSASCIIStringEncoding];;
err = malloc(130);
if((encrypt_len = RSA_private_encrypt(strlen(msg), (unsigned char*)msg, (unsigned char*)encrypt, rsaKeyPair, RSA_PKCS1_PADDING)) == -1) {
ERR_load_crypto_strings();
ERR_error_string(ERR_get_error(), err);
fprintf(stderr, "Error encrypting message: %s\n", err);
}
NSString *validatorBase64 = [[[NSString stringWithFormat:#"%s",encrypt] dataUsingEncoding:NSUTF8StringEncoding] base64EncodedString];
NSString *validator = [[validatorBase64 componentsSeparatedByCharactersInSet:[NSCharacterSet newlineCharacterSet]] componentsJoinedByString:#""];
You have an issue on this line:
NSString *validatorBase64 = [[[NSString stringWithFormat:#"%s",encrypt] dataUsingEncoding:NSUTF8StringEncoding] base64EncodedString];
because encrypt is binary bytes and not a null-terminated string. This is likely what is causing your variable length results.
Here's a short program in pure C that uses RSA_private_encrypt/RSA_public_decrypt:
#include <stdio.h>
#include <string.h>
#include <openssl/bio.h>
#include <openssl/evp.h>
#include <openssl/rsa.h>
int main() {
BIGNUM *e = BN_new();
BN_set_word(e, 65537);
RSA *rsaKeyPair = RSA_new();
RSA_generate_key_ex(rsaKeyPair, 2048, e, NULL);
BN_free(e);
const unsigned char *plaintext = (unsigned char *)"HELLO";
unsigned char crypttext[RSA_size(rsaKeyPair)];
const int cryptLength = RSA_private_encrypt(
strlen((const char *)plaintext), plaintext,
crypttext,
rsaKeyPair,
RSA_PKCS1_PADDING);
printf("encrypted length: %d\n", cryptLength);
printf("encrypted data: ");
for (int i = 0; i < cryptLength; ++i)
printf("%02x", crypttext[i]);
printf("\n");
printf("encrypted data (base64): ");
BIO *base64 = BIO_new(BIO_s_mem());
base64 = BIO_push(BIO_new(BIO_f_base64()), base64);
BIO_write(base64, crypttext, cryptLength);
BIO_flush(base64);
char *base64Data;
const long base64Length = BIO_get_mem_data(base64, &base64Data);
for (int i = 0; i < base64Length; ++i)
printf("%c", base64Data[i]);
printf("\n");
BIO_free(base64);
unsigned char decrypted[RSA_size(rsaKeyPair)];
const int plainLength = RSA_public_decrypt(
cryptLength, crypttext,
decrypted,
rsaKeyPair,
RSA_PKCS1_PADDING);
printf("decrypted length: %d\n", plainLength);
decrypted[plainLength] = 0;
printf("decrypted data: %s\n", (const char *)decrypted);
RSA_free(rsaKeyPair);
return 0;
}
Sample output:
$ ./rsa
encrypted length: 256
encrypted data: 2a884725dacdb961a51db22444b23a7802c5c612038ad8067bfe8b2db50e2c110fa2fc198ead4db314b9af57ada233228b7f07e09f821dd1928f2358f337bafa6915ae1f394b787a2250f19ff9e8babf9ffce0d7efebff95be5e017225223c05f8d3f93fa1126a9e77d485b38d01bbdf041fece43a388855695f9acd150f968aa23d0f7c247339f9953074171ad168cb06f2b6ff1c59dbde687a97da4360f0883b2a4d399b5213d3dee9a061ad0335f711acbecb212bc8ec1b5c2a3f9dbfc7d695c3593dc634b8b32727c7072cdcc716dfa2e86732fd54dfdbb193c0b0e0cb6d81f408cc12c4b97308c166dfbb0c8934dcba92d2e528c994ed9f10ec44d51ecb
encrypted data (base64): KohHJdrNuWGlHbIkRLI6eALFxhIDitgGe/6LLbUOLBEPovwZjq1NsxS5r1etojMi
i38H4J+CHdGSjyNY8ze6+mkVrh85S3h6IlDxn/nour+f/ODX7+v/lb5eAXIlIjwF
+NP5P6ESap531IWzjQG73wQf7OQ6OIhVaV+azRUPloqiPQ98JHM5+ZUwdBca0WjL
BvK2/xxZ295oepfaQ2DwiDsqTTmbUhPT3umgYa0DNfcRrL7LISvI7BtcKj+dv8fW
lcNZPcY0uLMnJ8cHLNzHFt+i6Gcy/VTf27GTwLDgy22B9AjMEsS5cwjBZt+7DIk0
3LqS0uUoyZTtnxDsRNUeyw==
decrypted length: 5
decrypted data: HELLO
#rhashimoto: u really helped me.. Now I get a fix length encrypted string..
But the base64 generated doesn't match on server, might be I am forwarding the public key in wrong way..
-(void)generateKeyPairs{
RSA *rsaKeyPair = NULL;
EVP_PKEY *PrivateKey = NULL;
rsaKeyPair = RSA_new();
BIGNUM *e = NULL;
e = BN_new();
BN_set_word(e, 65537);
RSA_generate_key_ex(rsaKeyPair, 2048, e, NULL);
// Now we need a private key object
PrivateKey = EVP_PKEY_new();
BIO *pri = BIO_new(BIO_s_mem());
BIO *pub = BIO_new(BIO_s_mem());
PEM_write_bio_RSAPrivateKey(pri, rsaKeyPair, NULL, NULL, 0, NULL, NULL);
PEM_write_bio_RSAPublicKey(pub, rsaKeyPair);
size_t pri_len = BIO_pending(pri);
size_t pub_len = BIO_pending(pub);
char *pri_key = malloc(pri_len + 1);
char *pub_key = malloc(pub_len + 1);
BIO_read(pri, pri_key, pri_len);
BIO_read(pub, pub_key, pub_len);
pri_key[pri_len] = '\0';
pub_key[pub_len] = '\0';
NSString *PK = [[[NSString stringWithFormat:#"%s",pri_key] componentsSeparatedByCharactersInSet:[NSCharacterSet newlineCharacterSet]] componentsJoinedByString:#""];
NSString *privateKey = [self RemoveKeyLabelFrom:[NSString stringWithFormat:#"%#",PK]];
NSString *PKK = [[[NSString stringWithFormat:#"%s",pub_key] componentsSeparatedByCharactersInSet:[NSCharacterSet newlineCharacterSet]] componentsJoinedByString:#""];
NSString *publicKey = [self RemoveKeyLabelFrom:[NSString stringWithFormat:#"%#",PKK]];
NSLog(#"Private Key: %#",privateKey);
NSLog(#"Public Key: %#",publicKey);
}
- (NSString *)RemoveKeyLabelFrom:(NSString *)str {
str = [str stringByReplacingOccurrencesOfString:#"-----BEGIN RSA PRIVATE KEY-----" withString:#""];
str = [str stringByReplacingOccurrencesOfString:#"-----END RSA PRIVATE KEY-----" withString:#""];
str = [str stringByReplacingOccurrencesOfString:#"-----BEGIN RSA PUBLIC KEY-----" withString:#""];
str = [str stringByReplacingOccurrencesOfString:#"-----END RSA PUBLIC KEY-----" withString:#""];
return str;
}
Please tell me wht am I missing to get the proper public key..