Home > Software engineering >  Swift "encrypt" String using RSA Private key with Security framework
Swift "encrypt" String using RSA Private key with Security framework

Time:11-19

I am working on a static library that takes an input String, encrypts it with the private key then returns it. (Because the client request to use private key on the mobile side.)

I am modifying code from this post and this post to work with the private key, but the result string always returns nil.

Key for testing:

let privateTestingKey = "-----BEGIN RSA PRIVATE KEY-----MIICXwIBAAKBgQDPJfl0gC95kB3sS0SOfXW45UooiYjT3ZeHRDn/eG So09x7cXLVbRILxxXfaU 9b5Yw2wETMvuzhP8C5Qd5PQy5lkH5LjcqJjDTvOBuBxNOSY4rmlqdy/skqM4FHjTGdwI2nZiVkjqOFXM XZ94Ar pszJifbSPElR1pO4NfT76QIDAQABAoGBAJjtoRdoFyR4yA6NlsRXTRS ehwpRVGcc2TScrrvL/ejB2DFuFOgJyNvXE4fHWK4y9j FP2rsJbRnyFhbu0O/VRNPa5Llnejea 5ov/3wxFqiae7pn6bjmiW/xdy0ycLWo90wLX9QqYjKBHomnh15FIajmSuRaUlBaL6DiZt010tAkEA0zLTPiH35meyXim7I7 mMmAN68zGu3cYpqq8i/xlHvXjWrBDV5saxRDsm97ktFqiRpvIoV3n0ZoT 3xjKhGINwJBAPsXM9hWdmWUXmaaAnXZDmKbNltRSZaaz4BktGRUJM0grt7kMndfBgd2JENNzYTfLpOWMIfHXpW9T4pVCjg5TN8CQQDGZf91ZbmYUx HP5KSUY4R0pQhR/wEzSt2HfwDUPW5cOnEHsMUQBuUtoJfJrMYDfBVfjCqDiogh6pv2/jX4yJfAkEAqwsvQhwEI0Zi2DnpmyX1aq6Y5LQHERT8bVYsnHvFZgbxmNySlEai8MpGAaMqcW0naVpSTOw/PnnriSxM/efquQJBAM/3eRqzBHp/v3bRM npSgEOaeOmtNiz oNrJ09mUToyDTkuFOg7J9ojNkeviR4xqs9Hoos8lP3ho1uVcE3/tRQ=-----END RSA PRIVATE KEY-----"

Code for RSA encryption:

import Foundation
import Security

public class MPEncryption {
  var publicKey: SecKey!
  
  public init(publicKeyString: String) throws {
    self.publicKey = try makePublicKey(from: publicKeyString)
  }
  
  private func makePublicKey(from keyString: String) throws -> SecKey {
    let pubKeyData = Data(base64Encoded: sanitize(key: keyString))!
    
    let query: [String: Any] = [
      kSecAttrKeyType as String: kSecAttrKeyTypeRSA,
      kSecAttrKeyClass as String: kSecAttrKeyClassPrivate,
      kSecAttrKeySizeInBits as String: 1024
    ]
    
    var unmanagedError: Unmanaged<CFError>?
    
    guard let pubKeyRef = SecKeyCreateWithData(pubKeyData as CFData, query as CFDictionary, &unmanagedError) else {
      throw unmanagedError!.takeRetainedValue() as Error
    }
    
    return pubKeyRef
  }
  
  public func encrypt(value: String) throws -> String {
    let valueData = value.data(using: .utf8)!
    
    let bufferSize = SecKeyGetBlockSize(publicKey) - 11
    let buffers = makeBuffers(fromData: valueData, bufferSize: bufferSize)
    
    var encryptedData = Data()
    
    for buffer in buffers {
      var encryptionError: Unmanaged<CFError>?
      
      guard let encryptedBuffer = SecKeyCreateEncryptedData(publicKey, .rsaEncryptionPKCS1, buffer as CFData, &encryptionError) as Data? else {
        throw encryptionError!.takeRetainedValue() as Error
      }
      
      encryptedData.append(encryptedBuffer)
    }
    
    return encryptedData.base64EncodedString()
  }
  
  private func makeBuffers(fromData data: Data, bufferSize: Int) -> [Data] {
    guard data.count > bufferSize else {
      return [data]
    }
    
    var buffers: [Data] = []
    
    for i in 0..<bufferSize {
      let start = i * bufferSize
      
      let lengthOffset = start   bufferSize
      let length = lengthOffset < data.count ? bufferSize : data.count - start
      
      let bufferRange = Range<Data.Index>(NSMakeRange(start, length))!
      buffers.append(data.subdata(in: bufferRange))
    }
    
    return buffers
  }
  
  private func sanitize(key: String) -> String {
    let headerRange = key.range(of: "-----BEGIN RSA PRIVATE KEY-----")
    let footerRange = key.range(of: "-----END RSA PRIVATE KEY-----")
    
    var sanitizedKey = key
    
    if let headerRange = headerRange, let footerRange = footerRange {
      let keyRange = Range<String.Index>(uncheckedBounds: (lower: headerRange.upperBound, upper: footerRange.lowerBound))
      sanitizedKey = String(key[keyRange])
    }
    
    return sanitizedKey
      .trimmingCharacters(in: .whitespacesAndNewlines)
      .components(separatedBy: "\n")
      .joined()
  }
}

I can't locate the problem, any suggestion is appreciated.

CodePudding user response:

When the encrypt() method is called, the following error message is displayed on my machine:

encrypt:RSA:PKCS1: algorithm not supported by the key <SecKeyRef algorithm id: 1, key type: RSAPrivateKey, ...>

This is not surprising, since the code tries to encrypt with a private key (labeled in the code as public key, which is misleading!). However, a private key is applied only for signing, while a public key is used for encryption.

Encryption with the private key is not consistently implemented by libraries. Some extract the public key from the private key and encrypt with the public key, some create a signature with the private key and others do not support this at all (like this library here).

It seems most likely to me that your requirement is to create a signature. Technically, encryption and signing are similar and differ in that encryption uses the public key for modular exponentiation and signing uses the private key. In addition, the padding variants differ. However, the purposes are completely different; encryption is about confidentiality, while signing is about verifying authenticity.

So you could try signing instead of encrypting. For this, the code has to be changed only slightly (however, sooner or later the variable names should also be adapted):

guard let encryptedBuffer = SecKeyCreateSignature(publicKey, .rsaSignatureDigestPKCS1v15Raw, buffer as CFData, &encryptionError) as Data? else {
  throw encryptionError!.takeRetainedValue() as Error
}

encryptedData now contains the signature (and publicKey denotes the private key).

Note that for rsaSignatureDigestPKCS1v15Raw the message is signed directly, i.e. without hashing or considering the digest ID. Since no digest ID is specified, this might be the correct variant.

However, be aware that the message length is limited (analogous to encryption). Compliant with the standard is a hashing and the consideration of the digest ID, which is achieved e.g. with rsaSignatureMessagePKCS1v15SHA256 for SHA-256.

  • Related