diff --git a/src/libraries/Common/src/Interop/Android/System.Security.Cryptography.Native.Android/Interop.Cipher.cs b/src/libraries/Common/src/Interop/Android/System.Security.Cryptography.Native.Android/Interop.Cipher.cs index 8b987ce31ea5c0..8cc861a948c2ec 100644 --- a/src/libraries/Common/src/Interop/Android/System.Security.Cryptography.Native.Android/Interop.Cipher.cs +++ b/src/libraries/Common/src/Interop/Android/System.Security.Cryptography.Native.Android/Interop.Cipher.cs @@ -136,6 +136,34 @@ internal static bool EvpCipherFinalEx( return EvpCipherFinalEx(ctx, ref MemoryMarshal.GetReference(output), out bytesWritten); } + [LibraryImport(Libraries.AndroidCryptoNative, EntryPoint = "AndroidCryptoNative_AeadCipherFinalEx")] + [return: MarshalAs(UnmanagedType.Bool)] + private static unsafe partial bool EvpAeadCipherFinalEx( + SafeEvpCipherCtxHandle ctx, + byte* outm, + out int outl, + [MarshalAs(UnmanagedType.Bool)] out bool authTagMismatch); + + internal static unsafe bool EvpAeadCipherFinalEx( + SafeEvpCipherCtxHandle ctx, + Span output, + out int bytesWritten, + out bool authTagMismatch) + { + scoped Span notNullOutput = output; + + // We can't pass null down to the native shim, so create a valid pointer if we have an empty span. + if (notNullOutput.IsEmpty) + { + notNullOutput = (stackalloc byte[1]).Slice(1); + } + + fixed (byte* pOutput = &MemoryMarshal.GetReference(notNullOutput)) + { + return EvpAeadCipherFinalEx(ctx, pOutput, out bytesWritten, out authTagMismatch); + } + } + [LibraryImport(Libraries.AndroidCryptoNative, EntryPoint = "AndroidCryptoNative_CipherSetTagLength")] [return: MarshalAs(UnmanagedType.Bool)] internal static partial bool CipherSetTagLength( diff --git a/src/libraries/System.Security.Cryptography/ref/System.Security.Cryptography.cs b/src/libraries/System.Security.Cryptography/ref/System.Security.Cryptography.cs index 99e9063d8ec784..a90b8977d5b077 100644 --- a/src/libraries/System.Security.Cryptography/ref/System.Security.Cryptography.cs +++ b/src/libraries/System.Security.Cryptography/ref/System.Security.Cryptography.cs @@ -270,6 +270,12 @@ protected AsymmetricSignatureFormatter() { } public abstract void SetHashAlgorithm(string strName); public abstract void SetKey(System.Security.Cryptography.AsymmetricAlgorithm key); } + public sealed partial class AuthenticationTagMismatchException : System.Security.Cryptography.CryptographicException + { + public AuthenticationTagMismatchException() { } + public AuthenticationTagMismatchException(string? message) { } + public AuthenticationTagMismatchException(string? message, System.Exception? inner) { } + } [System.Runtime.Versioning.UnsupportedOSPlatformAttribute("browser")] [System.Runtime.Versioning.UnsupportedOSPlatformAttribute("ios")] [System.Runtime.Versioning.UnsupportedOSPlatformAttribute("tvos")] diff --git a/src/libraries/System.Security.Cryptography/src/System.Security.Cryptography.csproj b/src/libraries/System.Security.Cryptography/src/System.Security.Cryptography.csproj index 996f80f668780f..ed68d33a835db9 100644 --- a/src/libraries/System.Security.Cryptography/src/System.Security.Cryptography.csproj +++ b/src/libraries/System.Security.Cryptography/src/System.Security.Cryptography.csproj @@ -283,6 +283,7 @@ + diff --git a/src/libraries/System.Security.Cryptography/src/System/Security/Cryptography/AeadCommon.Windows.cs b/src/libraries/System.Security.Cryptography/src/System/Security/Cryptography/AeadCommon.Windows.cs index fe10a0ed2ca10e..909f650a7aa3c2 100644 --- a/src/libraries/System.Security.Cryptography/src/System/Security/Cryptography/AeadCommon.Windows.cs +++ b/src/libraries/System.Security.Cryptography/src/System/Security/Cryptography/AeadCommon.Windows.cs @@ -104,7 +104,7 @@ public static unsafe void Decrypt( CryptographicOperations.ZeroMemory(plaintext); } - throw new CryptographicException(SR.Cryptography_AuthTagMismatch); + throw new AuthenticationTagMismatchException(); default: throw CreateCryptographicException(ntStatus); } diff --git a/src/libraries/System.Security.Cryptography/src/System/Security/Cryptography/AesCcm.Android.cs b/src/libraries/System.Security.Cryptography/src/System/Security/Cryptography/AesCcm.Android.cs index 39247bed98e25d..b7089bd3d101d7 100644 --- a/src/libraries/System.Security.Cryptography/src/System/Security/Cryptography/AesCcm.Android.cs +++ b/src/libraries/System.Security.Cryptography/src/System/Security/Cryptography/AesCcm.Android.cs @@ -69,11 +69,13 @@ private void EncryptCore( throw new CryptographicException(); } - if (!Interop.Crypto.EvpCipherFinalEx( + if (!Interop.Crypto.EvpAeadCipherFinalEx( ctx, ciphertextAndTag.Slice(ciphertextBytesWritten), - out int bytesWritten)) + out int bytesWritten, + out bool authTagMismatch)) { + Debug.Assert(!authTagMismatch); throw new CryptographicException(); } @@ -141,13 +143,20 @@ private void DecryptCore( plaintextBytesWritten += bytesWritten; - if (!Interop.Crypto.EvpCipherFinalEx( + if (!Interop.Crypto.EvpAeadCipherFinalEx( ctx, plaintext.Slice(plaintextBytesWritten), - out bytesWritten)) + out bytesWritten, + out bool authTagMismatch)) { CryptographicOperations.ZeroMemory(plaintext); - throw new CryptographicException(SR.Cryptography_AuthTagMismatch); + + if (authTagMismatch) + { + throw new AuthenticationTagMismatchException(); + } + + throw new CryptographicException(SR.Arg_CryptographyException); } plaintextBytesWritten += bytesWritten; diff --git a/src/libraries/System.Security.Cryptography/src/System/Security/Cryptography/AesCcm.OpenSsl.cs b/src/libraries/System.Security.Cryptography/src/System/Security/Cryptography/AesCcm.OpenSsl.cs index 6c78bee075e3a2..0aedd34fe2d1b3 100644 --- a/src/libraries/System.Security.Cryptography/src/System/Security/Cryptography/AesCcm.OpenSsl.cs +++ b/src/libraries/System.Security.Cryptography/src/System/Security/Cryptography/AesCcm.OpenSsl.cs @@ -104,7 +104,7 @@ private void DecryptCore( if (!Interop.Crypto.EvpCipherUpdate(ctx, plaintext, out int plaintextBytesWritten, ciphertext)) { plaintext.Clear(); - throw new CryptographicException(SR.Cryptography_AuthTagMismatch); + throw new AuthenticationTagMismatchException(); } if (plaintextBytesWritten != plaintext.Length) diff --git a/src/libraries/System.Security.Cryptography/src/System/Security/Cryptography/AesGcm.Android.cs b/src/libraries/System.Security.Cryptography/src/System/Security/Cryptography/AesGcm.Android.cs index 3e36ef16d38f02..471338a8e8a76c 100644 --- a/src/libraries/System.Security.Cryptography/src/System/Security/Cryptography/AesGcm.Android.cs +++ b/src/libraries/System.Security.Cryptography/src/System/Security/Cryptography/AesGcm.Android.cs @@ -74,11 +74,13 @@ private void EncryptCore( throw new CryptographicException(); } - if (!Interop.Crypto.EvpCipherFinalEx( + if (!Interop.Crypto.EvpAeadCipherFinalEx( _ctxHandle, ciphertextAndTag.Slice(ciphertextBytesWritten), - out int bytesWritten)) + out int bytesWritten, + out bool authTagMismatch)) { + Debug.Assert(!authTagMismatch); throw new CryptographicException(); } @@ -141,13 +143,20 @@ private void DecryptCore( plaintextBytesWritten += bytesWritten; - if (!Interop.Crypto.EvpCipherFinalEx( + if (!Interop.Crypto.EvpAeadCipherFinalEx( _ctxHandle, plaintext.Slice(plaintextBytesWritten), - out bytesWritten)) + out bytesWritten, + out bool authTagMismatch)) { CryptographicOperations.ZeroMemory(plaintext); - throw new CryptographicException(SR.Cryptography_AuthTagMismatch); + + if (authTagMismatch) + { + throw new AuthenticationTagMismatchException(); + } + + throw new CryptographicException(SR.Arg_CryptographyException); } plaintextBytesWritten += bytesWritten; diff --git a/src/libraries/System.Security.Cryptography/src/System/Security/Cryptography/AesGcm.OpenSsl.cs b/src/libraries/System.Security.Cryptography/src/System/Security/Cryptography/AesGcm.OpenSsl.cs index 1691f3357dbc2d..f11936951bc9d3 100644 --- a/src/libraries/System.Security.Cryptography/src/System/Security/Cryptography/AesGcm.OpenSsl.cs +++ b/src/libraries/System.Security.Cryptography/src/System/Security/Cryptography/AesGcm.OpenSsl.cs @@ -106,7 +106,7 @@ private void DecryptCore( out int bytesWritten)) { CryptographicOperations.ZeroMemory(plaintext); - throw new CryptographicException(SR.Cryptography_AuthTagMismatch); + throw new AuthenticationTagMismatchException(); } plaintextBytesWritten += bytesWritten; diff --git a/src/libraries/System.Security.Cryptography/src/System/Security/Cryptography/AuthenticationTagMismatchException.cs b/src/libraries/System.Security.Cryptography/src/System/Security/Cryptography/AuthenticationTagMismatchException.cs new file mode 100644 index 00000000000000..6c63045da0f130 --- /dev/null +++ b/src/libraries/System.Security.Cryptography/src/System/Security/Cryptography/AuthenticationTagMismatchException.cs @@ -0,0 +1,47 @@ +// Licensed to the .NET Foundation under one or more agreements. +// The .NET Foundation licenses this file to you under the MIT license. + +namespace System.Security.Cryptography +{ + /// + /// The exception that is thrown when a decryption operation with an authenticated cipher + /// has an authentication tag mismatch. + /// + public sealed class AuthenticationTagMismatchException : CryptographicException + { + /// + /// Initializes a new instance of the class with default + /// properties. + /// + public AuthenticationTagMismatchException() : base(SR.Cryptography_AuthTagMismatch) + { + } + + /// + /// Initializes a new instance of the class with a specified + /// error message. + /// + /// + /// The error message that explains the reason for the exception. + /// + public AuthenticationTagMismatchException(string? message) : base(message) + { + } + + /// + /// Initializes a new instance of the class with a specified + /// error message and a reference to the inner exception that is the cause of this exception. + /// + /// + /// The error message that explains the reason for the exception. + /// + /// + /// The exception that is the cause of the current exception. If the parameter is not + /// , the current exception is raised in a catch block that handles the inner exception. + /// + public AuthenticationTagMismatchException(string? message, Exception? inner) + : base(message, inner) + { + } + } +} diff --git a/src/libraries/System.Security.Cryptography/src/System/Security/Cryptography/ChaCha20Poly1305.Android.cs b/src/libraries/System.Security.Cryptography/src/System/Security/Cryptography/ChaCha20Poly1305.Android.cs index a36599202e3d1d..3c6f78d5425051 100644 --- a/src/libraries/System.Security.Cryptography/src/System/Security/Cryptography/ChaCha20Poly1305.Android.cs +++ b/src/libraries/System.Security.Cryptography/src/System/Security/Cryptography/ChaCha20Poly1305.Android.cs @@ -71,11 +71,13 @@ private void EncryptCore( throw new CryptographicException(); } - if (!Interop.Crypto.EvpCipherFinalEx( + if (!Interop.Crypto.EvpAeadCipherFinalEx( _ctxHandle, ciphertextAndTag.Slice(ciphertextBytesWritten), - out int bytesWritten)) + out int bytesWritten, + out bool authTagMismatch)) { + Debug.Assert(!authTagMismatch); throw new CryptographicException(); } @@ -133,13 +135,20 @@ private void DecryptCore( plaintextBytesWritten += bytesWritten; - if (!Interop.Crypto.EvpCipherFinalEx( + if (!Interop.Crypto.EvpAeadCipherFinalEx( _ctxHandle, plaintext.Slice(plaintextBytesWritten), - out bytesWritten)) + out bytesWritten, + out bool authTagMismatch)) { CryptographicOperations.ZeroMemory(plaintext); - throw new CryptographicException(SR.Cryptography_AuthTagMismatch); + + if (authTagMismatch) + { + throw new AuthenticationTagMismatchException(); + } + + throw new CryptographicException(SR.Arg_CryptographyException); } plaintextBytesWritten += bytesWritten; diff --git a/src/libraries/System.Security.Cryptography/src/System/Security/Cryptography/ChaCha20Poly1305.OpenSsl.cs b/src/libraries/System.Security.Cryptography/src/System/Security/Cryptography/ChaCha20Poly1305.OpenSsl.cs index 6fd211689ae82a..353f7429403228 100644 --- a/src/libraries/System.Security.Cryptography/src/System/Security/Cryptography/ChaCha20Poly1305.OpenSsl.cs +++ b/src/libraries/System.Security.Cryptography/src/System/Security/Cryptography/ChaCha20Poly1305.OpenSsl.cs @@ -106,7 +106,7 @@ private void DecryptCore( out int bytesWritten)) { CryptographicOperations.ZeroMemory(plaintext); - throw new CryptographicException(SR.Cryptography_AuthTagMismatch); + throw new AuthenticationTagMismatchException(); } plaintextBytesWritten += bytesWritten; diff --git a/src/libraries/System.Security.Cryptography/tests/AesCcmTests.cs b/src/libraries/System.Security.Cryptography/tests/AesCcmTests.cs index 1d662f9a085c85..8b68d7f1d61e3f 100644 --- a/src/libraries/System.Security.Cryptography/tests/AesCcmTests.cs +++ b/src/libraries/System.Security.Cryptography/tests/AesCcmTests.cs @@ -34,7 +34,7 @@ public static void EncryptTamperAADDecrypt(int dataLength, int additionalDataLen additionalData[0] ^= 1; byte[] decrypted = new byte[dataLength]; - Assert.Throws( + Assert.Throws( () => aesCcm.Decrypt(nonce, ciphertext, tag, decrypted, additionalData)); } } @@ -305,7 +305,7 @@ public static void InplaceEncryptTamperTagDecrypt() tag[0] ^= 1; - Assert.Throws( + Assert.Throws( () => aesCcm.Decrypt(nonce, data, tag, data)); Assert.Equal(new byte[data.Length], data); } @@ -347,7 +347,7 @@ public static void AesCcmNistTestsTamperTag(AEADTest testCase) byte[] plaintext = new byte[testCase.Plaintext.Length]; RandomNumberGenerator.Fill(plaintext); - Assert.Throws( + Assert.Throws( () => aesCcm.Decrypt(testCase.Nonce, ciphertext, tag, plaintext, testCase.AssociatedData)); Assert.Equal(new byte[plaintext.Length], plaintext); } @@ -370,7 +370,7 @@ public static void AesCcmNistTestsTamperCiphertext(AEADTest testCase) byte[] plaintext = new byte[testCase.Plaintext.Length]; RandomNumberGenerator.Fill(plaintext); - Assert.Throws( + Assert.Throws( () => aesCcm.Decrypt(testCase.Nonce, ciphertext, tag, plaintext, testCase.AssociatedData)); Assert.Equal(new byte[plaintext.Length], plaintext); } diff --git a/src/libraries/System.Security.Cryptography/tests/AesGcmTests.cs b/src/libraries/System.Security.Cryptography/tests/AesGcmTests.cs index 1a41a2beb3f83d..6ef5391e40e535 100644 --- a/src/libraries/System.Security.Cryptography/tests/AesGcmTests.cs +++ b/src/libraries/System.Security.Cryptography/tests/AesGcmTests.cs @@ -34,7 +34,7 @@ public static void EncryptTamperAADDecrypt(int dataLength, int additionalDataLen additionalData[0] ^= 1; byte[] decrypted = new byte[dataLength]; - Assert.Throws( + Assert.Throws( () => aesGcm.Decrypt(nonce, ciphertext, tag, decrypted, additionalData)); } } @@ -312,7 +312,7 @@ public static void InplaceEncryptTamperTagDecrypt() tag[0] ^= 1; - Assert.Throws( + Assert.Throws( () => aesGcm.Decrypt(nonce, data, tag, data)); Assert.Equal(new byte[data.Length], data); } @@ -354,7 +354,7 @@ public static void AesGcmNistTestsTamperTag(AEADTest testCase) byte[] plaintext = new byte[testCase.Plaintext.Length]; RandomNumberGenerator.Fill(plaintext); - Assert.Throws( + Assert.Throws( () => aesGcm.Decrypt(testCase.Nonce, ciphertext, tag, plaintext, testCase.AssociatedData)); Assert.Equal(new byte[plaintext.Length], plaintext); } @@ -377,7 +377,7 @@ public static void AesGcmNistTestsTamperCiphertext(AEADTest testCase) byte[] plaintext = new byte[testCase.Plaintext.Length]; RandomNumberGenerator.Fill(plaintext); - Assert.Throws( + Assert.Throws( () => aesGcm.Decrypt(testCase.Nonce, ciphertext, tag, plaintext, testCase.AssociatedData)); Assert.Equal(new byte[plaintext.Length], plaintext); } diff --git a/src/libraries/System.Security.Cryptography/tests/ChaCha20Poly1305Tests.cs b/src/libraries/System.Security.Cryptography/tests/ChaCha20Poly1305Tests.cs index 7d39b45922fb32..f381e17e73d190 100644 --- a/src/libraries/System.Security.Cryptography/tests/ChaCha20Poly1305Tests.cs +++ b/src/libraries/System.Security.Cryptography/tests/ChaCha20Poly1305Tests.cs @@ -35,7 +35,7 @@ public static void EncryptTamperAADDecrypt(int dataLength, int additionalDataLen additionalData[0] ^= 1; byte[] decrypted = new byte[dataLength]; - Assert.Throws( + Assert.Throws( () => chaChaPoly.Decrypt(nonce, ciphertext, tag, decrypted, additionalData)); } } @@ -271,7 +271,7 @@ public static void InplaceEncryptTamperTagDecrypt() tag[0] ^= 1; - Assert.Throws( + Assert.Throws( () => chaChaPoly.Decrypt(nonce, data, tag, data)); Assert.Equal(new byte[data.Length], data); } @@ -310,7 +310,7 @@ public static void Rfc8439TestsTamperTag(AEADTest testCase) tag[0] ^= 1; byte[] plaintext = RandomNumberGenerator.GetBytes(testCase.Plaintext.Length); - Assert.Throws( + Assert.Throws( () => chaChaPoly.Decrypt(testCase.Nonce, ciphertext, tag, plaintext, testCase.AssociatedData)); Assert.Equal(new byte[plaintext.Length], plaintext); } diff --git a/src/native/libs/System.Security.Cryptography.Native.Android/pal_cipher.c b/src/native/libs/System.Security.Cryptography.Native.Android/pal_cipher.c index 0b52b0a100a8c9..b09932392eea08 100644 --- a/src/native/libs/System.Security.Cryptography.Native.Android/pal_cipher.c +++ b/src/native/libs/System.Security.Cryptography.Native.Android/pal_cipher.c @@ -299,6 +299,48 @@ int32_t AndroidCryptoNative_CipherFinalEx(CipherCtx* ctx, uint8_t* outm, int32_t return CheckJNIExceptions(env) ? FAIL : SUCCESS; } + +int32_t AndroidCryptoNative_AeadCipherFinalEx(CipherCtx* ctx, uint8_t* outm, int32_t* outl, int32_t* authTagMismatch) +{ + if (!ctx) + return FAIL; + + abort_if_invalid_pointer_argument(outm); + abort_if_invalid_pointer_argument(outl); + abort_if_invalid_pointer_argument(authTagMismatch); + + JNIEnv* env = GetJNIEnv(); + + *outl = 0; + *authTagMismatch = 0; + + jbyteArray outBytes = (jbyteArray)(*env)->CallObjectMethod(env, ctx->cipher, g_cipherDoFinalMethod); + jthrowable ex = NULL; + + if (TryGetJNIException(env, &ex, false)) + { + if (ex == NULL) + { + return FAIL; + } + + if ((*env)->IsInstanceOf(env, ex, g_AEADBadTagExceptionClass)) + { + *authTagMismatch = 1; + } + + (*env)->DeleteLocalRef(env, ex); + return FAIL; + } + + jsize outBytesLen = (*env)->GetArrayLength(env, outBytes); + *outl = outBytesLen; + (*env)->GetByteArrayRegion(env, outBytes, 0, outBytesLen, (jbyte*) outm); + + (*env)->DeleteLocalRef(env, outBytes); + return CheckJNIExceptions(env) ? FAIL : SUCCESS; +} + int32_t AndroidCryptoNative_CipherCtxSetPadding(CipherCtx* ctx, int32_t padding) { if (!ctx) diff --git a/src/native/libs/System.Security.Cryptography.Native.Android/pal_cipher.h b/src/native/libs/System.Security.Cryptography.Native.Android/pal_cipher.h index 6b9555967a5a73..dc1bbe2211df53 100644 --- a/src/native/libs/System.Security.Cryptography.Native.Android/pal_cipher.h +++ b/src/native/libs/System.Security.Cryptography.Native.Android/pal_cipher.h @@ -36,6 +36,7 @@ PALEXPORT int32_t AndroidCryptoNative_CipherCtxSetPadding(CipherCtx* ctx, int32_ PALEXPORT int32_t AndroidCryptoNative_CipherUpdateAAD(CipherCtx* ctx, uint8_t* in, int32_t inl); PALEXPORT int32_t AndroidCryptoNative_CipherUpdate(CipherCtx* ctx, uint8_t* out, int32_t* outl, uint8_t* in, int32_t inl); PALEXPORT int32_t AndroidCryptoNative_CipherFinalEx(CipherCtx* ctx, uint8_t* outm, int32_t* outl); +PALEXPORT int32_t AndroidCryptoNative_AeadCipherFinalEx(CipherCtx* ctx, uint8_t* outm, int32_t* outl, int32_t* authTagMismatch); PALEXPORT CipherInfo* AndroidCryptoNative_Aes128Ecb(void); PALEXPORT CipherInfo* AndroidCryptoNative_Aes128Cbc(void); PALEXPORT CipherInfo* AndroidCryptoNative_Aes128Cfb8(void); diff --git a/src/native/libs/System.Security.Cryptography.Native.Android/pal_jni.c b/src/native/libs/System.Security.Cryptography.Native.Android/pal_jni.c index 7aa8b45768be96..0c4c732b44ef93 100644 --- a/src/native/libs/System.Security.Cryptography.Native.Android/pal_jni.c +++ b/src/native/libs/System.Security.Cryptography.Native.Android/pal_jni.c @@ -88,6 +88,9 @@ jclass g_sslCtxClass; jmethodID g_sslCtxGetDefaultMethod; jmethodID g_sslCtxGetDefaultSslParamsMethod; +// javax/crypto/spec/AEADBadTagException +jclass g_AEADBadTagExceptionClass; + // javax/crypto/spec/GCMParameterSpec jclass g_GCMParameterSpecClass; jmethodID g_GCMParameterSpecCtor; @@ -704,6 +707,8 @@ JNI_OnLoad(JavaVM *vm, void *reserved) g_ivPsClass = GetClassGRef(env, "javax/crypto/spec/IvParameterSpec"); g_ivPsCtor = GetMethod(env, false, g_ivPsClass, "", "([B)V"); + g_AEADBadTagExceptionClass = GetClassGRef(env, "javax/crypto/AEADBadTagException"); + g_GCMParameterSpecClass = GetClassGRef(env, "javax/crypto/spec/GCMParameterSpec"); g_GCMParameterSpecCtor = GetMethod(env, false, g_GCMParameterSpecClass, "", "(I[B)V"); diff --git a/src/native/libs/System.Security.Cryptography.Native.Android/pal_jni.h b/src/native/libs/System.Security.Cryptography.Native.Android/pal_jni.h index f8d89e2513115c..9294c0e13cb348 100644 --- a/src/native/libs/System.Security.Cryptography.Native.Android/pal_jni.h +++ b/src/native/libs/System.Security.Cryptography.Native.Android/pal_jni.h @@ -71,6 +71,9 @@ extern jmethodID g_cipherInitMethod; extern jmethodID g_cipherInit2Method; extern jmethodID g_getBlockSizeMethod; +// javax/crypto/spec/AEADBadTagException +extern jclass g_AEADBadTagExceptionClass; + // javax/crypto/spec/IvParameterSpec extern jclass g_ivPsClass; extern jmethodID g_ivPsCtor;