Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
Expand Up @@ -24,10 +24,10 @@ Decrypts a file using a symmetric algorithm and key, or using PGP with a private
| `AesKeySize` | AES key size | Property | `AesKeySize` | | `Aes256` | AES key size in bits used to encrypt the input. Applies only when `Algorithm = AES` and `Format = OpenSslEnc`; ignored otherwise. Must match the key size the producer used (e.g. `openssl enc -aes-128-cbc` / `-aes-192-cbc` / `-aes-256-cbc`). Not stored in the wire format — encrypt and decrypt sides must use matching values. |
| `OutputFilePath` | Output file path | InArgument | `string` | | | The full path where the decrypted file will be saved. When empty, the file is written next to the input file using the name `<input-name>_Decrypted<input-extension>`. |
| `OutputFileName` | Decrypted file name | InArgument | `string` | | | The file name to use for the decrypted file. Honored when `OutputFilePath` is empty. |
| `PrivateKeyFilePath` | Private key file path | InArgument | `string` | Conditional | | Path to your PGP private key file. Required when `Algorithm = PGP`. |
| `PrivateKeyFilePath` | Private key file path | InArgument | `string` | Conditional | | Path to your PGP private key file. Required when `Algorithm = PGP`. Paired with a hidden `IResource` alternative (`PrivateKeyFile`) selectable via a designer menu action. |
| `Passphrase` | Passphrase | InArgument | `string` | Conditional | | Passphrase that unlocks the private key. Provide either `Passphrase` or `PassphraseSecureString`. PGP only. |
| `PassphraseSecureString` | Passphrase (secure) | InArgument | `SecureString` | Conditional | | Secure-string variant of the passphrase. Provide either `Passphrase` or `PassphraseSecureString`. PGP only. |
| `PublicKeyFilePath` | Public key file path | InArgument | `string` | Conditional | | Path to the signer's PGP public key file. Required only when `VerifySignature = True`. |
| `PublicKeyFilePath` | Public key file path | InArgument | `string` | Conditional | | Path to the signer's PGP public key file. Required only when `VerifySignature = True`. Paired with a hidden `IResource` alternative (`PublicKeyFile`) selectable via a designer menu action. |

### Configuration

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -23,10 +23,10 @@ Decrypts a text string using a symmetric algorithm, or using PGP with a private
| `KeyFormat` | Key bytes format | Property | `KeyBytesFormat` | | `Encoded` | How the `Key` string is interpreted. `Hex` or `Base64` are required when `Format = Raw`. Symmetric algorithms only. |
| `KdfIterations` | KDF iterations | InArgument | `int` | | `0` | PBKDF2 iteration count. Must match the value used at encrypt time. `0` uses the format's OWASP-recommended default. Rejected for `Classic` and `Raw`. |
| `AesKeySize` | AES key size | Property | `AesKeySize` | | `Aes256` | AES key size in bits used to encrypt the input. Applies only when `Algorithm = AES` and `Format = OpenSslEnc`; ignored otherwise. Must match the key size the producer used (e.g. `openssl enc -aes-128-cbc` / `-aes-192-cbc` / `-aes-256-cbc`). Not stored in the wire format — encrypt and decrypt sides must use matching values. |
| `PrivateKeyFilePath` | Private key file path | InArgument | `string` | Conditional | | Path to your PGP private key file. Required when `Algorithm = PGP`. |
| `PrivateKeyFilePath` | Private key file path | InArgument | `string` | Conditional | | Path to your PGP private key file. Required when `Algorithm = PGP`. Paired with a hidden `IResource` alternative (`PrivateKeyFile`) selectable via a designer menu action. |
| `Passphrase` | Passphrase | InArgument | `string` | Conditional | | Passphrase that unlocks the private key. Provide either `Passphrase` or `PassphraseSecureString`. PGP only. |
| `PassphraseSecureString` | Passphrase (secure) | InArgument | `SecureString` | Conditional | | Secure-string variant of the passphrase. PGP only. |
| `PublicKeyFilePath` | Public key file path | InArgument | `string` | Conditional | | Path to the signer's PGP public key file. Required only when `VerifySignature = True`. |
| `PublicKeyFilePath` | Public key file path | InArgument | `string` | Conditional | | Path to the signer's PGP public key file. Required only when `VerifySignature = True`. Paired with a hidden `IResource` alternative (`PublicKeyFile`) selectable via a designer menu action. |

### Configuration

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -20,13 +20,13 @@ Encrypts a file using a symmetric algorithm and key, or using PGP with a recipie
| `KeyEncoding` | Key encoding | InArgument | `Encoding` | | UTF-8 | The encoding used to interpret the key. Symmetric algorithms only. |
| `Format` | Wire format | Property | `SymmetricWireFormat` | | `Classic` | The symmetric ciphertext layout. `Classic` (default) is UiPath's byte-stable layout; `Owasp2026` uses the same layout with stronger KDF iterations; `Raw` is caller-supplied key + IV for third-party interop; `OpenSslEnc` produces `openssl enc`-compatible output. Symmetric algorithms only. |
| `KeyFormat` | Key bytes format | Property | `KeyBytesFormat` | | `Encoded` | How the `Key` string is interpreted. `Hex` or `Base64` are required when `Format = Raw`. Symmetric algorithms only. |
| `Iv` | IV | InArgument | `string` | Conditional | | Initialization vector when `Format = Raw`. Interpreted per `KeyFormat`. Optional — leave empty to let the cipher generate one. Rejected for all other formats. |
| `Iv` | IV | InArgument | `string` | Conditional | | Initialization vector when `Format = Raw`. Interpreted per `KeyFormat`. Optional — leave empty to let the cipher generate one. Rejected for all other formats. Never reuse the same (Key, IV) pair — it breaks confidentiality, and under AEAD modes (AES-GCM, ChaCha20-Poly1305) also lets an attacker forge messages; use each pair at most once, or leave empty for a fresh random IV. |
| `KdfIterations` | KDF iterations | InArgument | `int` | | `0` | PBKDF2 iteration count. `0` uses the format's OWASP-recommended default (1 300 000 for `Owasp2026`, 600 000 for `OpenSslEnc`). Rejected for `Classic` and `Raw`. |
| `AesKeySize` | AES key size | Property | `AesKeySize` | | `Aes256` | AES key size in bits. Applies only when `Algorithm = AES` and `Format = OpenSslEnc`; ignored otherwise. Must match the key size the peer uses (e.g. `openssl enc -aes-128-cbc` / `-aes-192-cbc` / `-aes-256-cbc`). Not stored in the wire format — encrypt and decrypt sides must use matching values. |
| `OutputFilePath` | Output file path | InArgument | `string` | | | The full path where the encrypted file will be saved. When empty, the file is written next to the input file using the name `<input-name>_Encrypted<input-extension>`. |
| `OutputFileName` | Encrypted file name | InArgument | `string` | | | The file name to use for the encrypted file. Honored when `OutputFilePath` is empty. |
| `PublicKeyFilePath` | Public key file path | InArgument | `string` | Conditional | | Path to the recipient's PGP public key file. Required when `Algorithm = PGP`. |
| `PrivateKeyFilePath` | Private key file path | InArgument | `string` | Conditional | | Path to your PGP private key file. Required only when `SignData = True`. |
| `PublicKeyFilePath` | Public key file path | InArgument | `string` | Conditional | | Path to the recipient's PGP public key file. Required when `Algorithm = PGP`. Paired with a hidden `IResource` alternative (`PublicKeyFile`) selectable via a designer menu action. |
| `PrivateKeyFilePath` | Private key file path | InArgument | `string` | Conditional | | Path to your PGP private key file. Required only when `SignData = True`. Paired with a hidden `IResource` alternative (`PrivateKeyFile`) selectable via a designer menu action. |
| `Passphrase` | Passphrase | InArgument | `string` | Conditional | | Passphrase that unlocks the private key (signing). Provide either `Passphrase` or `PassphraseSecureString`. PGP-sign only. |
| `PassphraseSecureString` | Passphrase (secure) | InArgument | `SecureString` | Conditional | | Secure-string variant of the passphrase. PGP-sign only. |

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -21,11 +21,11 @@ Encrypts a text string using a symmetric algorithm, or using PGP with a recipien
| `PlaintextEncoding` | Text encoding | InArgument | `Encoding` | | UTF-8 | The encoding used to convert the input text to bytes before encryption. Set this to match the encoding expected by the third-party tool that will consume the ciphertext. Symmetric algorithms only. |
| `Format` | Wire format | Property | `SymmetricWireFormat` | | `Classic` | The symmetric ciphertext layout. `Classic` (default) is UiPath's byte-stable layout; `Owasp2026` uses the same layout with stronger KDF iterations; `Raw` is caller-supplied key + IV for third-party interop; `OpenSslEnc` produces `openssl enc`-compatible output. Symmetric algorithms only. |
| `KeyFormat` | Key bytes format | Property | `KeyBytesFormat` | | `Encoded` | How the `Key` string is interpreted. `Hex` or `Base64` are required when `Format = Raw`; otherwise the key is treated as a password. Symmetric algorithms only. |
| `Iv` | IV | InArgument | `string` | Conditional | | Initialization vector when `Format = Raw`. Interpreted per `KeyFormat`. Optional — leave empty to let the cipher generate one. Rejected for all other formats. |
| `Iv` | IV | InArgument | `string` | Conditional | | Initialization vector when `Format = Raw`. Interpreted per `KeyFormat`. Optional — leave empty to let the cipher generate one. Rejected for all other formats. Never reuse the same (Key, IV) pair — it breaks confidentiality, and under AEAD modes (AES-GCM, ChaCha20-Poly1305) also lets an attacker forge messages; use each pair at most once, or leave empty for a fresh random IV. |
| `KdfIterations` | KDF iterations | InArgument | `int` | | `0` | PBKDF2 iteration count. `0` uses the format's OWASP-recommended default (1 300 000 for `Owasp2026`, 600 000 for `OpenSslEnc`). Rejected for `Classic` and `Raw`. |
| `AesKeySize` | AES key size | Property | `AesKeySize` | | `Aes256` | AES key size in bits. Applies only when `Algorithm = AES` and `Format = OpenSslEnc`; ignored otherwise. Must match the key size the peer uses (e.g. `openssl enc -aes-128-cbc` / `-aes-192-cbc` / `-aes-256-cbc`). Not stored in the wire format — encrypt and decrypt sides must use matching values. |
| `PublicKeyFilePath` | Public key file path | InArgument | `string` | Conditional | | Path to the recipient's PGP public key file. Required when `Algorithm = PGP`. |
| `PrivateKeyFilePath` | Private key file path | InArgument | `string` | Conditional | | Path to your PGP private key file. Required only when `SignData = True`. |
| `PublicKeyFilePath` | Public key file path | InArgument | `string` | Conditional | | Path to the recipient's PGP public key file. Required when `Algorithm = PGP`. Paired with a hidden `IResource` alternative (`PublicKeyFile`) selectable via a designer menu action. |
| `PrivateKeyFilePath` | Private key file path | InArgument | `string` | Conditional | | Path to your PGP private key file. Required only when `SignData = True`. Paired with a hidden `IResource` alternative (`PrivateKeyFile`) selectable via a designer menu action. |
| `Passphrase` | Passphrase | InArgument | `string` | Conditional | | Passphrase that unlocks the private key (signing). Provide either `Passphrase` or `PassphraseSecureString`. PGP-sign only. |
| `PassphraseSecureString` | Passphrase (secure) | InArgument | `SecureString` | Conditional | | Secure-string variant of the passphrase. PGP-sign only. |

Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,49 @@
using Moq;
using UiPath.Cryptography.Activities.Helpers;
using UiPath.Platform.ResourceHandling;
using Xunit;

namespace UiPath.Cryptography.Activities.Tests.Helpers
{
/// <summary>
/// Covers the path↔resource precedence of <see cref="PgpFileResolver.ResolveLocalPath"/> — the
/// shared resolver used by the synchronous Encrypt/Decrypt Text &amp; File activities for the PGP
/// public/private key inputs. The string path wins; the resource is only consulted when the path
/// is empty (the resource-resolution adapter itself is exercised end-to-end by the PGP activity
/// tests, not here, since ToLocalResource needs the platform resource stack).
/// </summary>
public class PgpFileResolverTests
{
[Fact]
public void ResolveLocalPath_PathProvided_ReturnsPath_AndDoesNotTouchResource()
{
// Strict mock: any access to the resource would throw, proving the path short-circuits it.
var resource = new Mock<IResource>(MockBehavior.Strict);

var result = PgpFileResolver.ResolveLocalPath(@"C:\keys\key.asc", resource.Object);

Assert.Equal(@"C:\keys\key.asc", result);
resource.VerifyNoOtherCalls();
}

[Fact]
public void ResolveLocalPath_PathProvided_NullResource_ReturnsPath()
{
var result = PgpFileResolver.ResolveLocalPath(@"C:\keys\key.asc", null);

Assert.Equal(@"C:\keys\key.asc", result);
}

[Fact]
public void ResolveLocalPath_EmptyPath_NullResource_ReturnsEmpty()
{
Assert.Equal(string.Empty, PgpFileResolver.ResolveLocalPath(string.Empty, null));
}

[Fact]
public void ResolveLocalPath_NullPath_NullResource_ReturnsNull()
{
Assert.Null(PgpFileResolver.ResolveLocalPath(null, null));
}
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -674,6 +674,30 @@ public void PgpVerify_Has_IResource_Properties()
Assert.NotNull(typeof(PgpVerify).GetProperty(nameof(PgpVerify.PublicKeyFile)));
}

[Fact]
public void EncryptText_Has_PrivateKeyFile_IResource_Property()
{
Assert.NotNull(typeof(EncryptText).GetProperty(nameof(EncryptText.PrivateKeyFile)));
}

[Fact]
public void EncryptFile_Has_PrivateKeyFile_IResource_Property()
{
Assert.NotNull(typeof(EncryptFile).GetProperty(nameof(EncryptFile.PrivateKeyFile)));
}

[Fact]
public void DecryptText_Has_PrivateKeyFile_IResource_Property()
{
Assert.NotNull(typeof(DecryptText).GetProperty(nameof(DecryptText.PrivateKeyFile)));
}

[Fact]
public void DecryptFile_Has_PrivateKeyFile_IResource_Property()
{
Assert.NotNull(typeof(DecryptFile).GetProperty(nameof(DecryptFile.PrivateKeyFile)));
}

#endregion
}
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,9 @@
using System.Activities.Statements;
using System.IO;
using System.Security;
using Moq;
using UiPath.Cryptography.Enums;
using UiPath.Platform.ResourceHandling;
using Xunit;

#pragma warning disable CS0618 // tests intentionally set the obsolete PassphraseInputModeSwitch property to exercise the SecureString branch
Expand Down Expand Up @@ -197,6 +199,66 @@ public void PgpDecryptFile_WithoutPrivateKey_Throws()
}
}

[Fact]
public void PgpEncryptText_SignDataFalse_DoesNotResolvePrivateKeyResource()
{
// Regression (STUD-80718): with signing disabled, a bound-but-unused PrivateKeyFile
// resource must never be resolved — the private key isn't needed for a non-signing
// encrypt, and resolving it (e.g. from a Storage Bucket) can fail. A strict-behavior
// failure would surface as any interaction with the mock.
var privateKeyResource = new Mock<IResource>();

var encryptText = new EncryptText
{
Algorithm = EncryptionAlgorithm.PGP,
Input = new InArgument<string>("Hello PGP without signing!"),
PublicKeyFilePath = new InArgument<string>(_publicKeyPath),
SignData = false,
// Bind via a lambda (not a literal) — WF rejects Literal<T> of arbitrary reference types.
PrivateKeyFile = new InArgument<IResource>(c => privateKeyResource.Object),
};

var result = WorkflowInvoker.Invoke(encryptText);

Assert.False(string.IsNullOrEmpty(result));
privateKeyResource.VerifyNoOtherCalls();
}

[Fact]
public void PgpEncryptFile_SignDataFalse_DoesNotResolvePrivateKeyResource()
{
var tempInputFile = Path.Combine(Path.GetTempPath(), Path.GetRandomFileName());
var tempEncryptedFile = Path.Combine(Path.GetTempPath(), Path.GetRandomFileName());
try
{
File.WriteAllText(tempInputFile, "Hello PGP file without signing!");
var privateKeyResource = new Mock<IResource>();

var encryptFile = new EncryptFile
{
InputFilePath = new InArgument<string>(tempInputFile),
Algorithm = EncryptionAlgorithm.PGP,
PublicKeyFilePath = new InArgument<string>(_publicKeyPath),
OutputFilePath = new InArgument<string>(tempEncryptedFile),
SignData = false,
// Bind via a lambda (not a literal) — WF rejects Literal<T> of arbitrary reference types.
PrivateKeyFile = new InArgument<IResource>(c => privateKeyResource.Object),
Overwrite = true,
};

WorkflowInvoker.Invoke(encryptFile);

Assert.True(File.Exists(tempEncryptedFile));
Assert.True(new FileInfo(tempEncryptedFile).Length > 0);
privateKeyResource.VerifyNoOtherCalls();
}
finally
{
File.Delete(tempInputFile);
File.Delete(tempEncryptedFile);
}
}

[Fact]
public void PgpEncryptDecryptText_Activity_Works()
{
Expand Down
Loading