Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Private Key is interpreted as PEMKeyPair instead of PrivateKeyInfo #1024

Open
ITSamMed opened this issue Apr 18, 2024 · 3 comments
Open

Private Key is interpreted as PEMKeyPair instead of PrivateKeyInfo #1024

ITSamMed opened this issue Apr 18, 2024 · 3 comments
Assignees
Labels

Comments

@ITSamMed
Copy link

Describe the bug?

The PrivateKey parameter does not seem to be handled correctly for the OktaClient builder when setting directly.

Instead of treating the private key as a PrivateKey, it is getting treated as as PEMKeyPair.

What is expected to happen?

OktaClient is built successfully

PrivateKeyInfo privateKeyInfo = (PrivateKeyInfo) pemContent;
privateKey = jcaPEMKeyConverter.getPrivateKey(privateKeyInfo);

What is the actual behavior?

Exception is thrown

Cannot invoke "org.bouncycastle.asn1.x509.SubjectPublicKeyInfo.getEncoded()" because the return value of "org.bouncycastle.openssl.PEMKeyPair.getPublicKeyInfo()" is null

PEMKeyPair pemKeyPair = (PEMKeyPair) pemContent;
KeyPair keyPair = jcaPEMKeyConverter.getKeyPair(pemKeyPair);
privateKey = keyPair.getPrivate();

Reproduction Steps?

  1. JJWT
val keyPair = Jwts.SIG.ES512.keyPair().build()
Clients.builder().setPrivateKey(keyPair.privateKey).build()
  1. Java KeyStore
keytool -alias <alias> -genkeypair -keyalg ec -groupname secp521r1 -keystore <keystore>.p12 -storetype pkcs12
val type = "pkcs12"
val password = <password>
val path = <path>
val alias = <alias>
val keyStore =
	try {
		KeyStore.getInstance(type)
	} catch (ex: KeyStoreException) {
		println("No keystore provider of type '${type}'.")
		throw ex
	}

try {
	FileInputStream(path).use {
		keyStore.load(it, password.toCharArray())
	}
} catch (ex: Exception) {
	when (ex) {
		is FileNotFoundException -> println("Keystore not found by path '${path}'.")
		is IOException -> println("Incorrect password for keystore.")
	}
	throw ex
}

// Assumes password is the same for keystore and key entry.
val key = keyStore.getKey(alias, password.toCharArray())
if (key == null) {
	println("Keystore entry not found by alias '${alias}'.")
	throw UnrecoverableKeyException("Keystore entry not found by alias '${alias}'.")
}

// Assumes alias is the same for private and public key entry.
val certificate = keyStore.getCertificate(alias)

val keyPair = KeyPair(certificate.publicKey, key as PrivateKey)

Clients.builder().setPrivateKey(keyPair.privateKey).build()
  1. OpenSSL + BouncyCastle
openssl genpkey -out <key>.key -algorithm EC -pkeyopt ec_paramgen_curve:P-521 -aes-128-cbc
val privateKey: PrivateKey

FileReader(File("<path>")).use { keyReader ->

	val parser = PEMParser(keyReader)
	val pair = parser.readObject() as PKCS8EncryptedPrivateKeyInfo
	val jce = JceOpenSSLPKCS8DecryptorProviderBuilder().setProvider(BouncyCastleProvider())
	val decProv = jce.build(<password>.toCharArray())
	val info = pair.decryptPrivateKeyInfo(decProv)
	val converter = JcaPEMKeyConverter()
	privateKey = converter.getPrivateKey(info)
}

Clients.builder().setPrivateKey(privateKey).build()

Additional Information?

No response

Java Version

openjdk version "17.0.10" 2024-01-16
OpenJDK Runtime Environment Temurin-17.0.10+7 (build 17.0.10+7)
OpenJDK 64-Bit Server VM Temurin-17.0.10+7 (build 17.0.10+7, mixed mode, sharing)

SDK Version

implementation("com.okta.sdk:okta-sdk-api:16.0.0")
runtimeOnly("com.okta.sdk:okta-sdk-impl:16.0.0")

OS version

BuildNumber Caption OSArchitecture Version
19045 Microsoft Windows 10 Enterprise 64-bit 10.0.19045
@ITSamMed
Copy link
Author

It seems like this may be related specifically to Elliptic Curve Digital Signature Algorithm (ECDSA).

This builds just fine: Clients.builder().setPrivateKey(Jwts.SIG.RS256.keyPair().build().private).build()
This fails to build: Clients.builder().setPrivateKey(Jwts.SIG.ES256.keyPair().build().private).build()

@ITSamMed
Copy link
Author

What's weird is that passing an unencrypted PEM file path whose private key is ES512 also seems to work...

openssl genpkey -out unencrypted.key -algorithm EC -pkeyopt ec_paramgen_curve:P-521
Clients.builder().setPrivateKey("unencrypted.key").build()

@arvindkrishnakumar-okta
Copy link
Contributor

@ITSamMed Thanks for posting! I'd take a look.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
Projects
None yet
Development

No branches or pull requests

2 participants