Tambahkan tanda tangan digital dengan pengelola keamanan kustom di C#
Anda dapat membuat pengelola keamanan Anda sendiri dengan menerapkan algoritma enkripsi yang diinginkan. Adobe Acrobat tidak akan dapat membuka file semacam itu, tetapi Anda dapat bekerja dengannya menggunakan Aspose.Pdf. Untuk membuat pengelola, Anda harus menerapkan antarmuka:
public interface ICustomSecurityHandler
{
string Filter { get; }
string SubFilter { get; }
int Version { get; }
int Revision { get; }
int KeyLength { get; }
byte[] EncryptPermissions(int permissions);
byte[] GetOwnerKey(string userPassword, string ownerPassword);
byte[] GetUserKey(string userPassword);
void Initialize(EncryptionParameters parameters);
byte[] CalculateEncryptionKey(string password);
byte[] Encrypt(byte[] data, int objectNumber, int generation, byte[] key);
byte[] Decrypt(byte[] data, int objectNumber, int generation, byte[] key);
bool IsOwnerPassword(string password);
bool IsUserPassword(string password);
}
Contoh implementasi sederhana dari antarmuka (hanya untuk demonstrasi):
/// <summary>
/// The custom security handler interface.
/// </summary>
class CustomSecurityHandler : ICustomSecurityHandler
{
private EncryptionParameters _parameters;
/// <summary>
/// Gets the filter name.
/// </summary>
public string Filter
{
get
{
return "TestFilter";
}
}
/// <summary>
/// Gets the sub-filter name.
/// </summary>
public string SubFilter
{
get
{
return "TestsSubFilter";
}
}
/// <summary>
/// Gets the handler or encryption algorithm version.
/// </summary>
public int Version
{
get { return 1; }
}
/// <summary>
/// Gets the handler or encryption algorithm revision.
/// </summary>
public int Revision
{
get { return 2; }
}
/// <summary>
/// Gets the key length.
/// </summary>
public int KeyLength
{
get { return 8; }
}
/// <summary>
/// Encrypt the document's permissions field. The result will be written to the Perms encryption dictionary field.
/// When opening a document, the value can be obtained in <see cref="EncryptionParameters"/> via the Perms field.
/// Allows you to check if the document permissions have changed.
/// </summary>
/// <param name="permissions">The document permissions in integer representation.</param>
/// <returns>The encrypted array.</returns>
public byte[] EncryptPermissions(int permissions)
{
byte[] perms = new byte[16];
perms[0] = (byte) (permissions & 0xff);
perms[1] = (byte) ((permissions >> 8) & 0xff);
perms[2] = (byte) ((permissions >> 16) & 0xff);
perms[3] = (byte) ((permissions >> 24) & 0xff);
perms[4] = 0xff;
perms[5] = 0xff;
perms[6] = 0xff;
perms[7] = 0xff;
perms[8] = (byte) 'F';
perms[9] = (byte) 'a';
perms[10] = (byte) 'd';
perms[11] = (byte) 'b';
Random rnd = new Random();
perms[12] = (byte) rnd.Next(0, 0xff);// The random salt for example
perms[13] = (byte) rnd.Next(0, 0xff);// The random salt for example
perms[14] = (byte) rnd.Next(0, 0xff);// The random salt for example
perms[15] = (byte) rnd.Next(0, 0xff);// The random salt for example
for (var index = 0; index < perms.Length; index++)
{
perms[index] ^= 123;
}
return perms;
}
/// <summary>
/// Called to initialize the current instance for encryption.
/// Note that when encrypting, it will be filled with the data of the transferred properties <see cref="ICustomSecurityHandler"/>, and when opening the document from the encryption dictionary.
/// If the method is called during new encryption, then <see cref="EncryptionParameters.UserKey"/> and <see cref="EncryptionParameters.OwnerKey"/> will be null.
/// </summary>
/// <param name="parameters">The encryption parameters.</param>
public void Initialize(EncryptionParameters parameters)
{
_parameters = parameters;
}
/// <summary>
/// Calculate the EncryptionKey. Generally the key is calculated based on the UserKey.
/// You can use values from EncryptionParams, which contains the current parameters at the time of the call.
/// This value is passed as the key argument in <see cref="Encrypt"/> and <see cref="Decrypt"/>.
/// </summary>
/// <param name="password">Password entered by the user.</param>
/// <returns>The array of encryption key.</returns>
public byte[] CalculateEncryptionKey(string password)
{
string userPassword;
if (IsUserPassword(password))
{
userPassword = password;
}
else
{
userPassword = Encoding.UTF8.GetString(GetUserPassword(password));
}
string encKey = userPassword + Encoding.UTF8.GetString(_parameters.OwnerKey) + Encoding.UTF8.GetString(_parameters.UserKey);
byte[] bytes = Encoding.UTF8.GetBytes(encKey);
int sum = 0;
foreach (var b in bytes)
{
sum += b;
}
sum %= 127;
return new byte[] { (byte)sum};
}
/// <summary>
/// Encrypt the data array.
/// </summary>
/// <param name="data">Data to encrypt.</param>
/// <param name="objectNumber">Number of the object containing the encrypted data.</param>
/// <param name="generation">Generation of the object.</param>
/// <param name="key">Key obtained by the CalculateEncryptionKey method</param>
/// <returns>The encrypted data.</returns>
public byte[] Encrypt(byte[] data, int objectNumber, int generation, byte[] key)
{
byte[] result = new byte[data.Length];
for (int i = 0; i < data.Length; i++)
{
result[i] = (byte)(data[i] ^ key[0]);
}
return result;
}
/// <summary>
/// Decrypt the data array.
/// </summary>
/// <param name="data">Data to decrypt.</param>
/// <param name="objectNumber">Number of the object containing the encrypted data.</param>
/// <param name="generation">Generation of the object.</param>
/// <param name="key">Key obtained by the CalculateEncryptionKey method</param>
/// <returns>The decrypted data.</returns>
public byte[] Decrypt(byte[] data, int objectNumber, int generation, byte[] key)
{
byte[] result = new byte[data.Length];
for (int i = 0; i < data.Length; i++)
{
result[i] = (byte)(data[i] ^ key[0]);
}
return result;
}
/// <summary>
/// Check if the password is the document owner's password.
/// The method is called after Initialize. The method call is used in the PDF API.
/// </summary>
/// <param name="password">The password.</param>
/// <returns>True, if it is an owner password.</returns>
public bool IsOwnerPassword(string password)
{
// Just for the demonstration.
return !IsUserPassword(password);
}
/// <summary>
/// Check if the password belongs to the user (password for opening the document).
/// The method is called after Initialize. The method call is used in the PDF API.
/// </summary>
/// <param name="password">The password.</param>
/// <returns>True, if it is a password for opening the document.</returns>
public bool IsUserPassword(string password)
{
string u = Encoding.UTF8.GetString(_parameters.UserKey);
// So that an empty password is not determined part of the line.
if (u.Length != 0 && password.Length == 0)
{
return false;
}
return u.Contains(password);
}
/// <summary>
/// Creates an encoded array based on passwords that will be written to the O field of the encryption dictionary.
/// Should only rely on the arguments passed. The user password can be calculated from this field using the owner password.
/// Called during encryption to prepare it and populate the encryption dictionary.
/// The value will be available in <see cref="CalculateEncryptionKey"/> to get the key from the UserKey.
/// The passwords specified by the user when calling document encryption will be passed.
/// Passwords may not be specified or only one may be specified.
/// </summary>
/// <param name="userPassword">The user password.</param>
/// <param name="ownerPassword">The owner password.</param>
/// <returns>The array of owner key.</returns>
public byte[] GetOwnerKey(string userPassword, string ownerPassword)
{
byte[] bytes = Encoding.UTF8.GetBytes(ownerPassword);
int encKeyForUserPass = 0;
foreach (var b in bytes)
{
encKeyForUserPass += b;
}
encKeyForUserPass %= 127;
byte[] userBytes = Encoding.UTF8.GetBytes(userPassword);
for (var index = 0; index < userBytes.Length; index++)
{
userBytes[index] ^= (byte)encKeyForUserPass;
}
return userBytes;
}
/// <summary>
/// Creates an encoded array based on the user's password.
/// This value is typically used to check if the password belongs to the user or owner, and to get the encryption key.
/// Called during encryption to prepare it and populate the encryption dict.
/// The user-specified password is passed as an argument when calling document encryption.
/// </summary>
/// <param name="userPassword">The user password.</param>
/// <returns>The array of user key.</returns>
public byte[] GetUserKey(string userPassword)
{
string userKey = userPassword + "_123";
return Encoding.UTF8.GetBytes(userKey);
}
/// <summary>
/// Extract user password from the owner key.
/// </summary>
/// <param name="ownerPassword">The owner password.</param>
/// <returns>The array of user password.</returns>
private byte[] GetUserPassword(string ownerPassword)
{
byte[] bytes = Encoding.UTF8.GetBytes(ownerPassword);
int encKeyForUserPass = 0;
foreach (var b in bytes)
{
encKeyForUserPass += b;
}
encKeyForUserPass %= 127;
byte[] userPassword = new byte[_parameters.OwnerKey.Length];
for (var index = 0; index < _parameters.OwnerKey.Length; index++)
{
userPassword[index] = (byte)(_parameters.OwnerKey[index] ^ (byte)encKeyForUserPass);
}
return userPassword;
}
}
Contoh enkripsi dokumen:
Contoh membuka dokumen terenkripsi: