简体   繁体   English

如何在 JNA 中映射 Windows API CredWrite/CredRead?

[英]How to map Windows API CredWrite/CredRead in JNA?

I'm trying to map CredWrite/CredRead in JNA in order to store a thrid party credential used in my Java application in Windows Credential Manager (OS Windows 10).我正在尝试在 JNA 中映射 CredWrite/CredRead,以便将我的 Java 应用程序中使用的第三方凭据存储在 Windows 凭据管理器(操作系统 Windows 10)中。

Here're the original signatures in C:这是 C 中的原始签名:

// https://msdn.microsoft.com/en-us/library/aa375187(v=vs.85).aspx
BOOL CredWrite(
  _In_ PCREDENTIAL Credential,
  _In_ DWORD       Flags
);

// https://msdn.microsoft.com/en-us/library/aa374804(v=vs.85).aspx
BOOL CredRead(
  _In_  LPCTSTR     TargetName,
  _In_  DWORD       Type,
  _In_  DWORD       Flags,
  _Out_ PCREDENTIAL *Credential
);

typedef struct _CREDENTIAL {
  DWORD                 Flags;
  DWORD                 Type;
  LPTSTR                TargetName;
  LPTSTR                Comment;
  FILETIME              LastWritten;
  DWORD                 CredentialBlobSize;
  LPBYTE                CredentialBlob;
  DWORD                 Persist;
  DWORD                 AttributeCount;
  PCREDENTIAL_ATTRIBUTE Attributes;
  LPTSTR                TargetAlias;
  LPTSTR                UserName;
} CREDENTIAL, *PCREDENTIAL;

typedef struct _CREDENTIAL_ATTRIBUTE {
  LPTSTR Keyword;
  DWORD  Flags;
  DWORD  ValueSize;
  LPBYTE Value;
} CREDENTIAL_ATTRIBUTE, *PCREDENTIAL_ATTRIBUTE;

Here're my maps in Java:这是我的 Java 地图:

WinCrypt instance = (WinCrypt) Native.loadLibrary("Advapi32", WinCrypt.class, W32APIOptions.DEFAULT_OPTIONS);

public boolean CredWrite(
        CREDENTIAL.ByReference Credential,
        int Flags
        );

public boolean CredRead(
        String TargetName,
        int Type,
        int Flags,
        PointerByReference Credential
        );

public static class CREDENTIAL extends Structure {
    public int Flags;
    public int Type;
    public String TargetName;
    public String Comment;
    public FILETIME LastWritten;
    public int CredentialBlobSize;
    public byte[] CredentialBlob = new byte[128];
    public int Persist;
    public int AttributeCount;
    public CREDENTIAL_ATTRIBUTE.ByReference Attributes;
    public String TargetAlias;
    public String UserName;

    public static class ByReference extends CREDENTIAL implements Structure.ByReference {
        public ByReference() {
        }

        public ByReference(Pointer memory) {
            super(memory);                      // LINE 55
        }
    }

    public CREDENTIAL() {
        super();
    }

    public CREDENTIAL(Pointer memory) {
        super(memory); 
        read();                                 // LINE 65
    }

    @Override
    protected List<String> getFieldOrder() {
        return Arrays.asList(new String[] {
                "Flags",
                "Type",
                "TargetName",
                "Comment",
                "LastWritten",
                "CredentialBlobSize",
                "CredentialBlob",
                "Persist",
                "AttributeCount",
                "Attributes",
                "TargetAlias",
                "UserName"
        });
    }
}

public static class CREDENTIAL_ATTRIBUTE extends Structure {
    public String Keyword;
    public int Flags;
    public int ValueSize;
    public byte[] Value = new byte[128];

    public static class ByReference extends CREDENTIAL_ATTRIBUTE implements Structure.ByReference {
    }

    @Override
    protected List<String> getFieldOrder() {
        return Arrays.asList(new String[] {
                "Keyword",
                "Flags",
                "ValueSize",
                "Value"
        });
    }
}

First I tried to write a credential to Windows Credential Manager:首先,我尝试向 Windows 凭据管理器写入凭据:

String password = "passwordtest";
int cbCreds = 1 + password.length();

CREDENTIAL.ByReference credRef = new CREDENTIAL.ByReference();
credRef.Type = WinCrypt.CRED_TYPE_GENERIC;
credRef.TargetName = "TEST/account";
credRef.CredentialBlobSize = cbCreds;
credRef.CredentialBlob = password.getBytes();
credRef.Persist = WinCrypt.CRED_PERSIST_LOCAL_MACHINE;
credRef.UserName = "administrator";

boolean ok = WinCrypt.instance.CredWrite(credRef, 0);
int rc = Kernel32.INSTANCE.GetLastError();
String errMsg = Kernel32Util.formatMessage(rc);
System.out.println("CredWrite() - ok: " + ok + ", errno: " + rc + ", errmsg: " + errMsg);

Output of the try to write:尝试写入的输出:

CredWrite() - ok: false, errno: 87, errmsg: The parameter is incorrect.

Then I tried to read an existing credential from Windows Credential Manager:然后我尝试从 Windows 凭据管理器读取现有凭据:

PointerByReference pref = new PointerByReference();
boolean ok = WinCrypt.instance.CredRead("build-apps", WinCrypt.CRED_TYPE_DOMAIN_PASSWORD, 0, pref);
int rc = Kernel32.INSTANCE.GetLastError();
String errMsg = Kernel32Util.formatMessage(rc);
System.out.println("CredRead() - ok: " + ok + ", errno: " + rc + ", errmsg: " + errMsg);
CREDENTIAL cred = new CREDENTIAL.ByReference(pref.getPointer());        // LINE 44

Output of the try to read:尝试读取的输出:

CredRead() - ok: true, errno: 0, errmsg: The operation completed successfully.
Exception in thread "main" java.lang.IllegalArgumentException: Structure exceeds provided memory bounds
    at com.sun.jna.Structure.ensureAllocated(Structure.java:366)
    at com.sun.jna.Structure.ensureAllocated(Structure.java:346)
    at com.sun.jna.Structure.read(Structure.java:552)
    at com.abc.crypt.WinCrypt$CREDENTIAL.<init>(WinCrypt.java:65)
    at com.abc.crypt.WinCrypt$CREDENTIAL$ByReference.<init>(WinCrypt.java:55) 
    at com.abc.crypt.CryptTest.main(CryptTest.java:44)
Caused by: java.lang.IndexOutOfBoundsException: Bounds exceeds available space : size=8, offset=200
    at com.sun.jna.Memory.boundsCheck(Memory.java:203)
    at com.sun.jna.Memory$SharedMemory.boundsCheck(Memory.java:87)
    at com.sun.jna.Memory.share(Memory.java:131)
    at com.sun.jna.Structure.ensureAllocated(Structure.java:363)
    ... 5 more

So the try to write failed, the try to read succeeded but failed to create a CREDENTIAL object based on the output.因此尝试写入失败,尝试读取成功但未能根据输出创建 CREDENTIAL 对象。

According to the webpage of CredWrite API, the errno 87 I got in write test is the following error:根据CredWrite API的网页,我在写测试中得到的errno 87是以下错误:

ERROR_INVALID_PARAMETER ERROR_INVALID_PARAMETER

Certain fields cannot be changed in an existing credential.现有凭证中的某些字段无法更改。 This error is returned if a field does not match the value in a protected field of the existing credential.如果字段与现有凭据的受保护字段中的值不匹配,则会返回此错误。

However the value I put in CREDENTIAL instance is a new credential rather than an existing one in the Windows Credential Manager.但是,我放入 CREDENTIAL 实例的值是一个新的凭据,而不是 Windows 凭据管理器中的现有凭据。

Any suggestion or idea on how to fix/improve is appreciated.任何关于如何修复/改进的建议或想法表示赞赏。

=================================== ====================================

UPDATE AFTER APPLYING FIX:应用修复后更新:

New CredRead:新信用阅读:

