Skip to content

feat(cryptography): add PGP support for encryption, decryption, signing, and verification#514

Merged
adrianignat13 merged 15 commits intodevelopfrom
feat/cryptography_pgp_support
Mar 3, 2026
Merged

feat(cryptography): add PGP support for encryption, decryption, signing, and verification#514
adrianignat13 merged 15 commits intodevelopfrom
feat/cryptography_pgp_support

Conversation

@adrianignat13
Copy link
Member

@adrianignat13 adrianignat13 commented Feb 25, 2026

Summary

The Cryptography activity pack only supported symmetric algorithms (AES, DES, RC2, Rijndael). Users needed PGP (asymmetric, key-based) cryptography for encrypting/decrypting files and text, signing files, clear-signing files, verifying signatures, and generating key pairs — common requirements in enterprise automation workflows.

Changes

Extended existing activities (EncryptFile, DecryptFile, EncryptText, DecryptText) with a PGP algorithm option that dynamically shows/hides public key, private key, and passphrase inputs based on the selected algorithm.

Added 4 new activities:

  • PgpSignFile — binary signature
  • PgpClearSignFile — clear-text signature
  • PgpVerify — unified signature, clear-signature, and public-key verification
  • PgpGenerateKeyPair — key pair generation

Added helpers:

  • PgpStreamHelper — key file validation and stream lifecycle management
  • PgpFileSignHelper — shared sign/clear-sign logic

Reduced ViewModel duplication with shared base classes: EncryptCryptoViewModelBase, DecryptCryptoViewModelBase, and PgpSignViewModelBase.

Added ViewModels for all new and modified activities with full metadata registration and SVG icons.

Added tests: PgpTests (activity-level encrypt/decrypt/sign/verify round-trips) and PgpStandaloneTests (helper-level unit tests), with shared setup extracted into PgpTestBase.

Renamed SymmetricAlgorithms.cs → EncryptionAlgorithm.cs (file rename only — the enum was already named EncryptionAlgorithm).

Bug fixes and cleanup:

  • Fixed pre-existing bug in DecryptText.cs where ContinueOnError description pointed to Result_Description
  • Added argument validation: PGP encrypt/decrypt now throws ArgumentException when sign/verify is requested but required inputs (private key, passphrase, public key) are missing, instead of silently falling back to unsigned/unverified operation
  • Refactored TranslatePgpException to return Exception instead of void, eliminating unreachable return null workarounds and preserving original stack traces via ExceptionDispatchInfo
  • Added NonClosingStreamWrapper in PgpVerifyPublicKey to prevent BouncyCastle's decoder from closing the caller-provided stream
  • Added LocalizedDisplayName/LocalizedDescription attributes to all PGP properties for consistency with existing activity properties
  • Removed CopyLocalLockFileAssemblies from the library project; PgpCore dependency moved to the packaging project
  • Removed static IsRequired from PgpVerify metadata for InputFilePath/PublicKeyFilePath — ViewModel handles this dynamically per verification mode
  • Cleaned up SonarQube findings: removed unnecessary casts, redundant null-conditional operators, unused imports, and replaced Path.GetTempFileName with Path.GetRandomFileName in tests
  • Removed orphaned resource strings from consolidated verify activities

Uses PgpCore NuGet package (v6.5.3) as the underlying PGP implementation.

43 files changed — 4,655 insertions, 936 deletions.

Test plan

  • Build: 0 errors (warnings are all pre-existing)
  • Tests: 73/73 passing

🤖 Generated with Claude Code

adrianignat13 added a commit that referenced this pull request Feb 25, 2026
Address SonarQube S5445 findings from PR #514 — Path.GetTempFileName()
is insecure (predictable names, race conditions) and can throw when
>65535 temp files exist. Replaced all 19 occurrences in PgpStandaloneTests
and PgpTests with Path.Combine(Path.GetTempPath(), Path.GetRandomFileName()).

Also updated code-reviewer and software-engineer agent prompts to catch
this pattern and the false-positive enum rename breaking change in future
reviews.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
Copy link

Copilot AI left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Pull request overview

This PR adds comprehensive PGP (Pretty Good Privacy) cryptography support to the UiPath Cryptography activity pack, significantly expanding its capabilities beyond symmetric encryption algorithms. The implementation integrates PgpCore 6.5.3 and adds 4 new activities while extending 4 existing activities to support PGP operations.

