C#でカスタムセキュリティハンドラーを使用してデジタル署名を追加

Contents
[ ]

自分のセキュリティハンドラーを作成するには、希望する暗号化アルゴリズムを適用します。 Adobe Acrobatはそのようなファイルを開くことができませんが、Aspose.Pdfを使用して作業できます。 ハンドラーを作成するには、インターフェースを実装する必要があります。

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);
}

インターフェースの簡単な実装の例(デモ用のみ):

/// <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;
    }
}

文書の暗号化の例:

暗号化された文書を開く例: