Быстрая загрузка открытого ключа RSA из строки (MacOS)

Мне было интересно, как я могу загрузить открытый ключ RSA из строки и зашифровать с его помощью другую строку. Открытый ключ выглядит примерно так:

let key = """ -----BEGIN PUBLIC KEY----- blah blah blah -----END PUBLIC KEY-----"""

Я бы предпочел сделать это без внешней библиотеки, но если это упростит задачу, я готов их использовать. Заранее спасибо.

stackoverflow.com/a/56810842/2149779 должен решить вашу проблему
serg_zhd 24.12.2020 13:16

@serg_zhd, к сожалению, нет. SecKeyEncrypt не существует в macos.

Santiago Alvarez 28.12.2020 16:41
Почему в Python есть оператор "pass"?
Почему в Python есть оператор "pass"?
Оператор pass в Python - это простая концепция, которую могут быстро освоить даже новички без опыта программирования.
Коллекции в Laravel более простым способом
Коллекции в Laravel более простым способом
Привет, читатели, сегодня мы узнаем о коллекциях. В Laravel коллекции - это способ манипулировать массивами и играть с массивами данных. Благодаря...
JavaScript Вопросы с множественным выбором и ответы
JavaScript Вопросы с множественным выбором и ответы
Если вы ищете платформу, которая предоставляет вам бесплатный тест JavaScript MCQ (Multiple Choice Questions With Answers) для оценки ваших знаний,...
Массив зависимостей в React
Массив зависимостей в React
Все о массиве Dependency и его связи с useEffect.
1
2
1 331
1
Перейти к ответу Данный вопрос помечен как решенный

Ответы 1

Ответ принят как подходящий

Ключевые данные, которые у вас есть, закодированы в 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).

Santiago Alvarez 29.12.2020 14:17

Я обновил ответ, включив в него обратную связь об ошибках. Пожалуйста, дайте мне знать, если и каков результат любой из функций.

Bram 29.12.2020 16:27

Все та же проблема. Должен ли я преобразовать строку derData в массив байтов? Ошибка возникает из-за SecKeyCreateWithData, я думаю, что derData as! CFData вызывает ошибку, но я не знаю, как ее исправить.

Santiago Alvarez 29.12.2020 17:29

Попробуйте еще раз с новым дополнением преобразования PEM. Я настоятельно рекомендую вам прочитать, что такое кодировка и как она работает как для PEM, так и для DER. Обновленный ответ, вероятно, не очень масштабируемый, но не стесняйтесь попробовать.

Bram 29.12.2020 17:49

Все равно не сработает. Я очень ценю вашу помощь, но я думаю, что попробую какой-нибудь другой метод шифрования. Не могу поверить, что так сложно сделать что-то настолько простое. Спасибо.

Santiago Alvarez 29.12.2020 18:03

Внимательно прочитайте и подумайте. Вероятнее всего, верхний и нижний колонтитулы в примере не совпадают с верхним и нижним колонтитулами в строке PEM, поэтому они не удаляются. Попробуйте изменить мой пример, например, удалив «RSA». Перед вами так много информации и примеров кода, что вы должны быть в состоянии понять это на самом деле. Выпуск теперь лежит в кодировке PEM в DER. Прочтите несколько сообщений об этом или попытайтесь понять, в чем разница между PEM и DER. Посмотрите внимательно, что должно произойти, и попробуйте еще раз, но хорошо подумайте

Bram 29.12.2020 18:15

Да, все работает отлично, потому что я знаю, что делаю. Заголовки PEM могут отличаться от библиотеки к библиотеке. Прочитайте свой код, поймите свою ошибку. Вы упускаете детали. Программирование — это исследование и решение проблем. Проблема, с которой вы начали, абсолютно, на 100% решена. Ваша следующая проблема — кодировка PEM -> DER. Опять же, об этом много статей SO, так как все больше людей не понимают, что это такое и как это работает. Прочтите их, но, что более важно, поймите их. Я еще раз обновил свой ответ. Надеюсь, поможет

Bram 29.12.2020 19:14

Другие вопросы по теме