2016-03-04 3 views
3

Ответы SAML XML зашифрованы нашим сервером Gluu/Shibboleth с моим публичным сертификатом. Я прочитал спецификацию, и с помощью Stackoverflow реализовал решение. Однако после дешифрования я получаю случайные символы в конце.SAML RSA & AES Расшифровка - случайные нежелательные байты в конце

Ответы SAML используют RSA-ECB/MGF1 кодированный ключ AES-128-CBC. Поэтому сначала я должен декодировать ключ AES (байты), а затем использовать этот ключ AES для дешифрования ответа XML.

Вот мой код:

public static void main(String[] args) throws Exception { 
    Path p = Paths.get("C:\\Users\\jj\\Desktop\\myPrivateKey.key"); 
    String encryptedAESKey = "FUZLPtkLSUgOo0bETQ5hwP1OWNggGlWhG+Z......wF1G6twRjg=="; // from XML 
    byte[] aesKey = decryptWithPem("RSA/ECB/OAEPwithSHA1andMGF1Padding", "RSA", Util.base64DecodeAsBytes(encryptedAESKey), p); 
    String encryptedXML = "YfJu7h4Id09hpuoqthl3Ks/JqhIXm.....amb24JZu7cJZT3cEO2a2U6qi0VCyoXQ="; 
    byte[] decryptedData = decrypt("AES/CBC/NoPadding", "AES", Util.base64DecodeAsBytes(encryptedXML), aesKey); 
    for(int i = decryptedData.length - 20; i < decryptedData.length; i++) { 
     System.out.println("i: " + i + " -> " + decryptedData[i]); // print last 20 bytes 
    } 
    System.out.println(new String(decryptedData)); // prints <saml2:Assertion xmlns:saml2="urn:oasis:names:........</saml2:Assertion>�G{A 

} 

Обратите внимание, что случайные байты на заявлении для печати! Последняя строка выводит:

<saml2:Assertion xmlns:saml2="urn:oasis:names:........</saml2:Assertion>�G{A

я понял, что первые 16 байт в сообщении являются IV, так что я лишить их от сообщения (избавиться от хлама от начала сообщения). Но теперь я получаю случайные 5 байтов в конце сообщения. Эти байты:

i: 1931 -> -120 
i: 1932 -> 71 
i: 1933 -> 123 
i: 1934 -> 65 
i: 1935 -> 5 

Другие функции:

public static byte[] decryptWithPem(String alg, String pemAlg, byte[] encryptedData, Path pemPath) { 
    try { 
     Cipher cipher = Cipher.getInstance(alg, "BC"); 
     cipher.init(Cipher.DECRYPT_MODE, loadPrivateKey(pemPath, pemAlg)); 
     return cipher.doFinal(encryptedData); 
    } catch (Exception e) { 
     throw new RuntimeException(e); 
    } 
} 

private static PrivateKey loadPrivateKey(Path keyPath, String alg) { 
    try { 
     byte[] keyData = Util.base64DecodeAsBytes(IOUtil.fileToString(keyPath).replaceAll("\\s", "")); 
     KeyFactory keyFactory = KeyFactory.getInstance(alg); 
     EncodedKeySpec privateKeySpec = new PKCS8EncodedKeySpec(keyData); 
     return keyFactory.generatePrivate(privateKeySpec); 
    } catch(NoSuchAlgorithmException | InvalidKeySpecException e) { 
     throw new RuntimeException(e); 
    } 
} 

private static SecretKeySpec getSecretKeySpec(String alg, byte[] key) { 
    return new SecretKeySpec(key, alg); 
} 

public static byte[] decrypt(String alg, String keyAlg, byte[] dataToDecrypt, byte[] key) { 
    try { 
     Cipher cipher = Cipher.getInstance(alg, "BC"); 
     cipher.init(Cipher.DECRYPT_MODE, getSecretKeySpec(keyAlg, key), new IvParameterSpec(dataToDecrypt, 0, 16)); 
     return cipher.doFinal(Arrays.copyOfRange(dataToDecrypt, 16, dataToDecrypt.length)); 
    } catch (Exception e) { 
     throw new RuntimeException(e); 
    } 
} 

Я использую Надувной замок. Если я использую заполнение PKCS7, я получаю сообщение об ошибочном заполнении.

Способ шифрования ключа AES: http://www.w3.org/2001/04/xmlenc#rsa-oaep-mgf1p, http://www.w3.org/2000/09/xmldsig#sha1. Способ шифрования данных XML: http://www.w3.org/2001/04/xmlenc#aes128-cbc.

Возможно ли случайное заполнение сообщения?

------ EDIT ------

кажется, что спецификация использует ISO 10126 отступы, используйте "AES/CBC/ISO10126Padding" вместо "AES/CBC/NoPadding" ,

ответ

3

Возможно ли случайное заполнение сообщения?

Да, как указано в Padding section он использует случайное заполнение, говоря, что знаки вопроса может быть любое значение, но последний байт обозначает длину заполнения. Их пример: 0x2122232425262728??????????????08.

Это на самом деле ISO 10126 padding, и вы можете легко удалить его, глядя на последнего байта:

byte[] pp = cipher.doFinal(Arrays.copyOfRange(dataToDecrypt, 16, dataToDecrypt.length)); 
return Arrays.copyOf(pp, pp.length - pp[pp.length-1]); 

Обратите внимание, что если вы обработки Обивка самостоятельно, вы должны использовать NoPadding в alg.

+1

Большое вам спасибо! Я буду использовать «AES/CBC/ISO10126Padding», который, похоже, работает, если у вас есть BouncyCastle. – jn1kk