说到签名机制,首先要了解一下 加密解密 ,签名文件就是 加密解密 的过程。
加密 是将明文信息改变为难以读取的密文内容,使之不可读的过程。
解密 是通过特殊的对象,将密文还原为正常可读的内容的过程。而在这个过程中,我们所使用的方法,就是加密解密算法。
加密分为 对称加密 与 非对称加密(公开密钥加密) 。
对称加密就是加密和解密使用的都是同一套密钥
常见的对称密码算法有:
如下图,在使用对称密码时,一定会遇到密钥配送问题, 假设,Alice将使用对称密码加密过的消息发给了Bob, 只有将密钥发送给Bob,Bob才能完成解密, 在发送密钥过程中,可能会被Eve窃取密钥,最后Eve也能完成解密。
加密和解密使用的不是同一个密钥,即为非对称加密算法,也称公开密钥加密;
公钥密码中,密钥分为加密密钥、解密密钥2种,它们并不是同一个密钥, 公钥密码也被称为非对称密码(Asymmetric Cryptography)
在公钥密码中:
加密密钥 ,一般是公开的,因此该密钥称为 公钥 (public key)
解密密钥 ,由消息接收者自己保管的,不能公开,因此也称为 私钥 (private key) 公钥和私钥是一 一对应的,是不能单独生成的,一对公钥和密钥统称为密钥对(key pair)
这样就能 解决秘钥配送的问题 了,如下图:
上图解析:
这其中如果有第三者窃听,只有第2步和第4步能够监听数据,由于Bob公钥是公开的谁都可以获取,那么第二步也不用担心被谁获取,第4步如果数据被第三者截获,那么他看到的也是加密后的数据,由于他没有Bob的私钥,那么他也无法知道消息的真实内容。而且他即使篡改密文消息也无任何意义。
虽然非对称加密解决了密钥配送问题,但是它的加解密速度较慢,下面我们总结一下对称和非对称加密的优缺点:
混合密码 系统,是将对称密码和公钥密码的优势相结合的方法:
为本次通信随机生成的临时密钥; 作为对称密码的密钥,用于加密消息,提高速度
首先,消息发送者要拥有消息接收者的公钥; 生成会话密钥,作为对称密码的密钥,加密消息; 用消息接收者的公钥,加密会话密钥; 将前2步生成的加密结果,一并发给消息接收者。
发送出去的内容包括
用会话密钥加密的消息(加密方法:对称密码)
用公钥加密的会话密钥(加密方法:公钥密码)
1 消息接收者用自己的私钥解密出会话密钥
2 再用第1步解密出来的会话密钥,解密消息
发送过程,加密过程
接收过程,解密过程
1.Bob利用自己的私钥解密会话密钥(使用的是公钥密码解密,也就是非对称密码解密)
2.Bob利用会话密钥解密发送过来的消息(使用的是对称密码解密)
上面的加密算法解决了数据传输的安全问题,那么 数据的完整性 是没法验证的,就是我这个数据有没有被改过,因为公钥大家都能获取,如果有中间人拦截了消息,并改动了内容。那么我们如何验证这个 消息有没有变动 呢?
单向散列函数 ,又称单向 Hash函数 、 杂凑函数 ,就是把任意长的输入消息串变化成 固定长的输出串 且由输出串难以得到输入串的一种函数。这个输出串称为该消息的散列值。一般用于产生消息摘要,密钥加密等
单向散列函数,可以根据根据消息内容计算出散列值 散列值的长度和消息的长度无关 ,无论消息是1bit、10M、100G,单向散列函数都会计算出 固定长度的散列值 。
单向散列函数 ,又被称为 消息摘要函数 (message digest function),哈希函数输出的散列值,也被称为消息摘要(message digest)、指纹(fingerprint)
MD4、MD5 产生128bit的散列值,MD就是Message Digest的缩写,目前已经不安全 Mac终端上默认可以使用md5命令
SHA-1 产生160bit的散列值,目前已经不安全
SHA-2 SHA-256、SHA-384、SHA-512,散列值长度分别是256bit、384bit、512bit
SHA-3 全新标准
不同的数据生成的散列值是不一样的,只要你对一个文件改动过,那么它的散列值就会发生变化,要想确定我们的数据有没有发生变化,只要对比两次散列值相不相同就可以了,我们常常做的登录功能,在保存用户密码的时候就采用单项散列函数生成的值来进行保存,防止第三方人员串改密码。
数据防篡改的技术我们知道了,在数据传输的过程中,我们对数据生成一个散列值,和发送的数据一并发给接收者,当接收者收到这个数据的时候,它拿接收到的数据重新生成散列值,然后跟接收到的散列值进行比较,就可以判断这个数据有没有被人改过。
到此我们通过混合密码技术解决的传输数据的保密性,通过单项散列函数确定数据的一致性,但是还是没有解决 中间人截获篡改 的问题,因为散列函数中间人也可以重新生成一次,接下来我们就要讲数字签名了,他可以对消息发送者的真实性进行认证。
数字签名 (又称公钥数字签名)是只有信息的发送者才能产生的别人无法伪造的一段数字串,这段数字串同时也是对信息的发送者发送信息真实性的一个有效证明。
它是一种类似写在纸上的普通的物理签名,但是使用了公钥加密领域的技术来实现的,用于鉴别数字信息的方法。一套数字签名通常定义两种互补的运算,一个用于签名,另一个用于验证。数字签名是非对称密钥加密技术与数字摘要技术的应用。
说白了就是用用消息发送者的私钥进行签名就是数字签名
在数字签名中,任何人都可以使用公钥验证签名
在数字签名技术中,有以下2种行为:
生成签名 由消息的发送者完成,通过“签名密钥”生成
验证签名 由消息的接收者完成,通过“验证密钥”验证
数字签名由于是消息发送者的私钥进行签名,消息发送者的私钥只有他自己拥有,别人是没有的,从而我们通过私钥进行签名,别人通过消息发送者的公钥就能确定消息发送者的真实身份。
接下来我们看一下数字签名和公钥密码的对比:
上图Alice将要发送的消息用自己的私钥加密,发送给Bob,Bob用Alice的公钥解密消息,这里其实有一个不好的点,就是如果Alice如果发送的消息比较大,比如发1GB的视频文件,那这个签名过程就太慢了,本身非对称加密的速度就是比较慢的,
下面我们来看一个改进版的:
这里我们将要发送的消息先生成固定大小的散列值,然后再签名,这样签名文件就小的多了,然后我们将消息和签名一同发送该Bob,然后Bob再用公钥解密 对比等。
下面有关数字签名的一些点进行一下说明:
1 如果有人篡改了文件内容或者签名内容,会是什么结果? 结果是:签名验证失败,证明内容会篡改
2 数字签名不能保证机密性? 数字签名的作用不是为了保证机密性,仅仅是为了能够识别内容有没有被篡改
3 数字签名的作用
数字签名是能确定消息发送者,前提是你要确定你获取的公钥是确定是消息发送者的,如果你拿到的公钥是中间人伪造的,那么你就无法验证消息发送者的真实性了,就如下图:
[图片上传中...(image-b6d6e1-1614756605461-3)]
A问B要公钥,M从监听到了中间,B给A发的公钥被M拦截了并保存,M把他自己的公钥给了A,A以为这个公钥是B的,A用公钥加密发消息给B,M拦截然后用自己的私钥解密,修改消息内容后,然后用保存的公钥加密把消息发送给B,B解密消息。A,和B都以为是正常通信的,但消息确实不是那个消息了,那么如何确定公钥合法?也就是如何确定这个公钥就是B的呢?
接下来就是我们要讲的证书了,我们引入一个第三方权威机构来认正,说这个公钥就是B的。接下来我们来看一下。
CA是证书的签发机构,它是公钥基础设施(Public Key Infrastructure,PKI)的核心。CA是负责签发证书、认证证书、管理已颁发证书的机关。
CA 拥有一个证书(内含公钥和私钥)。网上的公众用户通过验证 CA 的签字从而信任 CA ,任何人都可以得到 CA 的证书(含公钥),用以验证它所签发的证书,密码学中的证书,全称叫公钥证书(Public-key Certificate,PKC),跟驾驶证类似 里面有姓名、邮箱等个人信息,以及此人的公钥; 并由认证机构(Certificate Authority,CA)施加数字签名。
图已经表示的很清楚了,消息发送者先向CA机构 注册自己的证书,那么任何拿到消息发送者的公钥都可以向CA进行验证公钥的真实性。
首先我们要知道iOS签名机制的作用是什么?
保证安装到用户手机上的APP都是经过Apple官方允许的
不管是真机调试,还是发布APP,开发者都需要经过一系列复杂的步骤:
大致如下图:
[图片上传中...(image-169a4f-1614756605461-0)]
总结:
1、.cerSigningRequest文件 : Mac公钥
2、.cer文件:利用Apple私钥(CA),对Mac公钥生成了数字签名
3、.mobileprovision : 利用Apple私钥,对【.cer证书 + devices + AppID + entitlements】进行数字签名
之前在项目上用到AES256加密解密算法,刚开始在java端加密解密都没有问题,在iOS端加密解密也没有问题。但是奇怪的是在java端加密后的文件在iOS端无法正确解密打开,然后简单测试了一下,发现在java端和iOS端采用相同明文,相同密钥加密后的密文不一样!上网查了资料后发现iOS中AES加密算法采用的填充是PKCS7Padding,而java不支持PKCS7Padding,只支持PKCS5Padding。我们知道加密算法由算法+模式+填充组成,所以这两者不同的填充算法导致相同明文相同密钥加密后出现密文不一致的情况。那么我们需要在java中用PKCS7Padding来填充,这样就可以和iOS端填充算法一致了。要实现在java端用PKCS7Padding填充,需要用到bouncycastle组件来实现,下面我会提供该包的下载。啰嗦了一大堆,下面是一个简单的测试,上代码!
001 package com.encrypt.file
002
003
004 import java.io.UnsupportedEncodingException
005 importjava.security.Key
006 import java.security.Security
007
008 importjavax.crypto.Cipher
009 importjavax.crypto.SecretKey
010 importjavax.crypto.spec.SecretKeySpec
011
012 public classAES256Encryption{
013
014 /**
015 * 密钥算法
016 * java6支持56位密钥,bouncycastle支持64位
017 * */
018 public static finalString KEY_ALGORITHM="AES"
019
020 /**
021 * 加密/解密算法/工作模式/填充方式
022 *
023 * JAVA6 支持PKCS5PADDING填充方式
024 * Bouncy castle支持PKCS7Padding填充方式
025 * */
026 public static finalString CIPHER_ALGORITHM="AES/ECB/PKCS7Padding"
027
028 /**
029 *
030 * 生成密钥,java6只支持56位密钥,bouncycastle支持64位密钥
031 * @return byte[] 二进制密钥
032 * */
033 public static byte[] initkey() throwsException{
034
035 // //实例化密钥生成器
036 // Security.addProvider(new org.bouncycastle.jce.provider.BouncyCastleProvider())
037 // KeyGenerator kg=KeyGenerator.getInstance(KEY_ALGORITHM, "BC")
038 // //初始化密钥生成器,AES要求密钥长度为128位、192位、256位
039 //// kg.init(256)
040 // kg.init(128)
041 // //生成密钥
042 // SecretKey secretKey=kg.generateKey()
043 // //获取二进制密钥编码形式
044 // return secretKey.getEncoded()
045 //为了便于测试,这里我把key写死了,如果大家需要自动生成,可用上面注释掉的代码
046 return new byte[] { 0x08, 0x08, 0x04, 0x0b, 0x02, 0x0f, 0x0b, 0x0c,
047 0x01, 0x03, 0x09, 0x07, 0x0c, 0x03, 0x07, 0x0a, 0x04, 0x0f,
048 0x06, 0x0f, 0x0e, 0x09, 0x05, 0x01, 0x0a, 0x0a, 0x01, 0x09,
049 0x06, 0x07, 0x09, 0x0d }
050 }
051
052 /**
053 * 转换密钥
054 * @param key 二进制密钥
055 * @return Key 密钥
056 * */
057 public static Key toKey(byte[] key) throwsException{
058 //实例化DES密钥
059 //生成密钥
060 SecretKey secretKey=newSecretKeySpec(key,KEY_ALGORITHM)
061 returnsecretKey
062 }
063
064 /**
065 * 加密数据
066 * @param data 待加密数据
067 * @param key 密钥
068 * @return byte[] 加密后的数据
069 * */
070 public static byte[] encrypt(byte[] data,byte[] key) throwsException{
071 //还原密钥
072 Key k=toKey(key)
073 /**
074 * 实例化
075 * 使用 PKCS7PADDING 填充方式,按如下方式实现,就是调用bouncycastle组件实现
076 * Cipher.getInstance(CIPHER_ALGORITHM,"BC")
077 */
078 Security.addProvider(new org.bouncycastle.jce.provider.BouncyCastleProvider())
079 Cipher cipher=Cipher.getInstance(CIPHER_ALGORITHM, "BC")
080 //初始化,设置为加密模式
081 cipher.init(Cipher.ENCRYPT_MODE, k)
082 //执行 *** 作
083 returncipher.doFinal(data)
084 }
085 /**
086 * 解密数据
087 * @param data 待解密数据
088 * @param key 密钥
089 * @return byte[] 解密后的数据
090 * */
091 public static byte[] decrypt(byte[] data,byte[] key) throwsException{
092 //欢迎密钥
093 Key k =toKey(key)
094 /**
095 * 实例化
096 * 使用 PKCS7PADDING 填充方式,按如下方式实现,就是调用bouncycastle组件实现
097 * Cipher.getInstance(CIPHER_ALGORITHM,"BC")
098 */
099 Cipher cipher=Cipher.getInstance(CIPHER_ALGORITHM)
100 //初始化,设置为解密模式
101 cipher.init(Cipher.DECRYPT_MODE, k)
102 //执行 *** 作
103 returncipher.doFinal(data)
104 }
105 /**
106 * @param args
107 * @throws UnsupportedEncodingException
108 * @throws Exception
109 */
110 public static void main(String[] args) throwsUnsupportedEncodingException{
111
112 String str="AES"
113 System.out.println("原文:"+str)
114
115 //初始化密钥
116 byte[] key
117 try {
118 key = AES256Encryption.initkey()
119 System.out.print("密钥:")
120 for(int i = 0i<key.lengthi++){
121 System.out.printf("%x", key[i])
122 }
123 System.out.print("\n")
124 //加密数据
125 byte[] data=AES256Encryption.encrypt(str.getBytes(), key)
126 System.out.print("加密后:")
127 for(int i = 0i<data.lengthi++){
128 System.out.printf("%x", data[i])
129 }
130 System.out.print("\n")
131
132 //解密数据
133 data=AES256Encryption.decrypt(data, key)
134 System.out.println("解密后:"+newString(data))
135 } catch (Exception e) {
136 // TODO Auto-generated catch block
137 e.printStackTrace()
138 }
139
140 }
141 }
运行程序后的结果截图:
ViewController.m文件
01 //
02 // ViewController.m
03 // AES256EncryptionDemo
04 //
05 // Created by 孙 裔 on 12-12-13.
06 // Copyright (c) 2012年 rich sun. All rights reserved.
07 //
08
09 #import "ViewController.h"
10 #import "EncryptAndDecrypt.h"
11
12 @interface ViewController ()
13
14 @end
15
16 @implementation ViewController
17 @synthesize plainTextField
18 - (void)viewDidLoad
19 {
20 [super viewDidLoad]
21 // Do any additional setup after loading the view, typically from a nib.
22 }
23
24 - (void)didReceiveMemoryWarning
25 {
26 [super didReceiveMemoryWarning]
27 // Dispose of any resources that can be recreated.
28 }
29 //这个函数实现了用户输入完后点击视图背景,关闭键盘
30 - (IBAction)backgroundTap:(id)sender{
31 [plainTextField resignFirstResponder]
32 }
33
34 - (IBAction)encrypt:(id)sender {
35
36 NSString *plainText = plainTextField.text//明文
37 NSData *plainTextData = [plainText dataUsingEncoding:NSUTF8StringEncoding]
38
39 //为了测试,这里先把密钥写死
40 Byte keyByte[] = {0x08,0x08,0x04,0x0b,0x02,0x0f,0x0b,0x0c,0x01,0x03,0x09,0x07,0x0c,0x03,
41 0x07,0x0a,0x04,0x0f,0x06,0x0f,0x0e,0x09,0x05,0x01,0x0a,0x0a,0x01,0x09,
42 0x06,0x07,0x09,0x0d}
43 //byte转换为NSData类型,以便下边加密方法的调用
44 NSData *keyData = [[NSData alloc] initWithBytes:keyByte length:32]
45 //
46 NSData *cipherTextData = [plainTextData AES256EncryptWithKey:keyData]
47 Byte *plainTextByte = (Byte *)[cipherTextData bytes]
48 for(int i=0i<[cipherTextData length]i++){
49 printf("%x",plainTextByte[i])
50 }
51
52 }
53 @end
附上出处链接:
欢迎分享,转载请注明来源:内存溢出
评论列表(0条)