Skip to content

Commit d8ad552

Browse files
Check CSR
Signed-off-by: tobiasKaminsky <[email protected]>
1 parent bb721e2 commit d8ad552

File tree

6 files changed

+130
-17
lines changed

6 files changed

+130
-17
lines changed

app/src/androidTest/java/com/nextcloud/client/EndToEndRandomIT.java

Lines changed: 65 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -39,6 +39,8 @@
3939
import com.owncloud.android.lib.resources.files.ReadFileRemoteOperation;
4040
import com.owncloud.android.lib.resources.status.OCCapability;
4141
import com.owncloud.android.lib.resources.status.OwnCloudVersion;
42+
import com.owncloud.android.lib.resources.users.DeletePrivateKeyOperation;
43+
import com.owncloud.android.lib.resources.users.DeletePublicKeyOperation;
4244
import com.owncloud.android.lib.resources.users.GetPrivateKeyOperation;
4345
import com.owncloud.android.lib.resources.users.GetPublicKeyOperation;
4446
import com.owncloud.android.lib.resources.users.SendCSROperation;
@@ -50,6 +52,8 @@
5052
import com.owncloud.android.utils.EncryptionUtils;
5153
import com.owncloud.android.utils.FileStorageUtils;
5254

55+
import org.bouncycastle.operator.OperatorCreationException;
56+
import org.conscrypt.OpenSSLRSAPublicKey;
5357
import org.junit.Before;
5458
import org.junit.BeforeClass;
5559
import org.junit.Rule;
@@ -58,7 +62,11 @@
5862

5963
import java.io.File;
6064
import java.io.IOException;
65+
import java.math.BigInteger;
6166
import java.security.KeyPair;
67+
import java.security.NoSuchAlgorithmException;
68+
import java.security.cert.CertificateException;
69+
import java.security.interfaces.RSAPrivateCrtKey;
6270
import java.util.ArrayList;
6371
import java.util.List;
6472
import java.util.Random;
@@ -474,6 +482,34 @@ public void testUploadWithDelete() throws Exception {
474482
assertFalse(new File(uploadedFile.getStoragePath()).exists());
475483
}
476484

485+
@Test
486+
public void testCheckCSR() throws NoSuchAlgorithmException, IOException, OperatorCreationException, CertificateException {
487+
deleteKeys();
488+
489+
// Create public/private key pair
490+
KeyPair keyPair = EncryptionUtils.generateKeyPair();
491+
492+
// create CSR
493+
AccountManager accountManager = AccountManager.get(targetContext);
494+
String userId = accountManager.getUserData(account, AccountUtils.Constants.KEY_USER_ID);
495+
String urlEncoded = CsrHelper.generateCsrPemEncodedString(keyPair, userId);
496+
497+
SendCSROperation operation = new SendCSROperation(urlEncoded);
498+
RemoteOperationResult result = operation.execute(account, targetContext);
499+
500+
assertTrue(result.isSuccess());
501+
String publicKeyString = (String) result.getData().get(0);
502+
503+
// check key
504+
RSAPrivateCrtKey privateKey = (RSAPrivateCrtKey) keyPair.getPrivate();
505+
OpenSSLRSAPublicKey publicKey = EncryptionUtils.convertPublicKeyFromString(publicKeyString);
506+
507+
BigInteger modulusPublic = publicKey.getModulus();
508+
BigInteger modulusPrivate = privateKey.getModulus();
509+
510+
assertEquals(modulusPrivate, modulusPublic);
511+
}
512+
477513
private void deleteFile(int i) {
478514
ArrayList<OCFile> files = new ArrayList<>();
479515
for (OCFile file : getStorageManager().getFolderContent(currentFolder, false)) {
@@ -529,11 +565,11 @@ public void reInit() throws Exception {
529565
private void useExistingKeys() throws Exception {
530566
// download them from server
531567
GetPublicKeyOperation publicKeyOperation = new GetPublicKeyOperation();
532-
RemoteOperationResult publicKeyResult = publicKeyOperation.execute(account, targetContext);
568+
RemoteOperationResult<String> publicKeyResult = publicKeyOperation.execute(account, targetContext);
533569

534570
assertTrue("Result code:" + publicKeyResult.getHttpCode(), publicKeyResult.isSuccess());
535571

536-
String publicKeyFromServer = (String) publicKeyResult.getData().get(0);
572+
String publicKeyFromServer = publicKeyResult.getResultData();
537573
arbitraryDataProvider.storeOrUpdateKeyValue(account.name,
538574
EncryptionUtils.PUBLIC_KEY,
539575
publicKeyFromServer);
@@ -559,7 +595,9 @@ private void useExistingKeys() throws Exception {
559595
TODO do not c&p code
560596
*/
561597
private static void createKeys() throws Exception {
562-
String publicKey;
598+
deleteKeys();
599+
600+
String publicKeyString;
563601

564602
// Create public/private key pair
565603
KeyPair keyPair = EncryptionUtils.generateKeyPair();
@@ -573,7 +611,18 @@ private static void createKeys() throws Exception {
573611
RemoteOperationResult result = operation.execute(account, targetContext);
574612

575613
if (result.isSuccess()) {
576-
publicKey = (String) result.getData().get(0);
614+
publicKeyString = (String) result.getData().get(0);
615+
616+
// check key
617+
RSAPrivateCrtKey privateKey = (RSAPrivateCrtKey) keyPair.getPrivate();
618+
OpenSSLRSAPublicKey publicKey = EncryptionUtils.convertPublicKeyFromString(publicKeyString);
619+
620+
BigInteger modulusPublic = publicKey.getModulus();
621+
BigInteger modulusPrivate = privateKey.getModulus();
622+
623+
if (modulusPrivate.compareTo(modulusPublic) != 0) {
624+
throw new RuntimeException("Wrong CSR returned");
625+
}
577626
} else {
578627
throw new Exception("failed to send CSR", result.getException());
579628
}
@@ -591,14 +640,25 @@ private static void createKeys() throws Exception {
591640
if (storePrivateKeyResult.isSuccess()) {
592641
arbitraryDataProvider.storeOrUpdateKeyValue(account.name, EncryptionUtils.PRIVATE_KEY,
593642
privateKeyString);
594-
arbitraryDataProvider.storeOrUpdateKeyValue(account.name, EncryptionUtils.PUBLIC_KEY, publicKey);
643+
arbitraryDataProvider.storeOrUpdateKeyValue(account.name, EncryptionUtils.PUBLIC_KEY, publicKeyString);
595644
arbitraryDataProvider.storeOrUpdateKeyValue(account.name, EncryptionUtils.MNEMONIC,
596645
generateMnemonicString());
597646
} else {
598647
throw new RuntimeException("Error uploading private key!");
599648
}
600649
}
601650

651+
private static void deleteKeys() {
652+
RemoteOperationResult<PrivateKey> privateKeyRemoteOperationResult = new GetPrivateKeyOperation().execute(client);
653+
RemoteOperationResult<String> publicKeyRemoteOperationResult = new GetPublicKeyOperation().execute(client);
654+
655+
if (privateKeyRemoteOperationResult.isSuccess() || publicKeyRemoteOperationResult.isSuccess()) {
656+
// delete keys
657+
assertTrue(new DeletePrivateKeyOperation().execute(client).isSuccess());
658+
assertTrue(new DeletePublicKeyOperation().execute(client).isSuccess());
659+
}
660+
}
661+
602662
private static String generateMnemonicString() {
603663
return "1 2 3 4 5 6";
604664
}

app/src/androidTest/java/com/owncloud/android/util/EncryptionTestIT.java

Lines changed: 15 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -36,6 +36,7 @@
3636

3737
import org.apache.commons.codec.binary.Hex;
3838
import org.apache.commons.io.FileUtils;
39+
import org.conscrypt.OpenSSLRSAPublicKey;
3940
import org.junit.Rule;
4041
import org.junit.Test;
4142
import org.junit.runner.RunWith;
@@ -45,11 +46,13 @@
4546
import java.io.FileOutputStream;
4647
import java.io.IOException;
4748
import java.io.InputStream;
49+
import java.math.BigInteger;
4850
import java.security.KeyPair;
4951
import java.security.KeyPairGenerator;
5052
import java.security.MessageDigest;
5153
import java.security.PrivateKey;
5254
import java.security.SecureRandom;
55+
import java.security.interfaces.RSAPrivateCrtKey;
5356
import java.util.Arrays;
5457
import java.util.HashMap;
5558
import java.util.HashSet;
@@ -178,6 +181,18 @@ public void encryptStringAsymmetricWrongPublicKey() throws Exception {
178181
decryptStringAsymmetric(encryptedString, keyPair2.getPrivate());
179182
}
180183

184+
@Test
185+
public void testModulus() throws Exception {
186+
KeyPair keyPair = EncryptionUtils.generateKeyPair();
187+
OpenSSLRSAPublicKey publicKey = (OpenSSLRSAPublicKey) keyPair.getPublic();
188+
RSAPrivateCrtKey privateKey = (RSAPrivateCrtKey) keyPair.getPrivate();
189+
190+
BigInteger modulusPublic = publicKey.getModulus();
191+
BigInteger modulusPrivate = privateKey.getModulus();
192+
193+
assertEquals(modulusPrivate, modulusPublic);
194+
}
195+
181196
@Test
182197
public void encryptStringSymmetricRandom() throws Exception {
183198
int max = 500;

app/src/main/java/com/owncloud/android/ui/dialog/SetupEncryptionDialogFragment.java

Lines changed: 16 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -295,12 +295,12 @@ protected String doInBackground(Void... voids) {
295295
// - decrypt private key, store unencrypted private key in database
296296

297297
GetPublicKeyOperation publicKeyOperation = new GetPublicKeyOperation();
298-
RemoteOperationResult publicKeyResult = publicKeyOperation.execute(user, getContext());
298+
RemoteOperationResult<String> publicKeyResult = publicKeyOperation.execute(user, getContext());
299299

300300
if (publicKeyResult.isSuccess()) {
301301
Log_OC.d(TAG, "public key successful downloaded for " + user.getAccountName());
302302

303-
String publicKeyFromServer = (String) publicKeyResult.getData().get(0);
303+
String publicKeyFromServer = publicKeyResult.getResultData();
304304
arbitraryDataProvider.storeOrUpdateKeyValue(user.getAccountName(),
305305
EncryptionUtils.PUBLIC_KEY,
306306
publicKeyFromServer);
@@ -357,7 +357,7 @@ protected String doInBackground(Void... voids) {
357357
// - encrypt private key, push key to server, store unencrypted private key in database
358358

359359
try {
360-
String publicKey;
360+
String publicKeyString;
361361

362362
// Create public/private key pair
363363
KeyPair keyPair = EncryptionUtils.generateKeyPair();
@@ -371,8 +371,13 @@ protected String doInBackground(Void... voids) {
371371
RemoteOperationResult result = operation.execute(user, getContext());
372372

373373
if (result.isSuccess()) {
374+
publicKeyString = (String) result.getData().get(0);
375+
376+
if (!EncryptionUtils.isMatchingKeys(keyPair, publicKeyString)) {
377+
throw new RuntimeException("Wrong CSR returned");
378+
}
379+
374380
Log_OC.d(TAG, "public key success");
375-
publicKey = (String) result.getData().get(0);
376381
} else {
377382
keyResult = KEY_FAILED;
378383
return "";
@@ -391,10 +396,14 @@ protected String doInBackground(Void... voids) {
391396
if (storePrivateKeyResult.isSuccess()) {
392397
Log_OC.d(TAG, "private key success");
393398

394-
arbitraryDataProvider.storeOrUpdateKeyValue(user.getAccountName(), EncryptionUtils.PRIVATE_KEY,
399+
arbitraryDataProvider.storeOrUpdateKeyValue(user.getAccountName(),
400+
EncryptionUtils.PRIVATE_KEY,
395401
privateKeyString);
396-
arbitraryDataProvider.storeOrUpdateKeyValue(user.getAccountName(), EncryptionUtils.PUBLIC_KEY, publicKey);
397-
arbitraryDataProvider.storeOrUpdateKeyValue(user.getAccountName(), EncryptionUtils.MNEMONIC,
402+
arbitraryDataProvider.storeOrUpdateKeyValue(user.getAccountName(),
403+
EncryptionUtils.PUBLIC_KEY,
404+
publicKeyString);
405+
arbitraryDataProvider.storeOrUpdateKeyValue(user.getAccountName(),
406+
EncryptionUtils.MNEMONIC,
398407
generateMnemonicString(true));
399408

400409
keyResult = KEY_CREATED;

app/src/main/java/com/owncloud/android/utils/CsrHelper.java

Lines changed: 7 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -20,6 +20,8 @@
2020
import java.io.IOException;
2121
import java.security.KeyPair;
2222

23+
import androidx.annotation.VisibleForTesting;
24+
2325
/**
2426
* copied & modified from:
2527
* https://github.com/awslabs/aws-sdk-android-samples/blob/master/CreateIotCertWithCSR/src/com/amazonaws/demo/csrcert/CsrHelper.java
@@ -55,13 +57,14 @@ public static String generateCsrPemEncodedString(KeyPair keyPair, String userId)
5557
* Create the certificate signing request (CSR) from private and public keys
5658
*
5759
* @param keyPair the KeyPair with private and public keys
58-
* @param userId userId of CSR owner
60+
* @param userId userId of CSR owner
5961
* @return PKCS10CertificationRequest with the certificate signing request (CSR) data
60-
* @throws IOException thrown if key cannot be created
62+
* @throws IOException thrown if key cannot be created
6163
* @throws OperatorCreationException thrown if contentSigner cannot be build
6264
*/
63-
private static PKCS10CertificationRequest generateCSR(KeyPair keyPair, String userId) throws IOException,
64-
OperatorCreationException {
65+
@VisibleForTesting
66+
public static PKCS10CertificationRequest generateCSR(KeyPair keyPair, String userId) throws IOException,
67+
OperatorCreationException {
6568
String principal = "CN=" + userId;
6669
AsymmetricKeyParameter privateKey = PrivateKeyFactory.createKey(keyPair.getPrivate().getEncoded());
6770
AlgorithmIdentifier signatureAlgorithm = new DefaultSignatureAlgorithmIdentifierFinder().find("SHA1WITHRSA");

app/src/main/java/com/owncloud/android/utils/EncryptionUtils.java

Lines changed: 26 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -46,6 +46,7 @@
4646
import com.owncloud.android.operations.UploadException;
4747

4848
import org.apache.commons.httpclient.HttpStatus;
49+
import org.conscrypt.OpenSSLRSAPublicKey;
4950

5051
import java.io.BufferedReader;
5152
import java.io.ByteArrayInputStream;
@@ -54,6 +55,7 @@
5455
import java.io.InputStream;
5556
import java.io.InputStreamReader;
5657
import java.io.RandomAccessFile;
58+
import java.math.BigInteger;
5759
import java.nio.charset.StandardCharsets;
5860
import java.security.InvalidAlgorithmParameterException;
5961
import java.security.InvalidKeyException;
@@ -69,6 +71,7 @@
6971
import java.security.cert.CertificateException;
7072
import java.security.cert.CertificateFactory;
7173
import java.security.cert.X509Certificate;
74+
import java.security.interfaces.RSAPrivateCrtKey;
7275
import java.security.spec.InvalidKeySpecException;
7376
import java.security.spec.KeySpec;
7477
import java.security.spec.PKCS8EncodedKeySpec;
@@ -852,10 +855,33 @@ public static RemoteOperationResult unlockFolder(OCFile parentFolder, OwnCloudCl
852855
}
853856
}
854857

858+
public static OpenSSLRSAPublicKey convertPublicKeyFromString(String string) throws CertificateException {
859+
String trimmedCert = string.replace("-----BEGIN CERTIFICATE-----\n", "")
860+
.replace("-----END CERTIFICATE-----\n", "");
861+
byte[] encodedCert = trimmedCert.getBytes(StandardCharsets.UTF_8);
862+
byte[] decodedCert = org.apache.commons.codec.binary.Base64.decodeBase64(encodedCert);
863+
864+
CertificateFactory certFactory = CertificateFactory.getInstance("X.509");
865+
InputStream in = new ByteArrayInputStream(decodedCert);
866+
X509Certificate certificate = (X509Certificate) certFactory.generateCertificate(in);
867+
return (OpenSSLRSAPublicKey) certificate.getPublicKey();
868+
}
869+
855870
public static void removeE2E(ArbitraryDataProvider arbitraryDataProvider, User user) {
856871
// delete stored E2E keys and mnemonic
857872
arbitraryDataProvider.deleteKeyForAccount(user.getAccountName(), EncryptionUtils.PRIVATE_KEY);
858873
arbitraryDataProvider.deleteKeyForAccount(user.getAccountName(), EncryptionUtils.PUBLIC_KEY);
859874
arbitraryDataProvider.deleteKeyForAccount(user.getAccountName(), EncryptionUtils.MNEMONIC);
860875
}
876+
877+
public static boolean isMatchingKeys(KeyPair keyPair, String publicKeyString) throws CertificateException {
878+
// check key
879+
RSAPrivateCrtKey privateKey = (RSAPrivateCrtKey) keyPair.getPrivate();
880+
OpenSSLRSAPublicKey publicKey = EncryptionUtils.convertPublicKeyFromString(publicKeyString);
881+
882+
BigInteger modulusPublic = publicKey.getModulus();
883+
BigInteger modulusPrivate = privateKey.getModulus();
884+
885+
return modulusPrivate.compareTo(modulusPublic) == 0;
886+
}
861887
}

build.gradle

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -6,7 +6,7 @@ buildscript {
66
daggerVersion = "2.44.2"
77
markwonVersion = "4.6.2"
88
prismVersion = "2.0.0"
9-
androidLibraryVersion = "master-SNAPSHOT"
9+
androidLibraryVersion = "deleteEncryptedKeys-SNAPSHOT"
1010
mockitoVersion = "4.9.0"
1111
mockitoKotlinVersion = "4.1.0"
1212
mockkVersion = "1.13.3"

0 commit comments

Comments
 (0)