// // Conveniently, Encrypt() and Decrypt() are identical save for the actual P/Invoke call to CNG. Thus, both // APIs invoke this common helper with the "transform" parameter determining whether encryption or decryption is done. // private byte[] EncryptOrDecrypt(byte[] data, RSAEncryptionPadding padding, EncryptOrDecryptAction encryptOrDecrypt) { if (data == null) { throw new ArgumentNullException(nameof(data)); } if (padding == null) { throw new ArgumentNullException(nameof(padding)); } unsafe { using (SafeNCryptKeyHandle keyHandle = GetDuplicatedKeyHandle()) { switch (padding.Mode) { case RSAEncryptionPaddingMode.Pkcs1: return(EncryptOrDecrypt(keyHandle, data, AsymmetricPaddingMode.NCRYPT_PAD_PKCS1_FLAG, null, encryptOrDecrypt)); case RSAEncryptionPaddingMode.Oaep: { using (SafeUnicodeStringHandle safeHashAlgorithmName = new SafeUnicodeStringHandle(padding.OaepHashAlgorithm.Name)) { BCRYPT_OAEP_PADDING_INFO paddingInfo = new BCRYPT_OAEP_PADDING_INFO() { pszAlgId = safeHashAlgorithmName.DangerousGetHandle(), // It would nice to put randomized data here but RSAEncryptionPadding does not at this point provide support for this. pbLabel = IntPtr.Zero, cbLabel = 0, }; return(EncryptOrDecrypt(keyHandle, data, AsymmetricPaddingMode.NCRYPT_PAD_OAEP_FLAG, &paddingInfo, encryptOrDecrypt)); } } default: throw new CryptographicException(SR.Cryptography_UnsupportedPaddingMode); } } } }
// Conveniently, Encrypt() and Decrypt() are identical save for the actual P/Invoke call to CNG. Thus, both // array-based APIs invoke this common helper with the "encrypt" parameter determining whether encryption or decryption is done. private unsafe byte[] EncryptOrDecrypt(byte[] data, RSAEncryptionPadding padding, bool encrypt) { if (data == null) { throw new ArgumentNullException(nameof(data)); } if (padding == null) { throw new ArgumentNullException(nameof(padding)); } using (SafeNCryptKeyHandle keyHandle = GetDuplicatedKeyHandle()) { switch (padding.Mode) { case RSAEncryptionPaddingMode.Pkcs1: return(EncryptOrDecrypt(keyHandle, data, AsymmetricPaddingMode.NCRYPT_PAD_PKCS1_FLAG, null, encrypt)); case RSAEncryptionPaddingMode.Oaep: IntPtr namePtr = Marshal.StringToHGlobalUni(padding.OaepHashAlgorithm.Name); try { var paddingInfo = new BCRYPT_OAEP_PADDING_INFO() { pszAlgId = namePtr, // It would nice to put randomized data here but RSAEncryptionPadding does not at this point provide support for this. pbLabel = IntPtr.Zero, cbLabel = 0, }; return(EncryptOrDecrypt(keyHandle, data, AsymmetricPaddingMode.NCRYPT_PAD_OAEP_FLAG, &paddingInfo, encrypt)); } finally { Marshal.FreeHGlobal(namePtr); } default: throw new CryptographicException(SR.Cryptography_UnsupportedPaddingMode); } } }
// // Conveniently, Encrypt() and Decrypt() are identical save for the actual P/Invoke call to CNG. Thus, both // APIs invoke this common helper with the "transform" parameter determining whether encryption or decryption is done. // private byte[] EncryptOrDecrypt(byte[] data, RSAEncryptionPadding padding, EncryptOrDecryptAction encryptOrDecrypt) { if (data == null) throw new ArgumentNullException(nameof(data)); if (padding == null) throw new ArgumentNullException(nameof(padding)); unsafe { using (SafeNCryptKeyHandle keyHandle = GetDuplicatedKeyHandle()) { switch (padding.Mode) { case RSAEncryptionPaddingMode.Pkcs1: return EncryptOrDecrypt(keyHandle, data, AsymmetricPaddingMode.NCRYPT_PAD_PKCS1_FLAG, null, encryptOrDecrypt); case RSAEncryptionPaddingMode.Oaep: { using (SafeUnicodeStringHandle safeHashAlgorithmName = new SafeUnicodeStringHandle(padding.OaepHashAlgorithm.Name)) { BCRYPT_OAEP_PADDING_INFO paddingInfo = new BCRYPT_OAEP_PADDING_INFO() { pszAlgId = safeHashAlgorithmName.DangerousGetHandle(), // It would nice to put randomized data here but RSAEncryptionPadding does not at this point provide support for this. pbLabel = IntPtr.Zero, cbLabel = 0, }; return EncryptOrDecrypt(keyHandle, data, AsymmetricPaddingMode.NCRYPT_PAD_OAEP_FLAG, &paddingInfo, encryptOrDecrypt); } } default: throw new CryptographicException(SR.Cryptography_UnsupportedPaddingMode); } } } }
// Conveniently, Encrypt() and Decrypt() are identical save for the actual P/Invoke call to CNG. Thus, both // array-based APIs invoke this common helper with the "encrypt" parameter determining whether encryption or decryption is done. private unsafe byte[] EncryptOrDecrypt(byte[] data, RSAEncryptionPadding padding, bool encrypt) { if (data == null) { throw new ArgumentNullException(nameof(data)); } if (padding == null) { throw new ArgumentNullException(nameof(padding)); } int modulusSizeInBytes = RsaPaddingProcessor.BytesRequiredForBitCount(KeySize); if (!encrypt && data.Length != modulusSizeInBytes) { throw new CryptographicException(SR.Cryptography_RSA_DecryptWrongSize); } if (encrypt && padding.Mode == RSAEncryptionPaddingMode.Pkcs1 && data.Length > modulusSizeInBytes - Pkcs1PaddingOverhead) { throw new CryptographicException( SR.Format(SR.Cryptography_Encryption_MessageTooLong, modulusSizeInBytes - Pkcs1PaddingOverhead)); } using (SafeNCryptKeyHandle keyHandle = GetDuplicatedKeyHandle()) { if (encrypt && data.Length == 0) { byte[] rented = CryptoPool.Rent(modulusSizeInBytes); Span <byte> paddedMessage = new Span <byte>(rented, 0, modulusSizeInBytes); try { if (padding == RSAEncryptionPadding.Pkcs1) { RsaPaddingProcessor.PadPkcs1Encryption(data, paddedMessage); } else if (padding.Mode == RSAEncryptionPaddingMode.Oaep) { RsaPaddingProcessor processor = RsaPaddingProcessor.OpenProcessor(padding.OaepHashAlgorithm); processor.PadOaep(data, paddedMessage); } else { throw new CryptographicException(SR.Cryptography_UnsupportedPaddingMode); } return(EncryptOrDecrypt(keyHandle, paddedMessage, AsymmetricPaddingMode.NCRYPT_NO_PADDING_FLAG, null, encrypt)); } finally { CryptographicOperations.ZeroMemory(paddedMessage); CryptoPool.Return(rented, clearSize: 0); } } switch (padding.Mode) { case RSAEncryptionPaddingMode.Pkcs1: return(EncryptOrDecrypt(keyHandle, data, AsymmetricPaddingMode.NCRYPT_PAD_PKCS1_FLAG, null, encrypt)); case RSAEncryptionPaddingMode.Oaep: IntPtr namePtr = Marshal.StringToHGlobalUni(padding.OaepHashAlgorithm.Name); try { var paddingInfo = new BCRYPT_OAEP_PADDING_INFO() { pszAlgId = namePtr, // It would nice to put randomized data here but RSAEncryptionPadding does not at this point provide support for this. pbLabel = IntPtr.Zero, cbLabel = 0, }; return(EncryptOrDecrypt(keyHandle, data, AsymmetricPaddingMode.NCRYPT_PAD_OAEP_FLAG, &paddingInfo, encrypt)); } finally { Marshal.FreeHGlobal(namePtr); } default: throw new CryptographicException(SR.Cryptography_UnsupportedPaddingMode); } } }
// Conveniently, Encrypt() and Decrypt() are identical save for the actual P/Invoke call to CNG. Thus, both // span-based APIs invoke this common helper with the "encrypt" parameter determining whether encryption or decryption is done. private unsafe bool TryEncryptOrDecrypt(ReadOnlySpan <byte> data, Span <byte> destination, RSAEncryptionPadding padding, bool encrypt, out int bytesWritten) { if (padding == null) { throw new ArgumentNullException(nameof(padding)); } using (SafeNCryptKeyHandle keyHandle = GetDuplicatedKeyHandle()) { if (encrypt && data.Length == 0) { int bufSize = RsaPaddingProcessor.BytesRequiredForBitCount(KeySize); byte[] rented = ArrayPool <byte> .Shared.Rent(bufSize); Span <byte> paddedMessage = new Span <byte>(rented, 0, bufSize); try { if (padding == RSAEncryptionPadding.Pkcs1) { RsaPaddingProcessor.PadPkcs1Encryption(data, paddedMessage); } else if (padding.Mode == RSAEncryptionPaddingMode.Oaep) { RsaPaddingProcessor processor = RsaPaddingProcessor.OpenProcessor(padding.OaepHashAlgorithm); processor.PadOaep(data, paddedMessage); } else { throw new CryptographicException(SR.Cryptography_UnsupportedPaddingMode); } return(TryEncryptOrDecrypt(keyHandle, paddedMessage, destination, AsymmetricPaddingMode.NCRYPT_NO_PADDING_FLAG, null, encrypt, out bytesWritten)); } finally { CryptographicOperations.ZeroMemory(paddedMessage); ArrayPool <byte> .Shared.Return(rented); } } switch (padding.Mode) { case RSAEncryptionPaddingMode.Pkcs1: return(TryEncryptOrDecrypt(keyHandle, data, destination, AsymmetricPaddingMode.NCRYPT_PAD_PKCS1_FLAG, null, encrypt, out bytesWritten)); case RSAEncryptionPaddingMode.Oaep: IntPtr namePtr = Marshal.StringToHGlobalUni(padding.OaepHashAlgorithm.Name); try { var paddingInfo = new BCRYPT_OAEP_PADDING_INFO() { pszAlgId = namePtr, pbLabel = IntPtr.Zero, // It would nice to put randomized data here but RSAEncryptionPadding does not at this point provide support for this. cbLabel = 0, }; return(TryEncryptOrDecrypt(keyHandle, data, destination, AsymmetricPaddingMode.NCRYPT_PAD_OAEP_FLAG, &paddingInfo, encrypt, out bytesWritten)); } finally { Marshal.FreeHGlobal(namePtr); } default: throw new CryptographicException(SR.Cryptography_UnsupportedPaddingMode); } } }