Search code examples
windowsbatch-filecmdpassword-generator

Password generator in cmd batch


I'm trying to create a simple password generator using batch. I know it would be much easier if I try using a real language but I need it to be in cmd batch. I need it to generate a 10-digit password that includes a minimum of 1 lowercase letter, 1 uppercase letter, 1 number and one special character.

I've found this code, but as you can see it doesn't make sure that all my restrictions apply

@Echo Off
cd %~dp0
Setlocal EnableDelayedExpansion
Set _RNDLength=10
Set _Alphanumeric=ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789~!@#$%
Set _Str=%_Alphanumeric%9876543210
echo %_Str:~18%
:_LenLoop
IF NOT "%_Str:~18%"=="" SET _Str=%_Str:~9%& SET /A _Len+=9& GOTO :_LenLoop
SET _tmp=%_Str:~9,1%
SET /A _Len=_Len+_tmp
Set _count=0
SET _RndAlphaNum=
:_loop
Set /a _count+=1
SET _RND=%Random%
Set /A _RND=_RND%%%_Len%
SET _RndAlphaNum=!_RndAlphaNum!!_Alphanumeric:~%_RND%,1!
If !_count! lss %_RNDLength% goto _loop
echo !_RndAlphaNum!

Solution

  • This problem have an inherent complication: the set of characters have different frequencies for each one of the required subsets (upcase, lowcase, digit and special), so it is less probable that a special character appears and more probable for a letter. However, even if the frequencies were equalized (repeating special characters and digits until all four subsets have the same number of characters) this don't guarantee that all four sets will appear at least one time.

    The solution below use a different approach: it manages separately each one of the subsets and creates a list of 10 numbers that indicate each subset. This way, it is possible to count the number of times that each subset appear in the list and easily force the required condition.

    @echo off
    
    setlocal
    set "set[1]=ABCDEFGHIJKLMNOPQRSTUVWXYZ"  &  set "len[1]=26"  &  set "num[1]=0"
    set "set[2]=abcdefghijklmnopqrstuvwxyz"  &  set "len[2]=26"  &  set "num[2]=0"
    set "set[3]=0123456789"                  &  set "len[3]=10"  &  set "num[3]=0"
    set "set[4]=~!@#$%%"                     &  set "len[4]=6"   &  set "num[4]=0"
    setlocal EnableDelayedExpansion
    
    rem Create a list of 10 random numbers between 1 and 4;
    rem the condition is that it must be at least one digit of each one
    
    rem Initialize the list with 10 numbers
    set "list="
    for /L %%i in (1,1,10) do (
       set /A rnd=!random! %% 4 + 1
       set "list=!list!!rnd! "
       set /A num[!rnd!]+=1
    )
    
    :checkList
    rem Check that all digits appear in the list at least one time
    set /A mul=num[1]*num[2]*num[3]*num[4]
    if %mul% neq 0 goto listOK
    
       rem Change elements in the list until fulfill the condition
    
       rem Remove first element from list
       set /A num[%list:~0,1%]-=1
       set "list=%list:~2%"
    
       rem Insert new element at end of list
       set /A rnd=%random% %% 4 + 1
       set "list=%list%%rnd% "
       set /A num[%rnd%]+=1
    
    goto checkList
    :listOK
    
    rem Generate the password with the sets indicated by the numbers in the list
    set "RndAlphaNum="
    for %%a in (%list%) do (
       set /A rnd=!random! %% len[%%a]
       for %%r in (!rnd!) do set "RndAlphaNum=!RndAlphaNum!!set[%%a]:~%%r,1!"
    )
    
    echo !RndAlphaNum!
    

    This code may be easily modified in order to generate a password of 10 characters with a given number of elements from each subset; to do that, just initialize the "num" array with the desired numbers (that must sum 10), decrement these numbers each time that a new element is inserted in the list and change the condition that the sum of all elements be zero.