Мне было интересно, как я могу загрузить открытый ключ RSA из строки и зашифровать с его помощью другую строку. Открытый ключ выглядит примерно так:
let key = """ -----BEGIN PUBLIC KEY----- blah blah blah -----END PUBLIC KEY-----"""
Я бы предпочел сделать это без внешней библиотеки, но если это упростит задачу, я готов их использовать. Заранее спасибо.
@serg_zhd, к сожалению, нет. SecKeyEncrypt не существует в macos.
Ключевые данные, которые у вас есть, закодированы в PEM . Однако Apple поддерживает кодировку DER для своего API безопасности. Итак, сначала нам нужно преобразовать данные вашего ключа в правильный формат. Пример ниже создан со случайным ключом RSA, экспортирующим открытый ключ в формат PEM. Заголовки PEM могут отличаться от библиотеки к библиотеке, поэтому обязательно удалите теги, прежде чем переходить к преобразованию DER.
var pem = "-----BEGIN RSA PUBLIC KEY-----\nMIIBCgKCAQEAs6AofVx+UAXcVjnIU0Z5SAGO/LPlTunA9zi7jNDcIrZTR8ULHTrm\naSAg/ycNR1/wUeac617RrFeQuoPSWjhZPRJrMa3faVMCqTgV2AmaPgKnPWBrY2ir\nGhnCnIAvD3sitCEKultjCstrTA71Jo/BuVaj6BVgaA/Qn3U9mQ+4JiEFiTxy4kOF\nes1/WwTLjRQYVf42oG350bTKw9F0MklTTZdiZKCQtc3op86A7VscFhwusY0CaZfB\nlRDnTgTMoUhZJpKSLZae93NVFSJY1sUANPZg8TzujqhRKt0g5HR/Ud61icvBbcx8\n+a3NzmuwPylvp5m6hz/l14Y7UZ8UT5deywIDAQAB\n-----END RSA PUBLIC KEY-----\n"
// Remove headers and footers from the PEM, leaving us with DER encoded data split by new lines
[
"-----BEGIN RSA PUBLIC KEY-----", "-----END RSA PUBLIC KEY-----",
"-----BEGIN PUBLIC KEY-----", "-----END PUBLIC KEY-----"
].forEach { pem = pem.replacingOccurrences(of: $0, with: "") }
// Construct DER data from the remaining PEM data
let der = Data(base64Encoded: pem, options: .ignoreUnknownCharacters)!
Теперь, когда у нас есть данные в кодировке DER, пришло время создать наш ключ. Сначала создайте атрибуты, описывающие ключ, после чего создайте ключ из данных DER. Обратите внимание, что der относится к типу Data, а не к типу String. Вообще говоря, криптооперации происходят на Data. Однако API безопасности использует CFData, но их можно легко заменить (as CFData или as Data). То же самое касается словаря атрибутов.
// Key generation attributes
let attributes: [String: Any] = [
String(kSecAttrKeyType): kSecAttrKeyTypeRSA,
String(kSecAttrKeyClass): kSecAttrKeyClassPublic,
String(kSecAttrKeySizeInBits): der.count * 8
]
// For simplicity I force unwrap here. The nil parameter can be used to extract an error
let key = SecKeyCreateWithData(der as CFData, attributes as CFDictionary, nil)!
Теперь, когда у нас есть ключ, мы можем использовать его для шифрования. Имейте в виду, однако, что RSA не может шифровать огромные объемы данных (технически это неверно, вы можете создавать фрагменты данных и делать это таким образом, но RSA не предназначен для этого. Если вы хотите сделать это, прочитайте об обмене ключами и симметричное шифрование. RSA не предназначен для такого поведения). Также обратите внимание, что OAEP (используемый в примере) имеет случайный фактор. Это означает, что вывод зашифрованного текста будет отличаться каждый раз, когда вы запускаете этот код. Это не значит, что он не работает, это просто свойство OAEP. Я также хотел бы отметить, что следует избегать заполнения PKCS1, когда это возможно, в пользу OAEP.
// An example message to encrypt
let plainText = "This is my secret".data(using: .utf8)!
// Perform the actual encryption
// Again force unwrapping for simplicity
let cipherText = SecKeyCreateEncryptedData(key, .rsaEncryptionOAEPSHA256, plainText as CFData, nil)! as Data
В приведенном выше примере используется .rsaEncryptionOAEPSHA256. Это один из доступных алгоритмов шифрования для RSA:
.rsaEncryptionRaw
.rsaEncryptionPKCS1
.rsaEncryptionOAEPSHA1
.rsaEncryptionOAEPSHA224
.rsaEncryptionOAEPSHA256
.rsaEncryptionOAEPSHA384
.rsaEncryptionOAEPSHA512
Я настоятельно рекомендую использовать один из вариантов OAEP, но это на ваше усмотрение. Надеюсь, это поможет. SecKeyCreateEncryptedData доступен для macOS с 10.12. Подробнее об этом можно прочитать здесь.
Спасибо за ваш ответ. Я пробовал, но это дает мне error: Execution was interrupted, reason: EXC_BAD_INSTRUCTION (code=EXC_I386_INVOP, subcode=0x0).
Я обновил ответ, включив в него обратную связь об ошибках. Пожалуйста, дайте мне знать, если и каков результат любой из функций.
Все та же проблема. Должен ли я преобразовать строку derData в массив байтов? Ошибка возникает из-за SecKeyCreateWithData, я думаю, что derData as! CFData вызывает ошибку, но я не знаю, как ее исправить.
Попробуйте еще раз с новым дополнением преобразования PEM. Я настоятельно рекомендую вам прочитать, что такое кодировка и как она работает как для PEM, так и для DER. Обновленный ответ, вероятно, не очень масштабируемый, но не стесняйтесь попробовать.
Все равно не сработает. Я очень ценю вашу помощь, но я думаю, что попробую какой-нибудь другой метод шифрования. Не могу поверить, что так сложно сделать что-то настолько простое. Спасибо.
Внимательно прочитайте и подумайте. Вероятнее всего, верхний и нижний колонтитулы в примере не совпадают с верхним и нижним колонтитулами в строке PEM, поэтому они не удаляются. Попробуйте изменить мой пример, например, удалив «RSA». Перед вами так много информации и примеров кода, что вы должны быть в состоянии понять это на самом деле. Выпуск теперь лежит в кодировке PEM в DER. Прочтите несколько сообщений об этом или попытайтесь понять, в чем разница между PEM и DER. Посмотрите внимательно, что должно произойти, и попробуйте еще раз, но хорошо подумайте
Да, все работает отлично, потому что я знаю, что делаю. Заголовки PEM могут отличаться от библиотеки к библиотеке. Прочитайте свой код, поймите свою ошибку. Вы упускаете детали. Программирование — это исследование и решение проблем. Проблема, с которой вы начали, абсолютно, на 100% решена. Ваша следующая проблема — кодировка PEM -> DER. Опять же, об этом много статей SO, так как все больше людей не понимают, что это такое и как это работает. Прочтите их, но, что более важно, поймите их. Я еще раз обновил свой ответ. Надеюсь, поможет