Encrypt a string with DPAPI¶
This operation is the string-level entry point into DPAPI's encrypt surface. It normalizes the overload chain's defaults and inputs, then delegates the actual Windows DPAPI call to the byte-array encrypt operation.
| Field | Value |
|---|---|
| Operation ID | OP-03 |
| Trigger | Called by Issue the legacy auth cookie step 4, or any direct consumer of the public DPAPI class |
| Inputs | key scope, plaintext, optional entropy, optional description |
| Outputs | Base64 ciphertext string |
| Primary components | DPAPI.Encrypt(string), Encrypt(KeyType, string), Encrypt(KeyType, string, string), Encrypt(KeyType, string, string, string) |
| Entry point | src/SAIF.NetCore.LegacyAuth/src/SAIF.NetCore.LegacyAuth/SAIF.NetCore.LegacyAuth/DPAPI.cs |
Steps¶
- Apply defaults through the overload chain: the instance
Encrypt(string)usesKeyType.UserKey; the shorter static overloads passstring.Emptyfor entropy and description (BR-07) - Coerce
nullplaintext and entropy to""(BR-08) - UTF-8 encode the plaintext and entropy (BR-09)
- Encrypt the bytes (BR-04)
- 4.1 Coerce
nullplaintext, entropy, and description to empty values (BR-08) - 4.2 Initialize three
DATA_BLOBs and aCRYPTPROTECT_PROMPTSTRUCTwith no prompt or window (BR-11) - 4.3 Copy the plaintext and entropy into unmanaged memory.
- 4.4 Compute flags: always
CRYPTPROTECT_UI_FORBIDDEN; addCRYPTPROTECT_LOCAL_MACHINEwhen the key scope isMachineKey(BR-04, BR-11) - 4.5 Call the native
crypt32!CryptProtectDataprimitive and copy the resulting ciphertext bytes out. - On failure: a non-Windows host causes the native call to throw
DllNotFoundException, but this is caught and rewrapped (not thrown directly) along with every other failure asException("DPAPI was unable to encrypt data.", innerException), with theDllNotFoundExceptionpreserved as the inner cause (BR-13, BR-12).
Origin: Steps 4.1–4.5 are implemented in Encrypt bytes with CryptProtectData and executed on behalf of this operation. 5. Base64-encode the ciphertext and return it (BR-09)
Process flow¶
The following diagram visualizes this operation's control flow, including the embedded byte-array encrypt sub-operation.
flowchart TD
Start(["Trigger: Encrypt called"]) --> S1["Apply overload<br>chain defaults"]
S1 --> S2["Coerce null plaintext<br>and entropy to empty"]
S2 --> S3["UTF-8 encode<br>plaintext and entropy"]
S3 --> S4["Encrypt bytes:<br>CryptProtectData"]
S4 --> D1{"Encryption failed?<br>incl. DllNotFoundException<br>on non-Windows hosts"}
D1 -->|Yes| ErrEnc(["Exception:<br>unable to encrypt data<br>(inner exception preserved)"])
D1 -->|No| S5["Base64-encode ciphertext<br>and return"]
S5 --> EndOk([Ciphertext string returned])
Failure and edge-case behavior¶
- Null plaintext or entropy arguments are coerced to empty strings rather than raising
ArgumentNullException(BR-08). - The instance
Encrypt(string)overload always uses the user key scope, differing from the machine-scope call the cookie service makes (BR-07) — a consumer calling it directly gets a different binding than expected. - Any failure raised inside the byte-array overload's try block — including a non-Windows host or non-default Windows installation directory, which causes the native call to throw
DllNotFoundException— surfaces to the caller as the same generic wrappedSystem.Exception("DPAPI was unable to encrypt data."), not as a specific exception type; the original cause is preserved only as the inner exception (BR-12, BR-13). This wrapping does not coverInitPrompt(called before that try block starts) or this operation's ownEncoding.UTF8.GetBytes/Convert.ToBase64Stringcalls, which run outside the byte-array overload's try/catch entirely and propagate any failure unwrapped.
Related documentation¶
Sources¶
src/SAIF.NetCore.LegacyAuth/src/SAIF.NetCore.LegacyAuth/SAIF.NetCore.LegacyAuth/DPAPI.cs