public boolean CredRead(
        String TargetName,
        int Type,
        int Flags,
        CREDENTIAL.ByReference Credential
        );

Test for CredRead:测试 CredRead:

CREDENTIAL.ByReference pref = new CREDENTIAL.ByReference();
boolean ok = WinCrypt.instance.CredRead("TEST/account", WinCrypt.CRED_TYPE_GENERIC, 0, pref);
int rc = Kernel32.INSTANCE.GetLastError();
String errMsg = Kernel32Util.formatMessage(rc);
System.out.println("CredRead() - ok: " + ok + ", errno: " + rc + ", errmsg: " + errMsg);
System.out.println(String.format("Read username = '%s', password='%S' (%d bytes)\n",
        pref.UserName, pref.CredentialBlob, pref.CredentialBlobSize));

Result:结果:

CredRead() - ok: true, errno: 0, errmsg: The operation completed successfully.
Read username = 'null', password='NULL' (0 bytes)

I checked how JNA samples in contrib use ByReference on out arg and they are doing in the same way by newing a ByReference and pass to the function.我检查了 contrib 中的 JNA 样本如何在 out arg 上使用 ByReference 并且它们以相同的方式通过新建 ByReference 并传递给函数来执行。

If you look at the WIN32 definition of CredRead() , the fourth parameter is of type PCREDENTIAL* ie it's a pointer to a pointer.如果您查看CredRead()的 WIN32 定义,则第四个参数的类型为 PCREDENTIAL*,即它是指向指针的指针。 So...所以...

  • you need to pass in the address of a pointer ie a 4-byte block of memory.您需要传入一个指针的地址,即一个 4 字节的内存块。
  • Windows allocates a block of memory to hold the CREDENTIAL structure, then tells you where it is by placing the address of that new memory block in the 4-byte block you passed in. Windows 分配一块内存来保存 CREDENTIAL 结构,然后通过将新内存块的地址放置在您传入的 4 字节块中来告诉您它的位置。
  • When you dereference your original pointer (the one you passed in to CredRead() ), you get another pointer (the 4-byte block), which itself needs to be dereferenced to get to the CREDENTIAL.当您取消引用原始指针(您传递给CredRead()指针)时,您会得到另一个指针(4 字节块),它本身需要取消引用才能获得 CREDENTIAL。

Welcome to C :-)欢迎来到 C :-)

TL;DR: The CREDENTIAL class needs to be defined like this: TL;DR:CREDENTIAL 类需要像这样定义:

public static class CREDENTIAL extends Structure {
    public int Flags;
    public int Type;
    public WString TargetName;
    public WString Comment;
    public FILETIME LastWritten;
    public int CredentialBlobSize;
    public Pointer CredentialBlob; // <== discussed below
    public int Persist;
    public int AttributeCount;
    public Pointer Attributes;
    public WString TargetAlias;
    public WString UserName;
    private Pointer RawMemBlock; // <== discussed below

    public CREDENTIAL() { }

    public CREDENTIAL( Pointer ptr ) 
    { 
        // initialize ourself from the raw memory block returned to us by ADVAPI32
        super( ptr ) ; 
        RawMemBlock = ptr ; 
        read() ;
    }

    @Override
    protected void finalize()
    {    
        // clean up
        WinCrypt.INSTANCE.CredFree( RawMemBlock ) ;
    }

    @Override
    protected List<String> getFieldOrder()
    {
        return Arrays.asList( new String[] { "Flags" , "Type" , "TargetName" , "Comment" , "LastWritten" , "CredentialBlobSize" , "CredentialBlob" , "Persist" , "AttributeCount" , "Attributes" , "TargetAlias" , "UserName" } ) ;
    }
} ;

To call CredRead() , declare it like this:要调用CredRead() ,请像这样声明:

public boolean CredRead( String target , int type , int flags , PointerByReference cred ) ;

and invoke it like this:并像这样调用它:

PointerByReference pptr = new PointerByReference() ;
boolean rc = WinCrypt.INSTANCE.CredRead( target , credType , 0 , pptr ) ;
if ( ! rc )
    ... ; // handle the error