Changes:

  • Added PGP algorithm option to existing EncryptFile, DecryptFile, EncryptText, DecryptText activities with dynamic UI toggling
  • Added 4 new PGP activities: PgpGenerateKeyPair, PgpSignFile, PgpClearSignFile, and unified PgpVerify
  • Renamed SymmetricAlgorithms enum to EncryptionAlgorithm to reflect broader scope (PGP is asymmetric)
  • Added helper classes for PGP stream/resource management and unified file signing logic
  • Added comprehensive test coverage (PgpTests and PgpStandaloneTests)
  • Fixed pre-existing bug in DecryptText.cs where ContinueOnError description pointed to Result_Description

Reviewed changes

Copilot reviewed 36 out of 42 changed files in this pull request and generated 1 comment.

Show a summary per file
File Description
UiPath.Cryptography.csproj Added PgpCore 6.5.3 dependency and CopyLocalLockFileAssemblies flag
EncryptionAlgorithm.cs Renamed from SymmetricAlgorithms.cs, added PGP enum member
CryptographyHelper.cs Added PGP methods for encrypt/decrypt/sign/verify with exception translation
PgpStreamHelper.cs Helper class for managing PGP key file streams and SecureString conversion
PgpFileSignHelper.cs Shared logic for PgpSignFile and PgpClearSignFile activities
Pgp*.cs (4 activities) New activities for key generation, signing, and verification
Encrypt/Decrypt*.cs (4 files) Extended with PGP support using ViewModel-driven visibility rules
*ViewModel.cs (8 files) ViewModels for all new and modified activities with dynamic property visibility
ActivitiesMetadata.json Metadata entries for all PGP properties and activities
*.resx Resource strings for PGP errors, modes, and property descriptions
Test files Comprehensive unit and integration tests for all PGP functionality
Packaging Added PgpCore and BouncyCastle DLLs to package output
Files not reviewed (2)
  • Activities/Cryptography/UiPath.Cryptography.Activities/Properties/UiPath.Cryptography.Activities.Designer.cs: Language not supported
  • Activities/Cryptography/UiPath.Cryptography/Properties/UiPath.Cryptography.Designer.cs: Language not supported

💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.

Copy link
Collaborator

@sorinconstantin sorinconstantin left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

nit: LocalizedDescriptionAttribute usage on properties is redundant when view models exist. LocalizedDisplayName should also be used only for properties marked as Required, so the validation message is pointing to the correct display name of a localized property

Copy link

Copilot AI left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Pull request overview

Copilot reviewed 37 out of 44 changed files in this pull request and generated 11 comments.

Files not reviewed (2)
  • Activities/Cryptography/UiPath.Cryptography.Activities/Properties/UiPath.Cryptography.Activities.Designer.cs: Language not supported
  • Activities/Cryptography/UiPath.Cryptography/Properties/UiPath.Cryptography.Designer.cs: Language not supported

💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.

adrianignat13 added a commit that referenced this pull request Feb 26, 2026
- Remove duplicate File.WriteAllBytes in EncryptFile/DecryptFile (CryptographyLocalItem constructor already writes)
- Clear Key/KeySecureString IsRequired flags when switching to PGP algorithm in both Encrypt/Decrypt ViewModels
- Move EncryptionKeys construction inside try blocks for consistent error translation
- Use string.IsNullOrEmpty for passphrase checks instead of null-only
- Preserve stack traces via ExceptionDispatchInfo when PGP exceptions are not translated
- Add File.Exists validation with localized errors in PgpStreamHelper before File.OpenRead
- Remove unused using System.IO from EncryptText.cs and DecryptText.cs
- Fix IsPrincipal metadata inconsistency for Result in EncryptText/DecryptText
- Remove redundant LocalizedDisplayName/LocalizedDescription from non-required PGP properties

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
Copy link

Copilot AI left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Pull request overview

Copilot reviewed 37 out of 44 changed files in this pull request and generated no new comments.

Files not reviewed (2)
  • Activities/Cryptography/UiPath.Cryptography.Activities/Properties/UiPath.Cryptography.Activities.Designer.cs: Language not supported
  • Activities/Cryptography/UiPath.Cryptography/Properties/UiPath.Cryptography.Designer.cs: Language not supported

💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.

Copy link

Copilot AI left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Pull request overview

Copilot reviewed 37 out of 44 changed files in this pull request and generated 6 comments.

Files not reviewed (2)
  • Activities/Cryptography/UiPath.Cryptography.Activities/Properties/UiPath.Cryptography.Activities.Designer.cs: Language not supported
  • Activities/Cryptography/UiPath.Cryptography/Properties/UiPath.Cryptography.Designer.cs: Language not supported

💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.

adrianignat13 added a commit that referenced this pull request Mar 2, 2026
Address SonarQube S5445 findings from PR #514 — Path.GetTempFileName()
is insecure (predictable names, race conditions) and can throw when
>65535 temp files exist. Replaced all 19 occurrences in PgpStandaloneTests
and PgpTests with Path.Combine(Path.GetTempPath(), Path.GetRandomFileName()).

Also updated code-reviewer and software-engineer agent prompts to catch
this pattern and the false-positive enum rename breaking change in future
reviews.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
adrianignat13 added a commit that referenced this pull request Mar 2, 2026
- Remove duplicate File.WriteAllBytes in EncryptFile/DecryptFile (CryptographyLocalItem constructor already writes)
- Clear Key/KeySecureString IsRequired flags when switching to PGP algorithm in both Encrypt/Decrypt ViewModels
- Move EncryptionKeys construction inside try blocks for consistent error translation
- Use string.IsNullOrEmpty for passphrase checks instead of null-only
- Preserve stack traces via ExceptionDispatchInfo when PGP exceptions are not translated
- Add File.Exists validation with localized errors in PgpStreamHelper before File.OpenRead
- Remove unused using System.IO from EncryptText.cs and DecryptText.cs
- Fix IsPrincipal metadata inconsistency for Result in EncryptText/DecryptText
- Remove redundant LocalizedDisplayName/LocalizedDescription from non-required PGP properties

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
@adrianignat13 adrianignat13 force-pushed the feat/cryptography_pgp_support branch from a2df16a to a5849f2 Compare March 2, 2026 19:46
@adrianignat13 adrianignat13 requested a review from Copilot March 2, 2026 19:57
Copy link

Copilot AI left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Pull request overview

Copilot reviewed 37 out of 43 changed files in this pull request and generated 4 comments.

Files not reviewed (2)
  • Activities/Cryptography/UiPath.Cryptography.Activities/Properties/UiPath.Cryptography.Activities.Designer.cs: Language not supported
  • Activities/Cryptography/UiPath.Cryptography/Properties/UiPath.Cryptography.Designer.cs: Language not supported

💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.

adrianignat13 added a commit that referenced this pull request Mar 3, 2026
- Add LocalizedDisplayName/LocalizedDescription attributes to PGP
  properties in EncryptFile, DecryptFile, EncryptText, DecryptText
- Fix typo "backwords" → "backwards" in CryptographyHelper
- Wrap publicKeyStream in NonClosingStreamWrapper in PgpVerifyPublicKey
  to prevent BouncyCastle's decoder from closing the caller's stream
- Remove CopyLocalLockFileAssemblies from library project; add PgpCore
  reference to packaging project instead
- Remove static IsRequired from InputFilePath/PublicKeyFilePath in
  PgpVerify metadata — ViewModel handles this dynamically per Mode

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
@adrianignat13 adrianignat13 requested a review from Copilot March 3, 2026 11:00
Copy link

Copilot AI left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Pull request overview

Copilot reviewed 37 out of 43 changed files in this pull request and generated 4 comments.

Files not reviewed (2)
  • Activities/Cryptography/UiPath.Cryptography.Activities/Properties/UiPath.Cryptography.Activities.Designer.cs: Language not supported
  • Activities/Cryptography/UiPath.Cryptography/Properties/UiPath.Cryptography.Designer.cs: Language not supported

💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.

adrianignat13 and others added 3 commits March 3, 2026 11:30
…ng, and verification

## Problem

The Cryptography activity pack only supported symmetric AES/DES/RC2/Rijndael
algorithms. Users needed PGP (asymmetric, key-based) cryptography for
encrypting/decrypting files and text, signing files, clear-signing files,
verifying signatures, and generating PGP key pairs — all common requirements
in enterprise automation workflows.

