Showing posts with label desktop. Show all posts
Showing posts with label desktop. Show all posts

Tuesday, March 27, 2012

Getting "is not static" when trying to deploy

I'm attemping to deploy a crypto class that we use on a desktop app to SQL Server. I'll use the encryptstring/decryptstring found in this class as User Defined Functions on SQL Server. When I try to deploy the class I'm getting this error:

Error 1 Method, property or field 'EncryptString' of class 'EncryptingDecryptingOASISPWDS.Crypto' in assembly 'EncryptingDecryptingOASISPWDS' is not static. EncryptingDecryptingOASISPWDS

Here is the code:

Imports System

Imports System.Data

Imports System.Data.SqlClient

Imports System.Data.SqlTypes

Imports Microsoft.SqlServer.Server

Imports System.Security.Cryptography

Imports System.Text 'for UnicodeEncoding

Imports System.IO 'for MemoryStream, FileStream, Stream, Path

Imports System.Threading 'for Thread.Sleep

Partial Public Class Crypto

'Private Const CodeVersion As String = "01.00.00"

'class member variables

Private m_sPassPhrase As String

'class private variables

Private mbytKey() As Byte 'crypto key

Private mbytIV() As Byte 'Initialization Vector

Private mbKeyIsSet As Boolean = False

Private mksKeyNotSetException As String = "Crypto passphrase is not set."

Private mksPassphraseTooSmall As String = "PassPhrase length must be at least {0} characters."

' class constructors

Public Sub New()

'no passphrase is useful for GetHashString/ValidPassword methods

End Sub

Public Sub New(ByVal CryptoPassPhrase As String)

PassPhrase = CryptoPassPhrase

End Sub

' class public properties

'generate and store encryption key & iv from passphrase

Public Property PassPhrase() As String

Get

Return m_sPassPhrase

End Get

Set(ByVal Value As String)

Const iMinLength As Integer = -1 '-1 disables min length

m_sPassPhrase = Value.Trim

'enforce a rule on minimum length if desired here

If (Value.Length > iMinLength) Or (iMinLength = -1) Then

Dim sha2 As New SHA256Managed()

'256 bits = 32 byte key

mbytKey = sha2.ComputeHash(BytesFromString(m_sPassPhrase))

'convert to Base64 for Initialization Vector, take last 16 chars

Dim sKey As String = Convert.ToBase64String(mbytKey)

mbytIV = Encoding.ASCII.GetBytes(sKey.Remove(0, sKey.Length - 16))

mbKeyIsSet = True

sha2 = Nothing

Else

mbKeyIsSet = False

Throw New Exception(String.Format(mksPassphraseTooSmall, (iMinLength + 1).ToString))

End If

End Set

End Property

'decrypt a stream

Public Function DecryptStream(ByVal EncryptedStream As MemoryStream) As MemoryStream

If mbKeyIsSet Then

Try

'create Crypto Service Provider, set key, transform and crypto stream

Dim oCSP As New RijndaelManaged()

oCSP.Key = mbytKey

oCSP.IV = mbytIV

Dim ct As ICryptoTransform = oCSP.CreateDecryptor()

Dim cs As CryptoStream = New CryptoStream(EncryptedStream, ct, CryptoStreamMode.Read)

'get bytes from encrypted stream

Dim byteArray(EncryptedStream.Length - 1) As Byte

Dim iBytesIn As Integer = cs.Read(byteArray, 0, EncryptedStream.Length)

cs.Close()

'create and write the decrypted output stream

Dim plainStream As New MemoryStream()

plainStream.Write(byteArray, 0, iBytesIn)

Return plainStream

Catch ex As Exception

Return Stream.Null

End Try

Else

Throw New Exception(mksKeyNotSetException)

End If

End Function

'decrypt a string - wrapper without Base64 flag (True by default)

Public Function DecryptString(ByVal EncryptedString As String) As String

Return _DecryptString(EncryptedString, True)

End Function

'decrypt a string - wrapper with Base64 flag

Public Function DecryptString(ByVal EncryptedString As String, ByVal Base64 As Boolean) As String

Return _DecryptString(EncryptedString, Base64)

End Function

'encrypt a stream

Public Function EncryptStream(ByVal PlainStream As MemoryStream) As MemoryStream

Try

'open stream for encrypted data

Dim encStream As New MemoryStream()

'create Crypto Service Provider, set key, transform and crypto stream

Dim oCSP As New RijndaelManaged()

