From 90c1a96e6288d69e5416d7c5e08f4cd2ece603a2 Mon Sep 17 00:00:00 2001 From: Khalid Alqinyah Date: Tue, 27 Jan 2015 16:18:13 +0300 Subject: [PATCH 1/3] Add encryption and decryption functions to Advapi32 --- .../com/sun/jna/platform/win32/Advapi32.java | 152 ++++++++++++ .../com/sun/jna/platform/win32/WinBase.java | 41 ++++ .../sun/jna/platform/win32/Advapi32Test.java | 225 ++++++++++++++++++ 3 files changed, 418 insertions(+) mode change 100644 => 100755 contrib/platform/src/com/sun/jna/platform/win32/Advapi32.java mode change 100644 => 100755 contrib/platform/src/com/sun/jna/platform/win32/WinBase.java mode change 100644 => 100755 contrib/platform/test/com/sun/jna/platform/win32/Advapi32Test.java diff --git a/contrib/platform/src/com/sun/jna/platform/win32/Advapi32.java b/contrib/platform/src/com/sun/jna/platform/win32/Advapi32.java old mode 100644 new mode 100755 index d34c06dc4b..cb5556bdb7 --- a/contrib/platform/src/com/sun/jna/platform/win32/Advapi32.java +++ b/contrib/platform/src/com/sun/jna/platform/win32/Advapi32.java @@ -17,6 +17,8 @@ import com.sun.jna.Structure; import com.sun.jna.WString; import com.sun.jna.platform.win32.WinBase.SECURITY_ATTRIBUTES; +import com.sun.jna.platform.win32.WinBase.FE_EXPORT_FUNC; +import com.sun.jna.platform.win32.WinBase.FE_IMPORT_FUNC; import com.sun.jna.platform.win32.WinNT.HANDLE; import com.sun.jna.platform.win32.WinNT.HANDLEByReference; import com.sun.jna.platform.win32.WinNT.PSID; @@ -35,6 +37,7 @@ import static com.sun.jna.platform.win32.WinDef.BOOLByReference; import static com.sun.jna.platform.win32.WinDef.DWORD; import static com.sun.jna.platform.win32.WinDef.DWORDByReference; +import static com.sun.jna.platform.win32.WinDef.ULONG; import static com.sun.jna.platform.win32.WinNT.GENERIC_MAPPING; import static com.sun.jna.platform.win32.WinNT.PRIVILEGE_SET; @@ -1558,4 +1561,153 @@ public boolean AccessCheck(Pointer pSecurityDescriptor, PRIVILEGE_SET PrivilegeSet, DWORDByReference PrivilegeSetLength, DWORDByReference GrantedAccess, BOOLByReference AccessStatus); + + /** + * Encrypts a file or directory. All data streams in a file are encrypted. All + * new files created in an encrypted directory are encrypted. + * + * @param lpFileName + * The name of the file or directory to be encrypted. + * @return If the function succeeds, the return value is nonzero. If the + * function fails, the return value is zero. To get extended error + * information, call GetLastError. + */ + public boolean EncryptFile(WString lpFileName); + + /** + * Decrypts an encrypted file or directory. + * + * @param lpFileName + * The name of the file or directory to be decrypted. + * @param dwReserved + * Reserved; must be zero. + * @return If the function succeeds, the return value is nonzero. If the + * function fails, the return value is zero. To get extended error + * information, call GetLastError. + */ + public boolean DecryptFile(WString lpFileName, DWORD dwReserved); + + /** + * Retrieves the encryption status of the specified file. + * + * @param lpFileName + * The name of the file. + * @param lpStatus + * A pointer to a variable that receives the encryption status of the + * file. + * @return If the function succeeds, the return value is nonzero. If the + * function fails, the return value is zero. To get extended error + * information, call GetLastError. + */ + public boolean FileEncryptionStatus(WString lpFileName, DWORDByReference lpStatus); + + /** + * Disables or enables encryption of the specified directory and the files in + * it. It does not affect encryption of subdirectories below the indicated + * directory. + * + * @param DirPath + * The name of the directory for which to enable or disable + * encryption. + * @param Disable + * Indicates whether to disable encryption (TRUE) or enable it + * (FALSE). + * @return If the function succeeds, the return value is nonzero. If the + * function fails, the return value is zero. To get extended error + * information, call GetLastError. + */ + public boolean EncryptionDisable(WString DirPath, boolean Disable); + + /** + * Opens an encrypted file in order to backup (export) or restore (import) the + * file. This is one of a group of Encrypted File System (EFS) functions that + * is intended to implement backup and restore functionality, while + * maintaining files in their encrypted state. + * + * @param lpFileName + * The name of the file to be opened. The string must consist of + * characters from the Windows character set. + * @param ulFlags + * The operation to be performed. + * @param pvContext + * The address of a context block that must be presented in subsequent + * calls to ReadEncryptedFileRaw, WriteEncryptedFileRaw, or + * CloseEncryptedFileRaw. Do not modify it. + * @return If the function succeeds, it returns ERROR_SUCCESS. If the function + * fails, it returns a nonzero error code defined in WinError.h. You can use + * FormatMessage with the FORMAT_MESSAGE_FROM_SYSTEM flag to get a generic + * text description of the error. + */ + public int OpenEncryptedFileRaw(WString lpFileName, ULONG ulFlags, + PointerByReference pvContext); + + /** + * Backs up (export) encrypted files. This is one of a group of Encrypted File + * System (EFS) functions that is intended to implement backup and restore + * functionality, while maintaining files in their encrypted state. + * + * @param pfExportCallback + * A pointer to the export callback function. The system calls the + * callback function multiple times, each time passing a block of the + * file's data to the callback function until the entire file has been + * read. For more information, see ExportCallback. + * @param pvCallbackContext + * A pointer to an application-defined and allocated context block. + * The system passes this pointer to the callback function as a + * parameter so that the callback function can have access to + * application-specific data. This can be a structure and can contain + * any data the application needs, such as the handle to the file that + * will contain the backup copy of the encrypted file. + * @param pvContext + * A pointer to a system-defined context block. The context block is + * returned by the OpenEncryptedFileRaw function. Do not modify it. + * @return If the function succeeds, the return value is ERROR_SUCCESS. If the + * function fails, it returns a nonzero error code defined in WinError.h. You + * can use FormatMessage with the FORMAT_MESSAGE_FROM_SYSTEM flag to get a + * generic text description of the error. + */ + public int ReadEncryptedFileRaw(FE_EXPORT_FUNC pfExportCallback, + Pointer pvCallbackContext, Pointer pvContext); + + /** + * Restores (import) encrypted files. This is one of a group of Encrypted File + * System (EFS) functions that is intended to implement backup and restore + * functionality, while maintaining files in. + * + * @param pfImportCallback + * A pointer to the import callback function. The system calls the + * callback function multiple times, each time passing a buffer that + * will be filled by the callback function with a portion of backed-up + * file's data. When the callback function signals that the entire + * file has been processed, it tells the system that the restore + * operation is finished. For more information, see ImportCallback. + * @param pvCallbackContext + * A pointer to an application-defined and allocated context block. + * The system passes this pointer to the callback function as a + * parameter so that the callback function can have access to + * application-specific data. This can be a structure and can contain + * any data the application needs, such as the handle to the file that + * will contain the backup copy of the encrypted file. + * @param pvContext + * A pointer to a system-defined context block. The context block is + * returned by the OpenEncryptedFileRaw function. Do not modify it. + * @return If the function succeeds, the return value is ERROR_SUCCESS. If the + * function fails, it returns a nonzero error code defined in WinError.h. You + * can use FormatMessage with the FORMAT_MESSAGE_FROM_SYSTEM flag to get a + * generic text description of the error. + */ + public int WriteEncryptedFileRaw(FE_IMPORT_FUNC pfImportCallback, + Pointer pvCallbackContext, Pointer pvContext); + + /** + * Closes an encrypted file after a backup or restore operation, and frees + * associated system resources. This is one of a group of Encrypted File + * System (EFS) functions that is intended to implement backup and restore + * functionality, while maintaining files in their encrypted state. + * + * @param pvContext + * A pointer to a system-defined context block. The + * OpenEncryptedFileRaw function returns the context block. + */ + public void CloseEncryptedFileRaw(Pointer pvContext); } diff --git a/contrib/platform/src/com/sun/jna/platform/win32/WinBase.java b/contrib/platform/src/com/sun/jna/platform/win32/WinBase.java old mode 100644 new mode 100755 index 4f32e37737..80a936e5f6 --- a/contrib/platform/src/com/sun/jna/platform/win32/WinBase.java +++ b/contrib/platform/src/com/sun/jna/platform/win32/WinBase.java @@ -153,6 +153,23 @@ public interface WinBase extends StdCallLibrary, WinDef, BaseTSD { int CREATE_DEFAULT_ERROR_MODE = 0x04000000; int CREATE_NO_WINDOW = 0x08000000; + /* File encryption status */ + int FILE_ENCRYPTABLE = 0; + int FILE_IS_ENCRYPTED = 1; + int FILE_SYSTEM_ATTR = 2; + int FILE_ROOT_DIR = 3; + int FILE_SYSTEM_DIR = 4; + int FILE_UNKNOWN = 5; + int FILE_SYSTEM_NOT_SUPPORT = 6; + int FILE_USER_DISALLOWED = 7; + int FILE_READ_ONLY = 8; + int FILE_DIR_DISALOWED = 9; + + /* Open encrypted files raw flags */ + int CREATE_FOR_IMPORT = 1; + int CREATE_FOR_DIR = 2; + int OVERWRITE_HIDDEN = 4; + /* Invalid return values */ int INVALID_FILE_SIZE = 0xFFFFFFFF; int INVALID_SET_FILE_POINTER = 0xFFFFFFFF; @@ -986,4 +1003,28 @@ public static interface COMPUTER_NAME_FORMAT { */ int ComputerNameMax = 8; } + + /** + * An application-defined callback function used with ReadEncryptedFileRaw. + * The system calls ExportCallback one or more times, each time with a block + * of the encrypted file's data, until it has received all of the file data. + * ExportCallback writes the encrypted file's data to another storage media, + * usually for purposes of backing up the file. + */ + public interface FE_EXPORT_FUNC extends Callback { + public DWORD callback(ByteByReference pbData, Pointer pvCallbackContext, + ULONG ulLength); + } + + /** + * An application-defined callback function used with WriteEncryptedFileRaw. + * The system calls ImportCallback one or more times, each time to retrieve a + * portion of a backup file's data. ImportCallback reads the data from a + * backup file sequentially and restores the data, and the system continues + * calling it until it has read all of the backup file data. + */ + public interface FE_IMPORT_FUNC extends Callback { + public DWORD callback(ByteByReference pbData, Pointer pvCallbackContext, + ULONGByReference ulLength); + } } diff --git a/contrib/platform/test/com/sun/jna/platform/win32/Advapi32Test.java b/contrib/platform/test/com/sun/jna/platform/win32/Advapi32Test.java old mode 100644 new mode 100755 index c144ee11de..c09dbee47e --- a/contrib/platform/test/com/sun/jna/platform/win32/Advapi32Test.java +++ b/contrib/platform/test/com/sun/jna/platform/win32/Advapi32Test.java @@ -12,7 +12,10 @@ */ package com.sun.jna.platform.win32; +import java.io.ByteArrayOutputStream; import java.io.File; +import java.io.FileWriter; +import java.io.IOException; import junit.framework.TestCase; @@ -38,6 +41,7 @@ import com.sun.jna.platform.win32.Winsvc.SC_HANDLE; import com.sun.jna.platform.win32.Winsvc.SC_STATUS_TYPE; import com.sun.jna.platform.win32.Winsvc.SERVICE_STATUS_PROCESS; +import com.sun.jna.ptr.ByteByReference; import com.sun.jna.ptr.IntByReference; import com.sun.jna.ptr.PointerByReference; @@ -906,4 +910,225 @@ public void testAccessCheck() { assertEquals(WinError.ERROR_INVALID_HANDLE, Kernel32.INSTANCE.GetLastError()); } + public void testEncryptFile() throws Exception { + // create a temp file + File file = createTempFile(); + WString lpFileName = new WString(file.getAbsolutePath()); + + // encrypt a read only file + file.setWritable(false); + assertFalse(Advapi32.INSTANCE.EncryptFile(lpFileName)); + assertEquals(WinError.ERROR_FILE_READ_ONLY, Kernel32.INSTANCE.GetLastError()); + + // encrypt a writable file + file.setWritable(true); + assertTrue(Advapi32.INSTANCE.EncryptFile(lpFileName)); + + file.delete(); + } + + public void testDecryptFile() throws Exception { + // create an encrypted file + File file = createTempFile(); + WString lpFileName = new WString(file.getAbsolutePath()); + assertTrue(Advapi32.INSTANCE.EncryptFile(lpFileName)); + + // decrypt a read only file + file.setWritable(false); + assertFalse(Advapi32.INSTANCE.DecryptFile(lpFileName, new DWORD(0))); + assertEquals(WinError.ERROR_FILE_READ_ONLY, Kernel32.INSTANCE.GetLastError()); + + // decrypt + file.setWritable(true); + assertTrue(Advapi32.INSTANCE.DecryptFile(lpFileName, new DWORD(0))); + + file.delete(); + } + + public void testFileEncryptionStatus() throws Exception { + DWORDByReference lpStatus = new DWORDByReference(); + + // create a temp file + File file = createTempFile(); + WString lpFileName = new WString(file.getAbsolutePath()); + + // unencrypted file + assertTrue(Advapi32.INSTANCE.FileEncryptionStatus(lpFileName, lpStatus)); + assertEquals(FILE_ENCRYPTABLE, lpStatus.getValue().intValue()); + + // read only file + file.setWritable(false); + assertTrue(Advapi32.INSTANCE.FileEncryptionStatus(lpFileName, lpStatus)); + assertEquals(FILE_READ_ONLY, lpStatus.getValue().intValue()); + + // encrypted file + file.setWritable(true); + assertTrue(Advapi32.INSTANCE.EncryptFile(lpFileName)); + assertTrue(Advapi32.INSTANCE.FileEncryptionStatus(lpFileName, lpStatus)); + assertEquals(FILE_IS_ENCRYPTED, lpStatus.getValue().intValue()); + + file.delete(); + } + + public void testEncryptionDisable() throws Exception { + DWORDByReference lpStatus = new DWORDByReference(); + + // create a temp dir + String filePath = System.getProperty("java.io.tmpdir") + File.separator + + System.nanoTime(); + WString DirPath = new WString(filePath); + File dir = new File(filePath); + dir.mkdir(); + + // check status + assertTrue(Advapi32.INSTANCE.FileEncryptionStatus(DirPath, lpStatus)); + assertEquals(FILE_ENCRYPTABLE, lpStatus.getValue().intValue()); + + // disable encryption + assertTrue(Advapi32.INSTANCE.EncryptionDisable(DirPath, true)); + assertTrue(Advapi32.INSTANCE.FileEncryptionStatus(DirPath, lpStatus)); + assertEquals(FILE_DIR_DISALOWED, lpStatus.getValue().intValue()); + + // enable encryption + assertTrue(Advapi32.INSTANCE.EncryptionDisable(DirPath, false)); + assertTrue(Advapi32.INSTANCE.FileEncryptionStatus(DirPath, lpStatus)); + assertEquals(FILE_ENCRYPTABLE, lpStatus.getValue().intValue()); + + // clean up + for (File file : dir.listFiles()) { + file.delete(); + } + dir.delete(); + } + + public void testOpenEncryptedFileRaw() throws Exception { + // create an encrypted file + File file = createTempFile(); + WString lpFileName = new WString(file.getAbsolutePath()); + assertTrue(Advapi32.INSTANCE.EncryptFile(lpFileName)); + + // open file for export + ULONG ulFlags = new ULONG(0); + PointerByReference pvContext = new PointerByReference(); + assertEquals(W32Errors.ERROR_SUCCESS, Advapi32.INSTANCE.OpenEncryptedFileRaw( + lpFileName, ulFlags, pvContext)); + + Advapi32.INSTANCE.CloseEncryptedFileRaw(pvContext.getValue()); + file.delete(); + } + + public void testReadEncryptedFileRaw() throws Exception { + // create an encrypted file + File file = createTempFile(); + WString lpFileName = new WString(file.getAbsolutePath()); + assertTrue(Advapi32.INSTANCE.EncryptFile(lpFileName)); + + // open file for export + ULONG ulFlags = new ULONG(0); + PointerByReference pvContext = new PointerByReference(); + assertEquals(W32Errors.ERROR_SUCCESS, Advapi32.INSTANCE.OpenEncryptedFileRaw( + lpFileName, ulFlags, pvContext)); + + // read encrypted file + final ByteArrayOutputStream outputStream = new ByteArrayOutputStream(); + FE_EXPORT_FUNC pfExportCallback = new FE_EXPORT_FUNC() { + @Override + public DWORD callback(ByteByReference pbData, Pointer + pvCallbackContext, ULONG ulLength) { + byte[] arr = pbData.getPointer().getByteArray(0, ulLength.intValue()); + try { + outputStream.write(arr); + } catch (IOException e) { + throw new RuntimeException(e); + } + return new DWORD(W32Errors.ERROR_SUCCESS); + } + }; + + assertEquals(W32Errors.ERROR_SUCCESS, Advapi32.INSTANCE.ReadEncryptedFileRaw( + pfExportCallback, null, pvContext.getValue())); + outputStream.close(); + + Advapi32.INSTANCE.CloseEncryptedFileRaw(pvContext.getValue()); + file.delete(); + } + + public void testWriteEncryptedFileRaw() throws Exception { + // create an encrypted file + File file = createTempFile(); + WString lpFileName = new WString(file.getAbsolutePath()); + assertTrue(Advapi32.INSTANCE.EncryptFile(lpFileName)); + + // open file for export + ULONG ulFlags = new ULONG(0); + PointerByReference pvContext = new PointerByReference(); + assertEquals(W32Errors.ERROR_SUCCESS, Advapi32.INSTANCE.OpenEncryptedFileRaw( + lpFileName, ulFlags, pvContext)); + + // read encrypted file + final ByteArrayOutputStream outputStream = new ByteArrayOutputStream(); + FE_EXPORT_FUNC pfExportCallback = new FE_EXPORT_FUNC() { + @Override + public DWORD callback(ByteByReference pbData, Pointer + pvCallbackContext, ULONG ulLength) { + byte[] arr = pbData.getPointer().getByteArray(0, ulLength.intValue()); + try { + outputStream.write(arr); + } catch (IOException e) { + throw new RuntimeException(e); + } + return new DWORD(W32Errors.ERROR_SUCCESS); + } + }; + + assertEquals(W32Errors.ERROR_SUCCESS, Advapi32.INSTANCE.ReadEncryptedFileRaw( + pfExportCallback, null, pvContext.getValue())); + outputStream.close(); + Advapi32.INSTANCE.CloseEncryptedFileRaw(pvContext.getValue()); + + // open file for import + WString lbFileName2 = new WString(System.getProperty("java.io.tmpdir") + + File.separator + "backup-" + file.getName()); + ULONG ulFlags2 = new ULONG(CREATE_FOR_IMPORT); + PointerByReference pvContext2 = new PointerByReference(); + assertEquals(W32Errors.ERROR_SUCCESS, Advapi32.INSTANCE.OpenEncryptedFileRaw( + lbFileName2, ulFlags2, pvContext2)); + + // write encrypted file + final IntByReference elementsReadWrapper = new IntByReference(0); + FE_IMPORT_FUNC pfImportCallback = new FE_IMPORT_FUNC() { + @Override + public DWORD callback(ByteByReference pbData, Pointer pvCallbackContext, + ULONGByReference ulLength) { + int elementsRead = elementsReadWrapper.getValue(); + int remainingElements = outputStream.size() - elementsRead; + int length = Math.min(remainingElements, ulLength.getValue().intValue()); + pbData.getPointer().write(0, outputStream.toByteArray(), elementsRead, + length); + elementsReadWrapper.setValue(elementsRead + length); + ulLength.setValue(new ULONG(length)); + return new DWORD(W32Errors.ERROR_SUCCESS); + } + }; + + assertEquals(W32Errors.ERROR_SUCCESS, Advapi32.INSTANCE.WriteEncryptedFileRaw( + pfImportCallback, null, pvContext2.getValue())); + Advapi32.INSTANCE.CloseEncryptedFileRaw(pvContext2.getValue()); + + file.delete(); + new File(lbFileName2.toString()).delete(); + } + + private File createTempFile() throws Exception { + String filePath = System.getProperty("java.io.tmpdir") + System.nanoTime() + + ".text"; + File file = new File(filePath); + file.createNewFile(); + FileWriter fileWriter = new FileWriter(file); + for (int i = 0; i < 1000; i++) { + fileWriter.write("Sample text " + i + System.getProperty("line.separator")); + } + fileWriter.close(); + return file; + } } From e2286cad149dbe9e0b3c7d2c48c7d56d9095ff0a Mon Sep 17 00:00:00 2001 From: Khalid Alqinyah Date: Tue, 27 Jan 2015 16:19:03 +0300 Subject: [PATCH 2/3] Add encryption and decryption helpers to Advapi32Util --- .../sun/jna/platform/win32/Advapi32Util.java | 158 ++++++++++++++++++ .../jna/platform/win32/Advapi32UtilTest.java | 88 ++++++++++ 2 files changed, 246 insertions(+) mode change 100644 => 100755 contrib/platform/src/com/sun/jna/platform/win32/Advapi32Util.java mode change 100644 => 100755 contrib/platform/test/com/sun/jna/platform/win32/Advapi32UtilTest.java diff --git a/contrib/platform/src/com/sun/jna/platform/win32/Advapi32Util.java b/contrib/platform/src/com/sun/jna/platform/win32/Advapi32Util.java old mode 100644 new mode 100755 index c28e348c24..6b08e38f54 --- a/contrib/platform/src/com/sun/jna/platform/win32/Advapi32Util.java +++ b/contrib/platform/src/com/sun/jna/platform/win32/Advapi32Util.java @@ -12,7 +12,9 @@ */ package com.sun.jna.platform.win32; +import java.io.ByteArrayOutputStream; import java.io.File; +import java.io.IOException; import java.util.ArrayList; import java.util.HashMap; import java.util.Iterator; @@ -37,6 +39,7 @@ import com.sun.jna.platform.win32.WinNT.SID_NAME_USE; import com.sun.jna.platform.win32.WinReg.HKEY; import com.sun.jna.platform.win32.WinReg.HKEYByReference; +import com.sun.jna.ptr.ByteByReference; import com.sun.jna.ptr.IntByReference; import com.sun.jna.ptr.LongByReference; import com.sun.jna.ptr.PointerByReference; @@ -2185,4 +2188,159 @@ public static boolean accessCheck(File file, AccessCheckPermission permissionToC return hasAccess; } + + /** + * Encrypts a file or directory. + * + * @param file + * The file or directory to encrypt. + */ + public static void encryptFile(File file) { + WString lpFileName = new WString(file.getAbsolutePath()); + if (!Advapi32.INSTANCE.EncryptFile(lpFileName)) { + throw new Win32Exception(Kernel32.INSTANCE.GetLastError()); + } + } + + /** + * Decrypts an encrypted file or directory. + * + * @param file + * The file or directory to decrypt. + */ + public static void decryptFile(File file) { + WString lpFileName = new WString(file.getAbsolutePath()); + if (!Advapi32.INSTANCE.DecryptFile(lpFileName, new DWORD(0))) { + throw new Win32Exception(Kernel32.INSTANCE.GetLastError()); + } + } + + /** + * Checks the encryption status of a file. + * + * @param file + * The file to check the status for. + * @return The status of the file. + */ + public static int fileEncryptionStatus(File file) { + DWORDByReference status = new DWORDByReference(); + WString lpFileName = new WString(file.getAbsolutePath()); + if (!Advapi32.INSTANCE.FileEncryptionStatus(lpFileName, status)) { + throw new Win32Exception(Kernel32.INSTANCE.GetLastError()); + } + return status.getValue().intValue(); + } + + /** + * Disables or enables encryption of the specified directory and the files in + * it. + * + * @param directory + * The directory for which to enable or disable encryption. + * @param disable + * TRUE to disable encryption. FALSE to enable it. + */ + public static void disableEncryption(File directory, boolean disable) { + WString dirPath = new WString(directory.getAbsolutePath()); + if (!Advapi32.INSTANCE.EncryptionDisable(dirPath, disable)) { + throw new Win32Exception(Native.getLastError()); + } + } + + /** + * Backup an encrypted file or folder without decrypting it. A file named + * "bar/sample.text" will be backed-up to "destDir/sample.text". A directory + * named "bar" will be backed-up to "destDir/bar". This method is NOT + * recursive. If you have an encrypted directory with encrypted files, this + * method must be called once for the directory, and once for each encrypted + * file to be backed-up. + * + * @param src + * The encrypted file or directory to backup. + * @param destDir + * The directory where the backup will be saved. + */ + public static void backupEncryptedFile(File src, File destDir) { + if (!destDir.isDirectory()) { + throw new IllegalArgumentException("destDir must be a directory."); + } + + ULONG readFlag = new ULONG(0); // Open the file for export (backup) + ULONG writeFlag = new ULONG(CREATE_FOR_IMPORT); // Import (restore) file + + if (src.isDirectory()) { + writeFlag.setValue(CREATE_FOR_IMPORT | CREATE_FOR_DIR); + } + + // open encrypted file for export + WString srcFileName = new WString(src.getAbsolutePath()); + PointerByReference pvContext = new PointerByReference(); + if (Advapi32.INSTANCE.OpenEncryptedFileRaw(srcFileName, readFlag, + pvContext) != W32Errors.ERROR_SUCCESS) { + throw new Win32Exception(Kernel32.INSTANCE.GetLastError()); + } + + // read encrypted file + final ByteArrayOutputStream outputStream = new ByteArrayOutputStream(); + FE_EXPORT_FUNC pfExportCallback = new FE_EXPORT_FUNC() { + @Override + public DWORD callback(ByteByReference pbData, Pointer pvCallbackContext, + ULONG ulLength) { + byte[] arr = pbData.getPointer().getByteArray(0, ulLength.intValue()); + try { + outputStream.write(arr); + } catch (IOException e) { + throw new RuntimeException(e); + } + return new DWORD(W32Errors.ERROR_SUCCESS); + } + }; + + if (Advapi32.INSTANCE.ReadEncryptedFileRaw(pfExportCallback, null, + pvContext.getValue()) != W32Errors.ERROR_SUCCESS) { + throw new Win32Exception(Kernel32.INSTANCE.GetLastError()); + } + + // close + try { + outputStream.close(); + } catch (IOException e) { + throw new RuntimeException(e); + } + Advapi32.INSTANCE.CloseEncryptedFileRaw(pvContext.getValue()); + + // open file for import + WString destFileName = new WString(destDir.getAbsolutePath() + File.separator + + src.getName()); + pvContext = new PointerByReference(); + if (Advapi32.INSTANCE.OpenEncryptedFileRaw(destFileName, writeFlag, + pvContext) != W32Errors.ERROR_SUCCESS) { + throw new Win32Exception(Kernel32.INSTANCE.GetLastError()); + } + + // write encrypted file + final IntByReference elementsReadWrapper = new IntByReference(0); + FE_IMPORT_FUNC pfImportCallback = new FE_IMPORT_FUNC() { + @Override + public DWORD callback(ByteByReference pbData, Pointer pvCallbackContext, + ULONGByReference ulLength) { + int elementsRead = elementsReadWrapper.getValue(); + int remainingElements = outputStream.size() - elementsRead; + int length = Math.min(remainingElements, ulLength.getValue().intValue()); + pbData.getPointer().write(0, outputStream.toByteArray(), elementsRead, + length); + elementsReadWrapper.setValue(elementsRead + length); + ulLength.setValue(new ULONG(length)); + return new DWORD(W32Errors.ERROR_SUCCESS); + } + }; + + if (Advapi32.INSTANCE.WriteEncryptedFileRaw(pfImportCallback, null, + pvContext.getValue()) != W32Errors.ERROR_SUCCESS) { + throw new Win32Exception(Kernel32.INSTANCE.GetLastError()); + } + + // close + Advapi32.INSTANCE.CloseEncryptedFileRaw(pvContext.getValue()); + } } diff --git a/contrib/platform/test/com/sun/jna/platform/win32/Advapi32UtilTest.java b/contrib/platform/test/com/sun/jna/platform/win32/Advapi32UtilTest.java old mode 100644 new mode 100755 index 835c409a2e..c8eb531046 --- a/contrib/platform/test/com/sun/jna/platform/win32/Advapi32UtilTest.java +++ b/contrib/platform/test/com/sun/jna/platform/win32/Advapi32UtilTest.java @@ -13,6 +13,7 @@ package com.sun.jna.platform.win32; import java.io.File; +import java.io.FileWriter; import java.util.Map; import java.util.TreeMap; @@ -30,6 +31,8 @@ import com.sun.jna.platform.win32.WinReg.HKEY; import com.sun.jna.platform.win32.WinReg.HKEYByReference; +import static com.sun.jna.platform.win32.WinBase.*; + /** * @author dblock[at]dblock[dot]org */ @@ -483,4 +486,89 @@ public void testGetEnvironmentBlock() { String block = Advapi32Util.getEnvironmentBlock(mockEnvironment); assertEquals("Environment block must comprise key=value pairs separated by NUL characters", expected, block); } + + public void testEncryptFile() throws Exception { + File file = createTempFile(); + assertEquals(FILE_ENCRYPTABLE, Advapi32Util.fileEncryptionStatus(file)); + Advapi32Util.encryptFile(file); + assertEquals(FILE_IS_ENCRYPTED, Advapi32Util.fileEncryptionStatus(file)); + file.delete(); + } + + public void testDecryptFile() throws Exception { + File file = createTempFile(); + Advapi32Util.encryptFile(file); + assertEquals(FILE_IS_ENCRYPTED, Advapi32Util.fileEncryptionStatus(file)); + Advapi32Util.decryptFile(file); + assertEquals(FILE_ENCRYPTABLE, Advapi32Util.fileEncryptionStatus(file)); + file.delete(); + } + + public void testDisableEncryption() throws Exception { + File dir = new File(System.getProperty("java.io.tmpdir") + File.separator + + System.nanoTime()); + dir.mkdir(); + assertEquals(FILE_ENCRYPTABLE, Advapi32Util.fileEncryptionStatus(dir)); + Advapi32Util.disableEncryption(dir, true); + assertEquals(FILE_DIR_DISALOWED, Advapi32Util.fileEncryptionStatus(dir)); + Advapi32Util.disableEncryption(dir, false); + assertEquals(FILE_ENCRYPTABLE, Advapi32Util.fileEncryptionStatus(dir)); + for (File file : dir.listFiles()) { + file.delete(); + } + dir.delete(); + } + + public void testBackupEncryptedFile() throws Exception { + // backup an encrypted file + File srcFile = createTempFile(); + Advapi32Util.encryptFile(srcFile); + File dest = new File(System.getProperty("java.io.tmpdir") + File.separator + + "backup" + System.nanoTime()); + dest.mkdir(); + + Advapi32Util.backupEncryptedFile(srcFile, dest); + + // simple check to see if a backup file exist + File backupFile = new File(dest.getAbsolutePath() + File.separator + + srcFile.getName()); + assertTrue(backupFile.exists()); + assertEquals(srcFile.length(), backupFile.length()); + + // backup an encrypted directory + File srcDir = new File(System.getProperty("java.io.tmpdir") + File.separator + + System.nanoTime()); + srcDir.mkdir(); + Advapi32Util.encryptFile(srcDir); + + Advapi32Util.backupEncryptedFile(srcDir, dest); + + // Check to see if a backup directory exist + File backupDir = new File(dest.getAbsolutePath() + File.separator + srcDir.getName()); + assertTrue(backupDir.exists()); + + // clean up + srcFile.delete(); + for (File file : srcDir.listFiles()) { + file.delete(); + } + srcDir.delete(); + for (File file : dest.listFiles()) { + file.delete(); + } + dest.delete(); + } + + private File createTempFile() throws Exception{ + String filePath = System.getProperty("java.io.tmpdir") + System.nanoTime() + + ".text"; + File file = new File(filePath); + file.createNewFile(); + FileWriter fileWriter = new FileWriter(file); + for (int i = 0; i < 1000; i++) { + fileWriter.write("Sample text " + i + System.getProperty("line.separator")); + } + fileWriter.close(); + return file; + } } From 70cf574a9ddcb4339d190dc0ae577f2ed06effb2 Mon Sep 17 00:00:00 2001 From: Khalid Alqinyah Date: Tue, 27 Jan 2015 16:19:36 +0300 Subject: [PATCH 3/3] Update change log --- CHANGES.md | 1 + 1 file changed, 1 insertion(+) mode change 100644 => 100755 CHANGES.md diff --git a/CHANGES.md b/CHANGES.md old mode 100644 new mode 100755 index c5f91c6332..d55f5009f1 --- a/CHANGES.md +++ b/CHANGES.md @@ -23,6 +23,7 @@ Features * [#365](https://github.com/twall/jna/pull/365): Added `com.sun.jna.platform.win32.Kernel32.GetComputerNameEx` support - [@lgoldstein](https://github.com/lgoldstein). * [#368](https://github.com/twall/jna/pull/368): Added `com.sun.jna.platform.win32.Kernel32.VirtualQueryEx`, `com.sun.jna.platform.win32.WinNT.MEMORY_BASIC_INFORMATION` and `MEM_COMMIT`, `MEM_FREE`, `MEM_RESERVE`, `MEM_IMAGE`, `MEM_MAPPED`, `MEM_PRIVATE` constants to `com.sun.jna.platform.win32.WinNT` - [@apsk](https://github.com/apsk). * Allow interoperation with JNI revision changes - [@twall](https://github.com/twall). +* [#391](https://github.com/twall/jna/pull/391): Added `EncryptFile`, `DecryptFile`, `FileEncryptionStatus`, `EncryptionDisable`, `OpenEncryptedFileRaw`, `ReadEncryptedFileRaw`, `WriteEncryptedFileRaw`, and `CloseEncryptedFileRaw` to `com.sun.jna.platform.win32.Advapi32` with related `Advapi32Util` helpers - [@khalidq](https://github.com/khalidq). Bug Fixes ---------