// Copyright (C) 2003-2026, Foxit Software Inc..
// All Rights Reserved.
//
// http://www.foxitsoftware.com
//
// The following code is copyrighted and contains proprietary information and trade secrets of Foxit Software Inc..
// You cannot distribute any part of Foxit PDF SDK to any third party or general public,
// unless there is a separate license agreement with Foxit Software Inc. which explicitly grants you such rights.
//
// This file contains an example to demonstrate how to use Foxit PDF SDK to do encryption and decryption for a PDF file
// by using password security method, custom security method, Foxit DRM security method, certificate security method
// or Microsoft RMS security method.
//
// NOTE: Before using this demo, user should ensure that openssl environment has been prepared:
//       user should ensure all the openssl header files included by "#include" can be found and put the "libssl.a" and "libcrypto.a"
//       in the directory "../../../lib".

#include "FSPDFObjC.h"

#include "openssl/bio.h"
#include "openssl/evp.h"
#include "openssl/objects.h"
#include "openssl/pem.h"
#include "openssl/pkcs12.h"
#include "openssl/pkcs7.h"
#include "openssl/sha.h"
#include "openssl/x509.h"

NSString *input_path = @"../input_files/";
NSString *output_path = @"../output_files/";
NSString *output_directory = @"../output_files/security/";

NSString* sn = @"bPxThFlBAYifO3yiW0C51bKbjPap1zHaf5aG5sX378TjFvB7Q0802A==";
NSString* key = @"8f3g1sGNtR8NAgfSNv+BvaJWy2fnrw36NZvzh8GUxYXvs+/YczAk4e34yqlOnmL4asgxGMO7+rlOcSHUSSKagx5g4G7VEXfkYLXnDDx77FOOedZPrFFcOCu1vT4+wOKzy1zkORq9cKtiFf44byH5sfRqf+OvELjep2teT9eF9yPCsSw2vRKljXlPy40DMP9XX1O5OTe+mSG3RvwKUMb0+RpG3YobM87j+/ynTDlJMMXw4vgwuTb1ZuAtlvkTdFz7HDLV81C6TYnn73ddHeGD3qYWBVsD4uJkDwyyH/SWr7QX0HSgVg8HSZqnab1VGdAUYxKY2vGWeOTJdu6qDGb5ZEb9O3kZZg+9LYAmUKzDfCYwfIsYZ8Y9LPC4wMx70kIq7JpNnzPvigrdshV8/ew1F0+qP/P5Z1EB53vDVrp2LVSuL873Wv4g3PzjZlNH+1qgT87hC+Buto4qsSUfP5Y2z0uApu+c/TfttCXbeMLSsw9TY1HQD3mkrZExMaQqRrAuXAcZWdvBnVuG3DCZmm8q0DLxJiCfYfViAU/AkbeKMIGaTJdrvIXBcssfde4nmU6qr0M4x4+Hs2ESddtY2QRtLAUBq/7h7GJlJHkEZr8ueaUy/6D/2vvAWz6wMLgmjlPbDTLqzfnCJylHCPtcJmu1SPPT9v1VreBX3+zNWcIT8HueyHEiUniqzwWkRKaotrnNb/zhnHLPpgaBSwKjyi8rOzzVwu/nY84G9ye4jsVjmkIRItKDrW7z22iS+QQuTwfqLVuMB0OD4jMkPCC2slzPfEeZnuh4+Rewln2qLRJVgI72zJ20OBWAelGSpO1Ii2Y3gOvojdmtUwx6R3Z1qFNWTTB25o9ytcux8BLOtTCkA8T8xaT4N339OTCsu/TmfahIDixKvr8cDa6F2WkBT8SOfv64W9PgiNv5B/BMOD2VWYaAl0N7yD0NkYYg/cDWRC4dfpdeip1jrr0k3mND0oSpIkxC+FicZbaYYwRdtJ9OSVWJ5OE0voLv8HgLze8mw6s4TrN3z+a2NQCveMa2VRN7YcEF7agPrvb/qU+otMwHae/wM7+f+HFAaOW+WTfzfxIVFB8/A1DN0NiEX+em4G7nO5F5ZcvfYPpaaioe8Nn33BbVLtMjXqsOBeM8AXiVfpD6YO75j7BqnBUPnivvD8BxJaaIQZvVSuQmvGm0ynt03IYwE8Rq+jYsunvsZ86jh5BiUR+Nm7sK0Qe9BdZn/7jdqohMv9czfMGUAscX+xBMunFbDpcRbWVpUADcm209L8Ndek2XY7YlCMxPgPVNvfWRF2jxehib9Tpd7zB5ZIDfAEH9zrpLfp29DLvCh2Yx8mH7xxdKG1O9Q0GdHphYHhej6smAcPm68R0GUQaTOLfXqNpueGJC1GAOI3Y4swjyt7jMTWcibnKLzwukWE5rnEDhn5n64JnyUM0ZFkRmtOiV";

