diff options
Diffstat (limited to 'app/src/main/java/org/pacien/tincapp/utils/PemUtils.kt')
-rw-r--r-- | app/src/main/java/org/pacien/tincapp/utils/PemUtils.kt | 62 |
1 files changed, 62 insertions, 0 deletions
diff --git a/app/src/main/java/org/pacien/tincapp/utils/PemUtils.kt b/app/src/main/java/org/pacien/tincapp/utils/PemUtils.kt new file mode 100644 index 0000000..3d59476 --- /dev/null +++ b/app/src/main/java/org/pacien/tincapp/utils/PemUtils.kt | |||
@@ -0,0 +1,62 @@ | |||
1 | package org.pacien.tincapp.utils | ||
2 | |||
3 | import org.bouncycastle.openssl.PEMException | ||
4 | import org.bouncycastle.openssl.PEMParser | ||
5 | import org.bouncycastle.openssl.jcajce.JcaPEMWriter | ||
6 | import org.bouncycastle.openssl.jcajce.JcePEMDecryptorProviderBuilder | ||
7 | import org.bouncycastle.openssl.jcajce.JcePEMEncryptorBuilder | ||
8 | import org.bouncycastle.util.encoders.Hex | ||
9 | import org.bouncycastle.util.io.pem.PemHeader | ||
10 | import org.bouncycastle.util.io.pem.PemObject | ||
11 | import java.io.File | ||
12 | import java.io.FileReader | ||
13 | import java.io.Writer | ||
14 | |||
15 | /** | ||
16 | * @author pacien | ||
17 | */ | ||
18 | object PemUtils { | ||
19 | |||
20 | private val PROVIDER = org.bouncycastle.jce.provider.BouncyCastleProvider() | ||
21 | private val ENCRYPTED_PROC_TYPE_HEADER = PemHeader("Proc-Type", "4,ENCRYPTED") | ||
22 | private val DEK_INFO_HEADER_KEY = "DEK-Info" | ||
23 | private val ALGO = "AES-256-CBC" | ||
24 | |||
25 | private class DekInfo(val algName: String, val iv: ByteArray) | ||
26 | |||
27 | private fun dekInfoHeader(iv: ByteArray) = PemHeader(DEK_INFO_HEADER_KEY, "$ALGO,${Hex.toHexString(iv)}") | ||
28 | private fun PemObject.getPemHeaders() = headers.map { it as PemHeader } | ||
29 | |||
30 | fun read(f: File): PemObject = PEMParser(FileReader(f)).readPemObject() | ||
31 | fun write(obj: PemObject, out: Writer) = JcaPEMWriter(out).apply { writeObject(obj) }.apply { close() } | ||
32 | fun isEncrypted(obj: PemObject) = obj.headers.contains(ENCRYPTED_PROC_TYPE_HEADER) | ||
33 | |||
34 | fun encrypt(obj: PemObject, passPhrase: String) = | ||
35 | JcePEMEncryptorBuilder(ALGO) | ||
36 | .setProvider(PROVIDER) | ||
37 | .build(passPhrase.toCharArray()) | ||
38 | .let { PemObject(obj.type, listOf(ENCRYPTED_PROC_TYPE_HEADER, dekInfoHeader(it.iv)), it.encrypt(obj.content)) } | ||
39 | |||
40 | fun decrypt(obj: PemObject, passPhrase: String?) = | ||
41 | if (isEncrypted(obj)) { | ||
42 | val dekInfo = try { | ||
43 | obj.getPemHeaders() | ||
44 | .find { it.name == DEK_INFO_HEADER_KEY }!! | ||
45 | .value!! | ||
46 | .split(',') | ||
47 | .let { DekInfo(it[0], Hex.decode(it[1])) } | ||
48 | } catch (e: Exception) { | ||
49 | throw PEMException("Malformed DEK-Info header.", e) | ||
50 | } | ||
51 | |||
52 | JcePEMDecryptorProviderBuilder() | ||
53 | .setProvider(PROVIDER) | ||
54 | .build(passPhrase?.toCharArray()) | ||
55 | .get(dekInfo.algName) | ||
56 | .decrypt(obj.content, dekInfo.iv) | ||
57 | .let { PemObject(obj.type, it) } | ||
58 | } else { | ||
59 | obj | ||
60 | } | ||
61 | |||
62 | } | ||