Last active
April 21, 2022 17:42
-
-
Save pilotmoon/67c1d71b54b0d7e4b5c665c6e305dc64 to your computer and use it in GitHub Desktop.
Create PKCS #1 encoded RSA public key from raw modulus and exponent
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
#!/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