Coding Stephan

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.

Super fast Content Prep

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 the Content 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 the Metadata folder which contains data required to decrypt the inner IntunePackage.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 to IntunePackage.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 always ProfileVersion1.
    • 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 always SHA256, 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

Conclusion

I hope you enjoyed this post, and if you have any feedback, please let me know on Twitter or LinkedIn.