How to do asymmetric encryption/decryption with OSX 10.7+ without openssl? - macos

Since openssl is deprecated in osx 10.7+, I'd like to switch from openssl to the internal osx keychain and crypto function.
But now I am stuck on asymmetric encryption/decryption.
How can I do encryption/decryption of a randomly generated symmetric key with a asymmetric (RSA) key. With openssl it's quite easy.
In the apple dev docs, they say that CommonCrypto supports asymmetric encryption, but while checking the headers, I can only see support for symmetric stuff.
Any hints?

Take a look at Cryptographic Message Syntax Services and see if that can do what you need.
Also, you're misreading the OpenSSL thing just a bit. The OpenSSL libraries that ship with the OS are deprecated. That doesn't mean you can't continue to use OpenSSL. OpenSSL is Open Source, and there's nothing stopping you from downloading it and using it freely in your application.
Apple's deprecation just means that if you use OpenSSL, you need to include your own copy of the OpenSSL libraries so that you are responsible for keeping your OpenSSL library up-to-date and for fixing any breakage that occurs whenever you do so. :-)
And if not, the iOS asymmetric encryption and decryption functions (SecKeyEncrypt and SecKeyDecrypt) do exist in OS X, and the iOS header even shows that they are available in OS X. I'm not sure why they aren't in the OS X SDK. I filed a bug, and it was marked as a dup.
It probably would not be possible for Apple to remove those functions in the future without breaking the Simulator, but if you're submitting to the app store and they give you grief about using them, here's a roughly compatible replacement for SecKeyEncrypt built using the Security Transforms API:
// Workaround for SecKeyEncrypt not really being public API in OS X
OSStatus OSXSecKeyEncrypt ( SecKeyRef key, SecPadding padding, const uint8_t *plainText, size_t plainTextLen, uint8_t *cipherText, size_t *cipherTextLen )
{
CFMutableDictionaryRef parameters = CFDictionaryCreateMutable(
kCFAllocatorDefault, 0, &kCFTypeDictionaryKeyCallBacks,
&kCFTypeDictionaryValueCallBacks);
CFDictionarySetValue(parameters, kSecAttrKeyType, kSecAttrKeyTypeAES);
CFErrorRef error = NULL;
SecTransformRef encrypt = SecEncryptTransformCreate(key, &error);
if (error) {
AFNSLog(#"Encryption failed: %#\n", (__bridge NSError *)error);
return (OSStatus)[(__bridge NSError *)error code];
}
SecTransformSetAttribute(
encrypt,
kSecPaddingKey,
NULL, // kSecPaddingPKCS1Key (rdar://13661366 : NULL means kSecPaddingPKCS1Key and
// kSecPaddingPKCS1Key fails horribly)
&error);
CFDataRef sourceData = CFDataCreate(kCFAllocatorDefault, plainText, plainTextLen);
SecTransformSetAttribute(encrypt, kSecTransformInputAttributeName,
sourceData, &error);
CFDataRef encryptedData = SecTransformExecute(encrypt, &error);
if (error) {
AFNSLog(#"Encryption failed: %#\n", (__bridge NSError *)error);
return (OSStatus)[(__bridge NSError *)error code];
}
if ((unsigned long)CFDataGetLength(encryptedData) > *cipherTextLen) {
return errSecBufferTooSmall;
}
*cipherTextLen = CFDataGetLength(encryptedData);
CFDataGetBytes(encryptedData, CFRangeMake(0, *cipherTextLen), cipherText);
return noErr;
}
You should be able to adapt the code for decryption fairly easily; I didn't need it for my purposes, so I didn't write that function.

Related

Issue with net-snmp v3 for authPriv for AES

I am creating a c++ project using the net-snmp libraries i build, I was able to interface with my hardware via SNMP v2c as well as SNMP v3 (authNoPriv). However, this was unsuccessful when I tried using authPriv, is there any advice on this?
What I suspect is that net-snmp does not support AES.
When i tried to run net-snmp directly, I see for the privacy protocol there's only the option for DES. So I would like to confirm does net-snmp supports both AES128 and DES privacy protocol?
For authNoPriv, I was returned with the Authentication failure when I used SHA-1 Authentication Protocol
For authPriv, I couldn't establish any connection with the SNMP hardware.
I suspect there is something wrong in my code, as there was no issue with authNoPriv with MD5 Authentication Protocol, but the above errors occur when I configured to the respective the security protocol.
// Definitions
const char * user = "snmpuser";
const char * our_v3_passphrase = "passphrase";
const char * our_v3_privphrase = "privphrase";
struct snmp_session session;
SOCK_STARTUP;
// Initialize the SNMP library
snmp_sess_init(&session);
session.peername = _strdup(argv[1])
// set the SNMP version number
session.version = SNMP_VERSION_3;
session.securityNameLen = strlen(session.securityName);
// set the security level
session.securityLevel = SNMP_SEC_LEVEL_AUTHPRIV; // SNMP_SEC_LEVEL_AUTHNOPRIV (for authNoPriv)
// set the authentication protocol
session.securityAuthProto = usmHMACMD5AuthProtocol; // usmHMACSHA1AuthProtocol
session.securityAuthProtoLen = USM_AUTH_PROTO_MD5_LEN; // USM_AUTH_PROTO_SHA_LEN
session.securityAuthKeyLen = USM_AUTH_KU_LEN;
// set authentication key to a hashed version of passphrase
if (generate_Ku(session.securityAuthProto, session.securityAuthProtoLen, (u_char *)our_v3_passphrase, strlen(our_v3_passphrase), session.securityAuthKey, &session.securityAuthKeyLen) != SNMPERR_SUCCESS) {
snmp_perror(argv[0]);
snmp_log(LOG_ERR, "Error generating Ku from authentication passphrase. \n");
SOCK_CLEANUP;
exit(1);
}
// set the privacy protocol
session.securityPrivProto = usmAES128PrivProtocol; // usmDESPrivProtocol
session.securityAuthProtoLen = USM_PRIV_PROTO_AES128_LEN; // USM_PRIV_PROTO_DES_LEN
session.securityAuthKeyLen = USM_PRIV_KU_LEN;
// set privacy key to a hashed version of privphrase
if (generate_Ku(session.securityAuthProto, session.securityAuthProtoLen, (u_char *)our_v3_privphrase, strlen(our_v3_privphrase), session.securityPrivKey, &session.securityPrivKeyLen) != SNMPERR_SUCCESS) {
snmp_perror(argv[0]);
snmp_log(LOG_ERR, "Error generating Ku from authentication passphrase. \n");
SOCK_CLEANUP;
exit(1);
}

Decode PKCS#7 Signature via Windows API?

I wish to parse and display the contents of an Authenticode PKCS#7 signature as extracted from a Window PE binary's Security Directory.
I can use OpenSSL to do this on the command line with "openssl pkcs7 -text -in extracted_signature.pks -inform DER -print_certs", however I need to do this via C/C++ and the Windows API. I cannot use the OpenSSL library itself.
Using the CryptDecodeObjectEx API I can begin to decode the extracted signature:
CRYPT_CONTENT_INFO * content_info;
DWORD len;
CryptDecodeObjectEx(
X509_ASN_ENCODING | PKCS_7_ASN_ENCODING,
PKCS_CONTENT_INFO,
pointer_to_extracted_signature,
length_of_extracted_signature,
CRYPT_DECODE_ALLOC_FLAG,
NULL,
&content_info,
&len
);
The above call completes successfully and content_info->pszObjId will have an OID of "1.2.840.113549.1.7.2" (szOID_RSA_signedData) however I am unable to find the structures needed to continue decoding. The available OID's for CryptDecodeObjectEx are listed here.
Can anybody please advise how to decode an Authenticode PKCS#7 signature via the Windows API?
I have found the correct way to decode an Authenticode PKCS#7 signature is to use CryptQueryObject with the CERT_QUERY_OBJECT_BLOB and CERT_QUERY_CONTENT_FLAG_PKCS7_SIGNED flags set. Code snippit below for anybody who might need to do this.
CERT_BLOB cert_blob;
HCERTSTORE cert_store = NULL;
HCRYPTMSG cert_msg = NULL;
cert_blob.pbData = pointer_to_extracted_signature;
cert_blob.cbData = length_of_extracted_signature;
CryptQueryObject(
CERT_QUERY_OBJECT_BLOB,
&cert_blob,
CERT_QUERY_CONTENT_FLAG_PKCS7_SIGNED,
CERT_QUERY_FORMAT_FLAG_BINARY,
0,
NULL,
NULL,
NULL,
&cert_store,
&cert_msg,
NULL
);
PCCERT_CONTEXT next_cert = NULL;
while( (next_cert = CertEnumCertificatesInStore( cert_store, next_cert ) ) != NULL )
{
// process next_cert...
}

Using xattr to set the Mac OSX quarantine property

There is a lot of information on StackOverflow and elsewhere about how to clear the Mac quarantine property.
In my case I would like to set it.
This is in order to test that my app is properly signed so that the user will hot get the "Untrusted Developer" warning after downloading it.
My app is particularly large (we distribute from a large file download site, not the store) and it is not convenient to have to upload and download to test this.
I have had some battles with code signing the past week so this testing is important to me.
Once a file has the quarantine property I see how I can alter it to have the values:
0002 = downloaded but never opened (this is the one that causes the warning)
0022 = app aborted by user from the warning dialogue (you hit 'cancel' in the dialogue)
0062 = app opened (at least) once (you hit 'open' in the dialogue)
But I don't know how to give it the property in the first place.
The code for this isn't hard, but you need FSRef's to do it, which are deprecated. That said, it still works on 10.9. You have to link with CoreServices.
int main(int argc, const char * argv[]) {
#autoreleasepool {
if (argc != 2) {
printf("quarantine <path>\n");
exit(1);
}
NSString *path = #(argv[1]);
OSStatus result;
FSRef pathRef;
result = FSPathMakeRef((UInt8*)[path UTF8String], &pathRef, 0);
if (result != noErr) {
NSLog(#"Error making ref (%d): %s", result, GetMacOSStatusCommentString(result));
exit(result);
}
NSDictionary *quarantineProperties = #{(__bridge id)kLSQuarantineTypeKey: (__bridge id)kLSQuarantineTypeOtherDownload};
result = LSSetItemAttribute(&pathRef,
kLSRolesAll,
kLSItemQuarantineProperties,
(__bridge CFTypeRef)quarantineProperties);
if (result != noErr) {
NSLog(#"Error setting attribute (%d): %s", result, GetMacOSStatusCommentString(result));
}
exit(result);
}
return 0;
}
Another approach is to just copy the quarantine information from one file to another. You can serialize xattr information like this:
xattr -p com.apple.quarantine file > file.xattr
You can then apply those attributes to another file like this:
xattr -w com.apple.quarantine "`cat file.xattr`" file
(That should work, but I haven't tested it with quarantining in particular. I use a similar technique to save code signatures and reapply them.)

How does OS X's defaults command get access to prefs of sandboxed apps?

I am writing a preferences editor tool (see http://www.tempel.org/PrefsEditor). It is effectively a GUI version of the defaults command.
I have trouble reading (let alone writing) preferences of random sandboxed applications, though.
For instance, when I try to get the keys of the Maps app, I get NULL returned:
CFArrayRef prefs = CFPreferencesCopyKeyList (CFSTR("com.apple.Maps"), kCFPreferencesCurrentUser, kCFPreferencesAnyHost);
However, the defaults command is able to read those prefs:
defaults read com.apple.Maps
I like to know how the defaults command accomplishes this, trying to do the same in my tool.
try that:
CFPropertyListRef prop = CFPreferencesCopyValue(CFSTR("ElementsVersion"),
CFSTR("/Users/karsten/Library/Containers/com.apple.Maps/Data/Library/Preferences/com.apple.Maps"),
CFSTR("kCFPreferencesCurrentUser"),
CFSTR("kCFPreferencesAnyHost"));
seems you need the path to the file, not just the bundle-id
Karsten’s answer is correct but for the sake of completeness, the defaults command uses the undocumented _CFPreferencesCopyApplicationMap() function to retrieve the full URL of the preferences.
#import <CoreFoundation/CoreFoundation.h>
extern CFDictionaryRef _CFPreferencesCopyApplicationMap(CFStringRef userName, CFStringRef hostName);
int main(int argc, char *argv[])
{
#autoreleasepool
{
CFDictionaryRef applicationMap = _CFPreferencesCopyApplicationMap(kCFPreferencesCurrentUser, kCFPreferencesAnyHost);
CFArrayRef urls = CFDictionaryGetValue(applicationMap, CFSTR("com.apple.mail"));
CFShow(urls);
CFRelease(applicationMap);
}
}
I've added to 0xced's excellent answer so that the code can be packaged into a command-line tool that accepts the bundle ID as an argument. Forgive me if this is obvious to experienced Mac programmers, but as someone who has never used CoreFoundation I found this to be non-trivial.
#import <CoreFoundation/CoreFoundation.h>
extern CFDictionaryRef _CFPreferencesCopyApplicationMap(CFStringRef userName, CFStringRef hostName);
int main(int argc, char *argv[]) {
#autoreleasepool {
if (argc < 2) {
// Print usage string & exit.
fprintf(stderr, "usage: GetPrefDomains bundle_id\n");
exit(1);
}
// Get the bundle ID from the first command-line argument.
CFStringRef bundleID = CFStringCreateWithCString(NULL, argv[1], kCFStringEncodingUTF8);
// Get the list of preference domain urls.
CFDictionaryRef applicationMap = _CFPreferencesCopyApplicationMap(kCFPreferencesCurrentUser, kCFPreferencesAnyHost);
CFArrayRef urls = CFDictionaryGetValue(applicationMap, bundleID);
// If no urls exist (invalid bundle ID), exit.
if (!urls) {
fprintf(stderr, "No preference domains found.\n");
exit(0);
}
// Print the paths to the preference domains.
CFIndex urlsCount = CFArrayGetCount(urls);
for (int i = 0; i < urlsCount; i++) {
CFURLRef url = CFArrayGetValueAtIndex(urls, i);
CFStringRef path = CFURLCopyPath(url);
printf("%s\n", CFStringGetCStringPtr(path, kCFStringEncodingUTF8));
}
// Clean up.
CFRelease(bundleID);
CFRelease(applicationMap);
}
}
Save the code as GetPrefDomains.m, compile, and invoke as:
GetPrefDomains com.apple.mail
This was useful to me because surprisingly the defaults command is case-sensitive and misbehaves silently with certain Apple applications that are under the filesystem protections in SIP on Mojave 10.14 or later (Safari & Mail, most notably). Add in the fact that Apple's capitalization rules are not consistent (com.apple.mail vs. com.apple.Notes), sandboxed preference paths, and the fact that that the filesystem is not case-sensitive and you quickly run into some very frustrating edge cases.

bundlePath returns empty when running as a Launch Deamon on MacOSX 10.5

I've got a Cocoa command-line app which is built to target 10.5 SDK. In the app, I have
NSString *appPath = [[NSBundle mainBundle] bundlePath];
NSLog(#"%#", appPath);
On Mac OSX 10.5, when I run the app from the command line, I get the expected output of the path. However, if I set the app up to run as a LaunchDeamon, it only outputs a '/'
It works as expected on 10.6 and on 10.7 both as a Deamon and as an app. Anyone know why the difference would be? Is there a better way to get the application path that would work on 10.5+?
UPDATE
For me, the solution in the accepted answer did not work. However, the comment about adding the "WorkingDirectory" key to the LaunchDeamon's plist file worked out. Apparently this is needed for Mac OS X 10.5, but not 10.6+.
Thanks for answering my clarifying question.
NSBundle depends on a existing bundle, with it's associated Info.plists and bundle ID's (e.g. com.apple.textedit.app), etc.
While a single binary is not a bundle, I'm guessing that Apple engineering fixed up [[NSBundle mainBundle] bundlePath] to do "the right thing" in 10.6 & 10.7. But you still need a solution for 10.5.
Maybe the UNIX library function char * getcwd(char *buf, size_t size) would get you to where you need to be.
For a proper solution, I'd recommend doing a run-time conditional check with code that looks something like this:
+ (NSString *) getAppPath
{
NSString * appPath = NULL;
SInt32 minorVersionNum;
OSErr err;
err = Gestalt(gestaltSystemVersionMinor,&minorVersionNum);
// do this only if we're running on anything *older* than 10.6
if((noErr == err) && (minorVersionNumber < 6))
{
// hopefully the below define is enough space for any returned path
#define kMaxPathLength 512
size_t bufferLength = kMaxPathLength;
char bufferToHoldPath[kMaxPathLength];
// initialize the buffer & guarantee null-terminated strings
bzero(&bufferToHoldPath,bufferLength);
if( getcwd(&bufferToHoldPath, bufferLength) != NULL)
{
appPath = [NSString stringWithUTF8String: bufferToHoldPath];
}
}
// this code runs for 10.6 and *newer*, and attempts it on
// 10.5 only if the above getcwd call failed
if(NULL == appPath)
{
appPath = [[NSBundle mainBundle] bundlePath];
}
return(appPath);
}
I did not test this code out so YMMV.

Resources