This article provides a practical, hands-on guide to programmatically processing Swank AES hard drives using C#. The previous article, "Getting Started: A Guide to Processing Swank AES Encrypted Hard Drives," explained the concepts and file structure. Here, we will translate that logic into functional code.
While these examples are in C#, the core cryptographic principles can be applied to any modern programming language like Java, Python, or Go.
1. Setup & Prerequisites
Before you begin, ensure you have the following:
- .NET Environment: This code is written for modern .NET (e.g., .NET 6/7/8). You will need the .NET SDK installed.
- Code Editor: Visual Studio 2022 or Visual Studio Code are recommended.
-
JSON Library: We will use
System.Text.Json, which is built into modern .NET. - Your Customer Key: You will have a unique Customer Encryption Key provided by Swank. This is a critical component for the decryption process.
Let's start by setting up a new console application and defining the essential variables.
C#
using System;
using System.IO;
using System.Text.Json;
using System.Text.Json.Serialization;
using System.Security.Cryptography;
using System.Threading.Tasks;
using System.Collections.Generic;
public class Program
{
// --- CONFIGURATION: SET THESE VALUES ---
// The root path to the Swank hard drive.
private const string driveRootPath = @"E:\"; // Example: E:\
// Your unique Customer Encryption Key provided by Swank, as a Base64 string.
private const string customerEncryptionKeyBase64 = "YOUR_BASE64_CUSTOMER_ENCRYPTION_KEY_HERE";
// -----------------------------------------
public static async Task Main(string[] args)
{
Console.WriteLine("Starting Swank Drive Processing...");
// The rest of our logic will go here.
Console.WriteLine("Processing complete.");
}
}
2. Defining the Data Models
To easily work with the Manifest.json file, we need to create C# classes that match its structure.
C#
// --- C# Models for Manifest.json ---
public class Manifest
{
[JsonPropertyName("ContentItems")]
public List<ContentItem> ContentItems { get; set; }
}
public class ContentItem
{
[JsonPropertyName("Title")]
public string Title { get; set; }
[JsonPropertyName("PublicityMetadataLocation")]
public string PublicityMetadataLocation { get; set; }
[JsonPropertyName("Identifiers")]
public Identifiers Identifiers { get; set; }
[JsonPropertyName("DRMKeyInfo")]
public List<DrmKeyInfo> DrmKeyInfo { get; set; }
[JsonPropertyName("Asset")]
public Asset Asset { get; set; }
}
public class Identifiers
{
[JsonPropertyName("FilmNumber")]
public string FilmNumber { get; set; }
}
public class DrmKeyInfo
{
[JsonPropertyName("Key")]
public string Key { get; set; }
}
public class Asset
{
[JsonPropertyName("MediaFileInfoLocation")]
public string MediaFileInfoLocation { get; set; }
[JsonPropertyName("Location")]
public string Location { get; set; }
}
3. The Decryption Engine
The core of the solution is a reusable decryption method. The process is identical for decrypting the Content Key and the content file. The only differences are the inputs (the encrypted data and the key used).
The critical steps are:
- Decode the input data.
- Extract the first 16 bytes as the Initialization Vector (IV).
- Use the remaining bytes as the encrypted payload.
- Perform AES-256-CBC decryption.
Let's create a helper class for this.
C#
// --- Cryptography Helper ---
public static class CryptoHelper
{
public static byte[] Decrypt(byte[] encryptedDataWithIv, byte[] key)
{
// 1. Extract the IV (first 16 bytes)
byte[] iv = new byte[16];
Array.Copy(encryptedDataWithIv, 0, iv, 0, iv.Length);
// 2. Extract the encrypted content (the rest of the data)
byte[] encryptedData = new byte[encryptedDataWithIv.Length - 16];
Array.Copy(encryptedDataWithIv, 16, encryptedData, 0, encryptedData.Length);
using (var aes = Aes.Create())
{
aes.Key = key;
aes.IV = iv;
aes.Mode = CipherMode.CBC;
aes.Padding = PaddingMode.PKCS7;
using (var decryptor = aes.CreateDecryptor(aes.Key, aes.IV))
{
return PerformCryptography(encryptedData, decryptor);
}
}
}
private static byte[] PerformCryptography(byte[] data, ICryptoTransform cryptoTransform)
{
using (var ms = new MemoryStream())
using (var cryptoStream = new CryptoStream(ms, cryptoTransform, CryptoStreamMode.Write))
{
cryptoStream.Write(data, 0, data.Length);
cryptoStream.FlushFinalBlock();
return ms.ToArray();
}
}
}
4. Putting It All Together: The Main Process
Now, let's update our Main method to read the manifest, loop through each title, and perform the decryption and file handling.
C#
public static async Task Main(string[] args)
{
Console.WriteLine("Starting Swank Drive Processing...");
// 1. Validate configuration
if (customerEncryptionKeyBase64.Contains("YOUR_BASE64"))
{
Console.ForegroundColor = ConsoleColor.Red;
Console.WriteLine("ERROR: Please set your 'customerEncryptionKeyBase64' in the Program.cs file.");
Console.ResetColor();
return;
}
byte[] customerEncryptionKey = Convert.FromBase64String(customerEncryptionKeyBase64);
// 2. Read and parse Manifest.json
string manifestPath = Path.Combine(driveRootPath, "Manifest.json");
if (!File.Exists(manifestPath))
{
Console.WriteLine($"ERROR: Manifest.json not found at '{manifestPath}'");
return;
}
string manifestJson = await File.ReadAllTextAsync(manifestPath);
Manifest manifest = JsonSerializer.Deserialize<Manifest>(manifestJson);
Console.WriteLine($"Found {manifest.ContentItems.Count} title(s) to process.");
// 3. Loop through each title in the manifest
foreach (var title in manifest.ContentItems)
{
Console.WriteLine($"--- Processing Title: {title.Title} ({title.Identifiers.FilmNumber}) ---");
try
{
// STEP A: Decrypt the Content Key
Console.WriteLine("Step A: Decrypting Content Key...");
// Get the encrypted key from the manifest
string encryptedContentKeyBase64 = title.DrmKeyInfo[0].Key;
byte[] encryptedContentKeyWithIv = Convert.FromBase64String(encryptedContentKeyBase64);
// Decrypt it using our Customer Key
byte[] decryptedContentKey = CryptoHelper.Decrypt(encryptedContentKeyWithIv, customerEncryptionKey);
Console.WriteLine("Content Key decrypted successfully.");
// STEP B: Decrypt the Content File
Console.WriteLine("Step B: Decrypting Content File...");
string contentFileName = $"{title.Identifiers.FilmNumber}.MP4"; // Assuming MP4 based on samples
string encryptedFilePath = Path.Combine(driveRootPath, title.Asset.Location, contentFileName);
if (!File.Exists(encryptedFilePath))
{
Console.WriteLine($"ERROR: Content file not found at '{encryptedFilePath}'");
continue;
}
byte[] encryptedFileWithIv = await File.ReadAllBytesAsync(encryptedFilePath);
// Decrypt the file using the decrypted Content Key from Step A
byte[] decryptedFileData = CryptoHelper.Decrypt(encryptedFileWithIv, decryptedContentKey);
// Save the decrypted file
string outputDirectory = Path.Combine(AppContext.BaseDirectory, "DecryptedOutput", title.Identifiers.FilmNumber);
Directory.CreateDirectory(outputDirectory);
string decryptedFilePath = Path.Combine(outputDirectory, contentFileName);
await File.WriteAllBytesAsync(decryptedFilePath, decryptedFileData);
Console.WriteLine($"Content file decrypted and saved to: {decryptedFilePath}");
// STEP C & D: Process Metadata and Publicity (Example)
Console.WriteLine("Step C/D: Locating metadata and publicity assets...");
string publicityMetadataPath = Path.Combine(driveRootPath, title.PublicityMetadataLocation);
string mediaInfoPath = Path.Combine(driveRootPath, title.Asset.MediaFileInfoLocation);
Console.WriteLine($"Publicity Metadata is at: {publicityMetadataPath}");
Console.WriteLine($"Media File Info is at: {mediaInfoPath}");
// In a real application, you would parse these JSON files and copy publicity assets.
}
catch (Exception ex)
{
Console.ForegroundColor = ConsoleColor.Red;
Console.WriteLine($"An error occurred while processing title {title.Title}: {ex.Message}");
Console.ResetColor();
}
}
Console.WriteLine("\nProcessing complete.");
}
Conclusion
This guide provides a complete, functional C# console application for processing Swank AES drives. By following this structure, you can automate the ingestion of content and metadata into your systems.
Key Takeaways:
-
The
Manifest.jsondrives everything: It is the single source of truth for file locations. - The Decryption pattern is consistent: The process of separating the 16-byte IV from the data and using AES-256-CBC with PKCS7 padding applies to both the Content Key and the Content File.
- Production Code: This example omits robust error handling, logging, and configuration management that would be necessary for a production environment. Please adapt it to fit your production standards.
Comments
0 comments
Please sign in to leave a comment.