oCSP.Key = mbytKey

oCSP.IV = mbytIV

Dim ct As ICryptoTransform = oCSP.CreateEncryptor()

Dim cs As CryptoStream = New CryptoStream(encStream, ct, CryptoStreamMode.Write)

'get input stream into byte array

Dim byteArray() As Byte = PlainStream.ToArray()

'write input bytes to crypto stream and close up

cs.Write(byteArray, 0, PlainStream.Length)

cs.FlushFinalBlock()

cs.Close()

Return encStream

Catch ex As Exception

Return Stream.Null

End Try

End Function

'encrypt a string - wrapper without Base64 flag (True by default)

<Microsoft.SqlServer.Server.SqlFunction()> _

Function EncryptString(ByVal PlainText As String) As String

Return _EncryptString(PlainText, True)

End Function

''encrypt a string - wrapper with Base64 flag

<Microsoft.SqlServer.Server.SqlFunction()> _

Public Function EncryptString2(ByVal PlainText As String, ByVal Base64 As Boolean) As String

Return _EncryptString(PlainText, Base64)

End Function

'calculates the hash of InputValue, returns a string

'- SHA1 hash is always 20 bytes (160 bits)

Public Function GetHashString(ByVal InputValue As String) As String

Try

Dim inputBytes() As Byte = BytesFromString(InputValue)

Dim hashValue() As Byte = New SHA1Managed().ComputeHash(inputBytes)

Return BytesToHexString(hashValue)

Catch ex As Exception

Return String.Empty

End Try

End Function

'returns True if hash of Passphrase matches HashValue

Public Function ValidPassword(ByVal Passphrase As String, ByVal HashValue As String) As Boolean

Return (GetHashString(Passphrase) = HashValue)

End Function

'internal string decryption

Private Function _DecryptString(ByVal EncryptedString As String, ByVal Base64 As Boolean) As String

Try

'put string in byte array depending on Base64 flag

Dim byteArray() As Byte

If Base64 Then

byteArray = Convert.FromBase64String(EncryptedString)

Else

byteArray = BytesFromString(EncryptedString)

End If

'create the streams, decrypt and return a string

Dim msEnc As New MemoryStream(byteArray)

Dim msPlain As MemoryStream = DecryptStream(msEnc)

Return BytesToString(msPlain.GetBuffer)

Catch ex As Exception

Return String.Empty

End Try

End Function

'internal string encryption

Private Function _EncryptString(ByVal PlainText As String, ByVal Base64 As Boolean) As String

Try

'put string in byte array

Dim byteArray() As Byte = BytesFromString(PlainText)

'create streams and encrypt

Dim msPlain As New MemoryStream(byteArray)

Dim msEnc As MemoryStream = EncryptStream(msPlain)

'return string depending on Base64 flag

If Base64 Then

Return Convert.ToBase64String(msEnc.ToArray)

Else

Return BytesToString(msEnc.ToArray)

End If

Catch ex As Exception

Return String.Empty

End Try

End Function

'returns a Unicode byte array from a string

Private Function BytesFromString(ByVal StringValue As String) As Byte()

Return (New UnicodeEncoding()).GetBytes(StringValue)

End Function

'returns a hex string from a byte array

Private Function BytesToHexString(ByVal byteArray() As Byte) As String

Dim sb As New StringBuilder(40)

Dim bValue As Byte

For Each bValue In byteArray

sb.AppendFormat(bValue.ToString("x2").ToUpper)

Next

Return sb.ToString

End Function

'returns a Unicode string from a byte array

Private Function BytesToString(ByVal byteArray() As Byte) As String

Return (New UnicodeEncoding()).GetString(byteArray)

End Function

'Return True when the file is available for writing

'- returns False if output file locked, for example

Private Function CheckWriteAccess(ByVal FileName As String) As Boolean

'2 second delay with 10,200

Dim iCount As Integer = 0 'retry count

Const iLimit As Integer = 10 'retries

Const iDelay As Integer = 200 'msec

While (iCount < iLimit)

Try

Dim fs As FileStream

fs = New FileStream(FileName, FileMode.Append, _

FileAccess.Write, FileShare.None)

fs.Close()

Return True

Catch ex As Exception

Thread.Sleep(iDelay)

Finally

iCount += 1

End Try

End While

Return False

End Function

End Class

All CLR functions / SP used by SQL Server are required to be Static since sql server cannot instantiate a class before using it... it uses only static methods that does not requires any instance.

