数字签名----sha1withrsa
项目中进行了sha1withrsa数字签名。对此总结一下吧。
浅谈加密与签名
我们一般的加密主要分2种。对称加密。非对称加密。顾名思义。对称加密就是秘钥是一样的。通过同一个秘钥加密解密。这一类的加密算法有DES,3DES,AES等。而非对称加密即使用公钥和秘钥2个。公钥进行加密,而私钥进行解密。这一类的加密算法主要RSA
数字签名并不是加密。他只是将传递的参数进行签名。服务器端可以验签。主要作用有2个:1.保证数据不会被篡改。2.保证请求的确是秘钥持有者发送的。 虽然他不是加密但是这里我们使用到了RSA加密。sha1withrsa顾名思义。是将加密对象进行sha1后进行rsa加密。当然。并不是简单的sha1后就直接rsa。中间有一些处理。具体的细节我不是很了解。也不需要了解除非你对算法极度感兴趣。可以去找找资料弄明白。不管怎样处理。最后我们得到了用rsa秘钥加密后的数据。一般情况下我们会base64成一个字符串,方便传输。这样的字符串就是数字签名了。服务器端只需要用对应的公钥进行验签就可以判断出是不是秘钥持有者发送的消息了。
浅谈rsa秘钥公钥以及pkcs1,pkcs8格式
首先推荐一个在线生成秘钥公钥以及加密解密的网站http://tool.chacuo.net/cryptrsapubkey
自己生成公钥秘钥一般情况下就需要使用openssl工具了。使用工具执行命令
genrsa -out rsa_private_key.pem 2048
2048指的是位数。一般情况下1024也够用了。如果要求高点还是2048位更加合适点。生成的pem文件里面以-----BEGIN RSA PRIVATE KEY-----开头-----END RSA PRIVATE KEY-----结尾的字符串。中间的部分才是秘钥的base64字符串.这个是pkcs1格式的。也就是原本的rsa密钥。
rsa -in rsa_private_key.pem -pubout -out rsa_public_key.pem
上面的命令会根据刚刚的私钥生成pkcs8格式的公钥。
如果需要pkcs8个格式的私钥(java就是用pkcs8格式的)需要
pkcs8 -topk8 -inform PEM -in rsa_private_key.pem -outform PEM -nocrypt
会打印出来以-----BEGIN PRIVATE KEY-----开头。以-----END PRIVATE KEY-----结尾的私钥。
这里我们需要了解一下pkcs1格式和pkcs8格式的区别和关联:
pkcs1的格式才是原本的rsa的密钥。而pkcs8的格式是在pkcs1的数据上增加一些信息。具体这些信息是什么我并没有去深入了解。我们只需要知道pkcs1和pkcs8之间是可以转换的。以2048位的为例:
公钥 pkcs8 转换为 pkcs1 其实就是将前面的32位去除即可
私钥的转换需要使用openssl
pkcs8转rsa:
openssl rsa -in pkcs8密钥 -out rsa密钥
rsa转pkcs8:
openssl pkcs8 -topk8 -inform PEM -in rsa密钥 -outform PEM -nocrypt -out pkcs8密钥
而在iOS端可能需要生成p12文件。
1. 生成模长为1024bit的私钥文件private_key.pem
openssl genrsa -out private_key.pem 1024
2. 生成证书请求文件rsaCertReq.csr
openssl req -new -key private_key.pem -out rsaCerReq.csr
注意:这一步会提示输入国家、省份、mail等信息,可以根据实际情况填写,或者全部不用填写,直接全部敲回车.
3. 生成证书rsaCert.crt,并设置有效时间为1年
openssl x509 -req -days 3650 -in rsaCerReq.csr -signkey private_key.pem -out rsaCert.crt
4. 生成供iOS使用的公钥文件public_key.der
openssl x509 -outform der -in rsaCert.crt -out public_key.der
5. 生成供iOS使用的私钥文件private_key.p12
openssl pkcs12 -export -out private_key.p12 -inkey private_key.pem -in rsaCert.crt
注意:这一步会提示给私钥文件设置密码,直接输入想要设置密码即可,然后敲回车,然后再验证刚才设置的密码,再次输入密码,然后敲回车,完毕!
在解密时,private_key.p12文件需要和这里设置的密码配合使用,因此需要牢记此密码.
关于数字签名的java,iOS ,以及 lua(使用openresty)的实现
java使用的是pkcs8格式。这个需要知道。接下来的我就直接贴代码吧:
public static boolean rsaCheckContent(String content, String sign, String publicKey,
String charset) throws Exception {
try {
PublicKey pubKey = getPublicKeyFromX509("RSA",
new ByteArrayInputStream(publicKey.getBytes()));
java.security.Signature signature = java.security.Signature
.getInstance(Constants.SIGN_ALGORITHMS);//"SHA1WithRSA"
signature.initVerify(pubKey);
if (StringUtils.isEmpty(charset)) {
signature.update(content.getBytes());
} else {
signature.update(content.getBytes(charset));
}
byte[] d = Base64.decode(sign.getBytes());
return signature.verify(d);
} catch (Exception e) {
return false;
//throw new Exception("RSAcontent = " + content + ",sign=" + sign + ",charset = " + charset, e);
}
}
publicstaticPublicKey getPublicKeyFromX509(Stringalgorithm,
InputStreamins)throwsException {
KeyFactorykeyFactory= KeyFactory.getInstance(algorithm);
StringWriterwriter=newStringWriter();
StreamUtil.io(newInputStreamReader(ins),writer);
byte[]encodedKey=writer.toString().getBytes();
encodedKey= Base64.decode(encodedKey);
returnkeyFactory.generatePublic(newX509EncodedKeySpec(encodedKey));
}
ios的代码如下--
#define kChosenDigestLength CC_SHA1_DIGEST_LENGTH
- (NSData*)getHashBytes:(NSData*)plainText {
CC_SHA1_CTXctx;
uint8_t* hashBytes =NULL;
NSData* hash =nil;
// Malloc a buffer to hold hash.
hashBytes =malloc(kChosenDigestLength*sizeof(uint8_t) );
memset((void*)hashBytes,0x0,kChosenDigestLength);
// Initialize the context.
CC_SHA1_Init(&ctx);
// Perform the hash.
CC_SHA1_Update(&ctx, (void*)[plainTextbytes], (CC_LONG)[plainTextlength]);
// Finalize the output.
CC_SHA1_Final(hashBytes, &ctx);
// Build up the SHA1 blob.
hash = [NSDatadataWithBytes:(constvoid*)hashByteslength:(NSUInteger)kChosenDigestLength];
if(hashBytes)free(hashBytes);
returnhash;
}
-(NSString*)signTheDataSHA1WithRSA:(NSString*)plainText
{
uint8_t* signedBytes =NULL;
size_tsignedBytesSize =0;
OSStatussanityCheck =noErr;
NSData* signedHash =nil;
NSString* path = [[NSBundlemainBundle]pathForResource:@"privateKey"ofType:@"p12"];
NSData* data = [NSDatadataWithContentsOfFile:path];
NSMutableDictionary* options = [[NSMutableDictionaryalloc]init];// Set the private key query dictionary.
[optionssetObject:@"123456"forKey:(id)kSecImportExportPassphrase];
CFArrayRefitems =CFArrayCreate(NULL,0,0,NULL);
OSStatussecurityError =SecPKCS12Import((CFDataRef) data, (CFDictionaryRef)options, &items);
if(securityError!=noErr) {
returnnil;
}
CFDictionaryRefidentityDict =CFArrayGetValueAtIndex(items,0);
SecIdentityRefidentityApp =(SecIdentityRef)CFDictionaryGetValue(identityDict,kSecImportItemIdentity);
SecKeyRefprivateKeyRef=nil;
SecIdentityCopyPrivateKey(identityApp, &privateKeyRef);
signedBytesSize =SecKeyGetBlockSize(privateKeyRef);
NSData*plainTextBytes = [plainTextdataUsingEncoding:NSUTF8StringEncoding];
signedBytes =malloc( signedBytesSize *sizeof(uint8_t) );// Malloc a buffer to hold signature.
memset((void*)signedBytes,0x0, signedBytesSize);
sanityCheck =SecKeyRawSign(privateKeyRef,
kSecPaddingPKCS1SHA1,
(constuint8_t*)[[selfgetHashBytes:plainTextBytes]bytes],
kChosenDigestLength,
(uint8_t*)signedBytes,
&signedBytesSize);
if(sanityCheck ==noErr)
{
signedHash = [NSDatadataWithBytes:(constvoid*)signedByteslength:(NSUInteger)signedBytesSize];
}
else
{
returnnil;
}
if(signedBytes)
{
free(signedBytes);
}
NSString*signatureResult = [[NSStringalloc]initWithData:[signedHashbase64EncodedDataWithOptions:0]encoding:NSASCIIStringEncoding];
returnsignatureResult;
}
我一开始打算服务器使用java验签。可是发现nginx-lua 模块有
https://github.com/doujiang24/lua-resty-rsa
使用这个效果不错哦。不过需要注意的是他使用的是pkcs1的格式
主要代码如下:
local resty_rsa = require "resty.rsa"
local priv, err = resty_rsa:new({ private_key = RSA_PRIV_KEY })
local algorithm = "SHA1"
local pub, err = resty_rsa:new({ public_key = RSA_PUBLIC_KEY, algorithm = algorithm })
if not pub then
ngx.say("new rsa err: ", err)
return
end
local serverSign = ngx.decode_base64(signStr)
local verify, err = pub:verify(stringForSign,serverSign)
if(verify) then
ngx.exec("@appBack")
else
ngx.say("签名不正确")
end
好了就记录这些吧。。