CREDENTIAL cred = new CREDENTIAL( pptr.getValue() ) ;
String userName = cred.UserName.toString() ;
String password = new String( cred.CredentialBlob.getByteArray(0,cred.CredentialBlobSize) , "UTF-16LE" ) ;

The credential blob is another block of memory allocated by Windows, so you don't need to allocate it yourself, Windows will do it, and will tell you where it is by putting its address in the CredentialBlob field.凭据 blob 是 Windows 分配的另一块内存,因此您不需要自己分配它,Windows 会自己分配,并通过将其地址放在 CredentialBlob 字段中来告诉您它的位置。

Since Windows has allocated these blocks of memory for you, and since it has no way of knowing when you will be finished with them, it's your responsibility to free them.由于 Windows 已为您分配了这些内存块,并且无法知道您何时完成它们,因此您有责任释放它们。 So, the CREDENTIAL constructor keeps a copy of the raw pointer CredRead() gave it, and calls CredFree() in the finalizer, to free that memory.因此,CREDENTIAL 构造函数保留了给它的原始指针CredRead()的副本,并在终结器中调用CredFree()以释放该内存。 CredFree() is declared like this: CredFree()声明如下:

public void CredFree( Pointer cred ) ;

To save a credential, you need to prepare the credential blob in the way that CredWrite() is expecting ie by storing a pointer to it in the CREDENTIAL.CredentialBlob field:要保存凭据,您需要按照CredWrite()期望的方式准备凭据 blob,即通过在 CREDENTIAL.CredentialBlob 字段中存储指向它的指针:

// prepare the credential blob
byte[] credBlob = password.getBytes( "UTF-16LE" ) ;
Memory credBlobMem = new Memory( credBlob.length ) ;
credBlobMem.write( 0 , credBlob , 0 , credBlob.length ) ;

// create the credential
CREDENTIAL cred = new CREDENTIAL() ;
cred.Type = CRED_TYPE_GENERIC ;
cred.TargetName = new WString( target ) ;
cred.CredentialBlobSize = (int) credBlobMem.size() ;
cred.CredentialBlob = credBlobMem ;
cred.Persist = CRED_PERSIST_LOCAL_MACHINE ;
cred.UserName = new WString( userName ) ;

// save the credential
boolean rc = WinCrypt.INSTANCE.CredWrite( cred , 0 ) ;
if ( ! rc )
    ... ; // handle the error

As an addendum, all this will run into problems if it's being run under a service account, or any other account that doesn't have a permanent profile.作为附录,如果它在服务帐户或任何其他没有永久配置文件的帐户下运行,所有这些都会遇到问题。 I needed to do this for a job being run via Task Scheduler, using a service account that didn't have interactive login rights, and what happens is:我需要为通过 Task Scheduler 运行的作业执行此操作,使用没有交互式登录权限的服务帐户,结果是:

  • I created a batch file that set the passwords, and ran it via Task Scheduler (so that it runs under the service account, and the passwords go into the correct store)我创建了一个设置密码的批处理文件,并通过任务计划程序运行它(以便它在服务帐户下运行,并且密码进入正确的存储)
  • Windows creates a temporary profile (check the Event Log) and the passwords go into that. Windows 会创建一个临时配置文件(检查事件日志)并将密码放入其中。
  • Another batch file to dump the passwords showed that they had been set successfully.另一个转储密码的批处理文件显示它们已成功设置。
  • Running the main job works, since the temporary profile is still around, but after 5 or 10 minutes, Windows deletes it, including the passwords you've set :-/, so the next time you run the main job, it fails because the passwords are no longer there.运行主作业是有效的,因为临时配置文件仍然存在,但是 5 或 10 分钟后,Windows 将其删除,包括您设置的密码:-/,因此下次运行主作业时,它会失败,因为密码不再存在。