So you must declare

<Microsoft.SqlServer.Server.SqlFunction()> _

Static Function EncryptString(ByVal PlainText As String) As String

...


Wednesday, March 7, 2012

Get Sql Err Message 15422 when activating application role.

Running VB 2005 Express Edition and Sql Server 2005 Express Edition (SQLX).

Developing a desktop application which calls a local instance of ".\sqlexpress".

This app needs to set data base options and add/del various table columns.

When activating the application role, I get the following message:

HariCari SQL Error/s 15422 - Application roles can only be activated at the ad hoc level.

Anyone know what this message means?

I have searched SQL Server Books On-Line and been unable to find a list of Sql err numbers. Either I have missed the obvious or Books On-Line has missed the obvious.

Thanks

Gary

You cannot call sp_setapprole or sp_unsetapprole within another stored procedure. These procedures must be called directly (that's what the message means by ad hoc level).

Here's the sp_setapprole topic:

http://msdn.microsoft.com/library/default.asp?url=/library/en-us/tsqlref/ts_sp_sa-sz_6tt1.asp

It mentions in the remarks section that: "The sp_setapprole stored procedure can be executed only by direct Transact-SQL statements; it cannot be executed within another stored procedure or from within a user-defined transaction."

Thanks
Laurentiu

|||Many thanks Laurentiu!!!

Obviously, I will not continue going down a path which is not intended to work. You have save me time and agravitation.

Again, Thanks!!!

Gary|||Hi,

I have being trying to setup application roles in my application. I have been reading a couple of threads (the one with Ian), and am still a bit lost.

When you say that application roles must be called directly, how do you get the 3rd party application to to call this SP via ODBC and if you have to manualy type in the SP with the password, doesn't that allow the user (inputer) access to the database.

Please help.|||

You must make the calls directly from your application code, not by embedding them in a stored procedure and calling the stored procedure from the application code. Your application should issue the setapprole request directly, not call a procedure that calls sp_setapprole.

Thanks
Laurentiu

|||This is a snippet of C/C++ ODBC code I'm using. I have a valid statement handle allocated and is freed upon function return.

sqlrc = SQLBindParameter(hstmt, 1, SQL_PARAM_INPUT, SQL_C_CHAR, SQL_CHAR, parmlen[0], 0, app, parmlen[0], &cb[0]);
sqlrc = SQLBindParameter(hstmt, 2, SQL_PARAM_INPUT, SQL_C_CHAR, SQL_CHAR, parmlen[1], 0, pw, parmlen[1], &cb[1]);

strcpy(buff, "{call sp_setapprole (?,?)}");
sqlrc = SQLExecDirect(hstmt, buff, SQL_NTS);

I have examined the stored proc code and have noticed that it signals this error when the nest level is greater than 1. Is there some code in ODBC that creates a SP on the fly and then calls the SQLExecDirect ?

Many thanks.|||

I am not sure what you are asking for. For ODBC questions, you may want to post on the SQL Server Data Access forum.

If you want to find out how to set an application role withing your application, here is a simple C# example that does that. It assumes you have created the application role in a database named test with the following commands:

create database test

use test

create application role approle with password = 'Password)^'

In the following function, you should replace the YOURSERVERNAME string with the name of your SQL Server instance:

static void sql_test()
{
SqlConnection sqlConn = null;
String strConn = "Persist Security Info=false;";

strConn += "Server=\'" + "YOURSERVERNAME"
+ "\';Integrated Security=true;";

try
{
sqlConn = new SqlConnection(strConn);
sqlConn.Open();
}
catch (Exception e)
{
Console.WriteLine(e.Message);
return;
}

sqlConn.ChangeDatabase("test");

SqlCommand sqlCmd = new SqlCommand("select user_name()", sqlConn);
try
{
Console.WriteLine(sqlCmd.ExecuteScalar());
}
catch (Exception e)
{
Console.WriteLine(e.Message);
return;
}

sqlCmd = new SqlCommand("sp_setapprole 'approle', 'Password)^'", sqlConn);
try
{
sqlCmd.ExecuteNonQuery();
}
catch (Exception e)
{
Console.WriteLine(e.Message);
return;
}

sqlCmd = new SqlCommand("select user_name()", sqlConn);
try
{
Console.WriteLine(sqlCmd.ExecuteScalar());
}
catch (Exception e)
{
Console.WriteLine(e.Message);
return;
}
}

This should print your original context and then the context of approle, showing that approle was set.

Thanks
Laurentiu

Get Sql Err Message 15422 when activating application role.

Running VB 2005 Express Edition and Sql Server 2005 Express Edition (SQLX).

Developing a desktop application which calls a local instance of ".\sqlexpress".

This app needs to set data base options and add/del various table columns.

When activating the application role, I get the following message:

HariCari SQL Error/s 15422 - Application roles can only be activated at the ad hoc level.

Anyone know what this message means?

I have searched SQL Server Books On-Line and been unable to find a list of Sql err numbers. Either I have missed the obvious or Books On-Line has missed the obvious.

Thanks

Gary

You cannot call sp_setapprole or sp_unsetapprole within another stored procedure. These procedures must be called directly (that's what the message means by ad hoc level).

Here's the sp_setapprole topic:

http://msdn.microsoft.com/library/default.asp?url=/library/en-us/tsqlref/ts_sp_sa-sz_6tt1.asp

It mentions in the remarks section that: "The sp_setapprole stored procedure can be executed only by direct Transact-SQL statements; it cannot be executed within another stored procedure or from within a user-defined transaction."

Thanks
Laurentiu

|||Many thanks Laurentiu!!!

Obviously, I will not continue going down a path which is not intended to work. You have save me time and agravitation.

Again, Thanks!!!

Gary|||Hi,

I have being trying to setup application roles in my application. I have been reading a couple of threads (the one with Ian), and am still a bit lost.

When you say that application roles must be called directly, how do you get the 3rd party application to to call this SP via ODBC and if you have to manualy type in the SP with the password, doesn't that allow the user (inputer) access to the database.

Please help.|||

You must make the calls directly from your application code, not by embedding them in a stored procedure and calling the stored procedure from the application code. Your application should issue the setapprole request directly, not call a procedure that calls sp_setapprole.

Thanks
Laurentiu

|||This is a snippet of C/C++ ODBC code I'm using. I have a valid statement handle allocated and is freed upon function return.

sqlrc = SQLBindParameter(hstmt, 1, SQL_PARAM_INPUT, SQL_C_CHAR, SQL_CHAR, parmlen[0], 0, app, parmlen[0], &cb[0]);
sqlrc = SQLBindParameter(hstmt, 2, SQL_PARAM_INPUT, SQL_C_CHAR, SQL_CHAR, parmlen[1], 0, pw, parmlen[1], &cb[1]);

strcpy(buff, "{call sp_setapprole (?,?)}");
sqlrc = SQLExecDirect(hstmt, buff, SQL_NTS);

I have examined the stored proc code and have noticed that it signals this error when the nest level is greater than 1. Is there some code in ODBC that creates a SP on the fly and then calls the SQLExecDirect ?

Many thanks.|||

I am not sure what you are asking for. For ODBC questions, you may want to post on the SQL Server Data Access forum.

If you want to find out how to set an application role withing your application, here is a simple C# example that does that. It assumes you have created the application role in a database named test with the following commands:

create database test

use test

create application role approle with password = 'Password)^'

In the following function, you should replace the YOURSERVERNAME string with the name of your SQL Server instance:

static void sql_test()
{
SqlConnection sqlConn = null;
String strConn = "Persist Security Info=false;";

strConn += "Server=\'" + "YOURSERVERNAME"
+ "\';Integrated Security=true;";

try
{
sqlConn = new SqlConnection(strConn);
sqlConn.Open();
}
catch (Exception e)
{
Console.WriteLine(e.Message);
return;
}

sqlConn.ChangeDatabase("test");

SqlCommand sqlCmd = new SqlCommand("select user_name()", sqlConn);
try
{
Console.WriteLine(sqlCmd.ExecuteScalar());
}
catch (Exception e)
{
Console.WriteLine(e.Message);
return;
}

sqlCmd = new SqlCommand("sp_setapprole 'approle', 'Password)^'", sqlConn);
try
{
sqlCmd.ExecuteNonQuery();
}
catch (Exception e)
{
Console.WriteLine(e.Message);
return;
}

sqlCmd = new SqlCommand("select user_name()", sqlConn);
try
{
Console.WriteLine(sqlCmd.ExecuteScalar());
}
catch (Exception e)
{
Console.WriteLine(e.Message);
return;
}
}

This should print your original context and then the context of approle, showing that approle was set.

Thanks
Laurentiu