## Solution Considerations

- Extend existing Encrypt/Decrypt activities with a PGP algorithm option rather
  than creating entirely separate activities, keeping the API surface minimal.
- Create dedicated activities only for PGP-specific operations (sign, clear-sign,
  verify, generate key pair) that have no symmetric equivalent.
- Use PgpCore NuGet package as the underlying PGP implementation.
- Share signing logic via PgpFileSignHelper; manage key stream lifecycle via
  PgpStreamHelper.

## Actual Fix

- Extended EncryptFile, DecryptFile, EncryptText, DecryptText with PGP algorithm
  support (public/private key + optional passphrase).
- Added new activities: PgpSignFile, PgpClearSignFile, PgpVerify (unified
  signature + clear-signature + public-key verification), PgpGenerateKeyPair.
- Added ViewModels for all new and modified activities with rule-based visibility
  toggling between symmetric and PGP property groups.
- Added PgpStreamHelper, PgpFileSignHelper, CryptographyLocalItem model,
  PgpVerifyMode enum, and TranslatePgpException for user-friendly error messages.
- Added comprehensive xUnit tests: PgpTests (activity-level round-trips) and
  PgpStandaloneTests (helper-level tests).
- Registered all activities in ActivitiesMetadata.json with icons.

## Caveats

- Metadata IsPrincipal mismatch on PgpVerify.Result (cosmetic, ViewModel wins
  at runtime).
- Orphaned resource strings remain for removed intermediate verify activities
  (cleanup item).
Address SonarQube S5445 findings from PR #514 — Path.GetTempFileName()
is insecure (predictable names, race conditions) and can throw when
>65535 temp files exist. Replaced all 19 occurrences in PgpStandaloneTests
and PgpTests with Path.Combine(Path.GetTempPath(), Path.GetRandomFileName()).

Also updated code-reviewer and software-engineer agent prompts to catch
this pattern and the false-positive enum rename breaking change in future
reviews.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
- S2583: Replace unnecessary null-conditional telemetry calls with #if ENABLE_DEFAULT_TELEMETRY blocks in PgpSignFile, PgpClearSignFile, PgpGenerateKeyPair, PgpVerify
- S3776: Reduce cognitive complexity by extracting helper methods in EncryptFile, DecryptFile, EncryptText, DecryptText
- S3928: Use resource strings instead of nameof() for ArgumentNullException parameter names in PgpGenerateKeyPair, PgpVerify
- S1854: Remove dead store increments in all 8 ViewModel files
- S3881: Add proper IDisposable pattern in PgpStandaloneTests and PgpTests
- S2325: Make GetPassphraseSecureString static in test classes

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
adrianignat13 and others added 10 commits March 3, 2026 11:30
- Remove duplicate File.WriteAllBytes in EncryptFile/DecryptFile (CryptographyLocalItem constructor already writes)
- Clear Key/KeySecureString IsRequired flags when switching to PGP algorithm in both Encrypt/Decrypt ViewModels
- Move EncryptionKeys construction inside try blocks for consistent error translation
- Use string.IsNullOrEmpty for passphrase checks instead of null-only
- Preserve stack traces via ExceptionDispatchInfo when PGP exceptions are not translated
- Add File.Exists validation with localized errors in PgpStreamHelper before File.OpenRead
- Remove unused using System.IO from EncryptText.cs and DecryptText.cs
- Fix IsPrincipal metadata inconsistency for Result in EncryptText/DecryptText
- Remove redundant LocalizedDisplayName/LocalizedDescription from non-required PGP properties

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
…elds

Set IsRequired alongside IsVisible using the same condition expression,
making property state easier to reason about at a glance.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
Instead of silently falling back to unsigned encrypt or unverified
decrypt, throw ArgumentException when sign=true without private
key/passphrase, or verifySignature=true without public key.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
Change ThrowTranslatedPgpException (void) to TranslatePgpException
(returns Exception) so callers use "throw TranslatePgpException(ex)"
and the compiler sees all paths return/throw — removing the
"return null; // unreachable" workarounds.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
- Add LocalizedDisplayName/LocalizedDescription attributes to PGP
  properties in EncryptFile, DecryptFile, EncryptText, DecryptText
- Fix typo "backwords" → "backwards" in CryptographyHelper
- Wrap publicKeyStream in NonClosingStreamWrapper in PgpVerifyPublicKey
  to prevent BouncyCastle's decoder from closing the caller's stream
- Remove CopyLocalLockFileAssemblies from library project; add PgpCore
  reference to packaging project instead
- Remove static IsRequired from InputFilePath/PublicKeyFilePath in
  PgpVerify metadata — ViewModel handles this dynamically per Mode

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
- Replace nameof(...) with localized resource strings in PgpFileSignHelper
  and PgpStreamHelper exception parameter names so error messages reference
  UI field names instead of internal parameter identifiers
- Add shared display name resources: PrivateKeyFilePathDisplayName,
  PublicKeyFilePathDisplayName, PassphraseDisplayName
- Add null/whitespace check for inputFilePath in PgpVerify and use
  PgpVerify-specific display name resource
- Restore IsRequired: true for PublicKeyFilePath in PgpVerify metadata
  (always required per [RequiredArgument] attribute)

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
@adrianignat13 adrianignat13 force-pushed the feat/cryptography_pgp_support branch from 5bcd476 to ba2fe81 Compare March 3, 2026 11:30
adrianignat13 and others added 2 commits March 3, 2026 11:58
…et dependency

Refactor PgpVerify.Execute to reduce cognitive complexity from 16 to 15
by consolidating the File.Exists check and removing the else branch.
Declare PgpCore as a PackageReference (NuGet dependency) in the packaging
project instead of bundling DLLs, matching the pattern used by other
dependencies like System.Security.Cryptography.Cng.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
…, fix metadata formatting

Add null/whitespace check for inputFilePath in PgpFileSignHelper before
File.Exists to produce a clear error message. Centralize PgpCore version
in Directory.build.targets (matching the pattern for other dependencies).
Fix stray trailing comma in ActivitiesMetadata.json.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
@sonarqubecloud
Copy link

sonarqubecloud bot commented Mar 3, 2026

Quality Gate Failed Quality Gate failed

Failed conditions
7.5% Duplication on New Code (required ≤ 3%)

See analysis details on SonarQube Cloud

Copy link

Copilot AI left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Pull request overview

Copilot reviewed 38 out of 44 changed files in this pull request and generated no new comments.

Files not reviewed (2)
  • Activities/Cryptography/UiPath.Cryptography.Activities/Properties/UiPath.Cryptography.Activities.Designer.cs: Language not supported
  • Activities/Cryptography/UiPath.Cryptography/Properties/UiPath.Cryptography.Designer.cs: Language not supported
Comments suppressed due to low confidence (1)

Activities/Cryptography/UiPath.Cryptography/EncryptionAlgorithm.cs:7

  • Renaming the public enum from SymmetricAlgorithms to EncryptionAlgorithm is a breaking API change: any external code and, more importantly, existing workflows/XAML serialized with the old enum type name may fail to compile/load. If backward compatibility is required, consider keeping a SymmetricAlgorithms enum (marked [Obsolete]) or adding a type-forward/serialization remap so older workflows still deserialize, while exposing the new name for new usage.

💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.

<EmbeddedResource Include="Resources\Icons\PGP_generate_keys.svg" />
<EmbeddedResource Include="Resources\Icons\PGP_sign_file.svg" />
<EmbeddedResource Include="Resources\Icons\PGP_clear_sign_file.svg" />
<EmbeddedResource Include="Resources\Icons\PGP_verify.svg" />
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

nit: we can do this with a wild card

namespace UiPath.Cryptography
{
public enum SymmetricAlgorithms
public enum EncryptionAlgorithm
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Isn't this a breaking change?

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

tl;dr No

explanation: the Algorithm property only accepts a value from the drop-down and that is what we store in the xaml. It could break if the user insists on having a variable/argument of that type and then on upgrade the type is no longer there, but having such a variable makes no sense.

@adrianignat13 adrianignat13 merged commit 8b274c8 into develop Mar 3, 2026
17 of 18 checks passed
@adrianignat13 adrianignat13 deleted the feat/cryptography_pgp_support branch March 3, 2026 14:05
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

4 participants