I am happy to see this article. At that time, when I was doing RSA encryption and signature, the information on the Internet should not be too old. After that, I could not bear it anymore, this article will explain in detail how to implement RSA encryption and signature in iOS and fully synchronize with Java. This is my second blog. If you have any shortcomings, please advise.
What is RSA?
A: RSA is an asymmetric encryption algorithm. It is often used to encrypt transmitted data. It can be used together with the digest algorithm to sign text.
Padding in RSA encryption?
A: padding is the filling method. Because the plaintext to be encrypted in the RSA encryption algorithm is smaller than the modulus, padding restricts the length of the plaintext in some filling methods. We will introduce several padding modes and Segment encryption in detail later.
What is the difference between encryption and signature?
A: Encryption: The public key is placed on the client and the data is encrypted using the public key. After the server obtains the data, it uses the private key for decryption;
Signature: the private key is placed on the client, and the private key is used to sign the data. After the server obtains the data, it uses the public key for signature verification.
The former is completely encrypted; the latter is mainly used to prevent malicious attacks and prevent others from simulating our clients to attack our servers, resulting in server paralysis.
Basic principles
RSA uses a "Key pair" to encrypt and decrypt data. Before encryption and decryption, you need to save the Public Key and Private Key ).
Public key: used to encrypt data. Used to make Public. It is generally stored in the data provider, such as the iOS client.
Private key: used to decrypt data. It must be kept confidential. Private key leakage may cause security issues.
Security. framework in iOS provides support for the RSA algorithm. In this way, you need to process the key pair, generate a certificate based on the public key, and generate a key in p12 format through the private key. It's much easier to use strings for encryption and decryption in jave. (⊙ O ⊙ )...
Practice
Certificate generation
RSA encrypts the public key and private key. Apple does not support direct encryption and decryption using strings. We recommend that you use p12 files. Here we will teach you how to generate all the files used in encryption and provide them to Java. I thought this public key and private key had been around for a long time. % >_< %
Generate a private key with a modulo length of bits
Openssl genrsa-out private_key.pem 1024
Generate certification require file
Openssl req-new-key private_key.pem-out rsaCertReq. csr
Generate a certification and specify the Expiration Time
Openssl x509-req-days3650-in rsaCertReq. csr-signkey private_key.pem-out rsaCert. crt
Generate a public key for iOS
Openssl x509-outform der-in rsaCert. crt-out public_key.der
Generating a private key for iOS will allow you to enter the password, which will be used later when secKeyRef is generated
Openssl pkcs12-export-out private_key.p12-inkey private_key.pem-in rsaCert. crt
Generate the public key ending with pem for Java
Openssl rsa-in private_key.pem-out rsa_public_key.pem-pubout
Generate the private key ending with pem for Java to use openssl pkcs8-topk8-in private_key.pem-out pkcs8_private_key.pem-nocrypt
All the above steps are completed under the terminal (^__ ^)
SecKeyRef for generating public and private keys
// SecKeyRef corresponding to the private key generated based on your p12 file. If it is nil, check the steps for generating your p12 file.
-(SecKeyRef) getPrivateKeyRefrenceFromData :( NSData *) p12Data password :( NSString *) password {
SecKeyRef privateKeyRef = NULL;
NSMutableDictionary * options = [[NSMutableDictionary alloc] init];
[Options setObject: password forKey :( _ bridge id) kSecImportExportPassphrase];
CFArrayRef items = CFArrayCreate (NULL, 0, 0, NULL );
OSStatus securityError = SecPKCS12Import (_ bridge CFDataRef) p12Data, (_ bridge CFDictionaryRef) options, & items );
If (securityError = noErr & CFArrayGetCount (items)> 0 ){
CFDictionaryRef identityDict = CFArrayGetValueAtIndex (items, 0 );
SecIdentityRef identityApp = (SecIdentityRef) CFDictionaryGetValue (identityDict, kSecImportItemIdentity );
SecurityError = SecIdentityCopyPrivateKey (identityApp, & privateKeyRef );
If (securityError! = NoErr ){
PrivateKeyRef = NULL;
}
}
CFRelease (items );
Return privateKeyRef;
}
-
// SecKeyRef corresponding to the public key of your der file
-(SecKeyRef) getPublicKeyRefrenceFromeData: (NSData *) derData {
SecCertificateRef myCertificate = SecCertificateCreateWithData (kCFAllocatorDefault, (_ bridge CFDataRef) derData );
SecPolicyRef myPolicy = SecPolicyCreateBasicX509 ();
SecTrustRef myTrust;
OSStatus status = SecTrustCreateWithCertificates (myCertificate, myPolicy, & myTrust );
SecTrustResultType trustResult;
If (status = noErr ){
Status = SecTrustEvaluate (myTrust, & trustResult );
}
SecKeyRef securityKey = SecTrustCopyPublicKey (myTrust );
CFRelease (myCertificate );
CFRelease (myPolicy );
CFRelease (myTrust );
Return securityKey;
}
Encryption and decryption
-(NSData *) rsaEncryptData :( NSData *) data {
SecKeyRef key = [self getPublicKey];
Size_t cipherBufferSize = SecKeyGetBlockSize (key );
Uint8_t * cipherBuffer = malloc (cipherBufferSize * sizeof (uint8_t ));
Size_t blockSize = cipherBufferSize-11;
Size_t blockCount = (size_t) ceil ([data length]/(double) blockSize );
NSMutableData * encryptedData = [[NSMutableData alloc] init];
For (int I = 0; I <blockCount; I ++ ){
Unsigned long bufferSize = MIN (blockSize, [data length]-I * blockSize );
NSData * buffer = [data subdataWithRange: NSMakeRange (I * blockSize, bufferSize)];
OSStatus status = SecKeyEncrypt (key, kSecPaddingPKCS1, (const uint8_t *) [buffer bytes], [buffer length], cipherBuffer, & cipherBufferSize );
If (status! = NoErr ){
Return nil;
}
NSData * encryptedBytes = [[NSData alloc] initWithBytes :( const void *) cipherBuffer length: cipherBufferSize];
[EncryptedData appendData: encryptedBytes];
}
If (cipherBuffer ){
Free (cipherBuffer );
}
Return encryptedData;
}
-
-(NSData *) rsaDecryptData :( NSData *) data {
SecKeyRef key = [self getPrivatKey];
Size_t cipherBufferSize = SecKeyGetBlockSize (key );
Size_t blockSize = cipherBufferSize;
Size_t blockCount = (size_t) ceil ([data length]/(double) blockSize );
NSMutableData * decryptedData = [[NSMutableData alloc] init];
For (int I = 0; I <blockCount; I ++ ){
Unsigned long bufferSize = MIN (blockSize, [data length]-I * blockSize );
NSData * buffer = [data subdataWithRange: NSMakeRange (I * blockSize, bufferSize)];
Size_t cipherLen = [buffer length];
Void * cipher = malloc (cipherLen );
[Buffer getBytes: cipher length: cipherLen];
Size_t plainLen = SecKeyGetBlockSize (key );
Void * plain = malloc (plainLen );
OSStatus status = SecKeyDecrypt (key, kSecPaddingPKCS1, cipher, cipherLen, plain, & plainLen );
If (status! = NoErr ){
Return nil;
}
NSData * decryptedBytes = [[NSData alloc] initWithBytes :( const void *) plain length: plainLen];
[DecryptedData appendData: decryptedBytes];
}
Return decryptedData;
}
Padding in RSA encryption
RSA_PKCS1_PADDING mode, the most common mode
Requirement: the input must be at least 11 bytes shorter than the RSA key modulo (modulus), that is, RSA_size (rsa)-11. If the input plaintext is too long, it must be cut and then filled.
Output: same length as modulus
According to this requirement, for a 1024/8-bit key, block length = 117-11 = bytes
RSA_PKCS1_OAEP_PADDING
Input: RSA_size (rsa)-41
Output: same length as modulus
RSA_NO_PADDING is not filled
Input: it can be as long as the RSA key modulo. If the input plaintext is too long, it must be cut and then filled
Output: same length as modulus
Signature and verification
// Sign the data with sha256
-(NSData *) rsaSHA256SignData :( NSData *) plainData {
SecKeyRef key = [self getPrivatKey];
Size_t signedHashBytesSize = SecKeyGetBlockSize (key );
Uint8_t * signedHashBytes = malloc (signedHashBytesSize );
Memset (signedHashBytes, 0x0, signedHashBytesSize );
Size_t hashBytesSize = CC_SHA256_DIGEST_LENGTH;
Uint8_t * hashBytes = malloc (hashBytesSize );
If (! CC_SHA256 ([plainData bytes], (CC_LONG) [plainData length], hashBytes )){
Return nil;
}
SecKeyRawSign (key,
KSecPaddingPKCS1SHA256,
HashBytes,
HashBytesSize,
SignedHashBytes,
& SignedHashBytesSize );
NSData * signedHash = [NSData dataWithBytes: signedHashBytes
Length :( NSUInteger) signedHashBytesSize];
If (hashBytes)
Free (hashBytes );
If (signedHashBytes)
Free (signedHashBytes );
Return signedHash;
}
-
// If the signature data is successfully verified, YES is returned.
-(BOOL) rsaSHA256VerifyData :( NSData *) plainData withSignature :( NSData *) signature {
SecKeyRef key = [self getPublicKey];
Size_t signedHashBytesSize = SecKeyGetBlockSize (key );
Const void * signedHashBytes = [signature bytes];
Size_t hashBytesSize = CC_SHA256_DIGEST_LENGTH;
Uint8_t * hashBytes = malloc (hashBytesSize );
If (! CC_SHA256 ([plainData bytes], (CC_LONG) [plainData length], hashBytes )){
Return NO;
}
OSStatus status = SecKeyRawVerify (key,
KSecPaddingPKCS1SHA256,
HashBytes,
HashBytesSize,
SignedHashBytes,
SignedHashBytesSize );
Return status = errSecSuccess;
}