RSA Encryption function in Swift 4 from Public Key string

11,411

Solution 1

You can do like this way...

static func encrypt(string: String, publicKey: String?) -> String? {
        guard let publicKey = publicKey else { return nil }

        let keyString = publicKey.replacingOccurrences(of: "-----BEGIN PUBLIC KEY-----\n", with: "").replacingOccurrences(of: "\n-----END PUBLIC KEY-----", with: "")
        guard let data = Data(base64Encoded: keyString) else { return nil }

        var attributes: CFDictionary {
            return [kSecAttrKeyType         : kSecAttrKeyTypeRSA,
                    kSecAttrKeyClass        : kSecAttrKeyClassPublic,
                    kSecAttrKeySizeInBits   : 2048,
                    kSecReturnPersistentRef : kCFBooleanTrue] as CFDictionary
        }

        var error: Unmanaged<CFError>? = nil
        guard let secKey = SecKeyCreateWithData(data as CFData, attributes, &error) else {
            print(error.debugDescription)
            return nil
        }
        return encrypt(string: string, publicKey: secKey)
    }

    static func encrypt(string: String, publicKey: SecKey) -> String? {
        let buffer = [UInt8](string.utf8)

        var keySize   = SecKeyGetBlockSize(publicKey)
        var keyBuffer = [UInt8](repeating: 0, count: keySize)

        // Encrypto  should less than key length
        guard SecKeyEncrypt(publicKey, SecPadding.PKCS1, buffer, buffer.count, &keyBuffer, &keySize) == errSecSuccess else { return nil }
        return Data(bytes: keyBuffer, count: keySize).base64EncodedString()
    }

Solution 2

There is RSAUtils.swift class available on github. Add this file to your project.

Then you can use following functions to encrypt/decrypt any string content with base64-encoded public token:

/**
 Returns RSA encrypted Base64 encoded string with specified public key which is Base64 encoded string.

 - parameter withPublickKeyBase64: Base64 encoded string value of public key.
 - returns: RSA encrypted Base64 encoded string.
 */
func encryptRsaBase64(_ string: String, withPublickKeyBase64: String) -> String? {
    if let data = string.data(using: .utf8) {
        if let encrypted = RSAUtils.encryptWithRSAPublicKey(data, pubkeyBase64: withPublickKeyBase64, keychainTag: "") {
            return encrypted.base64EncodedString()
        }
    }
    return nil
}

/**
 Returns RSA decrypted Base64 encoded string with specified public key which is Base64 encoded string.

 - parameter withPublickKeyBase64: Base64 encoded string value of public key.
 - returns: RSA decrypted Base64 encoded string.
 */
func decryptRsaBase64Encrypted(_ string: String, withPublicKeyBase64: String) -> String? {
    if let encrypted = Data(base64Encoded: string, options: Data.Base64DecodingOptions.init(rawValue: 0)) {
        if let data = RSAUtils.decryptWithRSAPublicKey(encrypted, pubkeyBase64: withPublicKeyBase64, keychainTag: "") {
            return String(data: data, encoding: .utf8)
        }
    }
    return nil
}
Share:
11,411
Admin
Author by

Admin

Updated on June 18, 2022