bool GetCertificateInfo(NSString *certFilePath, NSMutableArray<NSData *> *envelopes, NSMutableData *initialKey, bool bEncryptMetaData, int len) {
	
	PKCS7        *p7;
	char        *p;
	
	NSData* certData = [NSData dataWithContentsOfFile:certFilePath];
	NSUInteger cbData = certData.length;
	
	char random[24] = {0};
	memset(random,0xff,24);
	for(int i = 0; i<20; i++)
		random[i] = rand();
	
//	ERR_load_BIO_strings();
	OpenSSL_add_all_algorithms();
	
	p = (char *)certData.bytes;
	
	X509 *x509_Cert=X509_new();
	d2i_X509(&x509_Cert,(const unsigned char **)&p,cbData);
	p7=PKCS7_new();
	PKCS7_set_type(p7,NID_pkcs7_enveloped);
	EVP_CIPHER *evp_cipher = (EVP_CIPHER *)EVP_des_ede3_cbc();
	
	PKCS7_set_cipher(p7,evp_cipher);
	PKCS7_add_recipient(p7,x509_Cert);//PKCS7_RECIP_INFO_new();
	BIO *p7bio = PKCS7_dataInit(p7, NULL);
	BIO_write(p7bio,random,24);
	BIO_flush(p7bio);
	PKCS7_dataFinal(p7, p7bio);
	unsigned char* derTmp;
	int derLen = i2d_PKCS7(p7,NULL);
	unsigned char * der  = (unsigned char*)malloc(derLen);
	derTmp = der;
	derLen = i2d_PKCS7(p7,(unsigned char **)&derTmp);
	BIO_free(p7bio);
	PKCS7_free(p7);
	unsigned char* envelopmem = der;
	
	int mem_size = 20;
	if (32 == len) mem_size = 32;
	unsigned char* initmem = new unsigned char[mem_size];
	memset(initmem, 0, sizeof(unsigned char)*mem_size);
	if (32 != len) {
		SHA_CTX c;
		SHA1_Init(&c);
		SHA1_Update(&c, random, 20);
		SHA1_Update(&c, envelopmem, derLen);
		if(!bEncryptMetaData)
		{
			uint32 tag = 0xffffffff;
			SHA1_Update(&c, &tag, sizeof(uint32));
		}
		SHA1_Final(initmem,&c);
	} else { //len=32, which means AES-256
		SHA256_CTX c;
		SHA256_Init(&c);
		SHA256_Update(&c, random, 20);
		SHA256_Update(&c, envelopmem, derLen);
		if(!bEncryptMetaData)
		{
			uint32 tag = 0xffffffff;
			SHA256_Update(&c, &tag, sizeof(uint32));
		}
		SHA256_Final(initmem,&c);
	}
	
	uint8* tinitialKey = new uint8[len + 1];
	memset(tinitialKey, 0, len + 1);
	memcpy(tinitialKey, initmem, len);
	[initialKey setData:[NSData dataWithBytes:tinitialKey length:len]];
	delete[] tinitialKey;
	delete[] initmem;

	[envelopes addObject:[NSData dataWithBytes:envelopmem length:derLen]];
	free(der);
	X509_free(x509_Cert);
	
	return true;
}

bool GetDecryptionKeyFromClientData(const void* clientData, const void* envelopeBuf, NSUInteger envelopeLen, void*& keyBuf, uint32* keyLen) {
	if (!keyBuf && keyLen)
		*keyLen = 0;
	if (!clientData || !envelopeBuf || envelopeLen < 1 || !keyLen || (keyBuf && *keyLen < 1))
		return false;
	
	char* pfxFile = (char*)clientData;
	const char* password = "123456";
	// Initialize the environment of the openssl.
	OpenSSL_add_all_algorithms();
//	ERR_load_BIO_strings();
	
	// Open the certificate file.
	FILE* fp = NULL;
	if (!(fp = fopen(pfxFile, "rb")))
		return false;
	
	// Retain PKCS12 object from certificate file.
	PKCS12* p12 = d2i_PKCS12_fp(fp, NULL);
	fclose(fp);
	if (!p12)
		return false;
	
	// Retain pkey, X509 certificate, certificate chains.
	EVP_PKEY* pkey = NULL;
	X509* x509 = NULL;
	STACK_OF(X509)* ca = NULL;
	if (!PKCS12_parse(p12, password, &pkey, &x509, &ca))
		return false;
	
	PKCS7* p7 = d2i_PKCS7(NULL, (const unsigned char**)&envelopeBuf, envelopeLen);
	BIO* p7bio = PKCS7_dataDecode(p7, pkey, NULL, x509);
	if (!p7bio)
		return false;
	
	*keyLen = (int)BIO_ctrl_pending(p7bio);
	if (*keyLen <= 0) {
		BIO_free(p7bio);
		return false;
	}
	
	keyBuf = malloc(*keyLen);
	if (!keyBuf) {
		BIO_free(p7bio);
		PKCS7_free(p7);
		return false;
	}
	
	*keyLen = BIO_read(p7bio, keyBuf, *keyLen);
	
	BIO_free(p7bio);
	PKCS7_free(p7);
	PKCS12_free(p12);
	return true;
}

#pragma mark - CertificateSecurityEvent

@interface CertificateSecurityEvent : NSObject <FSCertificateSecurityCallback>

@property (nonatomic, copy) NSString *filePath;

- (id)initWithPath:(NSString *)filePath;

@end

@implementation CertificateSecurityEvent

- (id)initWithPath:(NSString *)filePath {
	if (self = [super init]) {
		self.filePath = filePath;
	}
	return self;
}

-(NSData*)getDecryptionKey:(NSData*)envelope_buffer {
	uint8* keyBuf = NULL;
	uint32 keyLen = 0;
	
	bool ret = GetDecryptionKeyFromClientData((const void *)[self.filePath UTF8String], envelope_buffer.bytes, envelope_buffer.length, (void* &)keyBuf, &keyLen);
	
	if (!ret)
		return [NSData new];
	return [NSData dataWithBytes:keyBuf length:keyLen];
}

- (FSPDFDocEncryptType)GetSecurityType {
	return FSPDFDocEncryptCertificate;
}

@end

bool CertificateSecurity(NSString *input_file) {
	@try {
		FSPDFDoc *doc = [[FSPDFDoc alloc] initWithPath:input_file];
		FSErrorCode code = [doc load:nil];
		if (code != FSErrSuccess) {
			NSLog(@"The Doc [%@] Error: %d", input_file, (int)code);
			return false;
		}
		
		// Do encryption.
		NSMutableArray<NSData *> *envelopes = @[].mutableCopy;
		NSMutableData *initial_key = [NSMutableData new];
		NSString *cert_file_path = [input_path stringByAppendingPathComponent:@"foxit.cer"];
		if (!GetCertificateInfo(cert_file_path, envelopes, initial_key, true, 16)) {
			NSLog(@"[Failed] Cannot get certificate information from %@\n", cert_file_path);
			return false;
		}
		FSCertificateSecurityHandler *handler = [[FSCertificateSecurityHandler alloc] init];
		FSCertificateEncryptData *encrypt_data = [[FSCertificateEncryptData alloc] initWithIs_encrypt_metadata:YES cipher:FSSecurityHandlerCipherAES envelopes:envelopes];
		[handler initialize:encrypt_data encrypt_key:initial_key];
		
		[doc setSecurityHandler:handler];
		NSString *output_file = [output_directory stringByAppendingPathComponent:@"certificate_encrypt.pdf"];
		[doc saveAs:output_file save_flags:FSPDFDocSaveFlagNoOriginal];
		// Do decryption.
		CertificateSecurityEvent* callback = [[CertificateSecurityEvent alloc] initWithPath:[input_path stringByAppendingPathComponent:@"foxit_all.pfx"]];
		[FSLibrary registerSecurityCallback:@"Adobe.PubSec" callback:callback];
		
		FSPDFDoc *encrypted_doc = [[FSPDFDoc alloc] initWithPath:output_file];
		code = [encrypted_doc load:nil];
		if (code != FSErrSuccess) {
			NSLog(@"The Doc [%@] Error: %d", output_file, (int)code);
			return false;
		}
		
		output_file = [output_directory stringByAppendingPathComponent:@"certificate_decrypt.pdf"];
		[encrypted_doc removeSecurity];
		[encrypted_doc saveAs:output_file save_flags:FSPDFDocSaveFlagNoOriginal];
		
		[FSLibrary unregisterSecurityCallback:@"Adobe.PubSec"];
	}
	@catch (NSException *e) {
		NSLog(@"Unknown Exception, %@", e);
		return false;
	}
	return true;
}

#pragma mark - foxit DRM

@interface DRMSecurityEvent : NSObject <FSDRMSecurityCallback>

@property (nonatomic, copy) NSString *fileID;
@property (nonatomic, strong) NSData *initializeKey;

@end

@implementation DRMSecurityEvent

- (id)initWithFileID:(NSString *)fileID initializeKey:(NSData *)initializeKey {
	if (self = [super init]) {
		self.fileID = fileID;
		self.initializeKey = initializeKey;
	}
	return self;
}

- (FSPDFDocEncryptType)GetSecurityType {
	return FSPDFDocEncryptFoxitDRM;
}

- (FSSecurityHandlerCipherType)getCipherType:(FSPDFDoc *)document sub_filter:(NSString *)sub_filter {
	return FSSecurityHandlerCipherAES;
}

- (NSString *)getFileID:(FSPDFDoc *)document sub_filter:(NSString *)sub_filter {
	return self.fileID;
}

- (NSData *)getInitialKey:(FSPDFDoc *)document sub_filter:(NSString *)sub_filter {
	return self.initializeKey;
}

- (int)getKeyLength:(FSPDFDoc *)document sub_filter:(NSString *)sub_filter {
	return 16;
}

- (unsigned int)getUserPermissions:(FSPDFDoc *)document sub_filter:(NSString *)sub_filter {
	return 0xFFFFFFFC;
}

- (BOOL)isOwner:(FSPDFDoc *)document sub_filter:(NSString *)sub_filter {
	return TRUE;
}

@end

bool FoxitDRMDecrypt(NSString *input_file) {
	@try {
		FSPDFDoc *doc = [[FSPDFDoc alloc] initWithPath:input_file];
		FSErrorCode code = [doc load:nil];
		if (code != FSErrSuccess) {
			NSLog(@"The Doc [%@] Error: %d", input_file, (int)code);
			return false;
		}
		
		// Do encryption.
		FSDRMSecurityHandler *handler = [[FSDRMSecurityHandler alloc] init];
		NSString *file_id = @"Simple-DRM-file-ID";
		NSData *initialize_key = [@"Simple-DRM-initialize-key" dataUsingEncoding:NSUTF8StringEncoding];
		FSDRMEncryptData *encrypt_data = [[FSDRMEncryptData alloc] initWithIs_encrypt_metadata:YES sub_filter:@"Simple-DRM-filter" cipher:FSSecurityHandlerCipherAES key_length:16 is_owner:YES user_permissions:0xfffffffc];
		[handler initialize:encrypt_data file_id:file_id initial_key:initialize_key];
		[doc setSecurityHandler:handler];
		
		NSString *output_file = [output_directory stringByAppendingPathComponent:@"foxit_drm_encrypt.pdf"];
		[doc saveAs:output_file save_flags:FSPDFDocSaveFlagNoOriginal];
		
		// Do decryption.
		DRMSecurityEvent *callback = [[DRMSecurityEvent alloc] initWithFileID:file_id initializeKey:initialize_key];
		[FSLibrary registerSecurityCallback:@"FoxitDRM" callback:callback];
		FSPDFDoc *encrypted_doc = [[FSPDFDoc alloc] initWithPath:output_file];
		code = [encrypted_doc load:nil];
		if (code != FSErrSuccess) {
			NSLog(@"The Doc [%@] Error: %d", encrypted_doc, (int)code);
			return false;
		}
		
		output_file = [output_directory stringByAppendingPathComponent:@"foxit_drm_decrypt.pdf"];
		
		[encrypted_doc removeSecurity];
		[encrypted_doc saveAs:output_file save_flags:FSPDFDocSaveFlagNoOriginal];
		
		[FSLibrary unregisterSecurityCallback:@"FoxitDRM"];
	}
	@catch (NSException *e) {
		NSLog(@"Unknown Exception, %@", e);
		return false;
	}
	
	return true;
}

#pragma mark - Custom Security Callback

@interface CustomSecurityEvent : NSObject <FSCustomSecurityCallback>

@end

@implementation CustomSecurityEvent

- (FSPDFDocEncryptType)GetSecurityType {
	return FSPDFDocEncryptCustom;
}

- (void *)createContext:(NSString *)filter sub_filter:(NSString *)sub_filter encrypt_info:(NSString *)encrypt_info {
	return NULL;
}

- (NSData *)decryptData:(void *)decryptor encrypted_data:(void *)encrypted_data encrypted_data_len:(unsigned int)encrypted_data_len {
	char* decrypted_data = (char*)malloc(encrypted_data_len);
	for (uint32 i = 0; i < encrypted_data_len; i++)
		((char*)decrypted_data)[i] = ((char*)encrypted_data)[i] + 1;
	NSData *data = [NSData dataWithBytes:decrypted_data length:encrypted_data_len];
	free(decrypted_data);
	return data;
}

- (BOOL)encryptData:(void *)context obj_num:(int)obj_num gen_num:(int)gen_num src_data:(void *)src_data src_data_len:(unsigned int)src_data_len out_dst_buffer:(void *)out_dst_buffer out_dst_buffer_len:(unsigned int *)out_dst_buffer_len {
		for (uint32 i = 0; i < src_data_len; i++)
			((char*)out_dst_buffer)[i] = ((char*)src_data)[i] - 1;
		*out_dst_buffer_len = src_data_len;
		return TRUE;
}

- (NSData *)finishDecryptor:(void *)decryptor {
	return [NSData new];
}

- (FSSecurityHandlerCipherType)getCipher:(void *)context {
	return FSSecurityHandlerCipherAES;
}

- (unsigned int)getDecryptedSize:(void *)context src_data_size:(unsigned int)src_data_size {
	return src_data_size;
}

- (NSData *)getEncryptKey:(void *)context {
	return [@"key" dataUsingEncoding:NSUTF8StringEncoding];
}

- (unsigned int)getEncryptedSize:(void *)context obj_num:(int)obj_num gen_num:(int)gen_num src_data:(void *)src_data src_data_len:(unsigned int)src_data_len {
	return src_data_len;
}

