sql存储过程加密和解密(MSSQL)
在网络上,看到有SQL Server 2000和SQL Server 2005 的存储过程加密和解密的方法,后来分析了其中的代码,发现它们的原理都是一样的。后来自己根据实际的应用环境,编写了两个存储过程,一个加密存储过程(sp_EncryptObject),和一个解密存储过程(sp_EncryptObject),它们可以应用于SQL Server中的储过程,函数,视图,以及触发器。
感觉这两个存储过程蛮有意思的,拿来与大家分享;如果你看过类似的,就当作重温一下也好。
用于加密的存储过程 (sp_EncryptObject) :
存储过程(sp_EncryptObject)加密的方法是在存储过程,函数,视图的“As”位置前加上“with encryption”;如果是触发器,就在“for”位置前加“with encryption”。
如果触发器是{ AFTER | INSTEAD OF} 需要修改下面代码"For"位置:
- if objectproperty(object_id(@Object),'ExecIsAfterTrigger')=0 set @Replace='As' ; else set @Replace='For ';
存储过程完成代码:
- Use master
- Go
- if object_ID('[sp_EncryptObject]') is not null
- Drop Procedure [sp_EncryptObject]
- Go
- create procedure sp_EncryptObject
- (
- @Object sysname='All'
- )
- as
- /*
- 当@Object=All的时候,对所有的函数,存储过程,视图和触发器进行加密
- 调用方法:
- 1. Execute sp_EncryptObject 'All'
- 2. Execute sp_EncryptObject 'ObjectName'
- */
- begin
- set nocount on
- if @Object <>'All'
- begin
- if not exists(select 1 from sys.objects a where a.object_id=object_id(@Object) And a.type in('P','V','TR','FN','IF','TF'))
- begin
- --SQL Server 2008
- raiserror 50001 N'无效的加密对象!加密对象必须是函数,存储过程,视图或触发器。'
- --SQL Server 2012
- --throw 50001, N'无效的加密对象!加密对象必须是函数,存储过程,视图或触发器。',1
- return
- end
- if exists(select 1 from sys.sql_modules a where a.object_id=object_id(@Object) and a.definition is null)
- begin
- --SQL Server 2008
- raiserror 50001 N'对象已经加密!'
- --SQL Server 2012
- --throw 50001, N'对象已经加密!',1
- return
- end
- end
- declare @sql nvarchar(max),@C1 nchar(1),@C2 nchar(1),@type nvarchar(50),@Replace nvarchar(50)
- set @C1=nchar(13)
- set @C2=nchar(10)
- declare cur_Object
- cursor for
- select object_name(a.object_id) As ObjectName,a.definition
- from sys.sql_modules a
- inner join sys.objects b on b.object_id=a.object_id
- and b.is_ms_shipped=0
- and not exists(select 1
- from sys.extended_properties x
- where x.major_id=b.object_id
- and x.minor_id=0
- and x.class=1
- and x.name='microsoft_database_tools_support'
- )
- where b.type in('P','V','TR','FN','IF','TF')
- and (b.name=@Object or @Object='All')
- and b.name <>'sp_EncryptObject'
- and a.definition is not null
- order by Case
- when b.type ='V' then 1
- when b.type ='TR' then 2
- when b.type in('FN','IF','TF') then 3
- else 4 end,b.create_date,b.object_id
- open cur_Object
- fetch next from cur_Object into @Object,@sql
- while @@fetch_status=0
- begin
- Begin Try
- if objectproperty(object_id(@Object),'ExecIsAfterTrigger')=0 set @Replace='As' ; else set @Replace='For ';
- if (patindex('%'+@C1+@C2+@Replace+@C1+@C2+'%',@sql)>0)
- begin
- set @sql=Replace(@sql,@C1+@C2+@Replace+@C1+@C2,@C1+@C2+'With Encryption'+@C1+@C2+@Replace+@C1+@C2)
- end
- else if(patindex('%'+@C1+@Replace+@C1+'%',@sql)>0)
- begin
- set @sql=Replace(@sql,@C1+@Replace+@C1,@C1+'With Encryption'+@C1+@Replace+@C1)
- end
- else if(patindex('%'+@C2+@Replace+@C2+'%',@sql)>0)
- begin
- set @sql=Replace(@sql,@C2+@Replace+@C2,@C2+'With Encryption'+@C2+@Replace+@C2)
- end
- else if(patindex('%'+@C2+@Replace+@C1+'%',@sql)>0)
- begin
- set @sql=Replace(@sql,@C2+@Replace+@C1,@C1+'With Encryption'+@C2+@Replace+@C1)
- end
- else if(patindex('%'+@C1+@C2+@Replace+'%',@sql)>0)
- begin
- set @sql=Replace(@sql,@C1+@C2+@Replace,@C1+@C2+'With Encryption'+@C1+@C2+@Replace)
- end
- else if(patindex('%'+@C1+@Replace+'%',@sql)>0)
- begin
- set @sql=Replace(@sql,@C1+@Replace,@C1+'With Encryption'+@C1+@Replace)
- end
- else if(patindex('%'+@C2+@Replace+'%',@sql)>0)
- begin
- set @sql=Replace(@sql,@C2+@Replace,@C2+'With Encryption'+@C2+@Replace)
- end
- set @type =
- case
- when object_id(@Object,'P')>0 then 'Proc'
- when object_id(@Object,'V')>0 then 'View'
- when object_id(@Object,'TR')>0 then 'Trigger'
- when object_id(@Object,'FN')>0 or object_id(@Object,'IF')>0 or object_id(@Object,'TF')>0 then 'Function'
- end
- set @sql=Replace(@sql,'Create '+@type,'Alter '+@type)
- Begin Transaction
- exec(@sql)
- print N'已完成加密对象('+@type+'):'+@Object
- Commit Transaction
- End Try
- Begin Catch
- Declare @Error nvarchar(2047)
- Set @Error='Object: '+@Object+@C1+@C2+'Error: '+Error_message()
- Rollback Transaction
- print @Error
- print @sql
- End Catch
- fetch next from cur_Object into @Object,@sql
- end
- close cur_Object
- deallocate cur_Object
- end
- Go
- exec sp_ms_marksystemobject 'sp_EncryptObject' --标识为系统对象
- go
如果SQL Server 2012,请修改下面两个位置的代码。在SQL Server 2012,建议在使用throw来代替raiserror。
解密方法:
解密过程,最重要采用异或方法:
[字符1]经过函数 fn_x(x)加密变成[加密后字符1],如果我们已知[加密后字符1],反过来查[字符1],可以这样:
[字符1] = [字符2] ^ fn_x([字符2]) ^ [加密后字符1]
这里我列举一个简单的例子:
- --创建加密函数(fn_x)
- if object_id('fn_x') is not null drop function fn_x
- go
- create function fn_x
- (
- @x nchar(1)
- )returns nchar(1)
- as
- begin
- return(nchar((65535-unicode(@x))))
- end
- go
- declare @nchar_1_encrypt nchar(1),@nchar_2 nchar(1)
- --对字符'A'进行加密,存入变量@nchar_1_encrypt
- set @nchar_1_encrypt=dbo.fn_x(N'A')
- --參考的字符@nchar_2
- set @nchar_2='x'
- --算出@nchar_1_encrypt 加密前的字符
- select nchar(unicode(@nchar_2)^unicode(dbo.fn_x(@nchar_2))^unicode(@nchar_1_encrypt)) as [@nchar_1]
- /*
- @nchar_1
- --------------------
- A
- */
[注]: 从SQL Server 2000至 SQL Server 2012 采用异或方法都可以解密
用于解密的存储过程(sp_DecryptObject):
- Use master
- Go
- if object_ID('[sp_DecryptObject]') is not null
- Drop Procedure [sp_DecryptObject]
- Go
- create procedure sp_DecryptObject
- (
- @Object sysname, --要解密的对象名:函数,存储过程,视图或触发器
- @MaxLength int=4000 --评估内容的长度
- )
- as
- set nocount on
- /* 1. 解密 */
- if not exists(select 1 from sys.objects a where a.object_id=object_id(@Object) And a.type in('P','V','TR','FN','IF','TF'))
- begin
- --SQL Server 2008
- raiserror 50001 N'无效的对象!要解密的对象必须是函数,存储过程,视图或触发器。'
- --SQL Server 2012
- --throw 50001, N'无效的对象!要解密的对象必须是函数,存储过程,视图或触发器。',1
- return
- end
- if exists(select 1 from sys.sql_modules a where a.object_id=object_id(@Object) and a.definition is not null)
- begin
- --SQL Server 2008
- raiserror 50001 N'对象没有加密!'
- --SQL Server 2012
- --throw 50001, N'无效的对象!要解密的对象必须是函数,存储过程,视图或触发器。',1
- return
- end
- declare @sql nvarchar(max) --解密出来的SQL语句
- ,@imageval nvarchar(max) --加密字符串
- ,@tmpStr nvarchar(max) --临时SQL语句
- ,@tmpStr_imageval nvarchar(max) --临时SQL语句(加密后)
- ,@type char(2) --对象类型('P','V','TR','FN','IF','TF')
- ,@objectID int --对象ID
- ,@i int --While循环使用
- ,@Oject1 nvarchar(1000)
- set @objectID=object_id(@Object)
- set @type=(select a.type from sys.objects a where a.object_id=@objectID)
- declare @Space4000 nchar(4000)
- set @Space4000=replicate('-',4000)
- /*
- @tmpStr 会构造下面的SQL语句
- -------------------------------------------------------------------------------
- alter trigger Tr_Name on Table_Name with encryption for update as return /**/
- alter proc Proc_Name with encryption as select 1 as col /**/
- alter view View_Name with encryption as select 1 as col /**/
- alter function Fn_Name() returns int with encryption as begin return(0) end/**/
- */
- set @Oject1=quotename(object_schema_name(@objectID))+'.'+quotename(@Object)
- set @tmpStr=
- case
- when @type ='P ' then N'Alter Procedure '+@Oject1+' with encryption as select 1 as column1 '
- when @type ='V ' then N'Alter View '+@Oject1+' with encryption as select 1 as column1 '
- when @type ='FN' then N'Alter Function '+@Oject1+'() returns int with encryption as begin return(0) end '
- when @type ='IF' then N'Alter Function '+@Oject1+'() returns table with encryption as return(Select a.name from sys.types a) '
- when @type ='TF' then N'Alter Function '+@Oject1+'() returns @t table(name nvarchar(50)) with encryption as begin return end '
- else 'Alter Trigger '+@Oject1+'on '+quotename(object_schema_name(@objectID))+'.'+(select Top(1) quotename(object_name(parent_id)) from sys.triggers a where a.object_id=@objectID)+' with encryption for update as return '
- end
- set @tmpStr=@tmpStr+'/*'+@Space4000
- set @i=0
- while @i < (ceiling(@MaxLength*1.0/4000)-1)
- begin
- set @tmpStr=@tmpStr+ @Space4000
- Set @i=@i+1
- end
- set @tmpStr=@tmpStr+'*/'
- ------------
- set @imageval =(select top(1) a.imageval from sys.sysobjvalues a where a.objid=@objectID and a.valclass=1)
- begin tran
- exec(@tmpStr)
- set @tmpStr_imageval =(select top(1) a.imageval from sys.sysobjvalues a where a.objid=@objectID and a.valclass=1)
- rollback tran
- -------------
- set @tmpStr=stuff(@tmpStr,1,5,'create')
- set @sql=''
- set @i=1
- while @i<= (datalength(@imageval)/2)
- begin
- set @sql=@sql+isnull(nchar(unicode(substring(@tmpStr,@i,1)) ^ unicode(substring(@tmpStr_imageval,@i,1))^unicode(substring(@imageval,@i,1)) ),'')
- Set @i+=1
- end
- /* 2. 列印 */
- declare @patindex int
- while @sql>''
- begin
- set @patindex=patindex('%'+char(13)+char(10)+'%',@sql)
- if @patindex >0
- begin
- print substring(@sql,1,@patindex-1)
- set @sql=stuff(@sql,1,@patindex+1,'')
- end
- else
- begin
- set @patindex=patindex('%'+char(13)+'%',@sql)
- if @patindex >0
- begin
- print substring(@sql,1,@patindex-1)
- set @sql=stuff(@sql,1,@patindex,'')
- end
- else
- begin
- set @patindex=patindex('%'+char(10)+'%',@sql)
- if @patindex >0
- begin
- print substring(@sql,1,@patindex-1)
- set @sql=stuff(@sql,1,@patindex,'')
- end
- else
- begin
- print @sql
- set @sql=''
- end
- end
- end
- end
- Go
- exec sp_ms_marksystemobject 'sp_DecryptObject' --标识为系统对象
- go
如果SQL Server 2012,请修改下面两个位置的代码。方法类似于前面的加密过程:
搭建测试环境:
在一个测试环境中(DB: Test),先执行上面的加密存储过程(sp_EncryptObject)和解密存储过程(sp_EncryptObject);再创建两个表:TableA & TableB
- use test
- go
- --创建表: TableA & TableB
- if object_id('myTableA') is not null drop table myTableA
- if object_id('myTableB') is not null drop table myTableB
- go
- create table myTableA (ID int identity,data nvarchar(50),constraint PK_myTableA primary key(ID))
- create table myTableB (ID int ,data nvarchar(50),constraint PK_myTableB primary key(ID))
- go
接下来,我们要创建6个未加密的对象(对象类型包含 'P','V','TR','FN','IF','TF'):
1.视图(myView):
- if object_id('myView') is not null drop view myView
- go
- create view myView
- As
- select * from TableA;
- go
2.触发器(MyTrigger):
- if object_id('MyTrigger') is not null drop Trigger MyTrigger
- go
- create trigger MyTrigger
- on TableA
- for update
- As
- insert into TableB(ID,data) select a.ID,a.Data From Inserted a
- go
3.存储过程(MyProc):
- if object_id('MyProc') is not null drop proc MyProc
- go
- create proc MyProc
- (
- @data nvarchar(50)
- )
- As
- insert into TableA(data) values(@data)
- go
4.用户定义表值函数(TF)(MyFunction_TF):
- if object_id('MyFunction_TF') is not null drop function MyFunction_TF
- go
- create function MyFunction_TF
- (
- )
- returns @t table
- (
- id int,
- data nvarchar(50)
- )
- As
- begin
- insert @t(id,data) select id,data from TableA
- return
- end
- go
5.内联表值函数(IF) (MyFunction_IF):
- if object_id('MyFunction_IF') is not null drop function MyFunction_IF
- go
- create function MyFunction_IF
- (
- )
- returns table
- As
- return(select top(3) id, data from TableA order by id desc)
- go
6.标量函数(FN)(MyFunction_FN):
- if object_id('MyFunction_FN') is not null drop function MyFunction_FN
- go
- create function MyFunction_FN
- (
- )
- returns nvarchar(50)
- As
- begin
- return(select top(1) data from TableA order by id desc)
- end
- go
当执行完了上面的1-6步骤的脚本,我们通过查询系统视图sys.sql_modules,可以看到未加密前的定义信息:
- select b.name as object,b.type,a.definition
- from sys.sql_modules a
- inner join sys.objects b on b.object_id=a.object_id
- where b.create_date>=convert(date,getdate())
- order by b.object_id
加密测试:
下面我就通过调用加密存储过程(sp_EncryptObject),一次性对它们进行加密:
- use test
- go
- exec sp_EncryptObject 'all'
- go
当我们再查回系统视图sys.sql_modules,会发现definition列返回的是null值,说明定义内容已经给加密:
解密测试:
解密过程,必须在DAC连接SQL Server,我们这里例子是从 SSMS(SQL Server Management Studio) 查询编辑器启动 DAC,如图:
解密存储过程(sp_DecryptObject),只能一次对一个存储过程、函数、视图或触发器,进行解密:
- use test
- go
- exec sp_DecryptObject MyTrigger
- go
当定义内容长度超过4000,我们可以指定@MaxLength的值,如:
- exec sp_DecryptObject fn_My,20000
- go
这里(fn_My)是一个函数,定义内容超过了8000:
... ...
小结:
虽然,上面的脚本,我已经在SQL Server 2008 R2 和SQL Server 2012测试过,但无法避免一些未知错误 。
sql存储过程加密和解密(MSSQL)的更多相关文章
- SqlServer存储过程加密与解密
★ 加密存储过程 ★: IF EXISTS (SELECT name FROM sysobjects WHERE name = 'encrypt_this' AND type = 'P') DRO ...
- (转)对存储过程进行加密和解密(SQL 2008/SQL 2012)
原文地址:http://www.cnblogs.com/wghao/archive/2012/12/30/2837642.html 开始: 在网络上,看到有SQL Server 2000和SQL Se ...
- SQL SERVER 2008破解加密存储过程(修正存储过程过长解密出来是空白的问题)
SQLServer2005里使用with encryption选项创建的存储过程仍然和sqlserver2000里一样,都是使用XOR进行了的加密.和2000不一样的是,在2005的系统表syscom ...
- MS 数据库存储过程加密解密
存储过程加密解密在网上有很多,刚刚好最近需要用到,所以就查询了一下资料.记录一下 加密方法:执行如下存储过程 DECLARE @sp_name nvarchar(400) DECLARE @sp_co ...
- 查看SQL SERVER 加密存储过程,函数,触发器,视图
原文:查看SQL SERVER 加密存储过程,函数,触发器,视图 create PROCEDURE sp_decrypt(@objectname varchar(50))ASbeginset noc ...
- Sql Server数据的加密与解密
Sql Server数据的加密与解密 在sql server中,我们如何为数据进行加密与解密,避免使用者窃取机密数据? 对于一些敏感数据,如密码.卡号,一般不能使用正常数值来存储.否则会有安全隐患.以 ...
- SQL SERVER 2008 使用TDE加密和解密
SQL SERVER 2008 加密和解密,这样的文件在互联网上不胜枚举,本文的寓意还是一样,一为记录,二可以为开发者提供在实现过程中的注意事项. TDE: Transparent data encr ...
- Sql Server简单加密与解密 【转】
前言: 在SQL Server 2005和SQL Server 2008之前.如果希望加密敏感数据,如财务信息.工资或身份证号,必须借助外部应用程序或算法.SQL Server 2005引入内建数据加 ...
- 使用EncryptByPassPhrase和DecryptByPassPhrase对MS SQLServer某一字段时行加密和解密
在数据库实现加密与解密的文章,Insus.NET较早前也有写过,可以在本博客中可以搜索得到. 今天使用EncryptByPassPhrase和DecryptByPassPhrase来简单实现. 在数据 ...
随机推荐
- LPC43xx State Configurable Timer : SCT
- .yaml 文件格式简介
命名 YAML 的意思其实是:"Yet Another Markup Language"(仍是一种置标语言)的缩写. 功能 YAML的语法和其他高阶语言类似,并且可以简单表达清单. ...
- Visual Studio 2013 sqlce 配置(转)
Visual Studio 2013 把內建 SQL CE 的管理工具拿掉了 下载SQL Server Compact Toolbox by ErikEJ并安装 打开VS2013,新建一工程,在“视图 ...
- 高通与MTK瓜分天下?手机处理器品牌分析
http://mobile.pconline.com.cn/337/3379352.html [PConline 杂谈]如果你向朋友请教买一台怎样的台式机或者笔记本的话,很多时候那朋友会根据你对电脑的 ...
- Android之循环显示图像的Android Gallery组件
转自:http://www.blogjava.net/nokiaguy/archive/2010/08/23/329721.html Gallery组件主要用于横向显示图像列表,不过按常规做法.Gal ...
- Java多线程中join方法的理解
thread.Join把指定的线程加入到当前线程,可以将两个交替执行的线程合并为顺序执行的线程.比如在线程B中调用了线程A的Join()方法,直到线程A执行完毕后,才会继续执行线程B. t.join( ...
- C#里,如何模拟DataGridView里的一个Cell的Click事件。
//假设dgv是一个DataGridView. //我要点击第3行的第二个cell. //当然,要有一个点击事件.假设dgv_CellClick是那个点击事件. dgv_CellClick(dgv, ...
- [Web 前端] 如何构建React+Mobx+Superagent的完整框架
ReactJS并不像angular一样是一个完整的前端框架,严格的说它只是一个UI框架,负责UI页面的展示,如果用通用的框架MVC来说,ReactJs只负责View了,而Angular则是一个完整的前 ...
- Svg.Js A标签,链接操作
一.创建a标签,为a标签添加内容 <div id="svg1"></div> <script> //SVG.A 链接创建 var draw = ...
- LaTeX技巧24:LaTeX常用命令集锦
\hyphenation{word list} %断字命令:\showthe\topmargin %显示某个参数的数值或者内容: 在tex编译过程中出现行溢出(overflow hbox)是由于断字程 ...