[SerializedPersonalData]
The [SerializedPersonalData] attribute marks a non-string property (such as DateTime, int, decimal, or other value types) as personal data that requires binary serialization before encryption. The encrypted bytes are stored in a separate companion byte[] property.
Why a Separate Attribute?
[PersonalData] works by replacing the property's string value with Base64-encoded ciphertext. This does not work for non-string types like DateTime or int because the ciphertext cannot be stored in a typed property. [SerializedPersonalData] solves this by:
- Serializing the value to a
byte[]using a built-in binary serializer. - Encrypting the bytes with AES-256-GCM.
- Storing the encrypted result in a companion
byte[]?property. - Leaving the original property at its default value after encryption (for storage optimization, the actual value is in the encrypted bytes).
Basic Usage
public class EmployeeRecord
{
[DataSubjectId]
public Guid EmployeeId { get; set; }
[PersonalData]
public string Name { get; set; } = "";
[SerializedPersonalData]
public DateTime DateOfBirth { get; set; }
/// <summary>
/// Companion field — stores the encrypted bytes of DateOfBirth.
/// Follows the default naming convention: {PropertyName}Encrypted.
/// </summary>
public byte[]? DateOfBirthEncrypted { get; set; }
[SerializedPersonalData]
public int SocialSecurityNumber { get; set; }
/// <summary>
/// Companion field for SocialSecurityNumber.
/// </summary>
public byte[]? SocialSecurityNumberEncrypted { get; set; }
}Each [SerializedPersonalData] field requires a companion byte[]? property following the naming convention {PropertyName}Encrypted:
| Source Property | Companion Property |
|---|---|
DateOfBirth | DateOfBirthEncrypted |
SocialSecurityNumber | SocialSecurityNumberEncrypted |
During EncryptAsync:
- The value of
DateOfBirthis serialized to bytes, encrypted, and stored inDateOfBirthEncrypted. - The companion field holds the ciphertext. The original property's value can be zeroed or left as-is in the stored entity.
During DecryptAsync:
- The bytes in
DateOfBirthEncryptedare decrypted, deserialized, and written back toDateOfBirth. - The companion field is set to
nullafter successful decryption.
Custom Companion Field Name
If your companion property does not follow the default naming convention, use the EncryptedFieldName property:
public class LegacyRecord
{
[DataSubjectId]
public Guid Id { get; set; }
[SerializedPersonalData(EncryptedFieldName = "CustomBytes")]
public DateTime HireDate { get; set; }
/// <summary>
/// Custom-named companion field specified via EncryptedFieldName.
/// </summary>
public byte[]? CustomBytes { get; set; }
}Here, the encrypted bytes for HireDate are stored in CustomBytes instead of the default HireDateEncrypted.
Properties Reference
| Property | Type | Default | Description |
|---|---|---|---|
Group | string? | null | Links this field to a specific [DataSubjectId] group. Same as [PersonalData].Group. |
EncryptedFieldName | string? | null | Name of the companion byte[]? property. If not set, defaults to {PropertyName}Encrypted. |
Companion Field Convention
The companion field must be:
- A public instance property.
- Of type
byte[]?(nullable byte array). - On the same class as the
[SerializedPersonalData]property.
If the companion property cannot be found, Tayra logs a warning and skips encryption for that field.
Database Mapping
When using Entity Framework Core or Marten, make sure the companion byte[]? property is mapped to a column in your database. The original typed property may also need to be mapped if you want it available after decryption. Some developers mark the original property as a computed/transient property and only persist the encrypted bytes.
Crypto-Shredding Behavior
After the encryption key is deleted:
DecryptAsyncsets the original property to its type's default value (default(DateTime),0forint, etc.).- The companion
byte[]?property is set tonull.
There is no custom MaskValue property on [SerializedPersonalData] -- the type's default value is always used.
Supported Types
The built-in binary serializer supports these types:
| Type | Serialization |
|---|---|
int | 4-byte little-endian |
long | 8-byte little-endian |
double | 8-byte IEEE 754 |
decimal | 16-byte |
DateTime | 8-byte ticks (Int64) |
DateTimeOffset | 8-byte ticks + 2-byte offset |
Guid | 16-byte |
bool | 1-byte |
For other types, consider converting to string and using [PersonalData] instead.
See Also
[PersonalData]-- For string properties[DataSubjectId]-- Key derivation[DeepPersonalData]-- Nested objects- Attributes Overview -- All four attributes at a glance
