Мы реализуем генерацию ключей RSA, необходимую для использования с внешним API.
Открытый ключ RSA должен быть быть отправлен внешнему API, который зашифрует некоторый текст, вернет его, и мы сможем его расшифровать.
Мы попробовали реализовать это в трех решениях, два работают, однако наша реализация Swift для iOS не работает (он использует тот же API, что и другая реализация. Копирование пар ключей RSA, сгенерированных на других платформах, и жесткое их кодирование в Swift работает отлично, поэтому проблема почти наверняка связана с генерацией ключей).< /p>
Главный вопрос здесь: Реализация 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?
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?
guard let publicKeyData = SecKeyCopyExternalRepresentation(publicKey, &error) as Data? else {
print("Error getting external representation of public key")
return nil
}
return publicKeyData.base64EncodedString()
}
Код: Выделить всё
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);
}
Код: Выделить всё
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));
}
Подробнее здесь: https://stackoverflow.com/questions/784 ... -platforms
Мобильная версия