- (unsigned int)getUserPermissions:(void *)context user_permission:(FSPDFDocUserPermissions)user_permission {
	return 0xffffffc;
}

- (BOOL)isOwner:(void *)context {
	return TRUE;
}

- (BOOL)releaseContext:(void *)context {
	return TRUE;
}

- (void *)startDecryptor:(void *)context obj_num:(int)obj_num gen_num:(int)gen_num {
	return NULL;
}
-(BOOL)useStandardCryptoMethod:(void*)context {
    return FALSE;
}
@end

bool CustomSecurity(NSString *input_file) {
	@try {
		FSPDFDoc *doc = [[FSPDFDoc alloc] initWithPath:input_file];
		FSErrorCode code = [doc load:nil];
		if (code != FSErrSuccess) {
			NSLog(@"The Doc [%@] Error: %d", input_file, (int)code);
			return false;
		}
		
		// Do encryption.
		CustomSecurityEvent *callback = [[CustomSecurityEvent alloc] init];
		FSCustomSecurityHandler *handler = [[FSCustomSecurityHandler alloc] init];
		NSString *encrypt_info = @"Foxit simple demo custom security";
		FSCustomEncryptData *encrypt_data = [[FSCustomEncryptData alloc] initWithIs_encrypt_metadata:YES filter:@"Custom" sub_filter:@"foxit-simple-demo"];
		[handler initialize:encrypt_data callback:callback encrypt_info:encrypt_info];
		[doc setSecurityHandler:handler];
		NSString *output_file = [output_directory stringByAppendingPathComponent:@"custom_encrypt.pdf"];
		[doc saveAs:output_file save_flags:FSPDFDocSaveFlagNoOriginal];
		
		// Do decryption.
		[FSLibrary registerSecurityCallback:@"Custom" callback:callback];
		FSPDFDoc *encrypted_doc = [[FSPDFDoc alloc] initWithPath:output_file];
		code = [encrypted_doc load:nil];
		if (code != FSErrSuccess) {
			NSLog(@"The Doc [%@] Error: %d", output_file, (int)code);
			return false;
		}
		
		output_file = [output_directory stringByAppendingPathComponent:@"custom_decrypt.pdf"];
		[encrypted_doc removeSecurity];
		[encrypted_doc saveAs:output_file save_flags:FSPDFDocSaveFlagNoOriginal];
		
		[FSLibrary unregisterSecurityCallback:@"Custom"];
	}
	@catch (NSException *e) {
		NSLog(@"Unknown Exception, %@", e);
		return false;
	}
	return true;
}

#pragma mark - RMSSecurityCallback

@interface RMSSecrutiyCallback : NSObject <FSRMSSecurityCallback>

@property (nonatomic, strong) NSData *encryptKey;

- (id)initWithEncryptKey:(NSData *)key;

@end

@implementation RMSSecrutiyCallback

- (id)initWithEncryptKey:(NSData *)key {
	if (self = [super init]) {
		self.encryptKey = key;
	}
	return self;
}

- (FSPDFDocEncryptType)GetSecurityType {
	return FSPDFDocEncryptRMS;
}

- (void *)createContext:(NSString *)filter sub_filter:(NSString *)sub_filter encrypt_info:(NSString *)encrypt_info {
	return NULL;
}

- (NSData *)decryptData:(void *)decryptor encrypted_data:(void *)encrypted_data encrypted_data_len:(unsigned int)encrypted_data_len {
	char* decryptedData = (char*)malloc(encrypted_data_len);
	for (uint32 i = 0; i < encrypted_data_len; i++)
		((char*)decryptedData)[i] = ((char*)encrypted_data)[i] + 5;
	NSData *data = [NSData dataWithBytes:decryptedData length:encrypted_data_len];
	free(decryptedData);
	return data;
}

