La migration du soleil.misc.BASE64 to Java 8 java.util.Base64

Question

Sont le Java 8 java.util.Base64 Encodeur et le Décodeur MIME une baisse-dans le remplacement pour L'API Java interne non supportée sun.misc.BASE64Encoder et sun.misc.BASE64Decoder?

Ce que je pense jusqu'à présent et pourquoi

basé sur mon enquête et des tests rapides (voir code ci-dessous) il faut une baisse-dans le remplacement parce que

  • <!-Sur la base de son JavaDoc est un encodeur de caractères BASE64 comme spécifié dans RFC1521. Cette RFC est une partie de l' MIME cahier des charges...
  • java.util.Base64 basé sur son JavaDoc utilise "L'Alphabet de base 64" comme spécifié dans le tableau 1 de RFC 2045 pour l'encodage et le décodage... MIME

en supposant que non des changements significatifs dans la RFC 1521 et 2045 (Je n'ai pas pu en trouver) et basé sur mon test rapide en utilisant le Java 8 Base64 MIME Encoder/Decoder devraient être très bien.

Ce que je cherche

  • une source faisant autorité confirmant ou réfutant le point de "remplacement direct" ou
  • un contre-exemple qui montre un cas où java.util.Base64 a un comportement différent du soleil.misc.BASE64Encoder OpenJDK Java 8 mise en œuvre (8u40-b25) (BASE64Decoder) OR
  • tout ce que vous pensez que les réponses ci-dessus question certainement

Pour référence

mon code de test

public class Base64EncodingDecodingRoundTripTest {

    public static void main(String[] args) throws IOException {
        String test1 = " ~!@#$%^& *()_+=`| }{[];: "?><,./ ";
        String test2 = test1 + test1;

        encodeDecode(test1);
        encodeDecode(test2);
    }

    static void encodeDecode(final String testInputString) throws IOException {
        sun.misc.BASE64Encoder unsupportedEncoder = new sun.misc.BASE64Encoder();
        sun.misc.BASE64Decoder unsupportedDecoder = new sun.misc.BASE64Decoder();

        Base64.Encoder mimeEncoder = java.util.Base64.getMimeEncoder();
        Base64.Decoder mimeDecoder = java.util.Base64.getMimeDecoder();

        String sunEncoded = unsupportedEncoder.encode(testInputString.getBytes());
        System.out.println("sun.misc encoded: " + sunEncoded);

        String mimeEncoded = mimeEncoder.encodeToString(testInputString.getBytes());
        System.out.println("Java 8 Base64 MIME encoded: " + mimeEncoded);

        byte[] mimeDecoded = mimeDecoder.decode(sunEncoded);
        String mimeDecodedString = new String(mimeDecoded, Charset.forName("UTF-8"));

        byte[] sunDecoded = unsupportedDecoder.decodeBuffer(mimeEncoded); // throws IOException
        String sunDecodedString = new String(sunDecoded, Charset.forName("UTF-8"));

        System.out.println(String.format("sun.misc decoded: %s | Java 8 Base64 decoded:  %s", sunDecodedString, mimeDecodedString));

        System.out.println("Decoded results are both equal: " + Objects.equals(sunDecodedString, mimeDecodedString));
        System.out.println("Mime decoded result is equal to test input string: " + Objects.equals(testInputString, mimeDecodedString));
        System.out.println("n");
    }
}
20
demandé sur Ivo Mori 2016-02-09 23:20:33

3 réponses

Voici un petit programme de test qui illustre une différence dans les chaînes codées:

byte[] bytes = new byte[57];
String enc1 = new sun.misc.BASE64Encoder().encode(bytes);
String enc2 = new String(java.util.Base64.getMimeEncoder().encode(bytes),
                         StandardCharsets.UTF_8);

System.out.println("enc1 = <" + enc1 + ">");
System.out.println("enc2 = <" + enc2 + ">");
System.out.println(enc1.equals(enc2));

Sa sortie est:

enc1 = <AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA
>
enc2 = <AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA>
false

notez que la sortie encodée de sun.misc.BASE64Encoder a une nouvelle ligne à la fin. Il n'a pas d' toujours ajouter une nouvelle ligne, mais il arrive de le faire si la chaîne encodée a exactement 76 caractères sur sa dernière ligne. (L'auteur de java.util.Base64 considéré que c'était un petit bug dans le sun.misc.BASE64Encoder mise en oeuvre-voir revue thread).

cela peut sembler banal, mais si vous aviez un programme qui s'appuyait sur ce comportement spécifique, changer d'encodeur pourrait entraîner une sortie mal formée. Par conséquent, je conclus que java.util.Base64 un drop-in de remplacement pour sun.misc.BASE64Encoder.

bien sûr, le intentionjava.util.Base64 est qu'il s'agit d'un équivalent fonctionnel, conforme à la RFC, haute performance, entièrement supporté et un remplacement spécifié qui est destiné à supporter la migration de code de sun.misc.BASE64Encoder. Vous devez être conscient de certains cas de bord comme celui-ci lors de la migration, cependant.

31
répondu Stuart Marks 2016-02-13 19:30:50

il n'y a aucun changement à la spécification base64 entre rfc1521 et rfc2045.

toutes les implémentations de base64 peuvent être considérées comme des remplacements directs les unes des autres, les seules différences entre les implémentations de base64 sont:

  1. l'alphabet utilisé.
  2. les API fournies (par exemple, certaines ne peuvent agir que sur un tampon d'entrée complet, tandis que d'autres peuvent être des machines à états finis vous permettant de continuer à pousser des morceaux d'entrée à travers eux jusqu'à ce que vous avez terminé).

l'alphabet MIME base64 est resté constant entre les versions RFC (il a ou des logiciels plus anciens de casser) et est: ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz+/

Wikipédia notes, seuls les 2 derniers caractères peuvent changer entre base64 implémentations.

comme exemple d'une implémentation de base64 qui changer les 2 derniers caractères, le IMAP MUTF-7 la spécification utilise ce qui suit: base64 alphabet:ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz+,

La raison de ce changement est que l' / le caractère est souvent utilisé comme délimiteur de chemin et comme L'encodage MUTF-7 est utilisé pour aplatir les chemins de répertoire non-ASCII en ASCII, le / le caractère doit être évité dans les segments encodés.

3
répondu jstedfast 2016-02-11 23:02:44

en supposant que les deux encodeurs sont libres de bogues, alors la RFC requiert des encodages distincts pour chaque séquence de 0 octet, 1 octet, 2 octets et 3 octets. Les séquences plus longues sont divisées en autant de séquences de 3 octets que nécessaire, suivies d'une séquence finale. Par conséquent, si les deux implémentations gèrent toutes 16,843,009 (1+256+65536+16777216) les séquences possibles correctement, puis les deux implémentations sont également identiques.

ces tests ne prennent que quelques minutes. En modifiant légèrement votre test code, je l'ai fait et mon installation Java 8 a passé tous les tests. Par conséquent, la mise en œuvre publique peut être utilisée pour remplacer le soleil en toute sécurité.divers mise en œuvre.

Voici mon code de test:

import java.util.Base64;
import java.util.Arrays;
import java.io.IOException;

public class Base64EncodingDecodingRoundTripTest {

    public static void main(String[] args) throws IOException {
        System.out.println("Testing zero byte encoding");
        encodeDecode(new byte[0]);

        System.out.println("Testing single byte encodings");
        byte[] test = new byte[1];
        for(int i=0;i<256;i++) {
            test[0] = (byte) i;
            encodeDecode(test);
        }
        System.out.println("Testing double byte encodings");
        test = new byte[2];
        for(int i=0;i<65536;i++) {
            test[0] = (byte) i;
            test[1] = (byte) (i >>> 8);
            encodeDecode(test);
        }
        System.out.println("Testing triple byte encodings");
        test = new byte[3];
        for(int i=0;i<16777216;i++) {
            test[0] = (byte) i;
            test[1] = (byte) (i >>> 8);
            test[2] = (byte) (i >>> 16);
            encodeDecode(test);
        }
        System.out.println("All tests passed");
    }

    static void encodeDecode(final byte[] testInput) throws IOException {
        sun.misc.BASE64Encoder unsupportedEncoder = new sun.misc.BASE64Encoder();
        sun.misc.BASE64Decoder unsupportedDecoder = new sun.misc.BASE64Decoder();

        Base64.Encoder mimeEncoder = java.util.Base64.getMimeEncoder();
        Base64.Decoder mimeDecoder = java.util.Base64.getMimeDecoder();

        String sunEncoded = unsupportedEncoder.encode(testInput);
        String mimeEncoded = mimeEncoder.encodeToString(testInput);

        // check encodings equal
        if( ! sunEncoded.equals(mimeEncoded) ) {
            throw new IOException("Input "+Arrays.toString(testInput)+" produced different encodings (sun=\""+sunEncoded+"\", mime=\""+mimeEncoded+"\")");
        }

        // Check cross decodes are equal. Note encoded forms are identical
        byte[] mimeDecoded = mimeDecoder.decode(sunEncoded);
        byte[] sunDecoded = unsupportedDecoder.decodeBuffer(mimeEncoded); // throws IOException
        if(! Arrays.equals(mimeDecoded,sunDecoded) ) {
            throw new IOException("Input "+Arrays.toString(testInput)+" was encoded as \""+sunEncoded+"\", but decoded as sun="+Arrays.toString(sunDecoded)+" and mime="+Arrays.toString(mimeDecoded));
        }

    }
}
1
répondu Simon G. 2016-02-12 15:30:13