The solution is to create a permanent profile, ideally by logging in interactively, which only needs to be done once.解决方案是创建一个永久的配置文件,理想情况下通过交互登录,只需要完成一次。 If you can't do this, it's possible to do it programmatically although you will need admin rights for this.如果您无法执行此操作,则可以通过编程方式执行此操作,尽管您需要为此具有管理员权限。

CredRead.PCREDENTIAL should be a CREDENTIAL.ByReference . CredRead.PCREDENTIAL应该是CREDENTIAL.ByReference Using PointerByReference ends up passing in a pointer to a NULL value instead of the expected pointer to CREDENTIAL struct.使用PointerByReference最终会传入一个指向 NULL 值的指针,而不是指向CREDENTIAL结构的预期指针。

CREDENTAL.CredentialBlob needs to be a Pointer or PointerType (probably Memory if you're initializing the block yourself). CREDENTAL.CredentialBlob需要是PointerPointerType (如果您自己初始化块,可能是Memory )。 Using an inline byte array shifts the entire structure by the array size, where the callee is expecting a pointer to a block of memory.使用内联字节数组会按数组大小移动整个结构,其中被调用者需要一个指向内存块的指针。

UPDATE更新

I think I misread the declaration of CredRead() .我想我误读了CredRead()的声明。

CredRead should continue to use PointerByReference . CredRead应该继续使用PointerByReference Use PointerByReference.getValue() to extract the "returned" pointer value from CredRead() in order to create a new CREDENTIALS instance based on the pointer.使用PointerByReference.getValue()CredRead()中提取“返回的”指针值,以便基于指针创建新的CREDENTIALS实例。 PointerByReference.getPointer() gives you the address of the memory allocated to hold the pointer value. PointerByReference.getPointer()为您提供分配用于保存指针值的内存地址。

public boolean CredWrite(
    CREDENTIAL Credential,
    int Flags
    );

public boolean CredRead(
    String TargetName,
    int Type,
    int Flags,
    PointerByReference pref
    );

PointerByReference pref = new PointerByReference()
CredRead(name, type, flags, pref);
creds = new Credentials(pref.getValue())

Microsoft provides an MIT-licensed Java library for accessing VSTS tokens. Microsoft 提供了一个 MIT 许可的 Java 库来访问 VSTS 令牌。 https://github.com/microsoft/vsts-authentication-library-for-java https://github.com/microsoft/vsts-authentication-library-for-java

They provide a JNA mapping to Credential Manager functions and usage here: https://github.com/microsoft/vsts-authentication-library-for-java/tree/master/storage/src/main/java/com/microsoft/alm/storage/windows/internal他们在此处提供到凭证管理器功能和用法的 JNA 映射: https : //github.com/microsoft/vsts-authentication-library-for-java/tree/master/storage/src/main/java/com/microsoft/alm /存储/窗口/内部

Very helpful if you're starting from scratch.如果您从头开始,非常有帮助。

Based on taka's answer, but with following additional considerations, I implemented a full sample.基于 taka 的回答,但考虑到以下其他因素,我实现了一个完整的示例。

Following additional corrections and aspects have been considered:已考虑以下附加更正和方面:

  • In CredReadW, the targetName must be of type WString, not String.在 CredReadW 中,targetName 必须是 WString 类型,而不是 String。 When writing data to the windows vauld using CredWriteW, also a WString was used already.使用 CredWriteW 将数据写入 windows Vault 时,也已经使用了 WString。
  • Simplification: instead of using the getFieldOrder() method, I used the annotation style.简化:我没有使用 getFieldOrder() 方法,而是使用了注释样式。
  • Reading Kernel32 GetLastError directly is not recommended, because JNA may invoke other calls that delete the former LastError.不建议直接读取 Kernel32 GetLastError,因为 JNA 可能会调用其他调用删除之前的 LastError。 As discussed in How to make GetLastError reliably work with JNA?如何使 GetLastError 可靠地与 JNA 一起工作中所述? I changed to catching the last error as exception.我改为将最后一个错误作为异常捕获。
  • As already shown by taka, there is no need to add "1+" to the credentialBlobSize - but it's more important to use the real memory or byte[] size, because the string length contains less chars because of UTF-8 encoding, than the resulting memory in UTF-16LE encoding for Windows API functions.正如 taka 已经表明的那样,没有必要在 credentialBlobSize 中添加“1+”——但使用真实内存或字节 [] 大小更重要,因为由于 UTF-8 编码,字符串长度包含的字符数少于Windows API 函数的 UTF-16LE 编码的结果内存。

Full sample: (note that it requires JNA library; I was using JNA Version 5.6.0 available at https://github.com/java-native-access/jna )完整示例:(请注意,它需要 JNA 库;我使用的是https://github.com/java-native-access/jna 上提供的 JNA 5.6.0 版)

package at.christoph-bimminger.sample;

import java.io.UnsupportedEncodingException;
import java.util.Arrays;
import java.util.List;

import com.sun.jna.LastErrorException;
import com.sun.jna.Library;
import com.sun.jna.Memory;
import com.sun.jna.Native;
import com.sun.jna.Platform;
import com.sun.jna.Pointer;
import com.sun.jna.Structure;
import com.sun.jna.Structure.FieldOrder;
import com.sun.jna.WString;
import com.sun.jna.ptr.PointerByReference;

public class Main {


    public interface WinCrypt extends Library {

        WinCrypt INSTANCE = (WinCrypt) Native.load("Advapi32", WinCrypt.class);

        boolean CredWriteW(CREDENTIAL.ByReference credentialw, int flags) throws LastErrorException;

        boolean CredReadW(WString TargetName, int Type, int Flags, PointerByReference pptr) throws LastErrorException;

        public static final class Type {
            /**
             * The credential is a generic credential. The credential will not be used by
             * any particular authentication package. The credential will be stored securely
             * but has no other significant characteristics.
             */
            final static int CRED_TYPE_GENERIC = 1;

            /**
             * The credential is a password credential and is specific to Microsoft's
             * authentication packages. The NTLM, Kerberos, and Negotiate authentication
             * packages will automatically use this credential when connecting to the named
             * target.
             */
            final static int CRED_TYPE_DOMAIN_PASSWORD = 2;

            /**
             * The credential is a certificate credential and is specific to Microsoft's
             * authentication packages. The Kerberos, Negotiate, and Schannel authentication
             * packages automatically use this credential when connecting to the named
             * target.
             * 
             */
            final static int CRED_TYPE_DOMAIN_CERTIFICATE = 3;

            /**
             * This value is no longer supported. Windows Server 2003 and Windows XP: The
             * credential is a password credential and is specific to authentication
             * packages from Microsoft. The Passport authentication package will
             * automatically use this credential when connecting to the named target.
             * 
             * Additional values will be defined in the future. Applications should be
             * written to allow for credential types they do not understand.
             * 
             */
            final static int CRED_TYPE_DOMAIN_VISIBLE_PASSWORD = 4;

            /**
             * The credential is a certificate credential that is a generic authentication
             * package. Windows Server 2008, Windows Vista, Windows Server 2003 and Windows
             * XP: This value is not supported.
             */
            final static int CRED_TYPE_GENERIC_CERTIFICATE = 5;

            /**
             * The credential is supported by extended Negotiate packages. Windows Server
             * 2008, Windows Vista, Windows Server 2003 and Windows XP: This value is not
             * supported.
             * 
             */
            final static int CRED_TYPE_DOMAIN_EXTENDED = 6;

            /**
             * The maximum number of supported credential types.Windows Server 2008, Windows
             * Vista, Windows Server 2003 and Windows XP: This value is not supported.
             * 
             */
            final static int CRED_TYPE_MAXIMUM = 7;

            final static int CRED_TYPE_MAXIMUM_EX = CRED_TYPE_MAXIMUM + 1000;
        }

        public static final class Persist {
            final static int CRED_PERSIST_SESSION = 1;
            final static int CRED_PERSIST_LOCAL_MACHINE = 2;
            final static int CRED_PERSIST_ENTERPRISE = 3;
        }

    }

    /**
     * Representation of native struct FILETIME. See
     * https://docs.microsoft.com/en-us/windows/win32/api/minwinbase/ns-minwinbase-filetime
     * 
     * @author Christoph Bimminger
     *
     */
    @FieldOrder({ "dwLowDateTime", "dwHighDateTime" })
    public static final class FILETIME extends Structure {
        public int dwLowDateTime;
        public int dwHighDateTime;
    }

    /**
     * Representation of native struct CREDENTIALW. See
     * https://docs.microsoft.com/en-us/windows/win32/api/wincred/ns-wincred-credentialw
     * 
     * @author Christoph Bimminger
     *
     */
    @FieldOrder({ "flags", "type", "targetName", "comment", "lastWritten", "credentialBlobSize", "credentialBlob",
            "persist", "attributeCount", "attributes", "targetAlias", "userName" })
    public static class CREDENTIAL extends Structure {
        public int flags;
        public int type;
        public WString targetName;
        public WString comment;
        public FILETIME lastWritten;
        public int credentialBlobSize = 256;
        public Pointer credentialBlob;
        public int persist;
        public int attributeCount;
        public CREDENTIAL_ATTRIBUTE.ByReference attributes;
        public WString targetAlias;
        public WString userName;

        public static class ByReference extends CREDENTIAL implements Structure.ByReference {
            public ByReference() {
            }

            public ByReference(Pointer memory) {
                super(memory); // LINE 55
            }
        }

        public CREDENTIAL() {
            super();
        }

        public CREDENTIAL(Pointer memory) {
            super(memory);
            read(); // LINE 65
        }

    }

    public static class CREDENTIAL_ATTRIBUTE extends Structure {
        public String Keyword;
        public int Flags;
        public int ValueSize;
        public byte[] Value = new byte[128];

        public static class ByReference extends CREDENTIAL_ATTRIBUTE implements Structure.ByReference {
        }

        @Override
        protected List<String> getFieldOrder() {
            return Arrays.asList(new String[] { "Keyword", "Flags", "ValueSize", "Value" });
        }
    }

    public static void main(String[] args) throws UnsupportedEncodingException {
        if (!Platform.isWindows())
            throw new UnsatisfiedLinkError("This sample requires a windows environment, it uses wincred.h");

        { // --- SAVE
            String password = "brillant";

            // prepare the credential blob
            byte[] credBlob = password.getBytes("UTF-16LE");
            Memory credBlobMem = new Memory(credBlob.length);
            credBlobMem.write(0, credBlob, 0, credBlob.length);

            int cbCreds = credBlob.length;

            CREDENTIAL.ByReference cred = new CREDENTIAL.ByReference();
            cred.type = WinCrypt.Type.CRED_TYPE_GENERIC;
            cred.targetName = new WString("FOO/account");
            cred.credentialBlobSize = cbCreds;
            cred.credentialBlob = credBlobMem;
            cred.persist = WinCrypt.Persist.CRED_PERSIST_LOCAL_MACHINE;
            cred.userName = new WString("paula");

            try {
                boolean ok = WinCrypt.INSTANCE.CredWriteW(cred, 0);
            } catch (LastErrorException error) {
                int rc = error.getErrorCode();
                String errMsg = error.getMessage();
                System.out.println(rc + ": " + errMsg);
                System.exit(1);

            }
        }

        ///////////////////// READ PASS

        try {
            PointerByReference pptr = new PointerByReference();
            boolean ok = WinCrypt.INSTANCE.CredReadW(new WString("FOO/account"), WinCrypt.Type.CRED_TYPE_GENERIC, 0,
                    pptr);
            CREDENTIAL cred = new CREDENTIAL(pptr.getValue());

            String password = new String(cred.credentialBlob.getByteArray(0, cred.credentialBlobSize), "UTF-16LE");

            System.out.println(password);
        } catch (LastErrorException error) {
            int rc = error.getErrorCode();
            String errMsg = error.getMessage();
            System.out.println(rc + ": " + errMsg);
            System.exit(1);

        }

    }

}

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM