簡體   English   中英

使用 Java 中的 windows 憑據管理器獲取憑據,用於身份驗證進入 git 和其他服務

[英]Use the windows credential manager in Java to get credentials for authentication into git and other services

目前我們將密碼存儲在純文本文件中,以登錄 git 和其他服務。 我們最終想擺脫它並使用 windows 憑證管理器。 我們面臨的唯一問題是我似乎沒有找到解決方案 java。

我已經看過以下主題:

但對我來說,他們沒有給我明確的答案,即使用哪個 api。 如果您能幫助我訪問這個憑證管理器,那就太棒了。

我自己正在尋找一種訪問 java 中的 Windows Credential Manager 的方法,我發現Jonas 的答案中的 Microsoft 庫提供了一種簡單的方法:

CredManagerBackedCredentialStore credentialStore = new CredManagerBackedCredentialStore();
Credential cred = credentialStore.get("MyCredentialManagerKey");

我使用的依賴項是implementation("com.microsoft.alm:auth-secure-storage:0.6.4") ,生成的Credential object 允許我讀取用戶/密碼。 此外, CredManagerBackedCredentialStore object 允許將條目添加到 Windows 憑據管理器中。

奶酪......不要使用那個圖書館。

A)它壞了

B)這是不必要的復雜(XML 支持的憑證 object?srsly?)

C) 它依賴於一些易受攻擊的庫。 不要那樣對你的客戶。 只是不要。 他們被困在 Java 上已經夠糟糕了。

將這兩個文件放入您的項目中,然后像這樣使用它們:

    WinCred wc = new WinCred();
    // Create a credential
    wc.setCredential("Application Name", "Username", "Password");
    // Get a credential
    Credential cred = wc.getCredential("Application Name");
    String username = cred.username;
    String password = cred.password;

    System.out.println(username);
    System.out.println(password);
    // Delete a credential
    wc.deleteCredential("Application Name")

將此放入您的 build.gradle:

    implementation('net.java.dev.jna:jna:5.11.0')
    implementation('net.java.dev.jna:jna-platform:5.11.0')

第一個文件:

package wincred;

import com.sun.jna.LastErrorException;
import com.sun.jna.Memory;
import com.sun.jna.Native;
import com.sun.jna.Pointer;
import com.sun.jna.Structure;
import com.sun.jna.platform.win32.WinBase;
import com.sun.jna.win32.StdCallLibrary;
import com.sun.jna.win32.W32APIOptions;

import java.util.Arrays;
import java.util.Collections;
import java.util.List;

/**
 * This class exposes functions from credential manager on Windows platform
 * via JNA.
 *
 * Please refer to MSDN documentations for each method usage pattern
 */
interface CredAdvapi32 extends StdCallLibrary {
    CredAdvapi32 INSTANCE = (CredAdvapi32) Native.load("Advapi32",
            CredAdvapi32.class, W32APIOptions.UNICODE_OPTIONS);
    /**
     * CredRead flag
     */
    int CRED_FLAGS_PROMPT_NOW = 0x0002;
    int CRED_FLAGS_USERNAME_TARGET = 0x0004;

    /**
     * Type of Credential
     */
    int CRED_TYPE_GENERIC = 1;
    int CRED_TYPE_DOMAIN_PASSWORD = 2;
    int CRED_TYPE_DOMAIN_CERTIFICATE = 3;
    int CRED_TYPE_DOMAIN_VISIBLE_PASSWORD = 4;
    int CRED_TYPE_GENERIC_CERTIFICATE = 5;
    int CRED_TYPE_DOMAIN_EXTENDED = 6;
    int CRED_TYPE_MAXIMUM = 7;       // Maximum supported cred type
    int CRED_TYPE_MAXIMUM_EX = CRED_TYPE_MAXIMUM + 1000;

    /**
     * CredWrite flag
     */
    int CRED_PRESERVE_CREDENTIAL_BLOB = 0x1;

    /**
     * Values of the Credential Persist field
     */
    int CRED_PERSIST_NONE = 0;
    int CRED_PERSIST_SESSION = 1;
    int CRED_PERSIST_LOCAL_MACHINE = 2;
    int CRED_PERSIST_ENTERPRISE = 3;

    /**
     * Credential attributes
     *
     * https://msdn.microsoft.com/en-us/library/windows/desktop/aa374790(v=vs.85).aspx
     *
     * typedef struct _CREDENTIAL_ATTRIBUTE {
     *   LPTSTR Keyword;
     *   DWORD  Flags;
     *   DWORD  ValueSize;
     *   LPBYTE Value;
     * } CREDENTIAL_ATTRIBUTE, *PCREDENTIAL_ATTRIBUTE;
     *
     */
    class CREDENTIAL_ATTRIBUTE extends Structure {

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

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

        /**
         *    Name of the application-specific attribute. Names should be of the form <CompanyName>_<Name>.
         *    This member cannot be longer than CRED_MAX_STRING_LENGTH (256) characters.
         */
        public String Keyword;

        /**
         *   Identifies characteristics of the credential attribute. This member is reserved and should be originally
         *   initialized as zero and not otherwise altered to permit future enhancement.
         */
        public int Flags;

        /**
         *   Length of Value in bytes. This member cannot be larger than CRED_MAX_VALUE_SIZE (256).
         */
        public int ValueSize;

        /**
         *   Data associated with the attribute. By convention, if Value is a text string, then Value should not
         *   include the trailing zero character and should be in UNICODE.
         *
         *   Credentials are expected to be portable. The application should take care to ensure that the data in
         *   value is portable. It is the responsibility of the application to define the byte-endian and alignment
         *   of the data in Value.
         */
        public Pointer Value;
    }

    /**
     * Pointer to {@See CREDENTIAL_ATTRIBUTE} struct
     */
    class PCREDENTIAL_ATTRIBUTE extends Structure {

        @Override
        protected List<String> getFieldOrder() {
            return Collections.singletonList("credential_attribute");
        }

        public PCREDENTIAL_ATTRIBUTE() {
            super();
        }

        public PCREDENTIAL_ATTRIBUTE(byte[] data) {
            super(new Memory(data.length));
            getPointer().write(0, data, 0, data.length);
            read();
        }

        public PCREDENTIAL_ATTRIBUTE(Pointer memory) {
            super(memory);
            read();
        }

        public Pointer credential_attribute;
    }

    /**
     * The CREDENTIAL structure contains an individual credential
     *
     * https://msdn.microsoft.com/en-us/library/windows/desktop/aa374788(v=vs.85).aspx
     *
     * 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;
     */
    class CREDENTIAL extends Structure {

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

        public CREDENTIAL() {
            super();
        }

        public CREDENTIAL(final int size) {
            super(new Memory(size));
        }

        public CREDENTIAL(Pointer memory) {
            super(memory);
            read();
        }

        /**
         *   A bit member that identifies characteristics of the credential. Undefined bits should be initialized
         *   as zero and not otherwise altered to permit future enhancement.
         *
         *   See MSDN doc for all possible flags
         */
        public int Flags;

        /**
         *   The type of the credential. This member cannot be changed after the credential is created.
         *
         *   See MSDN doc for all possible types
         */
        public int Type;

        /**
         *   The name of the credential. The TargetName and Type members uniquely identify the credential.
         *   This member cannot be changed after the credential is created. Instead, the credential with the old
         *   name should be deleted and the credential with the new name created.
         *
         *   See MSDN doc for additional requirement
         */
        public String TargetName;

        /**
         *   A string comment from the user that describes this credential. This member cannot be longer than
         *   CRED_MAX_STRING_LENGTH (256) characters.
         */
        public String Comment;

        /**
         *   The time, in Coordinated Universal Time (Greenwich Mean Time), of the last modification of the credential.
         *   For write operations, the value of this member is ignored.
         */
        public WinBase.FILETIME LastWritten;

        /**
         *   The size, in bytes, of the CredentialBlob member. This member cannot be larger than
         *   CRED_MAX_CREDENTIAL_BLOB_SIZE (512) bytes.
         */
        public int CredentialBlobSize;

        /**
         *   Secret data for the credential. The CredentialBlob member can be both read and written.
         *   If the Type member is CRED_TYPE_DOMAIN_PASSWORD, this member contains the plaintext Unicode password
         *   for UserName. The CredentialBlob and CredentialBlobSize members do not include a trailing zero character.
         *   Also, for CRED_TYPE_DOMAIN_PASSWORD, this member can only be read by the authentication packages.
         *
         *   If the Type member is CRED_TYPE_DOMAIN_CERTIFICATE, this member contains the clear test
         *   Unicode PIN for UserName. The CredentialBlob and CredentialBlobSize members do not include a trailing
         *   zero character. Also, this member can only be read by the authentication packages.
         *
         *   If the Type member is CRED_TYPE_GENERIC, this member is defined by the application.
         *   Credentials are expected to be portable. Applications should ensure that the data in CredentialBlob is
         *   portable. The application defines the byte-endian and alignment of the data in CredentialBlob.
         */
        public Pointer CredentialBlob;

        /**
         *   Defines the persistence of this credential. This member can be read and written.
         *
         *   See MSDN doc for all possible values
         */
        public int Persist;

        /**
         *   The number of application-defined attributes to be associated with the credential. This member can be
         *   read and written. Its value cannot be greater than CRED_MAX_ATTRIBUTES (64).
         */
        public int AttributeCount;

        /**
         *   Application-defined attributes that are associated with the credential. This member can be read
         *   and written.
         */
        //notTODO: Need to make this into array
        public CREDENTIAL_ATTRIBUTE.ByReference Attributes;

        /**
         *   Alias for the TargetName member. This member can be read and written. It cannot be longer than
         *   CRED_MAX_STRING_LENGTH (256) characters.
         *
         *   If the credential Type is CRED_TYPE_GENERIC, this member can be non-NULL, but the credential manager
         *   ignores the member.
         */
        public String TargetAlias;

        /**
         *   The user name of the account used to connect to TargetName.
         *   If the credential Type is CRED_TYPE_DOMAIN_PASSWORD, this member can be either a DomainName\UserName
         *   or a UPN.
         *
         *   If the credential Type is CRED_TYPE_DOMAIN_CERTIFICATE, this member must be a marshaled certificate
         *   reference created by calling CredMarshalCredential with a CertCredential.
         *
         *   If the credential Type is CRED_TYPE_GENERIC, this member can be non-NULL, but the credential manager
         *   ignores the member.
         *
         *   This member cannot be longer than CRED_MAX_USERNAME_LENGTH (513) characters.
         */
        public String UserName;
    }

    /**
     *  Pointer to {@see CREDENTIAL} struct
     */
    class PCREDENTIAL extends Structure {

        @Override
        protected List<String> getFieldOrder() {
            return Collections.singletonList("credential");
        }

        public PCREDENTIAL() {
            super();
        }

        public PCREDENTIAL(byte[] data) {
            super(new Memory(data.length));
            getPointer().write(0, data, 0, data.length);
            read();
        }

        public PCREDENTIAL(Pointer memory) {
            super(memory);
            read();
        }

        public Pointer credential;
    }

    /**
     * The CredRead function reads a credential from the user's credential set.
     *
     * The credential set used is the one associated with the logon session of the current token.
     * The token must not have the user's SID disabled.
     *
     * https://msdn.microsoft.com/en-us/library/windows/desktop/aa374804(v=vs.85).aspx
     *
     * @param targetName
     *      String that contains the name of the credential to read.
     * @param type
     *      Type of the credential to read. Type must be one of the CRED_TYPE_* defined types.
     * @param flags
     *      Currently reserved and must be zero.
     * @param pcredential
     *      Out - Pointer to a single allocated block buffer to return the credential.
     *      Any pointers contained within the buffer are pointers to locations within this single allocated block.
     *      The single returned buffer must be freed by calling <code>CredFree</code>.
     *
     * @return
     *      True if CredRead succeeded, false otherwise
     *
     * @throws LastErrorException
     *      GetLastError
     */
    boolean CredRead(String targetName, int type, int flags, PCREDENTIAL pcredential) throws LastErrorException;

    /**
     * The CredWrite function creates a new credential or modifies an existing credential in the user's credential set.
     * The new credential is associated with the logon session of the current token. The token must not have the
     * user's security identifier (SID) disabled.
     *
     * https://msdn.microsoft.com/en-us/library/windows/desktop/aa375187(v=vs.85).aspx
     *
     * @param credential
     *      A CREDENTIAL structure to be written.
     * @param flags
     *      Flags that control the function's operation. The following flag is defined.
     *      CRED_PRESERVE_CREDENTIAL_BLOB:
     *          The credential BLOB from an existing credential is preserved with the same
     *          credential name and credential type. The CredentialBlobSize of the passed
     *          in Credential structure must be zero.
     *
     * @return
     *      True if CredWrite succeeded, false otherwise
     *
     * @throws LastErrorException
     *      GetLastError
     */
    boolean CredWrite(CREDENTIAL credential, int flags) throws LastErrorException;

    /**
     * The CredDelete function deletes a credential from the user's credential set. The credential set used is the one
     * associated with the logon session of the current token. The token must not have the user's SID disabled.
     *
     * https://msdn.microsoft.com/en-us/library/windows/desktop/aa374787(v=vs.85).aspx
     *
     * @param targetName
     *      String that contains the name of the credential to read.
     * @param type
     *      Type of the credential to delete. Must be one of the CRED_TYPE_* defined types. For a list of the
     *      defined types, see the Type member of the CREDENTIAL structure.
     *      If the value of this parameter is CRED_TYPE_DOMAIN_EXTENDED, this function can delete a credential that
     *      specifies a user name when there are multiple credentials for the same target. The value of the TargetName
     *      parameter must specify the user name as Target|UserName.
     * @param flags
     *      Reserved and must be zero.
     *
     * @return
     *      True if CredDelete succeeded, false otherwise
     *
     * @throws LastErrorException
     *      GetLastError
     */
    boolean CredDelete(String targetName, int type, int flags) throws LastErrorException;

