Skip to content

Instantly share code, notes, and snippets.

@pilotmoon
Last active April 21, 2022 17:42
Show Gist options
  • Star 2 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save pilotmoon/67c1d71b54b0d7e4b5c665c6e305dc64 to your computer and use it in GitHub Desktop.
Save pilotmoon/67c1d71b54b0d7e4b5c665c6e305dc64 to your computer and use it in GitHub Desktop.
Create PKCS #1 encoded RSA public key from raw modulus and exponent
#!/usr/bin/env swift
/* This code takes an RSA public key as raw modulus and exponent, and packages them into the PKCS #1 format
as required by SecKeyCreateWithData function in the Apple Security framework.
It is heavily based on this StackOverflow answer by dnlggr here:
https://stackoverflow.com/questions/27132937/create-seckey-from-modulus-and-exponent-in-swift
Tip: you can paste this into Swift playgrounds (removing the top line),
or save as file with chmod +x and run directly in shell.
*/
import Cocoa
// example values - insert your modulus and exponent here
let modulusData = Data(base64Encoded: "wOTAXgzXqUgBs2U90Ul+Br2BcVHYYs1nN62th64f2BChEBgy4+GaEAUfGwUuD452Lx4acLAXLaU0gumIMfbQQ/u0TyMmyaKC03uH7mFvGcCgrDi8sQjhFwpkRYHCoNBs0L9+VQZbmYbjqpqHabPTjqqLuNOBaoctr4y+2V8+PNE=")!
var modulus = [UInt8](modulusData)
print("modulus, size \(modulus.count):", modulus)
let exponent: [UInt8] = [0x03] // encode the exponent as big-endian bytes
// let exponent: [UInt8] = [0x01, 0x00, 0x01] // example encoding of 65537
print("exponent:", exponent)
// prefix with 0x00 to indicate that it is a non-negative number
modulus.insert(0x00, at: 0)
// encode the modulus and exponent as INTEGERs
var modulusEncoded: [UInt8] = [0x02]
modulusEncoded.append(contentsOf: lengthField(of: modulus))
modulusEncoded.append(contentsOf: modulus)
var exponentEncoded: [UInt8] = [0x02]
exponentEncoded.append(contentsOf: lengthField(of: exponent))
exponentEncoded.append(contentsOf: exponent)
// combine these INTEGERs to a SEQUENCE
var sequenceEncoded: [UInt8] = [0x30]
sequenceEncoded.append(contentsOf: lengthField(of: (modulusEncoded + exponentEncoded)))
sequenceEncoded.append(contentsOf: (modulusEncoded + exponentEncoded))
// all done, we now have the PKCS#1 key
print("encoded key:",sequenceEncoded)
let keyData = Data(sequenceEncoded)
print("encoded key, base64:", keyData.base64EncodedString())
// let's check it actully works...
let attributes: [String: Any] = [
kSecAttrKeyType as String: kSecAttrKeyTypeRSA,
kSecAttrKeyClass as String: kSecAttrKeyClassPublic,
kSecAttrKeySizeInBits as String: modulus.count * 8
]
var error: Unmanaged<CFError>?
let publicKey = SecKeyCreateWithData(keyData as CFData, attributes as CFDictionary, &error)
print("publicKey:", publicKey ?? "👎")
print("error:", error ?? "👍")
// Expected output:
// publicKey: <SecKeyRef algorithm id: 1, key type: RSAPublicKey, version: 4, block size: 1024 bits, exponent: {hex: 3, decimal: 3}, modulus: ...
// helper function, used above, to compute the length field of a DER type
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
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment