Decodes the given Base64 encoded string to a byte array. @param input the Base64 encoded string to decode @return the decoded byte array @throws IllegalArgumentException if input is null or contains invalid Base64 characters
(String input)
| 104 | * @throws IllegalArgumentException if input is null or contains invalid Base64 characters |
| 105 | */ |
| 106 | public static byte[] decode(String input) { |
| 107 | if (input == null) { |
| 108 | throw new IllegalArgumentException("Input cannot be null"); |
| 109 | } |
| 110 | |
| 111 | if (input.isEmpty()) { |
| 112 | return new byte[0]; |
| 113 | } |
| 114 | |
| 115 | // Strict RFC 4648 compliance: length must be a multiple of 4 |
| 116 | if (input.length() % 4 != 0) { |
| 117 | throw new IllegalArgumentException("Invalid Base64 input length; must be multiple of 4"); |
| 118 | } |
| 119 | |
| 120 | // Validate padding: '=' can only appear at the end (last 1 or 2 chars) |
| 121 | int firstPadding = input.indexOf('='); |
| 122 | if (firstPadding != -1) { |
| 123 | if (firstPadding < input.length() - 2) { |
| 124 | throw new IllegalArgumentException("Padding '=' can only appear at the end (last 1 or 2 characters)"); |
| 125 | } |
| 126 | for (int i = firstPadding; i < input.length(); i++) { |
| 127 | if (input.charAt(i) != '=') { |
| 128 | throw new IllegalArgumentException("A padding '=' must not be followed by a non-padding character"); |
| 129 | } |
| 130 | } |
| 131 | } |
| 132 | |
| 133 | List<Byte> result = new ArrayList<>(); |
| 134 | |
| 135 | // Process input in groups of 4 characters |
| 136 | for (int i = 0; i < input.length(); i += 4) { |
| 137 | // Get up to 4 characters |
| 138 | int char1 = getBase64Value(input.charAt(i)); |
| 139 | int char2 = getBase64Value(input.charAt(i + 1)); |
| 140 | int char3 = input.charAt(i + 2) == '=' ? 0 : getBase64Value(input.charAt(i + 2)); |
| 141 | int char4 = input.charAt(i + 3) == '=' ? 0 : getBase64Value(input.charAt(i + 3)); |
| 142 | |
| 143 | // Combine four 6-bit groups into a 24-bit number |
| 144 | int combined = (char1 << 18) | (char2 << 12) | (char3 << 6) | char4; |
| 145 | |
| 146 | // Extract three 8-bit bytes |
| 147 | result.add((byte) ((combined >> 16) & 0xFF)); |
| 148 | if (input.charAt(i + 2) != '=') { |
| 149 | result.add((byte) ((combined >> 8) & 0xFF)); |
| 150 | } |
| 151 | if (input.charAt(i + 3) != '=') { |
| 152 | result.add((byte) (combined & 0xFF)); |
| 153 | } |
| 154 | } |
| 155 | |
| 156 | // Convert List<Byte> to byte[] |
| 157 | byte[] resultArray = new byte[result.size()]; |
| 158 | for (int i = 0; i < result.size(); i++) { |
| 159 | resultArray[i] = result.get(i); |
| 160 | } |
| 161 | |
| 162 | return resultArray; |
| 163 | } |