    /**
     * The CredFree function frees a buffer returned by any of the credentials management functions.
     *
     * https://msdn.microsoft.com/en-us/library/windows/desktop/aa374796(v=vs.85).aspx
     *
     * @param credential
     *      Pointer to CREDENTIAL to be freed
     *
     * @throws LastErrorException
     *      GetLastError
     */
    void CredFree(Pointer credential) throws LastErrorException;
}

另一個文件:

package wincred;

import java.io.UnsupportedEncodingException;
import java.nio.charset.Charset;

import com.sun.jna.LastErrorException;
import com.sun.jna.Memory;
import com.sun.jna.Native;
import com.sun.jna.Pointer;

public class WinCred implements CredAdvapi32 {

  public static <T> T coalesce(final T maybeNullValue, final T nonNullValue) {
    return maybeNullValue == null ? nonNullValue : maybeNullValue;
  }

  public class Credential {
    public String target;
    public String username;
    public String password;

    public Credential(String target, String username, String password) {
      this.target = coalesce(target, "");
      this.username = coalesce(username, "");
      this.password = coalesce(password, "");
    }
  }

  public Credential getCredential(String target) {
    PCREDENTIAL pcredMem = new PCREDENTIAL();

    try {
    if (CredRead(target, 1, 0, pcredMem)) {
      CREDENTIAL credMem = new CREDENTIAL(pcredMem.credential);
      byte[] passwordBytes = credMem.CredentialBlob.getByteArray(0, credMem.CredentialBlobSize);

      String password = new String(passwordBytes, Charset.forName("UTF-16LE"));
      Credential cred = new WinCred.Credential(credMem.TargetName, credMem.UserName, password);
      return cred;
    } else {
      int err = Native.getLastError();
      throw new LastErrorException(err);
    }
    } finally {
      CredFree(pcredMem.credential);
    }
  }

  public boolean setCredential(String target, String userName, String password) throws UnsupportedEncodingException {
    CREDENTIAL credMem = new CREDENTIAL();

    credMem.Flags = 0;
    credMem.TargetName = target;
    credMem.Type = CRED_TYPE_GENERIC;
    credMem.UserName = userName;
    credMem.AttributeCount = 0;
    credMem.Persist = CRED_PERSIST_ENTERPRISE;
    byte[] bpassword = password.getBytes("UTF-16LE");
    credMem.CredentialBlobSize = (int) bpassword.length;
    credMem.CredentialBlob = getPointer(bpassword);
    if (!CredWrite(credMem, 0)) {
      int err = Native.getLastError();
      throw new LastErrorException(err);
    } else {
        return true;
    }
  }

  public boolean deleteCredential(String target) throws UnsupportedEncodingException {
    if (!CredDelete(target, CRED_TYPE_GENERIC, 0)) {
      int err = Native.getLastError();
      throw new LastErrorException(err);
    } else {
        return true;
    }
  }

  private static Pointer getPointer(byte[] array) {
    Pointer p = new Memory(array.length);
    p.write(0, array, 0, array.length);

    return p;
  }

  @Override
  public boolean CredRead(String targetName, int type, int flags, PCREDENTIAL pcredential) throws LastErrorException {
    synchronized (INSTANCE) {
      return INSTANCE.CredRead(targetName, type, flags, pcredential);
    }
  }

  @Override
  public boolean CredWrite(CREDENTIAL credential, int flags) throws LastErrorException {
    synchronized (INSTANCE) {
      return INSTANCE.CredWrite(credential, flags);
    }
  }

  @Override
  public boolean CredDelete(String targetName, int type, int flags) throws LastErrorException {
    synchronized (INSTANCE) {
      return INSTANCE.CredDelete(targetName, type, flags);
    }
  }

  @Override
  public void CredFree(Pointer credential) throws LastErrorException {
    synchronized (INSTANCE) {
      INSTANCE.CredFree(credential);
    }
  }
}

實際上,我們公司也出現了同樣的問題。 我們有一個 Java 客戶端應用程序在 windows 上運行,客戶要求提供密碼保存功能。 因此,我們為此求助於 windows 憑證管理器。 這么多的背景。

似乎沒有 java 庫,盡管可能有一個。 (順便說一句:您的第二個鏈接是 404)。

Thus the way we planned is to write java code using the java native interface with C accessing the windows credential manager. 但我找到了這個庫:

https://github.com/microsoft/vsts-authentication-library-for-java/

其中有一個 java class 用於訪問 windows 憑證管理器:

https://github.com/microsoft/vsts-authentication-library-for-java/blob/master/storage/src/main/java/com/microsoft/alm/storage/windows/internal/CredManagerBackedSecureStore.Z93F725A07423FE1C889F448B33D2

我知道這不是一個真正的答案......但我可能會有所幫助。 干杯

暫無
暫無

聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.

 
粵ICP備18138465號  © 2020-2024 STACKOOM.COM