Java Question Converting encryption algorithm from vb.net to B4A

realblue

Member
Licensed User
Longtime User
Hi everybody;

Below code is my encyription algorithm that I use in my vb.net projects.

B4X:
Friend Shared Function EncryptPasswordMD5(ByVal plainText As String, ByVal p_strSaltValue As String) As String
        Dim strReturn As String = String.Empty
        Try
            Dim initVectorBytes As Byte()
            initVectorBytes = System.Text.Encoding.ASCII.GetBytes(m_strInitVector)

            Dim saltValueBytes As Byte()
            saltValueBytes = System.Text.Encoding.ASCII.GetBytes(p_strSaltValue)

            ' Convert our plaintext into a byte array.
            ' Let us assume that plaintext contains UTF8-encoded characters.
            Dim plainTextBytes As Byte()
            plainTextBytes = System.Text.Encoding.UTF8.GetBytes(plainText)

            ' First, we must create a password, from which the key will be derived.
            ' This password will be generated from the specified passphrase and 
            ' salt value. The password will be created using the specified hash 
            ' algorithm. Password creation can be done in several iterations.

            Dim password As Rfc2898DeriveBytes

            password = New Rfc2898DeriveBytes(m_strPassPhrase, _
            saltValueBytes, _
            m_strPasswordIterations)

            ' Use the password to generate pseudo-random bytes for the encryption
            ' key. Specify the size of the key in bytes (instead of bits).
            Dim keyBytes As Byte()
            Dim intKeySize As Integer = 0

            intKeySize = CType((m_intKeySize / 8), Integer)

            keyBytes = password.GetBytes(intKeySize)

            ' Create uninitialized Rijndael encryption object.
            Dim symmetricKey As System.Security.Cryptography.RijndaelManaged
            symmetricKey = New System.Security.Cryptography.RijndaelManaged

            ' It is reasonable to set encryption mode to Cipher Block Chaining
            ' (CBC). Use default options for other symmetric key parameters.
            symmetricKey.Mode = System.Security.Cryptography.CipherMode.CBC

            'symmetricKey.Padding = PaddingMode.Zeros


            ' Generate encryptor from the existing key bytes and initialization 
            ' vector. Key size will be defined based on the number of the key 
            ' bytes.
            Dim encryptor As System.Security.Cryptography.ICryptoTransform
            encryptor = symmetricKey.CreateEncryptor(keyBytes, initVectorBytes)

            ' Define memory stream which will be used to hold encrypted data.
            Dim memoryStream As System.IO.MemoryStream
            memoryStream = New System.IO.MemoryStream

            ' Define cryptographic stream (always use Write mode for encryption).
            Dim cryptoStream As System.Security.Cryptography.CryptoStream
            cryptoStream = New System.Security.Cryptography.CryptoStream(memoryStream, _
            encryptor, _
            System.Security.Cryptography.CryptoStreamMode.Write)
            ' Start encrypting.
            cryptoStream.Write(plainTextBytes, 0, plainTextBytes.Length)

            ' Finish encrypting.
            cryptoStream.FlushFinalBlock()

            ' Convert our encrypted data from a memory stream into a byte array.
            Dim cipherTextBytes As Byte()
            cipherTextBytes = memoryStream.ToArray()

            ' Close both streams.
            memoryStream.Close()
            cryptoStream.Close()

            ' Convert encrypted data into a base64-encoded string.
            Dim cipherText As String
            cipherText = Convert.ToBase64String(cipherTextBytes)

            ' Return encrypted string.
            strReturn = cipherText

        Catch ex As Exception
            strReturn = Nothing
        End Try

        Return strReturn

    End Function

Here are couple of example outputs:

RealBlue : mLvmg4ntx2mSewxoYrwBPA==
B4A : ZOKsIfBqR05Kr1KAzZsDuA==
c@nb3@goo6p@ssw0rd : BzEPjMg87c28cPlf5WePfhYVKHBNB0lU6jGJ0o+3V0Y=

In THIS thread there is another one is already converted.

Can anyone help me converting this one also.

Thanks in advance.
 

realblue

Member
Licensed User
Longtime User
Sorry I forgot to explain some variables

B4X:
dim m_strInitVector As String = "1234567890123456" '--- must be 16 bytes
dim p_strSaltValue As String = "12345678" 'Should be minimum 8 characters
dim m_strPassPhrase As String = "anytextstringisgoodhere"
dim m_strPasswordIterations As Integer = 2 '--- can be any number
dim m_intKeySize As Integer = 256 '--- can be 192 or 128 also
 

Erel

B4X founder
Staff member
Licensed User
Longtime User
I looked into your code. There isn't an equivalent API for Rfc2898DeriveBytes in Java. There is a code here that you can wrap and use instead: Java Rfc2898DeriveBytes · Medo's Home Page
However I do not know whether this code works properly or not.

Maybe you can share the keyBytes array between the server and the client?
 

realblue

Member
Licensed User
Longtime User
Hi Erel,

That link worked fine and it generated the same byte array with my key values. So this java code corresponds to the below part

