bdrister / aquaticprime Goto Github PK
View Code? Open in Web Editor NEWMac software licensing code using cryptographically signed license files
Home Page: http://github.com/bdrister/AquaticPrime
Mac software licensing code using cryptographically signed license files
Home Page: http://github.com/bdrister/AquaticPrime
What is it? The AquaticPrime framework is a secure registration method for your shareware applications, released as free open-source software. How is this possible? AquaticPrime uses RSA encryption to provide fairly good security - the same that is used to protect government documents. It is computationally infeasible for an attacker to generate fake serial numbers, despite the entire framework being open-source. Where did it come from? AquaticPrime was written primarily by Lucas Newman, circa 2005, and has enjoyed acceptance among the indie Mac developer community. Many developers have made their own tweaks and extensions to it since then, but for the most part they've stayed with the authors. I've (Benjamin Rister) created this new repository and volunteered to act as maintainer in an effort to reintegrate these changes so that everybody can benefit from them. How do I use this? Check out the wiki at http://wiki.github.com/bdrister/AquaticPrime.
The licence utility hangs at launch on 10.6.
A sample shows that [OAGradientTableView highlightSelectionInClipRect:] is getting called recursively.
Simple fix is to change the License Templates table view class in the nib from OAGradientTableView to AQTableView.
This keeps the item delete functionality but discards the troublesome gradient highlight.
Other fixes for this may be possible. Only tested on 10.6.
In scanning the implementations for issue 6, it appears to me at first glance that the STL implementation doesn't sort the key array, and as such is an invalid implementation.
This either needs to be fixed, or the implementation removed from distribution.
The php implementation of the license generator sometimes creates a signature that is shorter than some decoder expect. This happens when the license key starts / ends with 0 bytes.
Renaming a product causes template data loss if the rename causes the producer data to be sorted.
This occurs because the template data is not saved when the rename occurs.
This works okay if the product data order does not change. Fails otherwise.
Only tested on 10.6
The following patch saves the template data along with the product data on rename.
--- /Users/Jonathan/Documents/Download/bdrister-AquaticPrime-1a93412/License Utility/Classes/ProductController.m 2010-02-10 06:03:04.000000000 +0000
+++ /Users/Jonathan/Documents/Computing/Aquatic Prime/bdrister-AquaticPrime-1a93412/License Utility/Classes/ProductController.m 2010-02-14 13:20:17.000000000 +0000
@@ -272,13 +272,24 @@
NSString *newProductPath = [[@"~/Library/Application Support/Aquatic/Product Keys" stringByExpandingTildeInPath]
stringByAppendingString:[NSString stringWithFormat:@"/%@.plist", object]];
stringByAppendingString:[NSString stringWithFormat:@"/%@.plist", [productArray objectAtIndex:row]]];
stringByAppendingString:[NSString stringWithFormat:@"/%@.plist", object]];
// Select the renamed item
[productTable selectRowIndexes:[NSIndexSet indexSetWithIndex:[productArray indexOfObject:object]]
byExtendingSelection:NO];
}
Wondering if there is any plan to upgrade the crypto / ciphers and other security measures used in the license generation?
I am using FastSpring as license generator. Sometimes (1 of 1000) I get license file where 'data' tag ends with "==" instead of single "=". Such licenses fail the check via APCreateDictionaryForLicenseFile() (I use STL implementation with mingw-64).
Is it generator bug?
Can this be fixed/avoided by changing STL implementation code?
This is not necessarily the case; for instance, a quantity field might reasonably be an integer (and e.g. the Ruby version will happily generate such integer values in the plist).
Need to decide whether this is something that we are willing to stand behind as a requirement. Either the generators need to be updated to only generate strings, or the validators need to be updated to use the strings out of the XML tags instead of the parsed plist.
I've been Implementing AquaticPrime in my app and it seems its code only hash the "value" fields of the license but not the "key" fields;
Then if I have in a license file something like this:
...
[key]Demo_Version[/key]
[string]YES[/string]
[key]Extended_Features[/key]
[string]NO[/string]
...
the hash is "only" performed over the content of the tags
but if a clever user edits the license like i.e.:
...
[key]Extended_Features[/key]
[string]YES[/string>
[key]Demo_Version[/key]
[string]NO[/string]
...
as the code only hashes the "values" and they were not altered then the license will be validated w/o error !!
AquaticPrime hashes the string:
[value_1][value_2][value_3]...[value_n]
when it should be hashing the string:
[key_1][value_1][key_2][value_2][key_3][value_3]...[key_n][value_n]
the keys must be always be part of the hash.
Please fix this
Best
Patrick
The c implementation always fails when called from AquaticPrime.php (argument count mismatch).
Cause is the single quotes in the following around $fixedApostrophes in AquaticPrime.php line 129.
$passthruString = $aquatic_root."/aquaticprime $key $privKey '$fixedApostrophes'";
$fixedApostrophes is generated thus,
// Escape apostrophes by un-quoting, adding apos, then re-quoting
// so this turns ' into '\'' ... we have to double-slash for this php.
$fixedApostrophes = escapeshellarg($total);
escapeshellarg() is documented to enclose its return value in single quotes.
Fix is to remove the extra quotes.
$passthruString = $aquatic_root."/aquaticprime $key $privKey $fixedApostrophes";
I am trying to get this to work using the Core Foundation library on OSX.
However i am seeing really inconsistent license validation, sometimes it validates fine, then other times not, using exactly the same file.
Here is the code i am using at the moment:
- (void)checkApexProcess{
CFMutableStringRef key = CFStringCreateMutable(NULL, 0);
CFStringAppend(key, CFSTR("0xB57B7D0E180"));
CFStringAppend(key, CFSTR("E"));
CFStringAppend(key, CFSTR("E"));
CFStringAppend(key, CFSTR("C4FD9AAAFC389F8"));
CFStringAppend(key, CFSTR("438903BC20D0"));
CFStringAppend(key, CFSTR("E"));
CFStringAppend(key, CFSTR("E"));
CFStringAppend(key, CFSTR("1F02C25010F8F47C"));
CFStringAppend(key, CFSTR("86"));
CFStringAppend(key, CFSTR("C"));
CFStringAppend(key, CFSTR("C"));
CFStringAppend(key, CFSTR("4FABF44A4376945C6F8F92292B"));
CFStringAppend(key, CFSTR("D45043012F6959BD6D3E1D246D1C1B"));
CFStringAppend(key, CFSTR("6F4A53FA1C023B4A91BC4578504B1D"));
CFStringAppend(key, CFSTR("9BEA69A53FAE91FD36ACA165CA57"));
CFStringAppend(key, CFSTR("B"));
CFStringAppend(key, CFSTR("B"));
CFStringAppend(key, CFSTR(""));
CFStringAppend(key, CFSTR("B"));
CFStringAppend(key, CFSTR("4"));
CFStringAppend(key, CFSTR("4"));
CFStringAppend(key, CFSTR("C30A925FAFBB1CA595D5011DA0E"));
CFStringAppend(key, CFSTR("BF819BC6A909861DE8C5121FCA06AF"));
CFStringAppend(key, CFSTR("FB3290875E3ED23D1F"));
APSetKey(key);
NSURL* filePath;
NSOpenPanel* openDlg = [NSOpenPanel openPanel];
[openDlg setCanChooseFiles:YES];
[openDlg setCanChooseDirectories:NO];
CFURLRef filePathUrl;
bool Valid;
//CFDictionaryRef cfUserLicenseInfo;
NSDictionary* apexDict;
if ( [openDlg runModal] == NSOKButton )
{
for( NSURL* URL in [openDlg URLs] )
{
filePath = URL;
NSString* filePathString = [URL path];
filePathUrl = CFURLCreateWithFileSystemPath(kCFAllocatorDefault, (CFStringRef) filePathString, kCFURLPOSIXPathStyle, FALSE);
//CFDictionaryRef cfUserLicenseInfo = APCreateDictionaryForLicenseFile(filePathUrl);
Valid = APVerifyLicenseFile(filePathUrl);
//Valid = APVerifyLicenseData(cfUserLicenseInfo);
DDLogCVerbose( @"%@", filePath );
DDLogCVerbose( @"%@", filePathUrl );
DDLogCVerbose( @"%@", [URL path] );
//apexDict = [NSDictionary dictionaryWithContentsOfFile:(filePathString)];
}
//Valid = testValidLicenseFile2(filePathUrl);
if(Valid == true){
//[self storeLicenseFile:filePath];
[self enableApexButtons];
} else {
[self displayApexAlertInvalid];
[self removeApexFile];
}
}
}
This leads to mutually incompatible license files in cases where the keys sort differently.
Case-insensitive: Cocoa, PHP, CoreFoundation
Case-sensitive: Python, Ruby, C#
Given the prevalence of the PHP implementation, that algorithm should probably prevail. Should probably also make a stand on diacritic-sensitive sorting, too.
Building the CoreFoundation AquaticPrime against the 10.7 SDK finds deprecated methods:
/Users/fraser/Development/Harmony/AquaticPrime.c:41:14: warning: 'RSA_new' is deprecated [-Wdeprecated-declarations]
rsaKey = RSA_new();
/Users/fraser/Development/Harmony/AquaticPrime.c:44:5: warning: 'BN_hex2bn' is deprecated [-Wdeprecated-declarations]
BN_hex2bn(&rsaKey->e, "3");
/Users/fraser/Development/Harmony/AquaticPrime.c:63:9: warning: 'BN_hex2bn' is deprecated [-Wdeprecated-declarations]
BN_hex2bn(&rsaKey->n, keyCStringBuffer);
/Users/fraser/Development/Harmony/AquaticPrime.c:67:9: warning: 'BN_dec2bn' is deprecated [-Wdeprecated-declarations]
BN_dec2bn(&rsaKey->n, keyCStringBuffer);
/Users/fraser/Development/Harmony/AquaticPrime.c:132:27: warning: 'RSA_size' is deprecated [-Wdeprecated-declarations]
int checkDigestMaxSize = RSA_size(rsaKey)-11;
/Users/fraser/Development/Harmony/AquaticPrime.c:136:3: warning: 'RSA_public_decrypt' is deprecated [-Wdeprecated-declarations]
RSA_public_decrypt((int)sigDataLength, sigBytes, checkDigest, rsaKey, RSA_PKCS1_PADDING) != SHA_DIGEST_LENGTH) {
/Users/fraser/Development/Harmony/AquaticPrime.c:168:5: warning: 'SHA1_Init' is deprecated [-Wdeprecated-declarations]
SHA1_Init(&ctx);
/Users/fraser/Development/Harmony/AquaticPrime.c:181:9: warning: 'SHA1_Update' is deprecated [-Wdeprecated-declarations]
SHA1_Update(&ctx, valueBytes, strlen(valueBytes));
/Users/fraser/Development/Harmony/AquaticPrime.c:185:5: warning: 'SHA1_Final' is deprecated [-Wdeprecated-declarations]
SHA1_Final(digest, &ctx);
9 warnings generated.
The SHA1 functions can be replaced with CC_SHA1 equivalents. Not sure about the RSA or BN functions.
There is, probably, a better way to «make it harder to replace public key» then placing CFStringAppend(…)
function calls one just by one. Try mixing it with other pointless stuff:
// - (void)populateKeyView
[pubConstruct appendString: [NSString stringWithFormat:@"\tCFStringAppend(key, CFSTR(\"%@\"));\n", …
[pubConstruct appendString: /* any meaningless code goes here */ ];
[pubConstruct appendString: [NSString stringWithFormat:@"\tCFStringAppend(key, CFSTR(\"%@\"));\n", …
[pubConstruct appendString: /* another bit of trash code*/ ];
Still based on 10.4 SDK (which is now an optional install), and isn't 64-bit.
As this is a developer tool, this isn't super-high priority (though issue #3 was having trouble with this), but should probably be done before the next major OS release, since 10.4 SDK may totally disappear then. My inclination would be to make it 10.6/64-bit only.
I have just upgraded my dev environment to 10.11 and am now unable to compile my application that uses AquaticPrime as the openssl headers are no longer included in the SDK.
Right-click on product, choose Rename. No effect.
I recently discovered that if you use the PHP implementation with values that are already UTF8 encoded, the signature generated does not match the one that CoreFoundation will generate. Mostly this affects people who use their name which has special characters in it, and that comes across via XML already in UTF8.
I managed to fix the problem in my case with this patch:
https://gist.github.com/706902
But perhaps a better way would be to detect whether the values are in UTF8 already and only convert them if they're not?
Is there a PHP validation script available? I would like to check a user's license on my web-server to let them get access to coupon codes etc.
It seems that none of the values going into the XML are getting encoding. Having anything with an ampersand e.g. leads to verification failure. Example:
licence_data += u"\t" + signature + u"\n"
Python 2.0+ has XML encoding built in, so 'escape(signature)' would be the solution.
NSString version wouldn't be usable in a .c file; should just go ahead and use CFString since toll-free bridging would mean that it would work for the (discouraged) Obj-C implementation too.
Hi,
I've given the (quite old) C# implementation a try, and it looks like it can't properly validate licenses generated by either the php script, or the OS X application. No matter what I do, rsa.VerifyData()
returns false.
Anybody has any idea why this happens?
I have found sorting inconsistencies depending on the language used for validating a license i.e. C/C++ STL, PHP etc.
I think that could be easily attributed to the different supporting functions available on each of those languages.
I wonder why sorting before hashing is really necessary?? It does not make the system more secure and it makes things very complicated from a portability point of view.
why the hash is not calculated from the first key/value pair in the license til the last one and we avoid all this portability hassle??
Best,
Patrick
A declarative, efficient, and flexible JavaScript library for building user interfaces.
🖖 Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.
TypeScript is a superset of JavaScript that compiles to clean JavaScript output.
An Open Source Machine Learning Framework for Everyone
The Web framework for perfectionists with deadlines.
A PHP framework for web artisans
Bring data to life with SVG, Canvas and HTML. 📊📈🎉
JavaScript (JS) is a lightweight interpreted programming language with first-class functions.
Some thing interesting about web. New door for the world.
A server is a program made to process requests and deliver data to clients.
Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.
Some thing interesting about visualization, use data art
Some thing interesting about game, make everyone happy.
We are working to build community through open source technology. NB: members must have two-factor auth.
Open source projects and samples from Microsoft.
Google ❤️ Open Source for everyone.
Alibaba Open Source for everyone
Data-Driven Documents codes.
China tencent open source team.