Comments

  • Admin
    Admin almost 2 years

    My ultimate goal is to create a JWE string, given a public key for iOS.

    To make things easier for myself, I've broken down my steps so most pressingly, I need to create an encrypted key using RSA encryption from a secret key and public key string.

    I've tried a lot of things found here in stack overflow and other places of the internet. For various reasons they just haven't worked out.

    I am being guided by some Objective C code:

    /* Device Data encryption - create JWE given DS publicKey */ +(NSString *)createJWE:(NSString *)payload withPublicKey:(SecKeyRef)publicKey {
    // create secretKey for encryption
    NSData *secret = [self generateRandom:(KEY_SIZE*2)];
    NSData *hmacKey = [secret subdataWithRange:NSMakeRange(0, KEY_SIZE)]; NSData *aesKey = [secret subdataWithRange:NSMakeRange(KEY_SIZE,
    KEY_SIZE)];
      NSData *iv = [self generateRandom: IV_SIZE];
    // create header
    NSString *header = @"{\"enc\":\"A128CBC-HS256\",\"alg\":\"RSA-OAEP\"}";
    // encrypt secretKey
    NSData *encryptedKey = [self rsaEncrypt:secret key:publicKey];
      // encrypt payload
    NSData *encrypted = [self aesEncrypt:[payload dataUsingEncoding:NSUTF8StringEncoding] withKey:aesKey withIV:iv];
    NSString *basePayload = [encrypted unpaddedBase64URLEncoded];
    NSString *baseCEK = [encryptedKey unpaddedBase64URLEncoded];
    NSString *baseHeader = [[header dataUsingEncoding:NSUTF8StringEncoding]
    unpaddedBase64URLEncoded];
    NSString *baseIV = [iv unpaddedBase64URLEncoded];
    // create auth hash
    NSData *hmac = [self hmac: encrypted withKey: hmacKey withIV: iv withA:
    [baseHeader dataUsingEncoding:NSASCIIStringEncoding]];
    return [NSString stringWithFormat:@"%@.%@.%@.%@.%@", baseHeader, baseCEK, baseIV, basePayload, [[self hmacToTag: hmac] unpadded
    

    I am currently at this point of the function:

    NSData *encryptedKey = [self rsaEncrypt:secret key:publicKey];
    

    From this I have made the assumption that there is an rsaEncrypt function that accepts a secret and public key.

    The public key I have access to looks like this:

    let publicKey = """
    -----BEGIN PUBLIC KEY-----
    MIGfMA0GCSqGSIb3DQEBAQUAA4GNADCBiQKBgQDEChqe80lJLTTkJD3X3Lyd7Fj+
    zuOhDZkjuLNPog3YR20e5JcrdqI9IFzNbACY/GQVhbnbvBqYgyql8DfPCGXpn0+X
    NSxELIUw9Vh32QuhGNr3/TBpechrVeVpFPLwyaYNEk1CawgHCeQqf5uaqiaoBDOT
    qeox88Lc1ld7MsfggQIDAQAB
    -----END PUBLIC KEY-----
    """
    

    Examples found on the internet handles the public key as a SecKey type. This is my first hurdle to performing the encryption.

    1. How do I convert the string to a SecKey, if it must be converted?
    2. What should my rsaEncrypt function do?

    I expect the output to be an encrypted key after performing the encryption.

    Update: Another variation, One public key sent also looks like this:

    { "kty": "RSA", "kid": "UUIDkeyidentifierforDS", "use": "enc", "n": "n4EPtAOCc9AlkeQHPzHStgAbgs7bTZLwUBZdR8_KuKPEHLd4rHVTeT-O- XV2jRojdNhxJWTDvNd7nqQ0VEiZQHz_AJmSCpMaJMRBSFKrKb2wqVwGU_NsYOYL- QtiWN2lbzcEe6XC0dApr5ydQLrHqkHHig3RBordaZ6Aj-oBHqFEHYpPe7Tpe- OfVfHd1E6cS6M1FZcD1NNLYD5lFHpPI9bTwJlsde3uhGqC0ZCuEHg8lhzwOHrtIQbS0F Vbb9k3- tVTU4fg_3L_vniUFAKwuCLqKnS2BYwdq_mzSnbLY7h_qixoR7jig3__kRhuaxwUkRz5i aiQkqgc5gHdrNP5zw", "e": "AQAB" }
    
  • Admin
    Admin almost 5 years
    getting a fatal error trying to use the class: Thread 1: Fatal error: Unexpectedly found nil while unwrapping an Optional value
  • Admin
    Admin almost 5 years
    fails on this function ``` // Base64 decode a base64-ed string static fileprivate func base64Decode(_ strBase64: String) -> Data { let data = Data(base64Encoded: strBase64, options: []) return data! }```
  • Otto Cheley
    Otto Cheley over 3 years
    Where did you learn to make your own implementation of this xD, It worked for me :D
  • Man_Parashar
    Man_Parashar over 2 years
    getting nil on using this. Can you please share any tutorial or something that is required for it.
  • famfamfam
    famfamfam over 2 years
    can u provide decrypt code? Thanks