B4X:
            Dim password As Rfc2898DeriveBytes

            password = New Rfc2898DeriveBytes(m_strPassPhrase, _
            saltValueBytes, _
            m_strPasswordIterations)

            ' Use the password to generate pseudo-random bytes for the encryption
            ' key. Specify the size of the key in bytes (instead of bits).
            Dim keyBytes As Byte()
            Dim intKeySize As Integer = 0

            intKeySize = CType((m_intKeySize / 8), Integer)

            keyBytes = password.GetBytes(intKeySize)

Still I don't know how to convert other parts and wrap this java code into B4A.
 

realblue

Member
Licensed User
Longtime User
Hi Erel,

Sorry for the delay I had to finish another project.

I tried to create rfc2898 library but somehow I cannot declare it in my B4A project. Here is the output SLC gives :

Starting step: Compiling Java code.
Completed successfully.
Starting step: Creating jar file.
Completed successfully.
Starting step: Creating XML file.
Loading source file D:\SAMSUNG\Basic4android\SimpleLibraryCompiler\rfc2898\src\com\rfc2898\Rfc2898DeriveBytes.java...
Constructing Javadoc information...
[-doclet, BADoclet]
[-docletpath, D:\SAMSUNG\Basic4android\SimpleLibraryCompiler]
[-bootclasspath, D:\SAMSUNG\android-sdk-windows\platforms\android-13\android.jar]
[-classpath, D:\SAMSUNG\Basic4android\Basic4android\Basic4android.exe\../libraries\B4AShared.jar;D:\SAMSUNG\Basic4android\Basic4android\Basic4android.exe\../libraries\Core.jar;]
[-sourcepath, src]
[-b4atarget, D:\SAMSUNG\Basic4android\Basic4android\AdditionalLibraries\rfc2898.xml]
starting....
Working with class: com.rfc2898.Rfc2898DeriveBytes
No ShortName annotation found for class: Rfc2898DeriveBytes
finish: D:\SAMSUNG\Basic4android\Basic4android\AdditionalLibraries\rfc2898.xml

Completed successfully.
*** Don't forget to refresh the libraries list in the IDE (right click and choose Refresh) ***

I noticed that there is a warning regarding shortname but if I insert the following lines
B4X:
@Version(1.0f)
@ShortName("rfc2898")

SLC does not compile and gives an error :

Starting step: Compiling Java code.
javac 1.6.0_25
D:\SAMSUNG\Basic4android\SimpleLibraryCompiler\rfc2898\src\com\rfc2898\Rfc2898DeriveBytes.java:6: class, interface, or enum expected
import java.io.UnsupportedEncodingException;
^
1 error


Error.


PS : I took the code from the link you have given and just added package com.rfc2898; line
 

realblue

Member
Licensed User
Longtime User
No must be something else. Here is the code:

B4X:
package com.rfc2898;

@Version(1.0f)
@ShortName("rfc2898")

import java.io.UnsupportedEncodingException;
import java.security.InvalidKeyException;
import java.security.NoSuchAlgorithmException;
import javax.crypto.Mac;
import javax.crypto.spec.SecretKeySpec;

/**
 * RFC 2898 password derivation compatible with .NET Rfc2898DeriveBytes class. 
 */
public class Rfc2898DeriveBytes {

    private Mac _hmacSha1;
    private byte[] _salt;
    private int _iterationCount;

    private byte[] _buffer = new byte[20];
    private int _bufferStartIndex = 0;
    private int _bufferEndIndex = 0;
    private int _block = 1;

    
    /**
     * Creates new instance.
     * @param password The password used to derive the key.
     * @param salt The key salt used to derive the key.
     * @param iterations The number of iterations for the operation.
     * @throws NoSuchAlgorithmException HmacSHA1 algorithm cannot be found.
     * @throws InvalidKeyException Salt must be 8 bytes or more. -or- Password cannot be null.
     */
    public Rfc2898DeriveBytes(byte[] password, byte[] salt, int iterations) throws NoSuchAlgorithmException, InvalidKeyException {
       if ((salt == null) || (salt.length < 8)) { throw new InvalidKeyException("Salt must be 8 bytes or more."); }
       if (password == null) { throw new InvalidKeyException("Password cannot be null."); }
        this._salt = salt;
        this._iterationCount = iterations;
        this._hmacSha1 = Mac.getInstance("HmacSHA1");
        this._hmacSha1.init(new SecretKeySpec(password, "HmacSHA1"));
    }
    
    /**
     * Creates new instance.
     * @param password The password used to derive the key.
     * @param salt The key salt used to derive the key.
     * @param iterations The number of iterations for the operation.
     * @throws NoSuchAlgorithmException HmacSHA1 algorithm cannot be found.
     * @throws InvalidKeyException Salt must be 8 bytes or more. -or- Password cannot be null.
     * @throws UnsupportedEncodingException UTF-8 encoding is not supported. 
     */
    public Rfc2898DeriveBytes(String password, byte[] salt, int iterations) throws InvalidKeyException, NoSuchAlgorithmException, UnsupportedEncodingException  {
       this(password.getBytes("UTF8"), salt, iterations);
    }

    /**
     * Creates new instance.
     * @param password The password used to derive the key.
     * @param salt The key salt used to derive the key.
     * @throws NoSuchAlgorithmException HmacSHA1 algorithm cannot be found.
     * @throws InvalidKeyException Salt must be 8 bytes or more. -or- Password cannot be null.
     * @throws UnsupportedEncodingException UTF-8 encoding is not supported. 
     */
    public Rfc2898DeriveBytes(String password, byte[] salt) throws NoSuchAlgorithmException, InvalidKeyException, UnsupportedEncodingException {
       this(password, salt, 0x3e8);
    }


    /**
     * Returns a pseudo-random key from a password, salt and iteration count.
     * @param count Number of bytes to return.
     * @return Byte array.
     */
    public byte[] getBytes(int count) {
        byte[] result = new byte[count];
        int resultOffset = 0;
        int bufferCount = this._bufferEndIndex - this._bufferStartIndex;

        if (bufferCount > 0) { //if there is some data in buffer
            if (count < bufferCount) { //if there is enough data in buffer
               System.arraycopy(this._buffer, this._bufferStartIndex, result, 0, count);
                this._bufferStartIndex += count;
                return result;
            }
            System.arraycopy(this._buffer, this._bufferStartIndex, result, 0, bufferCount);
            this._bufferStartIndex = this._bufferEndIndex = 0;
            resultOffset += bufferCount;
        }

        while (resultOffset < count) {
            int needCount = count - resultOffset;
            this._buffer = this.func();
            if (needCount > 20) { //we one (or more) additional passes
               System.arraycopy(this._buffer, 0, result, resultOffset, 20);
                resultOffset += 20;
            } else {
               System.arraycopy(this._buffer, 0, result, resultOffset, needCount);
                this._bufferStartIndex = needCount;
                this._bufferEndIndex = 20;
                return result;
            }
        }
        return result;
    }

    
    private byte[] func() {
        this._hmacSha1.update(this._salt, 0, this._salt.length);
        byte[] tempHash = this._hmacSha1.doFinal(getBytesFromInt(this._block));

        this._hmacSha1.reset();
        byte[] finalHash = tempHash;
        for (int i = 2; i <= this._iterationCount; i++) {
            tempHash = this._hmacSha1.doFinal(tempHash);
            for (int j = 0; j < 20; j++) {
                finalHash[j] = (byte)(finalHash[j] ^ tempHash[j]);
            }
        }
        if (this._block == 2147483647) {
            this._block = -2147483648;
        } else {
            this._block += 1;
        }

        return finalHash;
    }

    private static byte[] getBytesFromInt(int i) {
       return new byte[] { (byte)(i >>> 24), (byte)(i >>> 16), (byte)(i >>> 8), (byte)i };
    }
   
}

if I remove these two lines SLC compiles OK

B4X:
@Version(1.0f)
@ShortName("rfc2898")
 

Erel

B4X founder
Staff member
Licensed User
Longtime User
The annotations are tied to the class. The correct code is:
B4X:
package com.rfc2898;



import java.io.UnsupportedEncodingException;
import java.security.InvalidKeyException;
import java.security.NoSuchAlgorithmException;
import javax.crypto.Mac;
import javax.crypto.spec.SecretKeySpec;
import anywheresoftware.b4a.BA;
import anywheresoftware.b4a.BA.*;

/**
 * RFC 2898 password derivation compatible with .NET Rfc2898DeriveBytes class. 
 */
 @Version(1.0f)
@ShortName("rfc2898")
public class Rfc2898DeriveBytes {

    private Mac _hmacSha1;
    private byte[] _salt;
    private int _iterationCount;

    private byte[] _buffer = new byte[20];
    private int _bufferStartIndex = 0;
    private int _bufferEndIndex = 0;
    private int _block = 1;
 

realblue

Member
Licensed User
Longtime User
Ok, I managed to create the library. But I get only one method that is getBytes.

How do I call Rfc2898DeriveBytes method or why don't I see it? What am I doing wrong?

B4X:
   Dim sEncryptKey As String = "min8chars" 'Should be minimum 8 characters
   Dim saltValueBytes() As Byte
        saltValueBytes = sEncryptKey.GetBytes("ASCII")         
   Dim r As Rfc2898DeriveBytes
   
   r.Rfc2898DeriveBytes("somepassword",saltValueBytes,2) ' I cannot compile this line!
   Dim keyBytes() As Byte
   keyBytes = r.GetBytes(32)
 

realblue

Member
Licensed User
Longtime User
Here is the Rfc2898DeriveBytes library

Hi Erel,

I finally created the lib, will you be able to help me convert rest of the code?

Thanks in advance.
 

Attachments

  • Rfc2898DeriveBytes.zip
    2.3 KB · Views: 334

realblue

Member
Licensed User
Longtime User
Hi Erel,

My first post is the code I am trying to convert.

rfc2898 corresponds the 3rd post.
 
Top