diff --git a/OpenKeychain-Test/src/test/java/org/sufficientlysecure/keychain/pgp/PgpEncryptDecryptTest.java b/OpenKeychain-Test/src/test/java/org/sufficientlysecure/keychain/pgp/PgpEncryptDecryptTest.java
index 008edcda4..b8205a792 100644
--- a/OpenKeychain-Test/src/test/java/org/sufficientlysecure/keychain/pgp/PgpEncryptDecryptTest.java
+++ b/OpenKeychain-Test/src/test/java/org/sufficientlysecure/keychain/pgp/PgpEncryptDecryptTest.java
@@ -438,6 +438,60 @@ public class PgpEncryptDecryptTest {
}
+ @Test
+ public void testForeignEncoding () throws Exception {
+ String plaintext = "ウィキペディア";
+ byte[] plaindata = plaintext.getBytes("iso-2022-jp");
+
+ { // some quick sanity checks
+ Assert.assertEquals(plaintext, new String(plaindata, "iso-2022-jp"));
+ Assert.assertNotEquals(plaintext, new String(plaindata, "utf-8"));
+ }
+
+ byte[] ciphertext;
+ { // encrypt data with a given passphrase
+ ByteArrayOutputStream out = new ByteArrayOutputStream();
+ ByteArrayInputStream in = new ByteArrayInputStream(plaindata);
+
+ InputData data = new InputData(in, in.available());
+ Builder b = new PgpSignEncrypt.Builder(
+ Robolectric.application,
+ new ProviderHelper(Robolectric.application),
+ null, // new DummyPassphraseCache(mPassphrase, 0L),
+ data, out);
+
+ b.setEncryptionMasterKeyIds(new long[]{ mStaticRing1.getMasterKeyId() });
+ b.setSymmetricEncryptionAlgorithm(PGPEncryptedData.AES_128);
+ // this only works with ascii armored output!
+ b.setEnableAsciiArmorOutput(true);
+ b.setCharset("iso-2022-jp");
+ SignEncryptResult result = b.build().execute();
+ Assert.assertTrue("encryption must succeed", result.success());
+
+ ciphertext = out.toByteArray();
+ }
+
+ { // decryption with provided passphrase should yield the same result
+
+ ByteArrayOutputStream out = new ByteArrayOutputStream();
+ ByteArrayInputStream in = new ByteArrayInputStream(ciphertext);
+ InputData data = new InputData(in, in.available());
+
+ PgpDecryptVerify.Builder b = builderWithFakePassphraseCache(data, out, null, null, null);
+ b.setPassphrase(mKeyPhrase1);
+ DecryptVerifyResult result = b.build().execute();
+ Assert.assertTrue("decryption with provided passphrase must succeed", result.success());
+ Assert.assertArrayEquals("decrypted ciphertext should equal plaintext bytes",
+ out.toByteArray(), plaindata);
+ Assert.assertEquals("charset should be read correctly",
+ "iso-2022-jp", result.getCharset());
+ Assert.assertEquals("decrypted ciphertext should equal plaintext",
+ new String(out.toByteArray(), result.getCharset()), plaintext);
+ Assert.assertNull("signature be empty", result.getSignatureResult());
+ }
+
+ }
+
private PgpDecryptVerify.Builder builderWithFakePassphraseCache (
InputData data, OutputStream out,
final String passphrase, final Long checkMasterKeyId, final Long checkSubKeyId) {
diff --git a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/operations/results/DecryptVerifyResult.java b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/operations/results/DecryptVerifyResult.java
index d81577102..86b37fea6 100644
--- a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/operations/results/DecryptVerifyResult.java
+++ b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/operations/results/DecryptVerifyResult.java
@@ -41,6 +41,9 @@ public class DecryptVerifyResult extends OperationResult {
OpenPgpSignatureResult mSignatureResult;
OpenPgpMetadata mDecryptMetadata;
+ // This holds the charset which was specified in the ascii armor, if specified
+ // https://tools.ietf.org/html/rfc4880#page56
+ String mCharset;
public long getKeyIdPassphraseNeeded() {
return mKeyIdPassphraseNeeded;
@@ -84,6 +87,14 @@ public class DecryptVerifyResult extends OperationResult {
mDecryptMetadata = decryptMetadata;
}
+ public String getCharset () {
+ return mCharset;
+ }
+
+ public void setCharset(String charset) {
+ mCharset = charset;
+ }
+
public boolean isPending() {
return (mResult & RESULT_PENDING) == RESULT_PENDING;
}
diff --git a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/operations/results/OperationResult.java b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/operations/results/OperationResult.java
index eba8f13af..5b28f0f5a 100644
--- a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/operations/results/OperationResult.java
+++ b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/operations/results/OperationResult.java
@@ -564,6 +564,7 @@ public abstract class OperationResult implements Parcelable {
MSG_DC_ASKIP_NO_KEY (LogLevel.DEBUG, R.string.msg_dc_askip_no_key),
MSG_DC_ASKIP_NOT_ALLOWED (LogLevel.DEBUG, R.string.msg_dc_askip_not_allowed),
MSG_DC_ASYM (LogLevel.DEBUG, R.string.msg_dc_asym),
+ MSG_DC_CHARSET (LogLevel.DEBUG, R.string.msg_dc_charset),
MSG_DC_CLEAR_DATA (LogLevel.DEBUG, R.string.msg_dc_clear_data),
MSG_DC_CLEAR_DECOMPRESS (LogLevel.DEBUG, R.string.msg_dc_clear_decompress),
MSG_DC_CLEAR_META_FILE (LogLevel.DEBUG, R.string.msg_dc_clear_meta_file),
diff --git a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/pgp/PgpDecryptVerify.java b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/pgp/PgpDecryptVerify.java
index b58df085f..6c987f484 100644
--- a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/pgp/PgpDecryptVerify.java
+++ b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/pgp/PgpDecryptVerify.java
@@ -234,6 +234,22 @@ public class PgpDecryptVerify extends BaseOperation {
boolean symmetricPacketFound = false;
boolean anyPacketFound = false;
+ // If the input stream is armored, and there is a charset specified, take a note for later
+ // https://tools.ietf.org/html/rfc4880#page56
+ String charset = null;
+ if (in instanceof ArmoredInputStream) {
+ for (String header : ((ArmoredInputStream) in).getArmorHeaders()) {
+ String[] pieces = header.split(":", 2);
+ if (pieces.length == 2 && "charset".equalsIgnoreCase(pieces[0])) {
+ charset = pieces[1].trim();
+ break;
+ }
+ }
+ if (charset != null) {
+ log.add(LogType.MSG_DC_CHARSET, indent, charset);
+ }
+ }
+
// go through all objects and find one we can decrypt
while (it.hasNext()) {
Object obj = it.next();
@@ -550,6 +566,7 @@ public class PgpDecryptVerify extends BaseOperation {
log.add(LogType.MSG_DC_OK_META_ONLY, indent);
DecryptVerifyResult result =
new DecryptVerifyResult(DecryptVerifyResult.RESULT_OK, log);
+ result.setCharset(charset);
result.setDecryptMetadata(metadata);
return result;
}
@@ -647,6 +664,7 @@ public class PgpDecryptVerify extends BaseOperation {
new DecryptVerifyResult(DecryptVerifyResult.RESULT_OK, log);
result.setDecryptMetadata(metadata);
result.setSignatureResult(signatureResultBuilder.build());
+ result.setCharset(charset);
return result;
}
@@ -807,7 +825,7 @@ public class PgpDecryptVerify extends BaseOperation {
while ((ch = fIn.read()) >= 0) {
bOut.write(ch);
if (ch == '\r' || ch == '\n') {
- lookAhead = readPassedEOL(bOut, ch, fIn);
+ lookAhead = readPastEOL(bOut, ch, fIn);
break;
}
}
@@ -824,7 +842,7 @@ public class PgpDecryptVerify extends BaseOperation {
do {
bOut.write(ch);
if (ch == '\r' || ch == '\n') {
- lookAhead = readPassedEOL(bOut, ch, fIn);
+ lookAhead = readPastEOL(bOut, ch, fIn);
break;
}
} while ((ch = fIn.read()) >= 0);
@@ -836,7 +854,7 @@ public class PgpDecryptVerify extends BaseOperation {
return lookAhead;
}
- private static int readPassedEOL(ByteArrayOutputStream bOut, int lastCh, InputStream fIn)
+ private static int readPastEOL(ByteArrayOutputStream bOut, int lastCh, InputStream fIn)
throws IOException {
int lookAhead = fIn.read();
diff --git a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/pgp/PgpSignEncrypt.java b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/pgp/PgpSignEncrypt.java
index 3c3bcc890..c2fa811bd 100644
--- a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/pgp/PgpSignEncrypt.java
+++ b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/pgp/PgpSignEncrypt.java
@@ -81,6 +81,7 @@ public class PgpSignEncrypt extends BaseOperation {
private boolean mCleartextInput;
private String mOriginalFilename;
private boolean mFailOnMissingEncryptionKeyIds;
+ private String mCharset;
private byte[] mNfcSignedHash = null;
private Date mNfcCreationTimestamp = null;
@@ -118,6 +119,7 @@ public class PgpSignEncrypt extends BaseOperation {
this.mNfcCreationTimestamp = builder.mNfcCreationTimestamp;
this.mOriginalFilename = builder.mOriginalFilename;
this.mFailOnMissingEncryptionKeyIds = builder.mFailOnMissingEncryptionKeyIds;
+ this.mCharset = builder.mCharset;
}
public static class Builder {
@@ -145,6 +147,7 @@ public class PgpSignEncrypt extends BaseOperation {
private byte[] mNfcSignedHash = null;
private Date mNfcCreationTimestamp = null;
private boolean mFailOnMissingEncryptionKeyIds = false;
+ private String mCharset = null;
public Builder(Context context, ProviderHelper providerHelper, Progressable progressable,
InputData data, OutputStream outStream) {
@@ -211,6 +214,11 @@ public class PgpSignEncrypt extends BaseOperation {
return this;
}
+ public Builder setCharset(String charset) {
+ mCharset = charset;
+ return this;
+ }
+
/**
* Also encrypt with the signing keyring
*
@@ -283,6 +291,10 @@ public class PgpSignEncrypt extends BaseOperation {
if (mVersionHeader != null) {
armorOut.setHeader("Version", mVersionHeader);
}
+ // if we have a charset, put it in the header
+ if (mCharset != null) {
+ armorOut.setHeader("Charset", mCharset);
+ }
out = armorOut;
} else {
out = mOutStream;
diff --git a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/remote/OpenPgpService.java b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/remote/OpenPgpService.java
index 9534cc49d..68677fb9c 100644
--- a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/remote/OpenPgpService.java
+++ b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/remote/OpenPgpService.java
@@ -557,6 +557,12 @@ public class OpenPgpService extends RemoteService {
result.putExtra(OpenPgpApi.RESULT_METADATA, metadata);
}
}
+
+ String charset = pgpResult.getCharset();
+ if (charset != null) {
+ result.putExtra(OpenPgpApi.RESULT_CHARSET, charset);
+ }
+
} else {
LogEntryParcel errorMsg = pgpResult.getLog().getLast();
throw new Exception(getString(errorMsg.mType.getMsgId()));
diff --git a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/DecryptTextFragment.java b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/DecryptTextFragment.java
index 83ba64ce2..78333a8d8 100644
--- a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/DecryptTextFragment.java
+++ b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/DecryptTextFragment.java
@@ -41,6 +41,8 @@ import org.sufficientlysecure.keychain.ui.util.Notify;
import org.sufficientlysecure.keychain.util.Log;
import org.sufficientlysecure.keychain.util.ShareHelper;
+import java.io.UnsupportedEncodingException;
+
public class DecryptTextFragment extends DecryptFragment {
public static final String ARG_CIPHERTEXT = "ciphertext";
@@ -194,7 +196,18 @@ public class DecryptTextFragment extends DecryptFragment {
byte[] decryptedMessage = returnData
.getByteArray(KeychainIntentService.RESULT_DECRYPTED_BYTES);
- mText.setText(new String(decryptedMessage));
+ String displayMessage;
+ if (pgpResult.getCharset() != null) {
+ try {
+ displayMessage = new String(decryptedMessage, pgpResult.getCharset());
+ } catch (UnsupportedEncodingException e) {
+ // if we can't decode properly, just fall back to utf-8
+ displayMessage = new String(decryptedMessage);
+ }
+ } else {
+ displayMessage = new String(decryptedMessage);
+ }
+ mText.setText(displayMessage);
pgpResult.createNotify(getActivity()).show();
diff --git a/OpenKeychain/src/main/res/values/strings.xml b/OpenKeychain/src/main/res/values/strings.xml
index b63ccec2d..a51420ba1 100644
--- a/OpenKeychain/src/main/res/values/strings.xml
+++ b/OpenKeychain/src/main/res/values/strings.xml
@@ -923,6 +923,7 @@
"Data not encrypted with known key, skipping…"
"Data not encrypted with allowed key, skipping…"
"Found block of asymmetrically encrypted data for key %s"
+ "Found charset header: '%s'"
"Processing literal data"
"Unpacking compressed data"
"Filename: %s"