項目中進行了sha1withrsa數(shù)字簽名利诺。對此總結(jié)一下吧涌哲。
淺談加密與簽名
我們一般的加密主要分2種胖缤。對稱加密。非對稱加密阀圾。顧名思義哪廓。對稱加密就是秘鑰是一樣的。通過同一個秘鑰加密解密初烘。這一類的加密算法有DES,3DES撩独,AES等。而非對稱加密即使用公鑰和秘鑰2個账月。公鑰進行加密综膀,而私鑰進行解密。這一類的加密算法主要RSA
數(shù)字簽名并不是加密局齿。他只是將傳遞的參數(shù)進行簽名剧劝。服務(wù)器端可以驗簽。主要作用有2個:1.保證數(shù)據(jù)不會被篡改抓歼。2.保證請求的確是秘鑰持有者發(fā)送的讥此。 雖然他不是加密但是這里我們使用到了RSA加密。sha1withrsa顧名思義谣妻。是將加密對象進行sha1后進行rsa加密萄喳。當(dāng)然。并不是簡單的sha1后就直接rsa蹋半。中間有一些處理他巨。具體的細節(jié)我不是很了解。也不需要了解除非你對算法極度感興趣∪就唬可以去找找資料弄明白捻爷。不管怎樣處理。最后我們得到了用rsa秘鑰加密后的數(shù)據(jù)份企。一般情況下我們會base64成一個字符串也榄,方便傳輸。這樣的字符串就是數(shù)字簽名了司志。服務(wù)器端只需要用對應(yīng)的公鑰進行驗簽就可以判斷出是不是秘鑰持有者發(fā)送的消息了甜紫。
淺談rsa秘鑰公鑰以及pkcs1,pkcs8格式
首先推薦一個在線生成秘鑰公鑰以及加密解密的網(wǎng)站http://tool.chacuo.net/cryptrsapubkey
自己生成公鑰秘鑰一般情況下就需要使用openssl工具了骂远。使用工具執(zhí)行命令
genrsa -out rsa_private_key.pem 2048
2048指的是位數(shù)囚霸。一般情況下1024也夠用了。如果要求高點還是2048位更加合適點吧史。生成的pem文件里面以-----BEGIN RSA PRIVATE KEY-----開頭-----END RSA PRIVATE KEY-----結(jié)尾的字符串。中間的部分才是秘鑰的base64字符串.這個是pkcs1格式的唠雕。也就是原本的rsa密鑰贸营。
rsa -in rsa_private_key.pem -pubout -out rsa_public_key.pem
上面的命令會根據(jù)剛剛的私鑰生成pkcs8格式的公鑰。
如果需要pkcs8個格式的私鑰(java就是用pkcs8格式的)需要
pkcs8 -topk8 -inform PEM -in rsa_private_key.pem -outform PEM -nocrypt
會打印出來以-----BEGIN PRIVATE KEY-----開頭岩睁。以-----END PRIVATE KEY-----結(jié)尾的私鑰钞脂。
這里我們需要了解一下pkcs1格式和pkcs8格式的區(qū)別和關(guān)聯(lián):
pkcs1的格式才是原本的rsa的密鑰。而pkcs8的格式是在pkcs1的數(shù)據(jù)上增加一些信息捕儒。具體這些信息是什么我并沒有去深入了解冰啃。我們只需要知道pkcs1和pkcs8之間是可以轉(zhuǎn)換的。以2048位的為例:
公鑰 pkcs8 轉(zhuǎn)換為 pkcs1 其實就是將前面的32位去除即可
私鑰的轉(zhuǎn)換需要使用openssl
pkcs8轉(zhuǎn)rsa:
openssl rsa -in pkcs8密鑰 -out rsa密鑰
rsa轉(zhuǎn)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等信息点弯,可以根據(jù)實際情況填寫扇调,或者全部不用填寫,直接全部敲回車.
3. 生成證書rsaCert.crt抢肛,并設(shè)置有效時間為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
注意:這一步會提示給私鑰文件設(shè)置密碼狼钮,直接輸入想要設(shè)置密碼即可,然后敲回車捡絮,然后再驗證剛才設(shè)置的密碼熬芜,再次輸入密碼,然后敲回車福稳,完畢涎拉!
在解密時,private_key.p12文件需要和這里設(shè)置的密碼配合使用,因此需要牢記此密碼.
關(guān)于數(shù)字簽名的java曼库,iOS 区岗,以及 lua(使用openresty)的實現(xiàn)
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;
}
我一開始打算服務(wù)器使用java驗簽慈缔。可是發(fā)現(xiàn)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
好了就記錄這些吧藐鹤。。