- (BOOL)encryptData:(void *)context obj_num:(int)obj_num gen_num:(int)gen_num src_data:(void *)src_data src_data_len:(unsigned int)src_data_len out_dst_buffer:(void *)out_dst_buffer out_dst_buffer_len:(unsigned int *)out_dst_buffer_len {
	for (uint32 i = 0; i < src_data_len; i++)
		((char*)out_dst_buffer)[i] = ((char*)src_data)[i] - 5;
	*out_dst_buffer_len = src_data_len;
	return TRUE;
}

- (NSData *)finishDecryptor:(void *)decryptor {
	return [NSData new];
}

- (FSSecurityHandlerCipherType)getCipher:(void *)context {
	return FSSecurityHandlerCipherAES;
}

- (unsigned int)getDecryptedSize:(void *)context src_data_size:(unsigned int)src_data_size {
	return src_data_size;
}

- (NSData *)getEncryptKey:(void *)context {
	return self.encryptKey;
}

- (unsigned int)getEncryptedSize:(void *)context obj_num:(int)obj_num gen_num:(int)gen_num src_data:(void *)src_data src_data_len:(unsigned int)src_data_len {
	return src_data_len;
}

- (unsigned int)getUserPermissions:(void *)context user_permission:(FSPDFDocUserPermissions)user_permission {
	return 0xffffffc;
}

- (BOOL)isOwner:(void *)context {
	return TRUE;
}

- (BOOL)releaseContext:(void *)context {
	return TRUE;
}

- (void *)startDecryptor:(void *)context obj_num:(int)obj_num gen_num:(int)gen_num {
	return NULL;
}
-(BOOL)useStandardCryptoMethod:(void*)context {
    return FALSE;
}
@end

bool RMSSecurity(NSString *input_file) {
	@try {
		FSPDFDoc *doc = [[FSPDFDoc alloc] initWithPath:input_file];
		FSErrorCode code = [doc load:nil];
		if (code != FSErrSuccess) {
			NSLog(@"The Doc [%@] Error: %d", input_file, (int)code);
			return false;
		}
		
		// Do encryption.
		RMSSecrutiyCallback *callback = [[RMSSecrutiyCallback alloc] initWithEncryptKey:[@"Simple-RMS-encrpty-key" dataUsingEncoding:NSUTF8StringEncoding]];
		FSRMSSecurityHandler *handler = [[FSRMSSecurityHandler alloc] init];
		NSArray<NSData *> *server_eul_list = @[
											   [@"WM-1" dataUsingEncoding:NSUTF8StringEncoding],
											   [@"This document has been encrypted by RMS encryption." dataUsingEncoding:NSUTF8StringEncoding],
											   [@"WM-2" dataUsingEncoding:NSUTF8StringEncoding],
											   [@"Just for simple demo rms security." dataUsingEncoding:NSUTF8StringEncoding]
											   ];
		FSRMSEncryptData *encrypt_data = [[FSRMSEncryptData alloc] initWithIs_encrypt_metadata:YES publish_license:@"PubishLicense_0123" server_eul_list:server_eul_list irm_version:1];
		[handler initialize:encrypt_data callback:callback];
		[doc setSecurityHandler:handler];
		NSString *output_file = [output_directory stringByAppendingPathComponent:@"rms_encrypt.pdf"];
		[doc saveAs:output_file save_flags:FSPDFDocSaveFlagNoOriginal];
		
		// Do decryption.
		[FSLibrary registerSecurityCallback:@"MicrosoftIRMServices" callback:callback];
		FSPDFDoc *encrypted_doc = [[FSPDFDoc alloc] initWithPath:output_file];
		code = [encrypted_doc load:nil];
		if (code != FSErrSuccess) {
			NSLog(@"The Doc [%@] Error: %d", output_file, (int)code);
			return false;
		}
		
		output_file = [output_directory stringByAppendingPathComponent:@"rms_decrypt.pdf"];
		[encrypted_doc removeSecurity];
		[encrypted_doc saveAs:output_file save_flags:FSPDFDocSaveFlagNoOriginal];
		
		[FSLibrary unregisterSecurityCallback:@"Simple-RMS-encrpty-key"];
	}
	@catch (NSException *e) {
		NSLog(@"Unknown Exception, %@", e);
		return false;
	}
	return true;
}

