Skip to content

Commit 4063c1a

Browse files
committed
Clean up p12 loading.
1 parent 9a6ae7c commit 4063c1a

6 files changed

Lines changed: 25 additions & 72 deletions

File tree

README.md

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -241,7 +241,7 @@ Key Pairs:
241241

242242
| Method | Description |
243243
|-------------------------------------|--------------------------------------------|
244-
| `KeyPair.ofPkcs12()` | Load private/public key from a .p12 file |
244+
| `KeyPair.ofPkcs12()` | Load private/public key from a .p12 stream |
245245

246246
### Key Rings
247247
The key loading lambda (keyProvider() function) receives the token's tainted

paseto/.api-validation/paseto.api

Lines changed: 5 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -580,7 +580,6 @@ public final class net/aholbrook/paseto/exception/Pkcs12LoadException : net/ahol
580580
public final class net/aholbrook/paseto/exception/Pkcs12LoadException$Reason : java/lang/Enum {
581581
public static final field ALGORITHM_NOT_FOUND Lnet/aholbrook/paseto/exception/Pkcs12LoadException$Reason;
582582
public static final field CERTIFICATE_ERROR Lnet/aholbrook/paseto/exception/Pkcs12LoadException$Reason;
583-
public static final field FILE_NOT_FOUND Lnet/aholbrook/paseto/exception/Pkcs12LoadException$Reason;
584583
public static final field INCORRECT_PASSWORD Lnet/aholbrook/paseto/exception/Pkcs12LoadException$Reason;
585584
public static final field IO_EXCEPTION Lnet/aholbrook/paseto/exception/Pkcs12LoadException$Reason;
586585
public static final field PRIVATE_KEY_NOT_FOUND Lnet/aholbrook/paseto/exception/Pkcs12LoadException$Reason;
@@ -717,16 +716,16 @@ public final class net/aholbrook/paseto/protocol/key/KeyPair {
717716
public final fun getSecretKey ()Lnet/aholbrook/paseto/protocol/key/AsymmetricSecretKey;
718717
public final fun getVersion ()Lnet/aholbrook/paseto/protocol/Version;
719718
public fun hashCode ()I
720-
public static final fun ofPkcs12 (Ljava/lang/String;Ljava/lang/String;Ljava/lang/String;)Lnet/aholbrook/paseto/protocol/key/KeyPair;
721-
public static final fun ofPkcs12 (Ljava/lang/String;Ljava/lang/String;Ljava/lang/String;Ljava/lang/String;)Lnet/aholbrook/paseto/protocol/key/KeyPair;
719+
public static final fun ofPkcs12 (Ljava/io/InputStream;Ljava/lang/String;Ljava/lang/String;)Lnet/aholbrook/paseto/protocol/key/KeyPair;
720+
public static final fun ofPkcs12 (Ljava/io/InputStream;Ljava/lang/String;Ljava/lang/String;Ljava/lang/String;)Lnet/aholbrook/paseto/protocol/key/KeyPair;
722721
}
723722

724723
public final class net/aholbrook/paseto/protocol/key/KeyPair$Companion {
725724
public final fun generate (Lnet/aholbrook/paseto/protocol/Version;Lnet/aholbrook/paseto/protocol/key/KeyLifecycle;)Lnet/aholbrook/paseto/protocol/key/KeyPair;
726725
public static synthetic fun generate$default (Lnet/aholbrook/paseto/protocol/key/KeyPair$Companion;Lnet/aholbrook/paseto/protocol/Version;Lnet/aholbrook/paseto/protocol/key/KeyLifecycle;ILjava/lang/Object;)Lnet/aholbrook/paseto/protocol/key/KeyPair;
727-
public final fun ofPkcs12 (Ljava/lang/String;Ljava/lang/String;Ljava/lang/String;)Lnet/aholbrook/paseto/protocol/key/KeyPair;
728-
public final fun ofPkcs12 (Ljava/lang/String;Ljava/lang/String;Ljava/lang/String;Ljava/lang/String;)Lnet/aholbrook/paseto/protocol/key/KeyPair;
729-
public static synthetic fun ofPkcs12$default (Lnet/aholbrook/paseto/protocol/key/KeyPair$Companion;Ljava/lang/String;Ljava/lang/String;Ljava/lang/String;Ljava/lang/String;ILjava/lang/Object;)Lnet/aholbrook/paseto/protocol/key/KeyPair;
726+
public final fun ofPkcs12 (Ljava/io/InputStream;Ljava/lang/String;Ljava/lang/String;)Lnet/aholbrook/paseto/protocol/key/KeyPair;
727+
public final fun ofPkcs12 (Ljava/io/InputStream;Ljava/lang/String;Ljava/lang/String;Ljava/lang/String;)Lnet/aholbrook/paseto/protocol/key/KeyPair;
728+
public static synthetic fun ofPkcs12$default (Lnet/aholbrook/paseto/protocol/key/KeyPair$Companion;Ljava/io/InputStream;Ljava/lang/String;Ljava/lang/String;Ljava/lang/String;ILjava/lang/Object;)Lnet/aholbrook/paseto/protocol/key/KeyPair;
730729
}
731730

732731
public final class net/aholbrook/paseto/protocol/key/SymmetricKey {

paseto/src/main/kotlin/net/aholbrook/paseto/exception/KeyExceptions.kt

Lines changed: 0 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -2,7 +2,6 @@ package net.aholbrook.paseto.exception
22

33
import net.aholbrook.paseto.InternalApi
44
import net.aholbrook.paseto.protocol.Version
5-
import java.io.FileNotFoundException
65
import java.io.IOException
76
import java.security.NoSuchAlgorithmException
87
import java.security.UnrecoverableKeyException
@@ -75,7 +74,6 @@ class EcKeyException @InternalApi constructor(msg: String, cause: Throwable? = n
7574
class Pkcs12LoadException @InternalApi constructor(val reason: Reason, cause: Throwable? = null) :
7675
KeyException(
7776
when (reason) {
78-
Reason.FILE_NOT_FOUND -> "File not found."
7977
Reason.ALGORITHM_NOT_FOUND -> "Key algorithm not found - $cause"
8078
Reason.UNRECOVERABLE_KEY -> "Unrecoverable key - $cause"
8179
Reason.IO_EXCEPTION -> "IO exception - $cause"
@@ -87,9 +85,6 @@ class Pkcs12LoadException @InternalApi constructor(val reason: Reason, cause: Th
8785
cause,
8886
) {
8987

90-
/** @param e Wrapped [FileNotFoundException]. */
91-
@InternalApi constructor(e: FileNotFoundException) : this(Reason.FILE_NOT_FOUND, e)
92-
9388
/** @param e Wrapped [NoSuchAlgorithmException]. */
9489
@InternalApi constructor(e: NoSuchAlgorithmException) : this(Reason.ALGORITHM_NOT_FOUND, e)
9590

@@ -113,9 +108,6 @@ class Pkcs12LoadException @InternalApi constructor(val reason: Reason, cause: Th
113108

114109
/** Categorical failure reason when loading a PKCS#12 keystore. */
115110
enum class Reason {
116-
/** Keystore path does not exist. */
117-
FILE_NOT_FOUND,
118-
119111
/** Required crypto algorithm is unavailable. */
120112
ALGORITHM_NOT_FOUND,
121113

paseto/src/main/kotlin/net/aholbrook/paseto/protocol/key/KeyPair.kt

Lines changed: 5 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -8,9 +8,8 @@ import net.aholbrook.paseto.exception.KeyVersionException
88
import net.aholbrook.paseto.exception.Pkcs12LoadException
99
import net.aholbrook.paseto.protocol.Purpose
1010
import net.aholbrook.paseto.protocol.Version
11-
import java.io.FileInputStream
12-
import java.io.FileNotFoundException
1311
import java.io.IOException
12+
import java.io.InputStream
1413
import java.security.KeyStore
1514
import java.security.KeyStoreException
1615
import java.security.NoSuchAlgorithmException
@@ -105,11 +104,11 @@ class KeyPair(val secretKey: AsymmetricSecretKey?, val publicKey: AsymmetricPubl
105104
}
106105

107106
/**
108-
* Load an RSA key pair from a PKCS#12 keystore.
107+
* Load an RSA key pair from a PKCS#12 keystore stream.
109108
*
110109
* This helper is intended for `v1.public` key material.
111110
*
112-
* @param keystoreFile Path to `.p12`/`.pfx` file.
111+
* @param input Stream providing the `.p12`/`.pfx` keystore data.
113112
* @param keystorePass Keystore password.
114113
* @param alias Alias containing key/certificate entries.
115114
* @param keyPass Private key password (defaults to [keystorePass]).
@@ -118,14 +117,14 @@ class KeyPair(val secretKey: AsymmetricSecretKey?, val publicKey: AsymmetricPubl
118117
@JvmStatic
119118
@JvmOverloads
120119
fun ofPkcs12(
121-
keystoreFile: String,
120+
input: InputStream,
122121
keystorePass: String,
123122
alias: String,
124123
keyPass: String = keystorePass,
125124
): KeyPair {
126125
try {
127126
val p12 = KeyStore.getInstance("PKCS12")
128-
p12.load(FileInputStream(keystoreFile), keystorePass.toCharArray())
127+
p12.load(input, keystorePass.toCharArray())
129128

130129
val privateKey = p12.getKey(alias, keyPass.toCharArray()) as? PrivateKey
131130
?: throw Pkcs12LoadException(Pkcs12LoadException.Reason.PRIVATE_KEY_NOT_FOUND)
@@ -137,8 +136,6 @@ class KeyPair(val secretKey: AsymmetricSecretKey?, val publicKey: AsymmetricPubl
137136
AsymmetricSecretKey.ofRawBytes(privateKey.encoded, Version.V1),
138137
AsymmetricPublicKey.ofRawBytes(publicKey.encoded, Version.V1),
139138
)
140-
} catch (e: FileNotFoundException) {
141-
throw Pkcs12LoadException(e)
142139
} catch (e: CertificateException) {
143140
throw Pkcs12LoadException(e) // Unlikely to ever throw.
144141
} catch (e: NoSuchAlgorithmException) {

paseto/src/test/kotlin/net/aholbrook/paseto/TestHelpers.kt

Lines changed: 3 additions & 15 deletions
Original file line numberDiff line numberDiff line change
@@ -3,7 +3,7 @@ package net.aholbrook.paseto
33
import net.aholbrook.paseto.protocol.Version
44
import net.aholbrook.paseto.protocol.key.KeyPair
55
import net.aholbrook.paseto.protocol.key.SymmetricKey
6-
import java.io.File
6+
import java.io.InputStream
77

88
val keyV1Local by lazy { SymmetricKey.generate(Version.V1) }
99
val keyV1Public by lazy { KeyPair.generate(Version.V1) }
@@ -15,18 +15,6 @@ val keyV4Local by lazy { SymmetricKey.generate(Version.V4) }
1515
val keyV4Public by lazy { KeyPair.generate(Version.V4) }
1616

1717
object TestFiles {
18-
fun p12ResourcePath(name: String): String {
19-
val stream = TestFiles::class.java.getResourceAsStream("/p12/$name")
20-
?: error("Unable to find /p12/$name in test resources")
21-
val temp = File.createTempFile(name.removeSuffix(".p12"), ".p12")
22-
temp.deleteOnExit()
23-
24-
stream.use { input ->
25-
temp.outputStream().use { output ->
26-
input.copyTo(output)
27-
}
28-
}
29-
30-
return temp.path
31-
}
18+
fun p12ResourceStream(name: String): InputStream = TestFiles::class.java.getResourceAsStream("/p12/$name")
19+
?: error("Unable to find /p12/$name in test resources")
3220
}

paseto/src/test/kotlin/net/aholbrook/paseto/protocol/key/Pkcs12Tests.kt

Lines changed: 11 additions & 34 deletions
Original file line numberDiff line numberDiff line change
@@ -17,9 +17,8 @@ import org.junit.jupiter.api.Test
1717
import org.junit.jupiter.params.ParameterizedTest
1818
import org.junit.jupiter.params.provider.Arguments
1919
import org.junit.jupiter.params.provider.MethodSource
20-
import java.io.File
21-
import java.io.FileNotFoundException
2220
import java.io.IOException
21+
import java.io.InputStream
2322
import java.security.KeyStore
2423
import java.security.KeyStoreException
2524
import java.security.NoSuchAlgorithmException
@@ -31,7 +30,7 @@ class Pkcs12Tests {
3130
@Test
3231
fun pkcs12Load_withDefaultKeyPassword() {
3332
val keys = KeyPair.ofPkcs12(
34-
keystoreFile = TestFiles.p12ResourcePath("rfc_v1_rsa.p12"),
33+
input = TestFiles.p12ResourceStream("rfc_v1_rsa.p12"),
3534
keystorePass = "testtest",
3635
alias = "test",
3736
)
@@ -43,7 +42,7 @@ class Pkcs12Tests {
4342
@Test
4443
fun pkcs12Load_withExplicitKeyPassword() {
4544
val keys = KeyPair.ofPkcs12(
46-
keystoreFile = TestFiles.p12ResourcePath("test_v1_rsa.p12"),
45+
input = TestFiles.p12ResourceStream("test_v1_rsa.p12"),
4746
keystorePass = "password",
4847
alias = "test",
4948
keyPass = "password",
@@ -53,27 +52,11 @@ class Pkcs12Tests {
5352
keys.publicKey.version shouldBe Version.V1
5453
}
5554

56-
@Test
57-
fun pkcs12Load_notFound() {
58-
val missingFile = File.createTempFile("missing-p12", ".p12").apply { delete() }
59-
60-
val ex = shouldThrow<Pkcs12LoadException> {
61-
KeyPair.ofPkcs12(
62-
keystoreFile = missingFile.path,
63-
keystorePass = "testtest",
64-
alias = "test",
65-
keyPass = "testtest",
66-
)
67-
}
68-
69-
ex.reason shouldBe Pkcs12LoadException.Reason.FILE_NOT_FOUND
70-
}
71-
7255
@Test
7356
fun pkcs12Load_wrongPassword() {
7457
val ex = shouldThrow<Pkcs12LoadException> {
7558
KeyPair.ofPkcs12(
76-
keystoreFile = TestFiles.p12ResourcePath("rfc_v1_rsa.p12"),
59+
input = TestFiles.p12ResourceStream("rfc_v1_rsa.p12"),
7760
keystorePass = "wrong",
7861
alias = "test",
7962
keyPass = "testtest",
@@ -87,7 +70,7 @@ class Pkcs12Tests {
8770
fun pkcs12Load_wrongAlias() {
8871
val ex = shouldThrow<Pkcs12LoadException> {
8972
KeyPair.ofPkcs12(
90-
keystoreFile = TestFiles.p12ResourcePath("rfc_v1_rsa.p12"),
73+
input = TestFiles.p12ResourceStream("rfc_v1_rsa.p12"),
9174
keystorePass = "testtest",
9275
alias = "wrong",
9376
keyPass = "testtest",
@@ -101,7 +84,7 @@ class Pkcs12Tests {
10184
fun pkcs12Load_wrongKeyPassword() {
10285
val ex = shouldThrow<Pkcs12LoadException> {
10386
KeyPair.ofPkcs12(
104-
keystoreFile = TestFiles.p12ResourcePath("rfc_v1_rsa.p12"),
87+
input = TestFiles.p12ResourceStream("rfc_v1_rsa.p12"),
10588
keystorePass = "testtest",
10689
alias = "test",
10790
keyPass = "wrong",
@@ -115,7 +98,7 @@ class Pkcs12Tests {
11598
fun pkcs12Load_noCertificate() {
11699
val ex = shouldThrow<Pkcs12LoadException> {
117100
KeyPair.ofPkcs12(
118-
keystoreFile = TestFiles.p12ResourcePath("test_v1_rsa_nopub.p12"),
101+
input = TestFiles.p12ResourceStream("test_v1_rsa_nopub.p12"),
119102
keystorePass = "password",
120103
alias = "test",
121104
keyPass = "password",
@@ -129,7 +112,7 @@ class Pkcs12Tests {
129112
fun pkcs12Load_corruptFile() {
130113
val ex = shouldThrow<Pkcs12LoadException> {
131114
KeyPair.ofPkcs12(
132-
keystoreFile = TestFiles.p12ResourcePath("test_v1_rsa_corrupt.p12"),
115+
input = TestFiles.p12ResourceStream("test_v1_rsa_corrupt.p12"),
133116
keystorePass = "password",
134117
alias = "test",
135118
keyPass = "password",
@@ -147,7 +130,6 @@ class Pkcs12Tests {
147130
expected: Pkcs12LoadException.Reason,
148131
) {
149132
val actual = when (cause) {
150-
is FileNotFoundException -> Pkcs12LoadException(cause)
151133
is NoSuchAlgorithmException -> Pkcs12LoadException(cause)
152134
is UnrecoverableKeyException -> Pkcs12LoadException(cause)
153135
is CertificateException -> Pkcs12LoadException(cause)
@@ -175,7 +157,7 @@ class Pkcs12Tests {
175157
every { KeyStore.getInstance(any()) } throws KeyStoreException()
176158

177159
val ex = shouldThrow<RuntimeException> {
178-
KeyPair.ofPkcs12("", "", "", "")
160+
KeyPair.ofPkcs12(InputStream.nullInputStream(), "", "", "")
179161
}
180162
withClue("check type KeyStoreException") {
181163
(ex.cause is KeyStoreException) shouldBe true
@@ -190,7 +172,7 @@ class Pkcs12Tests {
190172

191173
val ex = shouldThrow<Pkcs12LoadException> {
192174
KeyPair.ofPkcs12(
193-
keystoreFile = TestFiles.p12ResourcePath("rfc_v1_rsa.p12"),
175+
input = TestFiles.p12ResourceStream("rfc_v1_rsa.p12"),
194176
keystorePass = "testtest",
195177
alias = "test",
196178
)
@@ -208,7 +190,7 @@ class Pkcs12Tests {
208190

209191
val ex = shouldThrow<Pkcs12LoadException> {
210192
KeyPair.ofPkcs12(
211-
keystoreFile = TestFiles.p12ResourcePath("rfc_v1_rsa.p12"),
193+
input = TestFiles.p12ResourceStream("rfc_v1_rsa.p12"),
212194
keystorePass = "testtest",
213195
alias = "test",
214196
)
@@ -222,11 +204,6 @@ class Pkcs12Tests {
222204
companion object {
223205
@JvmStatic
224206
fun reasonMappings(): Stream<Arguments> = Stream.of(
225-
Arguments.of(
226-
"FileNotFoundException -> FILE_NOT_FOUND",
227-
FileNotFoundException(),
228-
Pkcs12LoadException.Reason.FILE_NOT_FOUND,
229-
),
230207
Arguments.of(
231208
"NoSuchAlgorithmException -> ALGORITHM_NOT_FOUND",
232209
NoSuchAlgorithmException(),

0 commit comments

Comments
 (0)