preface
Recently, I want to do the Internet authorization of iOS SDK, which involves data security verification, so I think of using RSA for signature and verification. The authorization process is as follows:
- 1. The client goes to our open platform for registration and authorization and gets
AppId
andAppSecret
. - 2. Customer integration
SDK
, the callRegister
Interface of the incomingAppId
andAppSecret
. - 3,
SDK
willAppId
And client platform related information to the server background. - 4. The server sends the latest server timestamp.
sign
, public key, and authorization related data to the client. - 5. The client uses the public key for signature verification.
- 6. Signature verification Calculates whether the authorization time is valid.
A brief introduction is that the server generates a secret key pair, uses the private key to sign the parameters submitted by the client and the open platform, and then delivers the signature and public key (processed string) to the client. After the client passes the signature check, the client performs authorization verification.
OpenSSL
useRSA
You need to call a very important libraryOpenSSL
, you can useCocoapods
Fast integration in projects:
I usually install the following version:
pod 'OpenSSL-Universal'.'~ > 1.0.2.20'
Copy the code
Import the relevant header file before using:
#include <openssl/rsa.h>
#include <openssl/pem.h>
#include <openssl/evp.h>
#include <openssl/err.h>
#include <openssl/bio.h>
Copy the code
Public and private keys
1. PEM public and private key files are generated
To verify the correctness of the interface, we need to generate a local key pair to test the local code:
1.1. Create a folder on the desktopRSA Key
The terminalCD
Go to this folder.
myz@myz mediapipe % cd /Users/myz/Desktop/RSA\ Key
Copy the code
1.2. The terminal generates a 1024 bit private key
openssl genrsa -out rsa_private_key.pem 1024
myz@myz RSA Key % openssl genrsa -out rsa_private_key.pem 1024 Generating RSA private key, 1024 bit long modulus ........ + + + + + +... ++++++ e is 65537 (0x10001) myz@myz RSA Key %Copy the code
1.3, use,RSA
Private key Generates public key
openssl rsa -in rsa_private_key.pem -out rsa_public_key.pem -pubout
2, generalRSA
The secret key format
Change the pem file to TXT to see the secret key text
2.1, the public key
-----BEGIN PUBLIC KEY-----
MIGfMA0GCSqGSIb3DQEBAQUAA4GNADCBiQKBgQDjQRu0fImY/AVasfAneO4G8sID
P3L9XDX+nZq8GBw4vlDzoWIqXGx8ETRMMRx+fdEz3Skdrlsp1+6NcYNSp0Id4b1x
mRw4A5zokwN/C6vcVpLZM86Kc/q+Pi9kWkDRUm32jUmI2qWtqyXIOGZMUIfoSVe/
9czeJ66JFX4zwlZEfwIDAQAB
-----END PUBLIC KEY-----
Copy the code
2.2, the private key
-----BEGIN RSA PRIVATE KEY-----
MIICXAIBAAKBgQDjQRu0fImY/AVasfAneO4G8sIDP3L9XDX+nZq8GBw4vlDzoWIq
XGx8ETRMMRx+fdEz3Skdrlsp1+6NcYNSp0Id4b1xmRw4A5zokwN/C6vcVpLZM86K
c/q+Pi9kWkDRUm32jUmI2qWtqyXIOGZMUIfoSVe/9czeJ66JFX4zwlZEfwIDAQAB
AoGAFZiWXWyIVvV8PMY0IEwpspdXQJ/C+bjNmMi5b66K4AmV/9ESVxw5YwDvi14P
ayXhv6AAzEVJfIx8qwxignRKoAZ8pB1pqzFoZfudIsnQHJiukfFuriJeCxAmL6GY
Yjtf0QGGqZQp5S0RUaHeYH3Z7KYztcBQP7Wdn3RYVW9VVUECQQD2/0DBeEdzLUH4
0aV8UlHV+cWAd/ODd3TWYxbJ9WJ1GnsPL70Eg38DEmw95XehReUkFpwWFLRoGhjd
DbldIs5XAkEA64miXwxSGjxgjzcDM3wiZIRAljXjWeXhEugZSZgPLssp4r0Z0bdB
R0t6whtzqwxPGg8opB4aDUuOjpp/A0ISGQJACDoyZv9hqeV9CBO7pmt7jFwYhxH3
y45EFwwP60RANlReewAFFMxog6qublVhab7RRiV2p4mjBMCxyVM2tHJ/WwJAbYEm
qTPsM+BgMBUuetA6mSrXcD6Lfa8fbg/UOd/lJyczSQQLrfGZ+tB/uSDULPDjEcV8
apjIGehH1crERDqCeQJBAIDxsUBMiPxwDnYRf9d1QDFgRfe8XU54bpjzWCUNo0+e
rVVst5NNybClxvh7gWX4PgWxR2g1uIkCecvROTRMIzo=
-----END RSA PRIVATE KEY-----
Copy the code
3. Read public and private keys
The public key formats of RSA encryption are as follows: The KEY header is —–BEGIN RSA PUBLIC KEY—–, and the KEY header is —–BEGIN PUBLIC KEY—–, which correspond to the PKCS#1 and PKCS#8 formats of RSA respectively. The functions used to load the RSA public key using the OpenSSL library are also different. For the string public key, use the PEM_read_bio_RSAPublicKey() function to load the key in PKCS#1 format, and use the PEM_read_bio_RSA_PUBKEY() function to load the key in PKCS#8 format. The PEM_read_bio_RSAPrivateKey function is usually used to read private keys.
3.1 Loading key Data toBIO
There are two ways to convert secret key data to BIO objects:
- through
BIO_new_mem_buf
Function to read
// Read from a string
NSString *keyString = @ "";
const char *buffer = [keyString UTF8String];
bio = BIO_new_mem_buf(buffer, (int)strlen(buffer));
// Read from byte
NSData *keyData = data;
bio = BIO_new_mem_buf((const void*)[keyData bytes], (int)keyData.length);
Copy the code
- through
BIO_puts
generate
// Read from byte
NSData *keyData = data;
BIO *bio = BIO_new(BIO_s_mem());
BIO_puts(bio, (void*)[keyData bytes]);
Copy the code
3.2 fromBIO
Object reads the public or private key
RSA *SLRSAReadKeyFromBIO(BIO *bio, NSString *key, BOOL isPublicKey) {
if (bio == NULL) return NULL;
if(! isPublicKey) {// Read private key
RSA *rsa = PEM_read_bio_RSAPrivateKey(bio, NULL.NULL.NULL);
BIO_free_all(bio);
return rsa;
}
NSString *pkcs1_header = @"-----BEGIN RSA PUBLIC KEY-----";
// NSString *pkcs8_header = @"-----BEGIN PUBLIC KEY-----";
RSA *rsa = NULL;
if (key && [key containsString:pkcs1_header]) {
rsa = PEM_read_bio_RSAPublicKey(bio, &rsa, NULL.NULL);
}
else {
rsa = PEM_read_bio_RSA_PUBKEY(bio, NULL.NULL.NULL);
}
BIO_free_all(bio);
return rsa;
}
Copy the code
3.3 Key Reading Interface
RSA *SLRSAReadKey(id key, BOOL isPublicKey) {
if(! key) {return NULL;
}
BIO *bio = NULL;
NSString *key = nil;
if ([keyObject isKindOfClass:[NSData class]]) {
NSData *keyData = (NSData*)keyObject;
bio = BIO_new_mem_buf((const void*)[keyData bytes], (int)keyData.length);
}
else if ([keyObject isKindOfClass:[NSString class]]) {
key = (NSString*)keyObject;
const char *buffer = [key UTF8String];
bio = BIO_new_mem_buf(buffer, (int)strlen(buffer));
}
if (bio == NULL) {
NSLog(@"--bio new mem buf failed--");
return NULL;
}
return SLRSAReadKeyFromBIO(bio, key, isPublicKey);
}
Copy the code
4, server public key assembly
Typically, the public key returned by the server is a base64 string with the header and end formats removed:
MIGfMA0GCSqGSIb3DQEBAQUAA4GNADCBiQKBgQDjQRu0fImY/AVasfAneO4G8sID
P3L9XDX+nZq8GBw4vlDzoWIqXGx8ETRMMRx+fdEz3Skdrlsp1+6NcYNSp0Id4b1x mRw4A5zokwN/C6vcVpLZM86Kc/q+Pi9kWkDRUm32jUmI2qWtqyXIOGZMUIfoSVe/ 9czeJ66JFX4zwlZEfwIDAQAB
These strings need to be processed before they can be normally read into the memory. If there is special processing on the back end, the client must perform different processing:
NSString *SLRSAPEMKeyFromBase64(NSString *base64Key, BOOL isPublicKey) {
NSMutableString *result = [NSMutableString string];
if (isPublicKey) {
[result appendString:@"-----BEGIN PUBLIC KEY-----\n"];
}else{
[result appendString:@"-----BEGIN RSA PRIVATE KEY-----\n"];
}
[result appendString:@ """"];
int count = 0;
for (int i = 0; i < [base64Key length]; ++i) {
unichar c = [base64Key characterAtIndex:i];
if (c == '\n' || c == '\r') {
continue;
}
[result appendFormat:@"%c", c];
if (++count == 64) {
[result appendString:@"\n"];
[result appendString:@ """"];
count = 0; }}if (isPublicKey) {
[result appendString:@"\n-----END PUBLIC KEY-----"];
}else{
[result appendString:@"\n-----END RSA PRIVATE KEY-----"];
}
return result;
}
Copy the code
Encryption and decryption
After read the secret key to memory correctly, we can add the decryption operation, the RSA public key and a private key can be used to encrypt and decrypt, commonly used in pairs, if using a public key encryption, usable decrypted, if using a private key encryption, usable public key to decrypt, usually in the right way would be to use the public key encryption and private key to decrypt.
RSA encryption
After the utF-8 character string is encrypted, the byte data needs to be converted to Base64 for transmission
//
int SLRSAEncrypt(BOOL isPublic, RSA *rsa, NSString *src, NSString **dest, int padding) {
if (rsa == NULL || src.length <= 0 || dest == NULL) {
if (rsa) RSA_free(rsa);
return - 1;
}
int flen = RSA_size(rsa);
char *dst = (char*)malloc(flen + 1);
bzero(dst, flen);
NSData *srcData = [src dataUsingEncoding:NSUTF8StringEncoding];
int ret = - 1;
if (isPublic) {
ret = RSA_public_encrypt((int)srcData.length, (uint8_t*)[srcData bytes], (uint8_t*)dst, rsa, padding);
}
else {
ret = RSA_private_encrypt((int)srcData.length, (uint8_t*)[srcData bytes], (uint8_t*)dst, rsa, padding);
}
if (ret < 0) {
isPublic ? NSLog(@"--rsa public encrypt failed--") : NSLog(@"--ras private encrypt failed--");
RSA_free(rsa);
free(dst);
return ret;
}
NSData *encryptData = [NSData dataWithBytes:(const void*)dst length:sizeof(char)*flen];
*dest = [encryptData base64EncodedStringWithOptions:0];
RSA_free(rsa);
free(dst);
return ret;
}
Copy the code
RSA decryption
The ciphertext to be decrypted is generally in Base64 format. Rsa decryption is performed after Base64 decoding. The decrypted bytes can be converted into corresponding format strings for display
int SLRSADecrypt(BOOL isPublic, RSA *rsa, NSString *src, NSString **dest, int padding) {
if (rsa == NULL || src.length <= 0 || dest == NULL) {
if (rsa) RSA_free(rsa);
return - 1;
}
int flen = RSA_size(rsa);
char *dst = (char*)malloc(flen + 1);
bzero(dst, flen);
NSData *srcData = [[NSData alloc]initWithBase64EncodedString:src options:0];
int ret = - 1;
if (isPublic) {
ret = RSA_public_decrypt((int)srcData.length, (uint8_t*)[srcData bytes], (uint8_t*)dst, rsa, padding);
}
else {
ret = RSA_private_decrypt((int)srcData.length, (uint8_t*)[srcData bytes], (uint8_t*)dst, rsa, padding);
}
if (ret < 0) {
isPublic ? NSLog(@"--rsa public encrypt failed--") : NSLog(@"--ras private encrypt failed--");
RSA_free(rsa);
free(dst);
return ret;
}
NSData *decryptData = [NSData dataWithBytes:(const void*)dst length:sizeof(char)*flen];
*dest = [[NSString alloc]initWithData:decryptData encoding:NSUTF8StringEncoding];
RSA_free(rsa);
free(dst);
return ret;
}
Copy the code
Signature and Verification
The RSA encryption scheme is different from the RSA signature scheme. Generally, the private key is responsible for signing and the public key for authentication.
The private key signature
The private key signature is usually stored on the server. After the signature, the server sends the sign and public key to the client, and the client checks the signature
// Sha1 signature, output the base64 character string after the signature
int SLRSASha1SignWithKey(id key, NSString *src, NSString **sign) {
if ( !key || src.length <= 0 || sign == NULL) {
return - 1;
}
RSA *rsa = SLRSAReadKey(key, NO);
if (rsa == NULL) {
NSLog(@"--read rsa private key from bio failed--");
return - 1;
}
NSData *srcData = [src dataUsingEncoding:NSUTF8StringEncoding];
unsigned char digest[SHA_DIGEST_LENGTH];
SHA1((const unsigned char *) [srcData bytes], (size_t)srcData.length, digest);
unsigned char *signValue = (unsigned char *)malloc(256);
unsigned int sign_len;
int ret = RSA_sign(NID_sha1, digest, SHA_DIGEST_LENGTH, signValue, &sign_len, rsa);
if (ret == 1) {
NSData* data = [NSData dataWithBytes:signValue length:sign_len];
*sign = [data base64EncodedStringWithOptions:0];
}
free(signValue);
RSA_free(rsa);
return ret;
}
Copy the code
A public key to verify
After the signature is authenticated, the client can perform other operations
// If 1 is returned, the result is correct
int SLRSASha1VerifyWithPublicKey(id key, NSString *src, NSString *sign) {
if ( !key || src.length <= 0 || sign.length <= 0) {
return - 1;
}
RSA *rsa = SLRSAReadKey(key, YES);
if (rsa == NULL) {
NSLog(@"--read rsa public key from bio failed--");
return - 1;
}
NSData *srcData = [src dataUsingEncoding:NSUTF8StringEncoding];
unsigned char digest[SHA_DIGEST_LENGTH];
SHA1((const unsigned char *) [srcData bytes], (size_t)srcData.length, digest);
// SHA_CTX sha_ctx = { 0 };
// unsigned char digest[SHA_DIGEST_LENGTH];
// int rc = 1;
// rc = SHA1_Init(&sha_ctx);
// if (1 ! = rc) { return NO; }
//
// rc = SHA1_Update(&sha_ctx, [srcData bytes], srcData.length);
// if (1 ! = rc) { return NO; }
//
// rc = SHA1_Final(digest, &sha_ctx);
// if (1 ! = rc) { return NO; }
// Convert base64 signatures to bytes
NSData *signData = [[NSData alloc]initWithBase64EncodedString:sign options:0];
int ret = RSA_verify(NID_sha1, digest, SHA_DIGEST_LENGTH, (const uint8_t*)[signData bytes], (uint32_t)signData.length, rsa);
RSA_free(rsa);
return ret;
}
Copy the code
summary
RSA asymmetric encryption algorithm has a wide range of applications in daily communication security, programmers should master and flexibly use to solve daily problems. This chapter only demonstrates the basic use of RSA encryption, decryption, signature, and authentication. Further research and updates will be made on RSA algorithm and OpenSSL open source library.