通过 void** 参数作为 char[] 与 JNA 获取函数结果
Get result of function by void** parameter as char[] with JNA
TL;DR:我将什么 JNA 类型用于 void**
指针(实际上是 char**
),以及如何访问它的结果?
我正在尝试使用 this C function:
从 Java 中的 macOS Keychain 获取密码
OSStatus SecKeychainFindGenericPassword(CFTypeRef keychainOrArray,
UInt32 serviceNameLength,
const char *serviceName,
UInt32 accountNameLength,
const char *accountName,
UInt32 *passwordLength,
void * _Nullable *passwordData,
SecKeychainItemRef _Nullable *itemRef);
问题是,我不确定如何处理 passwordData
参数。我设法从 C 调用了它(参见 post 的底部)。但是,在 Java 中,我得到了 passwordData
参数的不确定且不正确的结果。这是我尝试从 Java:
调用它
import com.sun.jna.*;
import com.sun.jna.ptr.*;
public class JnaTest {
public interface Security extends Library {
int SecKeychainFindGenericPassword(
Object keychainOrArray,
int serviceNameLength,
String serviceName,
int accountNameLength,
String accountName,
IntByReference passwordLength,
// I've also tried char[][] and Pointer, which both give errors
PointerByReference passwordData,
Object itemRef);
}
public static void main(String[] args) {
Security sec = Native.loadLibrary("Security", Security.class);
PointerByReference pass = new PointerByReference();
IntByReference len = new IntByReference();
int rc = sec.SecKeychainFindGenericPassword(
null,
10, "SurfWriter",
10, "MyUserAcct",
len, pass,
null);
System.out.println(rc); // 0, good
System.out.println(len.getValue()); // 11, same as in C
// This prints Unicode characters nondeterministically; buffer overrun?
System.out.println(new String(pass.getValue().getCharArray(0, len.getValue())));
}
}
我能够编写这个 C 程序来做同样的事情,而且它工作正常,所以问题几乎肯定是在 JNA 中读取 passwordData
:
#include <Security/Security.h>
int main() {
unsigned int len;
void* pass;
int rc = SecKeychainFindGenericPassword(
NULL,
10, "SurfWriter",
10, "MyUserAcct",
&len, &pass,
NULL);
printf("%d %s\n", len, pass);
}
我无法对其进行测试(因为我目前正在使用 Windows),但是如果您尝试像这样检索密码会怎样:
String password = new String(pass.getValue().getPointer(0).getCharArray(0, len.getValue()));
这只是一个想法,为了获得更有用的答案,我需要调试代码。
还是很纳闷,为什么returns长度是11
事实证明,如果我这样做,它会起作用:
pass.getValue().getString(0);
有效。完美。不幸的是,这不是一个完整的解决方案,因为出于安全原因,我想避免将密码存储在 String
s 中。又玩了一会儿,我发现这行得通:
new String(pass.getValue().getByteArray(0, len.getValue()-4));
(-4
是因为 len
由于某种原因太大了 4;我在 Security.h
的文档中找不到任何关于此的内容。)我意识到它之前打印的 "random" 密码在后半部分只是随机的,这使我得出结论:Java uses 16-bit char
s internally,所以我得到的每个字符实际上都是两个字符,我阅读了必要的两倍。 Java 将 2 个 ASCII 字符视为一个 UTF-16 字符。 (JNA 不应该处理这个吗?)
TL;DR:我将什么 JNA 类型用于 void**
指针(实际上是 char**
),以及如何访问它的结果?
我正在尝试使用 this C function:
从 Java 中的 macOS Keychain 获取密码OSStatus SecKeychainFindGenericPassword(CFTypeRef keychainOrArray,
UInt32 serviceNameLength,
const char *serviceName,
UInt32 accountNameLength,
const char *accountName,
UInt32 *passwordLength,
void * _Nullable *passwordData,
SecKeychainItemRef _Nullable *itemRef);
问题是,我不确定如何处理 passwordData
参数。我设法从 C 调用了它(参见 post 的底部)。但是,在 Java 中,我得到了 passwordData
参数的不确定且不正确的结果。这是我尝试从 Java:
import com.sun.jna.*;
import com.sun.jna.ptr.*;
public class JnaTest {
public interface Security extends Library {
int SecKeychainFindGenericPassword(
Object keychainOrArray,
int serviceNameLength,
String serviceName,
int accountNameLength,
String accountName,
IntByReference passwordLength,
// I've also tried char[][] and Pointer, which both give errors
PointerByReference passwordData,
Object itemRef);
}
public static void main(String[] args) {
Security sec = Native.loadLibrary("Security", Security.class);
PointerByReference pass = new PointerByReference();
IntByReference len = new IntByReference();
int rc = sec.SecKeychainFindGenericPassword(
null,
10, "SurfWriter",
10, "MyUserAcct",
len, pass,
null);
System.out.println(rc); // 0, good
System.out.println(len.getValue()); // 11, same as in C
// This prints Unicode characters nondeterministically; buffer overrun?
System.out.println(new String(pass.getValue().getCharArray(0, len.getValue())));
}
}
我能够编写这个 C 程序来做同样的事情,而且它工作正常,所以问题几乎肯定是在 JNA 中读取 passwordData
:
#include <Security/Security.h>
int main() {
unsigned int len;
void* pass;
int rc = SecKeychainFindGenericPassword(
NULL,
10, "SurfWriter",
10, "MyUserAcct",
&len, &pass,
NULL);
printf("%d %s\n", len, pass);
}
我无法对其进行测试(因为我目前正在使用 Windows),但是如果您尝试像这样检索密码会怎样:
String password = new String(pass.getValue().getPointer(0).getCharArray(0, len.getValue()));
这只是一个想法,为了获得更有用的答案,我需要调试代码。
还是很纳闷,为什么returns长度是11
事实证明,如果我这样做,它会起作用:
pass.getValue().getString(0);
有效。完美。不幸的是,这不是一个完整的解决方案,因为出于安全原因,我想避免将密码存储在 String
s 中。又玩了一会儿,我发现这行得通:
new String(pass.getValue().getByteArray(0, len.getValue()-4));
(-4
是因为 len
由于某种原因太大了 4;我在 Security.h
的文档中找不到任何关于此的内容。)我意识到它之前打印的 "random" 密码在后半部分只是随机的,这使我得出结论:Java uses 16-bit char
s internally,所以我得到的每个字符实际上都是两个字符,我阅读了必要的两倍。 Java 将 2 个 ASCII 字符视为一个 UTF-16 字符。 (JNA 不应该处理这个吗?)