diff options
Diffstat (limited to 'luni/src/main')
4 files changed, 189 insertions, 143 deletions
diff --git a/luni/src/main/java/org/apache/harmony/xnet/provider/jsse/NativeCrypto.java b/luni/src/main/java/org/apache/harmony/xnet/provider/jsse/NativeCrypto.java index ade8841..c2c2172 100644 --- a/luni/src/main/java/org/apache/harmony/xnet/provider/jsse/NativeCrypto.java +++ b/luni/src/main/java/org/apache/harmony/xnet/provider/jsse/NativeCrypto.java @@ -56,9 +56,15 @@ public final class NativeCrypto { public static native void EVP_PKEY_free(int pkey); - // --- General context handling functions (despite the names) -------------- + // --- Message digest functions -------------- - public static native int EVP_MD_CTX_create(); + public static native int EVP_get_digestbyname(String name); + + public static native int EVP_MD_size(int evp_md); + + public static native int EVP_MD_block_size(int evp_md); + + // --- Message digest context functions -------------- public static native void EVP_MD_CTX_destroy(int ctx); @@ -66,19 +72,15 @@ public final class NativeCrypto { // --- Digest handling functions ------------------------------------------- - public static native void EVP_DigestInit(int ctx, String algorithm); + public static native int EVP_DigestInit(int evp_md); public static native void EVP_DigestUpdate(int ctx, byte[] buffer, int offset, int length); public static native int EVP_DigestFinal(int ctx, byte[] hash, int offset); - public static native int EVP_MD_CTX_size(int ctx); - - public static native int EVP_MD_CTX_block_size(int ctx); - // --- Signature handling functions ---------------------------------------- - public static native void EVP_VerifyInit(int ctx, String algorithm); + public static native int EVP_VerifyInit(String algorithm); public static native void EVP_VerifyUpdate(int ctx, byte[] buffer, int offset, int length); diff --git a/luni/src/main/java/org/apache/harmony/xnet/provider/jsse/OpenSSLMessageDigestJDK.java b/luni/src/main/java/org/apache/harmony/xnet/provider/jsse/OpenSSLMessageDigestJDK.java index 3118a02..ba431bd 100644 --- a/luni/src/main/java/org/apache/harmony/xnet/provider/jsse/OpenSSLMessageDigestJDK.java +++ b/luni/src/main/java/org/apache/harmony/xnet/provider/jsse/OpenSSLMessageDigestJDK.java @@ -30,9 +30,14 @@ public class OpenSSLMessageDigestJDK extends MessageDigest implements Cloneable private int ctx; /** - * The OpenSSL version of the algorithm name for later use by reset. + * Holds the EVP_MD for the hashing algorithm, e.g. EVP_get_digestbyname("sha1"); */ - private final String openssl; + private final int evp_md; + + /** + * Holds the output size of the message digest. + */ + private final int size; /** * Holds a dummy buffer for writing single bytes to the digest. @@ -42,39 +47,22 @@ public class OpenSSLMessageDigestJDK extends MessageDigest implements Cloneable /** * Creates a new OpenSSLMessageDigest instance for the given algorithm * name. - * - * @param algorithm The standard name of the algorithm, e.g. "SHA-1". - * @param algorithm The name of the openssl algorithm, e.g. "sha1". */ - private OpenSSLMessageDigestJDK(String algorithm, String openssl) + private OpenSSLMessageDigestJDK(String algorithm, int evp_md, int size) throws NoSuchAlgorithmException { super(algorithm); - this.openssl = openssl; - - ctx = NativeCrypto.EVP_MD_CTX_create(); - try { - NativeCrypto.EVP_DigestInit(ctx, openssl); - } catch (Exception ex) { - throw new NoSuchAlgorithmException(ex.getMessage() + " (" + algorithm + ")"); - } - } - - @Override - protected byte[] engineDigest() { - byte[] result = new byte[NativeCrypto.EVP_MD_CTX_size(ctx)]; - NativeCrypto.EVP_DigestFinal(ctx, result, 0); - NativeCrypto.EVP_DigestInit(ctx, openssl); - return result; + this.evp_md = evp_md; + this.size = size; } @Override protected void engineReset() { - NativeCrypto.EVP_DigestInit(ctx, openssl); + free(); } @Override protected int engineGetDigestLength() { - return NativeCrypto.EVP_MD_CTX_size(ctx); + return size; } @Override @@ -85,51 +73,82 @@ public class OpenSSLMessageDigestJDK extends MessageDigest implements Cloneable @Override protected void engineUpdate(byte[] input, int offset, int len) { - NativeCrypto.EVP_DigestUpdate(ctx, input, offset, len); + NativeCrypto.EVP_DigestUpdate(getCtx(), input, offset, len); + } + + @Override + protected byte[] engineDigest() { + byte[] result = new byte[size]; + NativeCrypto.EVP_DigestFinal(getCtx(), result, 0); + ctx = 0; // EVP_DigestFinal frees the context as a side effect + return result; } public Object clone() throws CloneNotSupportedException { OpenSSLMessageDigestJDK d = (OpenSSLMessageDigestJDK) super.clone(); - d.ctx = NativeCrypto.EVP_MD_CTX_copy(ctx); + d.ctx = NativeCrypto.EVP_MD_CTX_copy(getCtx()); return d; } - @Override protected void finalize() throws Throwable { - try { + private int getCtx() { + if (ctx == 0) { + ctx = NativeCrypto.EVP_DigestInit(evp_md); + } + return ctx; + } + + private void free() { + if (ctx != 0) { NativeCrypto.EVP_MD_CTX_destroy(ctx); ctx = 0; + } + } + + @Override protected void finalize() throws Throwable { + try { + free(); } finally { super.finalize(); } } public static class MD5 extends OpenSSLMessageDigestJDK { + private static final int EVP_MD = NativeCrypto.EVP_get_digestbyname("md5"); + private static final int SIZE = NativeCrypto.EVP_MD_size(EVP_MD); public MD5() throws NoSuchAlgorithmException { - super("MD5", "md5"); + super("MD5",EVP_MD, SIZE); } } public static class SHA1 extends OpenSSLMessageDigestJDK { + private static final int EVP_MD = NativeCrypto.EVP_get_digestbyname("sha1"); + private static final int SIZE = NativeCrypto.EVP_MD_size(EVP_MD); public SHA1() throws NoSuchAlgorithmException { - super("SHA-1", "sha1"); + super("SHA-1", EVP_MD, SIZE); } } public static class SHA256 extends OpenSSLMessageDigestJDK { + private static final int EVP_MD = NativeCrypto.EVP_get_digestbyname("sha256"); + private static final int SIZE = NativeCrypto.EVP_MD_size(EVP_MD); public SHA256() throws NoSuchAlgorithmException { - super("SHA-256", "sha256"); + super("SHA-256", EVP_MD, SIZE); } } public static class SHA384 extends OpenSSLMessageDigestJDK { + private static final int EVP_MD = NativeCrypto.EVP_get_digestbyname("sha384"); + private static final int SIZE = NativeCrypto.EVP_MD_size(EVP_MD); public SHA384() throws NoSuchAlgorithmException { - super("SHA-384", "sha384"); + super("SHA-384", EVP_MD, SIZE); } } public static class SHA512 extends OpenSSLMessageDigestJDK { + private static final int EVP_MD = NativeCrypto.EVP_get_digestbyname("sha512"); + private static final int SIZE = NativeCrypto.EVP_MD_size(EVP_MD); public SHA512() throws NoSuchAlgorithmException { - super("SHA-512", "sha512"); + super("SHA-512", EVP_MD, SIZE); } } } diff --git a/luni/src/main/java/org/apache/harmony/xnet/provider/jsse/OpenSSLSignature.java b/luni/src/main/java/org/apache/harmony/xnet/provider/jsse/OpenSSLSignature.java index 8a460a4..451d395 100644 --- a/luni/src/main/java/org/apache/harmony/xnet/provider/jsse/OpenSSLSignature.java +++ b/luni/src/main/java/org/apache/harmony/xnet/provider/jsse/OpenSSLSignature.java @@ -80,7 +80,7 @@ public class OpenSSLSignature extends Signature { /** * Holds a pointer to the native message digest context. */ - private final int ctx; + private int ctx; /** * Holds a pointer to the native DSA key. @@ -141,7 +141,6 @@ public class OpenSSLSignature extends Signature { } this.evpAlgorithm = algorithm; - this.ctx = NativeCrypto.EVP_MD_CTX_create(); } @Override @@ -199,7 +198,7 @@ public class OpenSSLSignature extends Signature { } try { - NativeCrypto.EVP_VerifyInit(ctx, evpAlgorithm); + ctx = NativeCrypto.EVP_VerifyInit(evpAlgorithm); } catch (Exception ex) { throw new RuntimeException(ex); } diff --git a/luni/src/main/native/NativeCrypto.cpp b/luni/src/main/native/NativeCrypto.cpp index 0f9ae1d..3388acc 100644 --- a/luni/src/main/native/NativeCrypto.cpp +++ b/luni/src/main/native/NativeCrypto.cpp @@ -97,6 +97,13 @@ struct EC_KEY_Delete { }; typedef UniquePtr<EC_KEY, EC_KEY_Delete> Unique_EC_KEY; +struct EVP_MD_CTX_Delete { + void operator()(EVP_MD_CTX* p) const { + EVP_MD_CTX_destroy(p); + } +}; +typedef UniquePtr<EVP_MD_CTX, EVP_MD_CTX_Delete> Unique_EVP_MD_CTX; + struct EVP_PKEY_Delete { void operator()(EVP_PKEY* p) const { EVP_PKEY_free(p); @@ -184,18 +191,18 @@ static void freeSslErrorState(void) { * Checks this thread's OpenSSL error queue and throws a RuntimeException if * necessary. * - * @return 1 if an exception was thrown, 0 if not. + * @return true if an exception was thrown, false if not. */ -static int throwExceptionIfNecessary(JNIEnv* env, const char* location __attribute__ ((unused))) { +static bool throwExceptionIfNecessary(JNIEnv* env, const char* location __attribute__ ((unused))) { int error = ERR_get_error(); - int result = 0; + int result = false; if (error != 0) { char message[256]; ERR_error_string_n(error, message, sizeof(message)); JNI_TRACE("OpenSSL error in %s %d: %s", location, error, message); jniThrowRuntimeException(env, message); - result = 1; + result = true; } freeSslErrorState(); @@ -597,21 +604,6 @@ static void NativeCrypto_EVP_PKEY_free(JNIEnv*, jclass, EVP_PKEY* pkey) { } /* - * public static native int EVP_MD_CTX_create() - */ -static jint NativeCrypto_EVP_MD_CTX_create(JNIEnv* env, jclass) { - JNI_TRACE("NativeCrypto_EVP_MD_CTX_create"); - - EVP_MD_CTX* ctx = EVP_MD_CTX_create(); - if (ctx == NULL) { - jniThrowOutOfMemoryError(env, "Unable to allocate EVP_MD_CTX"); - } - JNI_TRACE("NativeCrypto_EVP_MD_CTX_create => %p", ctx); - return (jint) ctx; - -} - -/* * public static native void EVP_MD_CTX_destroy(int) */ static void NativeCrypto_EVP_MD_CTX_destroy(JNIEnv*, jclass, EVP_MD_CTX* ctx) { @@ -660,86 +652,107 @@ static jint NativeCrypto_EVP_DigestFinal(JNIEnv* env, jclass, EVP_MD_CTX* ctx, return -1; } - int result = -1; - ScopedByteArrayRW hashBytes(env, hash); if (hashBytes.get() == NULL) { return -1; } - EVP_DigestFinal(ctx, - reinterpret_cast<unsigned char*>(hashBytes.get() + offset), - reinterpret_cast<unsigned int*>(&result)); + unsigned int bytesWritten = -1; + int ok = EVP_DigestFinal(ctx, + reinterpret_cast<unsigned char*>(hashBytes.get() + offset), + &bytesWritten); + if (ok == 0) { + throwExceptionIfNecessary(env, "NativeCrypto_EVP_DigestFinal"); + } + EVP_MD_CTX_destroy(ctx); - throwExceptionIfNecessary(env, "NativeCrypto_EVP_DigestFinal"); + JNI_TRACE("NativeCrypto_EVP_DigestFinal(%p, %p, %d) => %d", ctx, hash, offset, bytesWritten); + return bytesWritten; +} - JNI_TRACE("NativeCrypto_EVP_DigestFinal(%p, %p, %d) => %d", ctx, hash, offset, result); - return result; +/* + * public static native int EVP_DigestInit(int) + */ +static int NativeCrypto_EVP_DigestInit(JNIEnv* env, jclass, EVP_MD* evp_md) { + JNI_TRACE("NativeCrypto_EVP_DigestInit(%d)", evp_md); + + if (evp_md == NULL) { + jniThrowNullPointerException(env, NULL); + return NULL; + } + + Unique_EVP_MD_CTX ctx(EVP_MD_CTX_create()); + if (ctx.get() == NULL) { + jniThrowOutOfMemoryError(env, "Unable to allocate EVP_MD_CTX"); + return NULL; + } + JNI_TRACE("NativeCrypto_EVP_DigestInit ctx=%p", ctx.get()); + + int ok = EVP_DigestInit(ctx.get(), evp_md); + if (ok == 0) { + bool exception = throwExceptionIfNecessary(env, "NativeCrypto_EVP_DigestInit"); + if (exception) { + return NULL; + } + } + return (jint) ctx.release(); } /* - * public static native void EVP_DigestInit(int, java.lang.String) + * public static native int EVP_get_digestbyname(java.lang.String) */ -static void NativeCrypto_EVP_DigestInit(JNIEnv* env, jclass, EVP_MD_CTX* ctx, jstring algorithm) { - JNI_TRACE("NativeCrypto_EVP_DigestInit(%p, %p)", ctx, algorithm); +static jint NativeCrypto_EVP_get_digestbyname(JNIEnv* env, jclass, jstring algorithm) { + JNI_TRACE("NativeCrypto_EVP_get_digestbyname(%p)", algorithm); - if (ctx == NULL || algorithm == NULL) { + if (algorithm == NULL) { jniThrowNullPointerException(env, NULL); - return; + return -1; } ScopedUtfChars algorithmChars(env, algorithm); if (algorithmChars.c_str() == NULL) { - return; + return NULL; } + JNI_TRACE("NativeCrypto_EVP_get_digestbyname(%s)", algorithmChars.c_str()); - JNI_TRACE("NativeCrypto_EVP_DigestInit(%p, %s)", ctx, algorithmChars.c_str()); - const EVP_MD* digest = EVP_get_digestbynid(OBJ_txt2nid(algorithmChars.c_str())); - - if (digest == NULL) { + const EVP_MD* evp_md = EVP_get_digestbyname(algorithmChars.c_str()); + if (evp_md == NULL) { jniThrowRuntimeException(env, "Hash algorithm not found"); - return; + return NULL; } - EVP_DigestInit(ctx, digest); - - throwExceptionIfNecessary(env, "NativeCrypto_EVP_DigestInit"); + JNI_TRACE("NativeCrypto_EVP_get_digestbyname(%s) => %d", algorithmChars.c_str(), result); + return (jint) evp_md; } /* - * public static native int EVP_MD_CTX_size(int) + * public static native int EVP_MD_size(int) */ -static jint NativeCrypto_EVP_MD_CTX_size(JNIEnv* env, jclass, EVP_MD_CTX* ctx) { - JNI_TRACE("NativeCrypto_EVP_MD_CTX_size(%p)", ctx); +static jint NativeCrypto_EVP_MD_size(JNIEnv* env, jclass, EVP_MD* evp_md) { + JNI_TRACE("NativeCrypto_EVP_MD_size(%p)", evp_md); - if (ctx == NULL) { + if (evp_md == NULL) { jniThrowNullPointerException(env, NULL); return -1; } - int result = EVP_MD_CTX_size(ctx); - - throwExceptionIfNecessary(env, "NativeCrypto_EVP_MD_CTX_size"); - - JNI_TRACE("NativeCrypto_EVP_MD_CTX_size(%p) => %d", ctx, result); + int result = EVP_MD_size(evp_md); + JNI_TRACE("NativeCrypto_EVP_MD_size(%p) => %d", ctx, result); return result; } /* - * public static int void EVP_MD_CTX_block_size(int) + * public static int void EVP_MD_block_size(int) */ -static jint NativeCrypto_EVP_MD_CTX_block_size(JNIEnv* env, jclass, EVP_MD_CTX* ctx) { - JNI_TRACE("NativeCrypto_EVP_MD_CTX_block_size(%p)", ctx); +static jint NativeCrypto_EVP_MD_block_size(JNIEnv* env, jclass, EVP_MD* evp_md) { + JNI_TRACE("NativeCrypto_EVP_MD_block_size(%p)", evp_md); - if (ctx == NULL) { + if (evp_md == NULL) { jniThrowNullPointerException(env, NULL); return -1; } - int result = EVP_MD_CTX_block_size(ctx); - - throwExceptionIfNecessary(env, "NativeCrypto_EVP_MD_CTX_block_size"); - - JNI_TRACE("NativeCrypto_EVP_MD_CTX_block_size(%p) => %d", ctx, result); + int result = EVP_MD_block_size(evp_md); + JNI_TRACE("NativeCrypto_EVP_MD_block_size(%p) => %d", evp_md, result); return result; } @@ -764,40 +777,52 @@ static void NativeCrypto_EVP_DigestUpdate(JNIEnv* env, jclass, EVP_MD_CTX* ctx, if (bufferBytes.get() == NULL) { return; } - EVP_DigestUpdate(ctx, - reinterpret_cast<const unsigned char*>(bufferBytes.get() + offset), - length); - - throwExceptionIfNecessary(env, "NativeCrypto_EVP_DigestUpdate"); + int ok = EVP_DigestUpdate(ctx, + reinterpret_cast<const unsigned char*>(bufferBytes.get() + offset), + length); + if (ok == 0) { + throwExceptionIfNecessary(env, "NativeCrypto_EVP_DigestUpdate"); + } } /* - * public static native void EVP_VerifyInit(int, java.lang.String) + * public static native int EVP_VerifyInit(java.lang.String) */ -static void NativeCrypto_EVP_VerifyInit(JNIEnv* env, jclass, EVP_MD_CTX* ctx, jstring algorithm) { - JNI_TRACE("NativeCrypto_EVP_VerifyInit(%p, %p)", ctx, algorithm); +static jint NativeCrypto_EVP_VerifyInit(JNIEnv* env, jclass, jstring algorithm) { + JNI_TRACE("NativeCrypto_EVP_VerifyInit(%p)", algorithm); - if (ctx == NULL || algorithm == NULL) { + if (algorithm == NULL) { jniThrowNullPointerException(env, NULL); - return; + return NULL; } + Unique_EVP_MD_CTX ctx(EVP_MD_CTX_create()); + if (ctx.get() == NULL) { + jniThrowOutOfMemoryError(env, "Unable to allocate EVP_MD_CTX"); + return NULL; + } + JNI_TRACE("NativeCrypto_EVP_VerifyInit ctx=%p", ctx.get()); + ScopedUtfChars algorithmChars(env, algorithm); if (algorithmChars.c_str() == NULL) { - return; + return NULL; } + JNI_TRACE("NativeCrypto_EVP_VerifyInit algorithmChars=%s", algorithmChars.c_str()); - JNI_TRACE("NativeCrypto_EVP_VerifyInit(%p, %s)", ctx, algorithmChars.c_str()); const EVP_MD* digest = EVP_get_digestbynid(OBJ_txt2nid(algorithmChars.c_str())); - if (digest == NULL) { jniThrowRuntimeException(env, "Hash algorithm not found"); - return; + return NULL; } - EVP_VerifyInit(ctx, digest); - - throwExceptionIfNecessary(env, "NativeCrypto_EVP_VerifyInit"); + int ok = EVP_VerifyInit(ctx.get(), digest); + if (ok == 0) { + bool exception = throwExceptionIfNecessary(env, "NativeCrypto_EVP_VerifyInit"); + if (exception) { + return NULL; + } + } + return (jint) ctx.release(); } /* @@ -816,11 +841,12 @@ static void NativeCrypto_EVP_VerifyUpdate(JNIEnv* env, jclass, EVP_MD_CTX* ctx, if (bufferBytes.get() == NULL) { return; } - EVP_VerifyUpdate(ctx, - reinterpret_cast<const unsigned char*>(bufferBytes.get() + offset), - length); - - throwExceptionIfNecessary(env, "NativeCrypto_EVP_VerifyUpdate"); + int ok = EVP_VerifyUpdate(ctx, + reinterpret_cast<const unsigned char*>(bufferBytes.get() + offset), + length); + if (ok == 0) { + throwExceptionIfNecessary(env, "NativeCrypto_EVP_VerifyUpdate"); + } } /* @@ -840,17 +866,17 @@ static int NativeCrypto_EVP_VerifyFinal(JNIEnv* env, jclass, EVP_MD_CTX* ctx, jb if (bufferBytes.get() == NULL) { return -1; } - int result = EVP_VerifyFinal(ctx, - reinterpret_cast<const unsigned char*>(bufferBytes.get() + offset), - length, - pkey); - - throwExceptionIfNecessary(env, "NativeCrypto_EVP_VerifyFinal"); - + int ok = EVP_VerifyFinal(ctx, + reinterpret_cast<const unsigned char*>(bufferBytes.get() + offset), + length, + pkey); + if (ok == 0) { + throwExceptionIfNecessary(env, "NativeCrypto_EVP_VerifyFinal"); + } JNI_TRACE("NativeCrypto_EVP_VerifyFinal(%p, %p, %d, %d, %p) => %d", - ctx, buffer, offset, length, pkey, result); + ctx, buffer, offset, length, pkey, ok); - return result; + return ok; } /** @@ -962,20 +988,20 @@ static int NativeCrypto_verifySignature(JNIEnv* env, jclass, JNI_TRACE("NativeCrypto_verifySignature algorithmChars=%s", algorithmChars.c_str()); Unique_RSA rsa(rsaCreateKey(modBytes.get(), modBytes.size(), expBytes.get(), expBytes.size())); - int result = -1; + int ok = -1; if (rsa.get() != NULL) { - result = rsaVerify(msgBytes.get(), msgBytes.size(), sigBytes.get(), sigBytes.size(), + ok = rsaVerify(msgBytes.get(), msgBytes.size(), sigBytes.get(), sigBytes.size(), algorithmChars.c_str(), rsa.get()); } - if (result == -1) { + if (ok == -1 || ok == 0) { if (!throwExceptionIfNecessary(env, "NativeCrypto_verifySignature")) { jniThrowRuntimeException(env, "Internal error during verification"); } } JNI_TRACE("NativeCrypto_verifySignature => %d", result); - return result; + return ok == 1; } static void NativeCrypto_RAND_seed(JNIEnv* env, jclass, jbyteArray seed) { @@ -3308,15 +3334,15 @@ static JNINativeMethod sNativeCryptoMethods[] = { NATIVE_METHOD(NativeCrypto, EVP_PKEY_new_DSA, "([B[B[B[B[B)I"), NATIVE_METHOD(NativeCrypto, EVP_PKEY_new_RSA, "([B[B[B[B[B)I"), NATIVE_METHOD(NativeCrypto, EVP_PKEY_free, "(I)V"), - NATIVE_METHOD(NativeCrypto, EVP_MD_CTX_create, "()I"), NATIVE_METHOD(NativeCrypto, EVP_MD_CTX_destroy, "(I)V"), NATIVE_METHOD(NativeCrypto, EVP_MD_CTX_copy, "(I)I"), NATIVE_METHOD(NativeCrypto, EVP_DigestFinal, "(I[BI)I"), - NATIVE_METHOD(NativeCrypto, EVP_DigestInit, "(ILjava/lang/String;)V"), - NATIVE_METHOD(NativeCrypto, EVP_MD_CTX_block_size, "(I)I"), - NATIVE_METHOD(NativeCrypto, EVP_MD_CTX_size, "(I)I"), + NATIVE_METHOD(NativeCrypto, EVP_DigestInit, "(I)I"), + NATIVE_METHOD(NativeCrypto, EVP_get_digestbyname, "(Ljava/lang/String;)I"), + NATIVE_METHOD(NativeCrypto, EVP_MD_block_size, "(I)I"), + NATIVE_METHOD(NativeCrypto, EVP_MD_size, "(I)I"), NATIVE_METHOD(NativeCrypto, EVP_DigestUpdate, "(I[BII)V"), - NATIVE_METHOD(NativeCrypto, EVP_VerifyInit, "(ILjava/lang/String;)V"), + NATIVE_METHOD(NativeCrypto, EVP_VerifyInit, "(Ljava/lang/String;)I"), NATIVE_METHOD(NativeCrypto, EVP_VerifyUpdate, "(I[BII)V"), NATIVE_METHOD(NativeCrypto, EVP_VerifyFinal, "(I[BIII)I"), NATIVE_METHOD(NativeCrypto, verifySignature, "([B[BLjava/lang/String;[B[B)I"), |