Changing Active Directory user password from java

2019-08-06 19:01发布

I have Active Directory, with Users in it, i am trying to change a users password from a Java Program as follows:

Properties prop = new Properties();
prop.put(Context.INITIAL_CONTEXT_FACTORY,"com.sun.jndi.ldap.LdapCtxFactory");
prop.put(Context.SECURITY_AUTHENTICATION, "simple");
prop.put(Context.SECURITY_PRINCIPAL,"user1");
prop.put(Context.SECURITY_CREDENTIALS,"pass1");
prop.put(Context.SECURITY_PROTOCOL,"ADSecurityProtocol");
prop.put(Context.PROVIDER_URL, "ldap://host:389/OU=My Org,DC=domain,DC=com");
try
{
     LdapContext ctx =new InitialLdapContext(prop,null);
     String oldPassword="pass1";
     String newPassword="passnew1";
     ModificationItem[] mods = new ModificationItem[2];
     String oldQuotedPassword = "\"" + oldPassword + "\"";
     byte[] oldUnicodePassword = oldQuotedPassword.getBytes("UTF-16LE");
     String newQuotedPassword = "\"" + newPassword + "\"";
     byte[] newUnicodePassword = newQuotedPassword.getBytes("UTF-16LE");

     mods[0] = new ModificationItem(DirContext.REMOVE_ATTRIBUTE,
                   new BasicAttribute("unicodePwd", oldUnicodePassword));
     mods[1] = new ModificationItem(DirContext.ADD_ATTRIBUTE,
                   new BasicAttribute("unicodePwd", newUnicodePassword));

     String theUserName="CN="+"user1"+",OU=My Org,DC=domain,DC=com";
     // Perform the update
     ctx.modifyAttributes(theUserName, mods);
     System.out.println("Changed Password for successfully");
     ctx.close();
}
     catch (Exception e) {
          System.err.println("Problem changing password: " + e);
}

The error message i get is:

Problem changing password: javax.naming.NamingException: 
[LDAP: error code 1 - 000020D6: SvcErr: DSID-031007DB, 
problem 5012 (DIR_ERROR), data 0]; remaining name 
'CN=user1,OU=My Org,DC=domain,DC=com'

Edit 1:

Based on Suggestions i have tried this with port 636 and ldaps as well:

prop.put(Context.PROVIDER_URL, "ldap://host:636/OU=My Org,DC=domain,DC=com");  
Also tried
prop.put(Context.PROVIDER_URL, "ldaps://host:636/OU=My Org,DC=domain,DC=com");  

I am getting MalformedURLException: Invalid URI: 
Invalid URI: Org,DC=domain,DC=com] 

When i try (not sure if anything is listening on 636, it appears it is tho):

$ telnet LDAPHost 636
Escape character is '^]'.
Connection closed by foreign host.

Edit2:

Changed:
 prop.put(Context.PROVIDER_URL, "ldap://host:636/OU=My Org,DC=domain,DC=com");  
to:
 prop.put(Context.PROVIDER_URL, "ldap://host:636/OU=My%20Org,DC=domain,DC=com"); 

The error is:

javax.naming.CommunicationException: simple bind failed: host:636 
[Root exception is java.net.SocketException: Connection reset]

Probably the LDAP Server is not even listening on ssl port: 636

5条回答
祖国的老花朵
2楼-- · 2019-08-06 19:28

[The unicodePwd] attribute can be written under restricted conditions [...] In order to modify this attribute, the client must have a 128-bit Secure Socket Layer (SSL) connection to the server.

You only have a plain unsecure ldap:// connection instead of ldaps://, so that won't work according to the above restrictions.

See more details at: http://support.microsoft.com/kb/269190

查看更多
该账号已被封号
3楼-- · 2019-08-06 19:32

Basically, you need to modify the unicodePwd attribute of the user.

I use AWS Simple AD implementation and, as it is based on Samba 4, this attribute doesn't work. You need to use clearTextPassword in this case. See my blog post about it: http://blog.techutils.space/2016/02/changing-samba4-aws-simple-ad-user.html

Note: AWS Simple AD doesn't support SSL yet, so find a way to mitigate this risk.

查看更多
甜甜的少女心
4楼-- · 2019-08-06 19:40

If you are on Windows, then you can call NetUserChangePassword() function that is available inside netapi32.dll.

Using JNA, the code will be like this:

public static int changeActiveDirectoryUserPassword(String activeDirectoryIP, String username, String oldPassword, String newPassword)
{
    int status = Netapi32.INSTANCE.NetUserChangePassword(activeDirectoryIP, username, oldPassword, newPassword);

    switch(status)
    {
        case WinError.ERROR_INVALID_PARAMETER: // The parameter is incorrect
        case WinError.ERROR_ACCESS_DENIED: // The user does not have access to the requested information
        case WinError.ERROR_INVALID_PASSWORD: // The user has entered an invalid password
        case LMErr.NERR_InvalidComputer: // The computer name is invalid
        case LMErr.NERR_NotPrimary: // The operation is allowed only on the primary domain controller of the domain
        case LMErr.NERR_UserNotFound: // The user name could not be found
        case LMErr.NERR_PasswordTooShort: // The password is shorter than required. (The password could also be too
                                          // long, be too recent in its change history, not have enough unique
                                          // characters, or not meet another password policy requirement.)
        case LMErr.NERR_Success: // function succeeds
        default: return status;
    }
}

Note: Unlike the ldap approach, this approach will work even if the old password is already expired.

查看更多
成全新的幸福
5楼-- · 2019-08-06 19:41

The JVM executing the password change call needs to be trusted by the directory service provider. This means importing a certificate generated from AD into the JVM trust store.

查看更多
Explosion°爆炸
6楼-- · 2019-08-06 19:42

You have to change the attribute. Try to change unicodePwd withuserpassword

from :

 mods[0] = new ModificationItem(DirContext.REMOVE_ATTRIBUTE,
               new BasicAttribute("unicodePwd", oldUnicodePassword));
 mods[1] = new ModificationItem(DirContext.ADD_ATTRIBUTE,
               new BasicAttribute("unicodePwd", newUnicodePassword));

to :

mods[0] = new ModificationItem(DirContext.REMOVE_ATTRIBUTE,
                   new BasicAttribute("userpassword", oldUnicodePassword));
mods[1] = new ModificationItem(DirContext.ADD_ATTRIBUTE,
                   new BasicAttribute("userpassword", newUnicodePassword));
查看更多
登录 后发表回答