Отличается ли генерация ключей Swift RSA от других платформ?

Главная проблема

Мы реализуем генерацию ключей RSA, необходимую для использования с внешним API.

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

Мы пытались реализовать это в трех решениях, два из них работают, однако наша реализация Swift для iOS не работает (она использует тот же API, что и другая реализация. Копирование пар ключей RSA, сгенерированных на других платформах, и жесткое кодирование их в Swift. работает отлично, так что проблема почти наверняка связана с генерацией ключей).

Главный вопрос здесь; отличается ли реализация RSA от Apple и, следовательно, несовместима с реализацией RSA Javascript и Java? Или есть что-то очевидное, чего мне здесь не хватает?

Поставщик, которого мы используем, вероятно, не собирается ничего делать, поскольку он, очевидно, работает с двумя другими платформами, поэтому проблема не в их API.

Для справки, поставщик определяет требование к открытому ключу как:

открытый ключ RSA длиной не менее 4096 бит в формате PKCS1, закодированный с использованием Простая кодировка Base64 UTF-8 (формат без mime).

Примеры кода

Наш быстрый код:

    func generateKeyPair() throws -> (publicKey: SecKey, privateKey: SecKey) {

        let attributes: [CFString: Any] = [
            kSecAttrKeyType: kSecAttrKeyTypeRSA,
            kSecAttrKeySizeInBits: 4096,
        ]

        var error: Unmanaged<CFError>?
        guard let privateKey = SecKeyCreateRandomKey(attributes as CFDictionary, &error) else {
            throw error!.takeRetainedValue() as Error
        }
        
        guard let publicKey = SecKeyCopyPublicKey(privateKey) else {
            throw NSError(domain: NSOSStatusErrorDomain, code: Int(errSecParam), userInfo: nil)
        }
        
        return (publicKey: publicKey, privateKey: privateKey)
    }
    

    func encodePublicKey(publicKey: SecKey) -> String? {
        var error: Unmanaged<CFError>?
        guard let publicKeyData = SecKeyCopyExternalRepresentation(publicKey, &error) as Data? else {
            print("Error getting external representation of public key")
            return nil
        }

        return publicKeyData.base64EncodedString()
    }

В настоящее время у нас есть следующий код, работающий на Java для приложения Android:

public KeyPair generateKeyPair() throws GeneralSecurityException {
  KeyPairGenerator keyPairGenerator = KeyPairGenerator.getInstance("RSA");
  keyPairGenerator.initialize(4096);
  return keyPairGenerator.generateKeyPair();
}


public String encodePublicKey(KeyPair keyPair) throws GeneralSecurityException {
  return new String(Base64.getEncoder().encode(keyPair.getPublic().getEncoded()), StandardCharsets.UTF_8))
}


public String decryptAccessToken(KeyPair keyPair, String initialisationVector, String encryptedToken, String encryptedSymmetricKey) throws GeneralSecurityException {
  PrivateKey privateKey = keyPair.getPrivate();
  Cipher rsaCipher = Cipher.getInstance("RSA/ECB/OAEPPadding");
  OAEPParameterSpec oaepParams = new OAEPParameterSpec("SHA-256", "MGF1", new MGF1ParameterSpec("SHA-256"), PSource.PSpecified.DEFAULT);
  rsaCipher.init(Cipher.DECRYPT_MODE, privateKey, oaepParams);
  byte[] decryptedSymmetricKey = rsaCipher.doFinal(Base64.getDecoder().decode(encryptedSymmetricKey));

  byte[] iv = Base64.getDecoder().decode(initialisationVector.getBytes(StandardCharsets.UTF_8));
  SecretKey aesKey = new SecretKeySpec(decryptedSymmetricKey, "AES");
  Cipher aesCipher = Cipher.getInstance("AES/GCM/NoPadding");
  aesCipher.init(Cipher.DECRYPT_MODE, aesKey, new GCMParameterSpec(128, iv));
  byte[] decryptedToken = aesCipher.doFinal(Base64.getDecoder().decode(encryptedToken.getBytes(StandardCharsets.UTF_8)));

  return new String(decryptedToken, StandardCharsets.UTF_8); 
}

А также следующий код, работающий на JavaScript для веб-приложения:

usePublicKey = new Promise((resolve, reject) => {
  crypto.subtle.generateKey({
    name: "RSA-OAEP",
    modulusLength: 4096,
    publicExponent: new Uint8Array([1, 0, 1]),
    hash: "SHA-256"
  }, true, ["encrypt", "decrypt"])
   .then((keyPair) => {
    crypto.subtle.exportKey("spki", keyPair.publicKey).then((exported) => {
      let encodedPublicKey = btoa(String.fromCharCode.apply(null, new Uint8Array(exported)));
      resolve(encodedPublicKey);
    });
    useAccessToken = (serviceResponse, targetCallback) => {
      let encryptedSymmetricKey = serviceResponse.encryptedSymmetricKey;
      let encodedIv = serviceResponse.initialisationVector;
      let encryptedToken = serviceResponse.token;
      decryptAccessToken(encryptedToken, encryptedSymmetricKey, encodedIv, keyPair.privateKey).then((decryptedToken) => {
        taretCallback(decryptedToken);
      })
    }
  });

});



async function decryptAccessToken(encryptedToken, encryptedSymmetricKey, encodedIv, privateKey) {
  let decodedEncryptedSymmetricKey = Uint8Array.from(atob(encryptedSymmetricKey), c => c.charCodeAt(0))
  let decryptedSymmetricKey = await crypto.subtle.decrypt({
    name: "RSA-OAEP",
    hash: {
      name: "SHA-256"
    }
  }, privateKey, decodedEncryptedSymmetricKey);
  let aesKey = await crypto.subtle.importKey("raw", decryptedSymmetricKey, "AES-GCM", true, ["encrypt", "decrypt"]);
  let decodedIv = Uint8Array.from(atob(encodedIv), c => c.charCodeAt(0));
  let decodedEncryptedToken = Uint8Array.from(atob(encryptedToken), c => c.charCodeAt(0))
  let rawDecryptedToken = await crypto.subtle.decrypt({
    name: "AES-GCM",
    iv: decodedIv
  }, aesKey, decodedEncryptedToken);
  return new TextDecoder('utf-8').decode(new DataView(rawDecryptedToken));
}

Заранее извиняюсь за длинный вопрос, постарался ответить по существу.

Для (публичных) ключей RSA Swift применяет формат PKCS#1, s. SecKeyCopyExternalRepresentation() , два других используют формат X.509/SPKI, s. exportKey() для API WebCrypto и getEncoded() для Java.

Topaco 17.05.2024 19:58
Стоит ли изучать PHP в 2026-2027 годах?
Стоит ли изучать PHP в 2026-2027 годах?
Привет всем, сегодня я хочу высказать свои соображения по поводу вопроса, который я уже много раз получал в своем сообществе: "Стоит ли изучать PHP в...
Поведение ключевого слова "this" в стрелочной функции в сравнении с нормальной функцией
Поведение ключевого слова "this" в стрелочной функции в сравнении с нормальной функцией
В JavaScript одним из самых запутанных понятий является поведение ключевого слова "this" в стрелочной и обычной функциях.
Приемы CSS-макетирования - floats и Flexbox
Приемы CSS-макетирования - floats и Flexbox
Здравствуйте, друзья-студенты! Готовы совершенствовать свои навыки веб-дизайна? Сегодня в нашем путешествии мы рассмотрим приемы CSS-верстки - в...
Тестирование функциональных ngrx-эффектов в Angular 16 с помощью Jest
В системе управления состояниями ngrx, совместимой с Angular 16, появились функциональные эффекты. Это здорово и делает код определенно легче для...
Концепция локализации и ее применение в приложениях React ⚡️
Концепция локализации и ее применение в приложениях React ⚡️
Локализация - это процесс адаптации приложения к различным языкам и культурным требованиям. Это позволяет пользователям получить опыт, соответствующий...
Пользовательский скаляр GraphQL
Пользовательский скаляр GraphQL
Листовые узлы системы типов GraphQL называются скалярами. Достигнув скалярного типа, невозможно спуститься дальше по иерархии типов. Скалярный тип...
1
1
97
1
Перейти к ответу Данный вопрос помечен как решенный

Ответы 1

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

К вашему сведению, я нашел этот репозиторий GitHub, который содержит функции, необходимые для преобразования формата Apple в формат X.509/SPKI, как сказал @Topaco в своем комментарии.

https://github.com/nextincrement/rsa-public-key-importer-exporter

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