How to create Private SecKey from modulus and private exponent

I am working on an application on IOS and I need to be able to decrypt an element that has been encrypted with a public key, but I only know the modulus and the private exponent of the private key. How can I obtain the private key from these elements in IOS? In java, with the following code I can get the private key from these elements and I can decrypt the element.

final RSAPrivateKeySpec privateSpec = new RSAPrivateKeySpec(MODULUS, PRIVATE_EXPONENT);
final KeyFactory factory = KeyFactory.getInstance("RSA"); 
final PrivateKey priv = factory.generatePrivate(privateSpec);

On ios, I got the following code, but it doesn't return the private key:

     let smodulus = "00c35d6ac32d287dab681c78272931b60ca442bd4505e1b306432af92ef6e07bc3e4e24c2bc2dac69c94753390c3793b80734ef906520a96cafb146ce587ab383e78efc58c5ccf56b5084f3f9bc719e646a0361155ab56b7d71b49f1f90673383ea0fdb4a67900c0973930aa3bc7071c479b4a5d229fcab7ce22b84c1637d7e70b"

    let sPrivateExponent = "1f2535700832fc55e73322b49b8ca14942a6a125efda1b805414f5c4d6f6d2c45828fb9bbe64c2651405db42a8e71ae54f7cb7969a5db5cc865aab41ac6f4f77deccb51496fe588f9dcd724a1d49cab96712621afc9c656c56dea011f8ab8e12a321fd62430902b15586a36093a6679e31b67017397da83b0992f5f816017701"

    let sPublicExponent = "010001"


   public static func getPrivateKey(smodulus: String, sPrivateExponent: String) -> SecKey? {
        
    let pubExponent: [UInt8] = [1, 0, 1]
    let exponent: [UInt8] = sPrivateExponent.hexaBytes
    var modulus: [UInt8] = smodulus.hexaBytes

     
    //modulus.insert(0x00, at: 0)
     
    var modulusEncoded: [UInt8] = []
    modulusEncoded.append(0x02)
    modulusEncoded.append(contentsOf: lengthField(of: modulus))
    modulusEncoded.append(contentsOf: modulus)

    var exponentEncoded: [UInt8] = []
    exponentEncoded.append(0x02)
    exponentEncoded.append(contentsOf: lengthField(of: exponent))
    exponentEncoded.append(contentsOf: exponent)
     
    var sequenceEncoded: [UInt8] = []
    sequenceEncoded.append(0x30)
    sequenceEncoded.append(contentsOf: lengthField(of: (modulusEncoded + exponentEncoded)))
    sequenceEncoded.append(contentsOf: (modulusEncoded + exponentEncoded))
     
    let keyData = Data(bytes: sequenceEncoded)
     
    // RSA key size is the number of bits of the modulus.
    let keySize = (modulus.count * 8)

    let attributes: [String: Any] = [
      kSecAttrKeyType as String: kSecAttrKeyTypeRSA,
      kSecAttrKeyClass as String: kSecAttrKeyClassPrivate,
      kSecAttrKeySizeInBits as String: keySize
    ]
     
     
    let privateKey = SecKeyCreateWithData(keyData as CFData, attributes as CFDictionary, nil)
           
     
    return privateKey
  }

 private static func lengthField(of valueField: [UInt8]) -> [UInt8] {
    var count = valueField.count

    if count < 128 {
      return [ UInt8(count) ]
    }

    // The number of bytes needed to encode count.
    let lengthBytesCount = Int((log2(Double(count)) / 8) + 1)

    // The first byte in the length field encoding the number of remaining bytes.
    let firstLengthFieldByte = UInt8(128 + lengthBytesCount)

    var lengthField: [UInt8] = []
    for _ in 0..<lengthBytesCount {
      // Take the last 8 bits of count.
      let lengthByte = UInt8(count & 0xff)
      // Add them to the length field.
      lengthField.insert(lengthByte, at: 0)
      // Delete the last 8 bits of count.
      count = count >> 8
    }

    // Include the first byte.
    lengthField.insert(firstLengthFieldByte, at: 0)

    return lengthField
  }


extension StringProtocol {
  var hexaData: Data { .init(hexa) }
  var hexaBytes: [UInt8] { .init(hexa) }
  private var hexa: UnfoldSequence<UInt8, Index> {
    sequence(state: startIndex) { startIndex in
      guard startIndex < self.endIndex else { return nil }
      let endIndex = self.index(startIndex, offsetBy: 2, limitedBy: self.endIndex) ?? self.endIndex
      defer { startIndex = endIndex }
      return UInt8(self[startIndex..<endIndex], radix: 16)
    }
  }
}

Accepted Reply

In case it helps anyone else, with CryptoSwift you can create an RSA key by passing these parameters and it is possible to decrypt the encrypted message with your public key.

Replies

I've not looked in detail, but I suspect java creates automatically some meta data. Which you would have to create yourself in Swift.

Hope this may help: https://baptistout.net/posts/convert-jwks-modulus-exponent-to-pem-or-ssh-public-key/

  • Thanks for your answer. The link that you have attached indicates how to create the public key, but I have solved this part. The problem resides in creating the private key using the modulus and private exponent (not to be confused with the public exponent that is used for the public key).

Add a Comment

In case it helps anyone else, with CryptoSwift you can create an RSA key by passing these parameters and it is possible to decrypt the encrypted message with your public key.

Just to close the loop here…

Apple does not have an API to build an RSA key from its components. My general advice on this front is:

  1. Stop using RSA. EC keys are better in almost every way.

  2. If you can’t, tweak your key generation code to combine the components into something that iOS can import. For more on that, see Importing Cryptographic Keys.

  3. If you can’t, you’ll need to write or acquire code to do this.

Share and Enjoy

Quinn “The Eskimo!” @ Developer Technical Support @ Apple
let myEmail = "eskimo" + "1" + "@" + "apple.com"