#pragma mark - StdSecurity

bool StdSecurity(NSString *input_file) {
	@try {
		FSPDFDoc *doc = [[FSPDFDoc alloc] initWithPath:input_file];
		FSErrorCode code = [doc load:nil];
		if (code != FSErrSuccess) {
			NSLog(@"The Doc [%@] Error: %d", input_file, (int)code);
			return false;
		}
		
		// Do encryption.
		FSStdSecurityHandler *handler = [[FSStdSecurityHandler alloc] init];
		FSStdEncryptData *encrypt_data = [[FSStdEncryptData alloc] initWithIs_encrypt_metadata:YES user_permissions:0xfffffffc cipher:FSSecurityHandlerCipherAES key_length:16];
		[handler initialize:encrypt_data user_password:@"user" owner_password:@"owner"];
		[doc setSecurityHandler:handler];
		
		NSString *output_file = [output_directory stringByAppendingPathComponent:@"std_encrypt_userpwd[user]_ownerpwd[owner].pdf"];
		[doc saveAs:output_file save_flags:FSPDFDocSaveFlagNoOriginal];
		
		// Do decryption.
		FSPDFDoc *encrypted_doc = [[FSPDFDoc alloc] initWithPath:output_file];
		code = [encrypted_doc load:@"owner"];
		if (code != FSErrSuccess) {
			NSLog(@"The Doc [%@] Error: %d", output_file, (int)code);
			return false;
		}
		
		[encrypted_doc removeSecurity];
		output_file = [output_directory stringByAppendingPathComponent:@"std_decrypt.pdf"];
		[encrypted_doc saveAs:output_file save_flags:FSPDFDocSaveFlagNoOriginal];
	}
	@catch (NSException *e) {
		NSLog(@"Unknown Exception, %@", e);
		return -1;
	}
}

bool SecurityImpl(NSString *pdf_file) {
	bool bIsOK = true;
	if (!CertificateSecurity(pdf_file)) {
		bIsOK = false;
	}
	if (!FoxitDRMDecrypt(pdf_file)) {
		bIsOK = false;
	}
	if (!CustomSecurity(pdf_file)) {
		bIsOK = false;
	}
	if (!RMSSecurity(pdf_file)) {
		bIsOK = false;
	}
	if (!StdSecurity(pdf_file)) {
		bIsOK = false;
	}
	
	return bIsOK;
}

int main(int argc, const char * argv[]) {
	int ret = 0;
	@autoreleasepool {
		
		NSFileManager *fileManager = [NSFileManager defaultManager];
		BOOL isDir = NO;
		if ([fileManager fileExistsAtPath:output_directory isDirectory:&isDir]) {
			if (!isDir) {
				NSError *error = nil;
				if (![fileManager removeItemAtPath:output_directory error:&error] || error) {
					return -1;
				}
				if (![fileManager createDirectoryAtPath:output_directory withIntermediateDirectories:YES attributes:nil error:&error] || error) {
					return -1;
				}
			}
		} else {
			NSError *error = nil;
			if (![fileManager createDirectoryAtPath:output_directory withIntermediateDirectories:YES attributes:nil error:&error] || error) {
				return -1;
			}
		}
		
		// Initialize library
		FSErrorCode errorCode = [FSLibrary initialize:sn key:key];
		if (errorCode != FSErrSuccess) {
			NSLog(@"Library Initialize Error: %ld", errorCode);
			return -1;
		}
		
		NSString *input_file = [input_path stringByAppendingPathComponent:@"AboutFoxit.pdf"];
		
		@try {
			if (!SecurityImpl(input_file)) {
				ret = 1;
			}
			
			printf("Security demo test.\r\n");
		} @catch (NSException *e) {
			NSLog(@"Unknown Exception, %@", e);
			ret = -1;
		}
	}
	[FSLibrary destroy];
	return ret;
}
