Creating IntuneWin files with C#
Now that I figured out how to decrypt IntuneWin files it is time to explain how you can create those files with just C# code. With this explanation you should be able to create IntuneWin files on any platform, that supports the .NET framework. And depending on your skills you might be able to port this code to other languages as well.
Specifications
Check-out this post if you want to know how I figured out these specs:
- The IntuneWin file is a ZIP file.
- Contains a
IntunePackage.intunewin
file inside theContent
folder, which is an encrypted zip file, it seems there is no compression used, so it’s just a container with all the setup files. - Contains a
Detection.xml
file inside theMetadata
folder which contains data required to decrypt the innerIntunePackage.intunewin
file
Creating the IntuneWin file
We need to create a ZIP file, with the following structure:
IntuneWinPackage
- Contents
- IntunePackage.intunewin (encrypted zip file)
- Metadata
- Detection.xml
The following four steps will take you through the process of creating an IntuneWin file.
1. Create the inner IntuneWin file
Let’s start by creating a temporary folder, with the same structure as the IntuneWin file, and zip the source folder into the inner IntuneWin file:
string packageFolder = Path.Combine(tempFolder, "IntuneWinPackage");
string packageContentFolder = Path.Combine(packageFolder, "Contents");
string encryptedPackageLocation = Path.Combine(packageContentFolder, EncryptedPackageFileName);
_logger.LogInformation("Compressing the source folder {Folder} to {EncryptedPackageLocation}", folder, encryptedPackageLocation);
await Zipper.ZipDirectory(folder, encryptedPackageLocation, false, false);
// And get the "unencrypted" size of the new zip.
long setupFileSize = new FileInfo(encryptedPackageLocation).Length;
// Zipper.ZipDirectory code
// This is actually just a wrapper around, with some create directory stuff around it
ZipFile.CreateFromDirectory(directory, targetZipFile, compressionLevel, includeBaseDirectory);
Check-out the complete Zipper Code.
2. Encrypt inner IntuneWin file
You’ll need Authenticated encryption to encrypt the inner IntuneWin file. You will also need to prepend the MAC to the start of the file, so you can verify the file has not been tampered with. The Encryptor outputs the needed EncryptionInfo, which you can add to the Detection.xml file in the next step.
applicationInfo.EncryptionInfo = await Encryptor.EncryptFileAsync(encryptedPackageLocation, cancellationToken);
The encrypted file will look like this:
- The first 32 bytes contain the HMAC hash of the rest of the file
- The second 32 bytes contain the AES Initialization Vector
- The rest is the encrypted content of the inner IntuneWin file.
This is written by this code:
// The first part of the encrypted file was skipped when writing the encrypted content
// by writing an empty buffer to the file.
await targetFileStream.WriteAsync(buffer, 0, offset + initializationVector.Length, cancellationToken);
// Re-open the encrypted file, and write the HMAC hash, and the initialization vector
using (FileStream encryptedFileStream = new FileStream(targetFile, FileMode.Open, FileAccess.ReadWrite, FileShare.None, bufferSize: 4096, useAsync: true))
{
// Skip the first 32 bytes, this is where the HMAC hash will be written
encryptedFileStream.Seek(offset, SeekOrigin.Begin);
// Write the initialization vector
await encryptedFileStream.WriteAsync(initializationVector, 0, initializationVector.Length, cancellationToken);
// Rewind the stream the start of the IV (this need to be hashed as well, to prevent tempering)
encryptedFileStream.Seek(offset, SeekOrigin.Begin);
// Compute the hash of the stream (except the first 32 bytes)
byte[] hash = await hmac.ComputeHashAsync(encryptedFileStream, cancellationToken);
// Save the hash to return later
encryptedFileHash = hash;
// Rewind the stream to the start of the file
encryptedFileStream.Seek(0L, SeekOrigin.Begin);
// Write the hash to the file
await encryptedFileStream.WriteAsync(hash, 0, hash.Length, cancellationToken);
// Close the stream
encryptedFileStream.Close();
}
3. Create the Detection.xml file
Now we need to create the Detection.xml file, this file contains details about the setup, mind the static ToolVersion
, this is taken from the actual tool and might change in the future. This file has to be in the Metadata
folder, and has to be named Detection.xml
.
The encryption info is returned by the encryptor from the previous step. This file has to be perfect, because if you fail to create a valid Detection.xml file, the IntuneWin file will not be accepted by Intune.
Name
is the name of the application, this can be anything you want.UnencryptedContentSize
is the size of the inner IntuneWin file, this is used to verify the file has not been tampered with, I guess.FileName
is the name of the inner IntuneWin file, the official tool always sets this toIntunePackage.intunewin
(the same as the name you should give the inner intunewin file).SetupFile
is the name of the setup file, this is the file that will be executed by the client. Not sure if this is needed.EncryptionInfo
is the encryption info from the previous step.EncryptionKey
is the base64 encoded AES key, used to encrypt the inner IntuneWin file. This is generated each time.MacKey
is the base64 encoded HMAC key, used to create the HMAC hash of the inner IntuneWin file. This is generated each time.InitializationVector
is the base64 encoded AES initialization vector, used to encrypt the inner IntuneWin file. This is generated each time.Mac
is the base64 encoded HMAC hash of the inner IntuneWin file. This is calculated upon encryption, and since the MacKey changes every time this will be different every time as well.ProfileIdentifier
is a static string, this is alwaysProfileVersion1
.FileDigest
is the base64 encoded SHA256 hash of the inner IntuneWin file before encryption. But since the inner file is a zip file, this is not always the same if you run the tool over the same setup.FileDigestAlgorithm
is a static string, this is alwaysSHA256
, meaning the file is hashed with SHA256 before encryption.
<ApplicationInfo xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:xsd="http://www.w3.org/2001/XMLSchema" ToolVersion="1.8.4.0">
<Name>Any app name will do</Name>
<UnencryptedContentSize>{setupFileSize}</UnencryptedContentSize>
<FileName>IntunePackage.intunewin</FileName>
<SetupFile>install-amd64.exe</SetupFile>
<EncryptionInfo>
<EncryptionKey>D+ybCjTETXC8MoVN50MHzD46e44S7utjv+joT3Z7beM=</EncryptionKey>
<MacKey>UXblbITBQud2iQguE4Xrb0odDH3RVSIwEiPokQaBydE=</MacKey>
<InitializationVector>KIkWu3WQuFiJdYGqerI1+Q==</InitializationVector>
<Mac>5Gu4ndv6RhtRBPaeNzHjMZJcSDJc9yy7l9zqZTPvevE=</Mac>
<ProfileIdentifier>ProfileVersion1</ProfileIdentifier>
<FileDigest>0mRcAQNg2d0h0yvjTnCUQ/ZcuNeQXaYZ1BUUBNTILaU=</FileDigest>
<FileDigestAlgorithm>SHA256</FileDigestAlgorithm>
</EncryptionInfo>
</ApplicationInfo>
The Detection.xml file may also contain MsiInfo
, which may contain update information.
<ApplicationInfo xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:xsd="http://www.w3.org/2001/XMLSchema" ToolVersion="1.8.4.0">
...
<MsiInfo>
<MsiProductCode>{productCode}</MsiProductCode>
<MsiProductVersion>{productVersion}</MsiProductVersion>
<MsiPackageCode>{packageCode}</MsiPackageCode>
<MsiPublisher>{publisher}</MsiPublisher>
<MsiUpgradeCode>{upgradeCode}</MsiUpgradeCode>
</MsiInfo>
</ApplicationInfo>
The official tool loads these details if you’re packaging a
.msi
file. I was not able to figure out how this works without using Windows APIs, so this part is skipped for my cross-platform library.
4. Create the IntuneWin file
You should now have a temp folder containing a IntuneWinPackage
folder, that has a MetaData
folder and a Contents
folder. The Contents
folder should contain a IntunePackage.intunewin
file, which is the encrypted inner IntuneWin file. And the Metadata
folder should contain a Detection.xml
file, which contains the encryption info. If you have this folder structure you can zip the IntuneWinPackage
folder into a new zip file, and rename it to anything_you_want.intunewin
.
Congratulations, you have created an IntuneWin file!
Library and PowerShell Module
The full code of this post can be found here, there is a library and a PowerShell module. The library is used by the PowerShell module, and can be used in your own code as well. That is, if your code is open-source as well, which is a requirement of the license.
Commercial support
This project is open-source and I invite everybody to contribute, but I cannot provide any support because I only created this as a hobby project. For commercial support, please contact me on LinkedIn so we can discuss the possibilities. It’s my choice to work on this project in my spare time, so if you have commercial gain from this project you should considering sponsoring me.
Series: Intune
- Get your apps in Intune
- Package apps for Intune
- Publish apps to Intune
- Remove platform dependency for WingetIntune
- Analysing Win32 Content Prep Tool
- Decrypting intunewin files
- Introducing Content Prep PowerShell
- Creating IntuneWin files with C#
- Hyper-V template for Intune
Conclusion
I hope you enjoyed this post, and if you have any feedback, please let me know on Twitter or LinkedIn.