I've a private key file .p12.
I'm using Objective-C.
I need to generate Signed digital signature XML like this:
The problem is, I cannot find out how to generate the value in <ds:X509Certificate>. I know it is a public key base64 encoded. But, I can't get the same string after testing various encoding methods..
Need your help!
Found the solution myself. It is so easy.
Using Apple's security.framework:
NSString* pfile = [[NSBundle mainBundle] pathForResource:#"rsa_user" ofType:#"p12" ];
// Load Certificate
NSData *p12data = [NSData dataWithContentsOfFile:pfile];
CFDataRef inP12data = (__bridge CFDataRef)p12data;
SecIdentityRef myIdentity;
SecTrustRef myTrust;
extractIdentityAndTrust(inP12data, &myIdentity, &myTrust);
SecCertificateRef myCertificate;
SecIdentityCopyCertificate(myIdentity, &myCertificate);
NSData *certificateData = (__bridge NSData *) SecCertificateCopyData(myCertificate);
// Output certificate base64 value. (This value goes to <ds:X509Certificate> tag )
NSLog(#"%#", [certificateData base64EncodedString]);
// Helper function
OSStatus extractIdentityAndTrust(CFDataRef inP12data, SecIdentityRef *identity, SecTrustRef *trust)
{
OSStatus securityError = errSecSuccess;
CFStringRef password = CFSTR("password");
const void *keys[] = { kSecImportExportPassphrase };
const void *values[] = { password };
CFDictionaryRef options = CFDictionaryCreate(NULL, keys, values, 1, NULL, NULL);
CFArrayRef items = CFArrayCreate(NULL, 0, 0, NULL);
securityError = SecPKCS12Import(inP12data, options, &items);
if (securityError == 0) {
CFDictionaryRef myIdentityAndTrust = CFArrayGetValueAtIndex(items, 0);
const void *tempIdentity = NULL;
tempIdentity = CFDictionaryGetValue(myIdentityAndTrust, kSecImportItemIdentity);
*identity = (SecIdentityRef)tempIdentity;
const void *tempTrust = NULL;
tempTrust = CFDictionaryGetValue(myIdentityAndTrust, kSecImportItemTrust);
*trust = (SecTrustRef)tempTrust;
}
if (options) {
CFRelease(options);
}
return securityError;
}
Related
It seems so much simpler to encrypt using java than obj-c with Tink. Given a known 32-bytes XChaCha20Poly1305 key, and a 16-bytes authenticated data (aad), how can the same be done in objective-c?
Java:
import com.google.crypto.tink.subtle.XChaCha20Poly1305;
XChaCha20Poly1305 xChaCha20Poly1305 = new XChaCha20Poly1305(key);
byte[] encryptedData = xChaCha20Poly1305.encrypt(plainData, aad);
Long story short, the following java and obj-c codes work together.
Java:
import com.google.crypto.tink.aead.AeadConfig;
import com.google.crypto.tink.*;
byte[] plainData = ...
byte[] key = ...//32 bytes
byte[] aad = ...
AeadConfig.register();
byte[] preffix = {0x1a,0x20};
byte[] fullKey = new byte[preffix.length + key.length];
System.arraycopy(preffix, 0, fullKey, 0, preffix.length);
System.arraycopy(key, 0, fullKey, preffix.length, key.length);
String fullKeyBase64 = new String(com.groups.network.aes.Base64.encode(fullKey), "UTF-8");
String jsonKey = "{\"primaryKeyId\":1635322858,\"key\":[{\"keyData\":{\"typeUrl\":\"type.googleapis.com/google.crypto.tink.XChaCha20Poly1305Key\",\"value\":\""+fullKeyBase64+"\",\"keyMaterialType\":\"SYMMETRIC\"},\"status\":\"ENABLED\",\"keyId\":1635322858,\"outputPrefixType\":\"TINK\"}]}";
KeysetHandle keysetHandle = CleartextKeysetHandle.read(
JsonKeysetReader.withString(jsonKey));
Aead aead = keysetHandle.getPrimitive(Aead.class);
byte[] encrypted = aead.encrypt(plainData, aad);
Objective C:
NSData* encryptedBytes = ...
NSData* aad = ...
NSData* key = ...//32-bytes
NSError *error = nil;
TINKAeadConfig *aeadConfig = [[TINKAeadConfig alloc] initWithError:&error];
if (!aeadConfig || error) {
//handle error
}
if (![TINKConfig registerConfig:aeadConfig error:&error]) {
//handle error
}
NSString* preffix = #"1a20";
NSMutableData* preffixData = [[NSMutableData alloc] init];
unsigned char whole_byte;
char byte_chars[3] = {'\0','\0','\0'};
int i;
for(i=0; i<[preffix length]/2;i++){
byte_chars[0] = [preffix characterAtIndex:i*2];
byte_chars[1] = [preffix characterAtIndex:i*2+1];
whole_byte = strtol(byte_chars, NULL, 16);
[preffixData appendBytes:&whole_byte length:1];
}
NSData* originalKeyData = [key dataUsingEncoding:NSUTF8StringEncoding];
NSMutableData* finalKey = [preffixData mutableCopy];
[finalKey appendData:originalKeyData];
NSString* jsonTmp = [NSString stringWithFormat:#"{\"primaryKeyId\":1635322858,\"key\":[{\"keyData\":{\"typeUrl\":\"type.googleapis.com/google.crypto.tink.XChaCha20Poly1305Key\",\"value\":\"%#\",\"keyMaterialType\":\"SYMMETRIC\"},\"status\":\"ENABLED\",\"keyId\":1635322858,\"outputPrefixType\":\"TINK\"}]}", [finalKey base64Encoding]];
NSData* jsonKeyData = [jsonTmp dataUsingEncoding:NSUTF8StringEncoding];
TINKJSONKeysetReader *reader = [[TINKJSONKeysetReader alloc] initWithSerializedKeyset:jsonKeyData error:&error];
if (!reader || error) {
//handle error
}
TINKKeysetHandle *handle = [[TINKKeysetHandle alloc] initCleartextKeysetHandleWithKeysetReader:reader error:&error];
if (!handle || error) {
//handle error
}
id<TINKAead> aead = [TINKAeadFactory primitiveWithKeysetHandle:handle error:&error];
if (!aead || error) {
//handle error
}
NSData *aadData = [aad dataUsingEncoding:NSUTF8StringEncoding];
NSData *plaintext = [aead decrypt:encryptedBytes withAdditionalData:aadData error:&error];
if (!plaintext || error) {
//handle error
}
Pay attention to the {0x1a,0x20}. This changes according to the type of encryption you use (here it's fine for XChacha20-Poly1305). This can be "discovered" with the Tinkey tool, which outputs a JSON key template if you run something like java -jar tinkey_deploy.jar create-keyset --key-template XCHACHA20_POLY1305 (the output base64 key needs to then be transformed to bytes and you'll need to figure which first 2 bytes correspond to which encryption)
I create public key using SecKeyCreateWithData. Key is created from Pem after stripping the headers.
I have tried to verify signature using
SecKeyRawVerify returns -9809 error (iOS)
SecKeyVerifySignature returns -67808 "RSA signature verification failed, no match" (iOS)
SecTransformExecute returns false. (OSx)
We use SHA256 and Unicode encoding to sign the message(C# RSACryptoProvider).
Code for creating public key:
NSDictionary* attributes =
#{ (id)kSecAttrKeyType: (id)kSecAttrKeyTypeRSA,
(id)kSecAttrKeySizeInBits: #2048,
(id)kSecPublicKeyAttrs:
#{ (id)kSecAttrIsPermanent: #YES,
(id)kSecAttrApplicationTag: tag1
},
(id)kSecAttrCanEncrypt:#YES,
(id)kSecAttrCanVerify:#YES,
(id)kSecAttrKeyClass: (id)kSecAttrKeyClassPublic
};
CFErrorRef error = NULL;
SecKeyRef keyRef = SecKeyCreateWithData((__bridge CFDataRef)publicKeyData,
(__bridge CFDictionaryRef)attributes,
&error);
Verification Code iOS:
size_t signedHashBytesSize = SecKeyGetBlockSize(keyRef);
const void* signedHashBytes = [signature bytes];
NSData *plainData = [dataToSign dataUsingEncoding:NSUTF16StringEncoding];
size_t hashBytesSize = CC_SHA256_DIGEST_LENGTH;
uint8_t* hashBytes = malloc(hashBytesSize);
if (!CC_SHA256([plainData bytes], (CC_LONG)[plainData length], hashBytes)) {
return nil;
}
OSStatus status1 = SecKeyRawVerify(keyRef,
kSecPaddingPKCS1,
hashBytes,
hashBytesSize,
signedHashBytes,
signedHashBytesSize);
Verification Code OSx:
verifier = SecVerifyTransformCreate(keyRef, (__bridge CFDataRef)self.digest, &errorCF);
if (errorCF) { CFShow(errorCF);}
SecTransformSetAttribute(verifier,
kSecTransformInputAttributeName,
(__bridge CFTypeRef)plainData,
&errorCF);
if (errorCF) { CFShow(errorCF); exit(-1); }
SecTransformSetAttribute(verifier,
kSecDigestTypeAttribute,
kSecDigestSHA2,
&errorCF);
if (errorCF) { CFShow(errorCF); exit(-1); }
SecTransformSetAttribute(verifier,
kSecDigestLengthAttribute,
(__bridge CFNumberRef)#256,
&errorCF);
if (errorCF) { CFShow(errorCF); exit(-1); }
CFBooleanRef result1 = NULL;
result1 = SecTransformExecute(verifier, &errorCF);
BOOL success = (result1 != NULL);
I am stuck any help would be appreciated.
I need two functions one which stores the p12 certificate in iOS Keychain and the other function to retrieve the stored p12 certificate.
-(BOOL)addItem:(NSData*)item forKey:(NSString*)key {
NSMutableDictionary *dict = [self newItemDictionaryForKey:key];
[dict setObject:item forKey:(id)kSecValueData];
OSStatus ossstatus = SecItemAdd((CFDictionaryRef)dict, NULL);
if(errSecSuccess != ossstatus) {
NSLog(#"Unable to add item for key %# %ld request dict:%#",key,ossstatus,dict);
}
return (errSecSuccess == ossstatus);
}
To the above function i am sending the NSData that i get from p12 file.
You have to split this into two tasks:
Converting certificate from p12 data to SecIdentityRef. With this I can help
Store private key and certificate chain in keychain - I'm struggling with this too, but I'm couple steps ahead comparing with you.
To perform a conversion, here is a code I'm using:
- (NSError *)setClientIdentityCertificateFromPKCS12Data: (NSData *)PKCS12Data withPassword: (NSString *)password
{
OSStatus securityError = errSecSuccess;
const void *keys[] = { kSecImportExportPassphrase };
const void *values[] = { (__bridge CFStringRef)password };
CFDictionaryRef optionsDictionary = NULL;
optionsDictionary = CFDictionaryCreate(
NULL, keys,
values, (password.length!=0 ? 1 : 0),
NULL, NULL);
CFArrayRef items = NULL;
securityError = SecPKCS12Import((__bridge CFDataRef)PKCS12Data,
optionsDictionary,
&items);
if (securityError == 0) {
CFDictionaryRef identityDic = (CFDictionaryRef)CFArrayGetValueAtIndex(items, 0);
SecIdentityRef secIdentity = (SecIdentityRef)CFDictionaryGetValue(identityDic, kSecImportItemIdentity);
CFArrayRef identityCertChain = (CFArrayRef)CFDictionaryGetValue(identityDic, kSecImportItemCertChain);
securityError = [self setClientIdentity: secIdentity withCertificateChain: identityCertChain];
}
if (optionsDictionary) {
CFRelease(optionsDictionary);
}
if (items) {
CFRelease(items);
}
NSError *error = nil;
if (securityError != errSecSuccess)
{
NSDictionary *info = nil;
#if !TARGET_OS_IPHONE
NSString *errorDescription = nil;
errorDescription = (__bridge_transfer NSString *)SecCopyErrorMessageString(securityError, NULL);
if (errorDescription)
{
info = #{ NSLocalizedDescriptionKey:errorDescription };
}
#endif
error = [NSError errorWithDomain: NSOSStatusErrorDomain
code: securityError
userInfo: info];
}
return error;
}
I am trying to get all the startup(login) application of my OSX10. In order to do so I have written the this code (given below):
-(NSMutableArray*)getStartUpApplicaitonPaths{
// Get the LoginItems list.
LSSharedFileListRef loginItemsRef = LSSharedFileListCreate(NULL, kLSSharedFileListSessionLoginItems, NULL);
if (loginItemsRef == nil) return nil;
// Iterate over the LoginItems.
NSArray *loginItems = (__bridge NSArray *)LSSharedFileListCopySnapshot(loginItemsRef, nil);
NSMutableArray* data = [NSMutableArray arrayWithArray:loginItems];
return data;
}
From the above code I am getting an NSMutableArray of __NSCFType objects. When I am trying to get the path by converting a object of the array
NSString* file = [NSString stringWithFormat:#"%#", [startupFiles objectAtIndex:0]];
I am getting the result given below:
BundleBinding [0x103] URL:
file:///Applications/iTunes.app/Contents/MacOS/iTunesHelper.app/
bundle identifier: com.apple.iTunesHelper iTunesHelper
I need to parse the URL of and Identifier from the string given above. Please help.
The objects ar eof type: LSSharedFileListItem which is only documented in the header file.
Here is some code that may help, it will NSLog() all the file names:
NSURL *itemURL = [[NSBundle mainBundle] bundleURL];
CFURLRef URLToToggle = (__bridge CFURLRef)itemURL;
LSSharedFileListRef loginItems = LSSharedFileListCreate(kCFAllocatorDefault, kLSSharedFileListSessionLoginItems, /*options*/ NULL);
if (loginItems) {
UInt32 seed = 0U;
Boolean found;
CFArrayRef currentLoginItems = LSSharedFileListCopySnapshot(loginItems,
&seed);
const CFIndex count = CFArrayGetCount(currentLoginItems);
for (CFIndex idx = 0; idx < count; ++idx) {
LSSharedFileListItemRef item = (LSSharedFileListItemRef)CFArrayGetValueAtIndex(currentLoginItems, idx);
CFURLRef outURL = NULL;
const UInt32 resolutionFlags = kLSSharedFileListNoUserInteraction | kLSSharedFileListDoNotMountVolumes;
#if (__MAC_OS_X_VERSION_MIN_REQUIRED >= MAC_OS_X_VERSION_10_10)
outURL = LSSharedFileListItemCopyResolvedURL(item, resolutionFlags, /*outError*/ NULL);
if (outURL == NULL) {
if (outURL)
CFRelease(outURL);
continue;
}
#else
OSStatus err = LSSharedFileListItemResolve(item, resolutionFlags, &outURL, /*outRef*/ NULL);
if (err != noErr || outURL == NULL) {
if (outURL)
CFRelease(outURL);
continue;
}
#endif
found = CFEqual(outURL, URLToToggle);
NSLog(#"%#", outURL);
CFRelease(outURL);
}
CFRelease(currentLoginItems);
CFRelease(loginItems);
}
Output in my instance:
file:///Volumes/User/dgrassi/Library/PreferencePanes/MouseLocator.prefPane/Contents/Resources/MouseLocatorAgent.app/
file:///Applications/iTunes.app/Contents/MacOS/iTunesHelper.app/
file:///Applications/Dropbox.app/
file:///Library/PreferencePanes/Screens%20Connect.prefPane/Contents/MacOS/Screens%20Connect.app/
file:///Library/Application%20Support/EyeTV/EyeTV%20Helper.app/
file:///Applications/Carbon%20Copy%20Cloner.app/Contents/Library/LoginItems/CCC%20User%20Agent.app/
This came from seafile-client
I have the following Code to read from a .p12 certificate and I want to hand the certifcate data over to an autentication challenge:
- (SecIdentityRef)getClientCertificate {
NSString *thePath = [[NSBundle mainBundle] pathForResource:#"fest" ofType:#"p12"];
if(!thePath)
return NULL;
NSData *PKCS12Data = [[NSData alloc] initWithContentsOfFile:thePath] ;
CFDataRef inPKCS12Data = (CFDataRef)CFBridgingRetain(PKCS12Data);
CFStringRef password = CFSTR("fest");
const void *keys[] = { kSecImportExportPassphrase };
const void *values[] = { password };
CFDictionaryRef optionsDictionary = CFDictionaryCreate(NULL, keys, values, 1, NULL, NULL);
CFArrayRef items = CFArrayCreate(NULL, 0, 0, NULL);
OSStatus ret = SecPKCS12Import(inPKCS12Data, optionsDictionary, &items);
if (ret != errSecSuccess)
{
// TODO: handle error.
NSLog(#"-> SecPKCS12Import error (%ld)", ret);
}
CFRelease(optionsDictionary);
CFDictionaryRef identityDict = CFArrayGetValueAtIndex(items, 0);
SecIdentityRef identityApp = nil;
if(!identityDict)
return nil;
identityApp = (SecIdentityRef)CFDictionaryGetValue(identityDict, kSecImportItemIdentity);
SecIdentityRef identity;
SecCertificateRef cert;
OSStatus err;
CFStringRef certName;
identity = identityApp;
assert( (identity != NULL) && (CFGetTypeID(identity) == SecIdentityGetTypeID()) );
cert = NULL;
err = SecIdentityCopyCertificate(identity, &cert);
assert(err == noErr);
assert(cert != NULL);
certName = SecCertificateCopySubjectSummary(cert);
assert(certName != NULL);
NSLog(#"%#" , (id) CFBridgingRelease(certName));
//NSDictionary *userInfo = [NSDictionary dictionaryWithObjectsAndKeys:(id)CFBridgingRelease(certName),#"USERID",nil];
[[NSNotificationCenter defaultCenter] postNotificationName:#"UserInfoReceived" object:nil];
//CFRelease(cert);
//CFRelease(certName);
return identityApp;
_identities = [[NSMutableArray alloc] initWithArray:(__bridge NSArray *)(items)];
_certs = [[NSMutableArray alloc] initWithArray:(__bridge NSArray *)(cert)];
}
At the end I want to assign the items CFArrayRef to the identities Array. But is does not work. Has somebody an idea?
Thanks!
A CFArrayRef is toll-free bridged to NSArray *, so you can cast it as such and then create a mutable copy. Try this:
_identities = [(NSArray *)items mutableCopy];