How to verify X509Certificate with EC key using Bouncy Castle - java

With Bouncy Castle added as a provider, the following piece of code:
private static boolean isSelfSigned(final X509Certificate cert) {
try {
final PublicKey key = cert.getPublicKey();
cert.verify(key);
return true;
} catch (final RuntimeException re) {
LOG.warn(re, "isSelfSigned: error.");
return false;
} catch (final GeneralSecurityException gse) {
LOG.warn(gse, "isSelfSigned: error.");
return false;
}
}
Results in the following two errors depending on the implementation class of cert:
java.security.InvalidKeyException: Supplied key (org.bouncycastle.jcajce.provider.asymmetric.ec.BCECPublicKey) is not a RSAPublicKey instance
or
java.security.InvalidKeyException: Supplied key (sun.security.ec.ECPublicKeyImpl) is not a RSAPublicKey instance
Does Bouncy Castle not support verifying EC signed certificates? There doesn't appear to be any parameters where I can indicate the keys are not RSA. How do I verify an EC signed certificate using Bouncy Castle?

This was a misunderstanding on my part. The check fails because the certificate does in fact have an EC key, but the parent certificate has an RSA key.

Related

cert.verify() throws java.security.InvalidKeyException: Supplied key is not a RSAPublicKey instance

I'm trying to check whether a certificate is self-signed.
public static void main(String[] args) throws CertificateException, IOException, GeneralSecurityException
{
// InputStream is = new URL("http://www.d-trust.net/cgi-bin/D-TRUST_Root_CA_2_2021.crt").openStream(); // ok
InputStream is = new URL("http://www.d-trust.net/cgi-bin/D-TRUST_Root_CA_1_2017.crt").openStream(); // not ok
CertificateFactory certFactory = CertificateFactory.getInstance("X.509");
X509Certificate cert = (X509Certificate) certFactory.generateCertificate(is);
System.out.println(cert);
System.out.println("Self signed? " + isSelfSigned(cert));
}
public static boolean isSelfSigned(X509Certificate cert) throws GeneralSecurityException
{
try
{
// Try to verify certificate signature with its own public key
PublicKey key = cert.getPublicKey();
System.out.println("key class: " + key.getClass().getName());
System.out.println("Algorithm: " + key.getAlgorithm());
cert.verify(key, new BouncyCastleProvider());
return true;
}
catch (SignatureException | InvalidKeyException ex)
{
// Invalid signature --> not self-signed
ex.printStackTrace();
return false;
}
}
I get this exception in isSelfSigned():
java.security.InvalidKeyException: Supplied key is not a RSAPublicKey instance
at org.bouncycastle.jcajce.provider.asymmetric.rsa.PSSSignatureSpi.engineInitVerify(Unknown Source)
at java.security.Signature$Delegate.engineInitVerify(Signature.java:1168)
at java.security.Signature.initVerify(Signature.java:460)
at sun.security.x509.X509CertImpl.verify(X509CertImpl.java:483)
at NewClass1.isSelfSigned(NewClass1.java:46)
at NewClass1.main(NewClass1.java:35)
This happens only with one of the URLs in my code, not the other one. The problematic certificate has algorithm 1.2.840.113549.1.1.10, which is RSASSA-PSS. I'm using BouncyCastle bcmail-jdk18on 1.72, which also uses bcprov-jdk18on and bcpkix-jdk18on as dependencies.
I'm assuming that this is a self-signed certificate, but of course I don't know for sure.
It turned out to be a java bug. I was using an older jdk8 version, and it runs fine on the current jdk8 version (Amazon Corretto 1.8.0_352). Thanks Topaco for your help.

InvalidKeySpecException when reading RSA private while building Spring bean

I am working with a legacy code base and encountering really puzzling behavior while instantiating RSAPrivateKey.
I have a RSA private key:
-----BEGIN RSA PRIVATE KEY-----
...
-----END RSA PRIVATE KEY-----
Based on the headers it looks like the RSA private key is in format PKCS#1.
I then have Spring config class as follows:
#Configuration
public class AppConfig {
// Gets the above RSA key from config file
#Value("${key}")
private String key;
#Bean
public String someRandomTestBean() {
try {
// builds the RSAPrivateKey without any errors even though key is in PKCS#1 format. Why???
RSAPrivateKey testKey = getRSAPrivateKeyFromPem(key);
} catch(NoSuchAlgorithmException | InvalidKeySpecException e) {
log.error("failed to read RSA key");
}
}
#Bean
public SomeObject anotherBean() {
SomeObjectBuilder builder = new SomeObjectBuilder();
RSAPrivateKey testKey = null;
try {
// This fails to build the key with exception: java.security.InvalidKeyException: IOException : algid parse error, not a sequence
// Okay that is expected since key is in PKCS#1 format
// What's odd here is that key is built successfully while constructing
// someRandomTestBean bean. How is that possible???
RSAPrivateKey testKey = getRSAPrivateKeyFromPem(key);
// Gets weirder here
// There is duplicate code in this legacy code base so TestUtil.getRSAPrivateKeyFromPem(...) is
// duplicate of the private function getRSAPrivateKeyFromPem from below.
// This also builds the RSAPrivateKey. How???
RSAPrivatekye testKey2 = TestUtil.getRSAPrivateKeyFromPem(key);
} catch(NoSuchAlgorithmException | InvalidKeySpecException e) {
log.error("failed to read RSA key");
}
return builder
.withKey(...)
.build();
}
private RSAPrivateKey getRSAPrivateKeyFromPem(String keyPem) {
String pem = new String(keyPem.getBytes(), StandardCharsets.UTF_8);
pem.replace("-----BEGIN RSA PRIVATE KEY-----", "");
pem.replace("-----END RSA PRIVATE KEY-----", "");
KeyFactory keyFactory = KeyFactory.getInstance("RSA");
PKCS8EncodedKeySpec keySpec = new PKCS8EncodedKeySpec(Base64.getDecoder().decode(pem));
return (RSAPrivateKey) keyFactory.generatePrivate(keySpec);
}
I was able to generate a new RSA key in PKCS#8 format everything works as expected. What's puzzling here is that I am able to instantiate RSAPrivateKey in some situations but not others while using the existing key, which appears to be in PKCS#1 format. Tried to diagnose for several hours now, but I've made not insight into it.

KeyStore "unsupported protection parameter"

I'm trying to store a SecretKey in the Android KeyStore, and I'm even directly following the documentation found here: KeyProtection: AES Key for Encryption / Description in GCM Mode, which still doesn't work.
I seem to get the following exception when I run this:
java.security.KeyStoreException: unsupported protection parameter
This exception is specifically raised when I call keyStore.setEntry(...).
I'm running Android 10 on a Samsung Galaxy S10, if that makes any difference.
#RequiresApi(23)
private static KeyStore.ProtectionParameter getProtectionParameter() {
return new KeyProtection.Builder(KeyProperties.PURPOSE_ENCRYPT | KeyProperties.PURPOSE_DECRYPT)
.setBlockModes(KeyProperties.BLOCK_MODE_GCM)
.setEncryptionPaddings(KeyProperties.ENCRYPTION_PADDING_NONE)
.build();
}
#RequiresApi(23)
private static SecretKey getSecretKey() throws Exception {
KeyStore keyStore = KeyStore.getInstance(KeyStore.getDefaultType());
keyStore.load(null);
if (keyStore.containsAlias(KEY_ALIAS)) {
final KeyStore.SecretKeyEntry secretKeyEntry = (KeyStore.SecretKeyEntry) keyStore
.getEntry(KEY_ALIAS, null);
return secretKeyEntry.getSecretKey();
}
final KeyGenerator keyGenerator = KeyGenerator
.getInstance(KeyProperties.KEY_ALGORITHM_AES, "AndroidKeyStore");
final KeyGenParameterSpec keyGenParameterSpec = new KeyGenParameterSpec.Builder(KEY_ALIAS,
KeyProperties.PURPOSE_ENCRYPT | KeyProperties.PURPOSE_DECRYPT)
.setBlockModes(KeyProperties.BLOCK_MODE_GCM)
.setEncryptionPaddings(KeyProperties.ENCRYPTION_PADDING_NONE)
.build();
keyGenerator.init(keyGenParameterSpec);
SecretKey k = keyGenerator.generateKey();
keyStore.setEntry(
KEY_ALIAS,
new KeyStore.SecretKeyEntry(k),
getProtectionParameter()
);
return getSecretKey();
}
It turns out the issue I had was in my KeyStore.getInstance and KeyGenerator.getInstance lines. One of them was using KeyStore.getDefaultType() where as the other one was using AndroidKeyStore. Changing this got rid of the unsupported protection parameter issue.
Additionally, after fixing that I ran into an issue with updating the Protection Parameter.
It turns out, that the following section of the code is incorrect
SecretKey k = keyGenerator.generateKey();
keyStore.setEntry(
KEY_ALIAS,
new KeyStore.SecretKeyEntry(k),
getProtectionParameter()
);
It tries to insert the key into the keystore twice, since generateKey() already inserts it into the KeyStore.
The fully working code would look like this
#RequiresApi(23)
private static SecretKey getSecretKey() throws Exception {
KeyStore keyStore = KeyStore.getInstance("AndroidKeyStore");
keyStore.load(null, null);
final KeyStore.SecretKeyEntry secretKeyEntry = (KeyStore.SecretKeyEntry) keyStore
.getEntry(KEY_ALIAS, null);
if (secretKeyEntry != null) {
return secretKeyEntry.getSecretKey();
}
final KeyGenerator keyGenerator = KeyGenerator
.getInstance(KeyProperties.KEY_ALGORITHM_AES, "AndroidKeyStore");
final KeyGenParameterSpec keyGenParameterSpec = new KeyGenParameterSpec.Builder(KEY_ALIAS,
KeyProperties.PURPOSE_ENCRYPT | KeyProperties.PURPOSE_DECRYPT)
.setBlockModes(KeyProperties.BLOCK_MODE_GCM)
.setEncryptionPaddings(KeyProperties.ENCRYPTION_PADDING_NONE)
.build();
keyGenerator.init(keyGenParameterSpec);
SecretKey k = keyGenerator.generateKey();
return getSecretKey();
}

Can't find private key from certificate

We are working on encryption-decryption using applet. We find some unexpected issue with digital certificate. One system has certificate and we can't find the private key from that certificate but by installing the same certificate again works fine.
Java Plug-in 10.25.2.17
Using JRE version 1.7.0_25-b17 Java HotSpot(TM) 64-Bit Server VM
User home directory = C:\Users\admin
To access private key we are using below code.
private PrivateKey getPrivateKeyFromKeyStore(String pubkey, KeyStore browser) {
PrivateKey privateKey = null;
String pubKey1 = "";
if (browser != null) {
try {
Field spiField = KeyStore.class.getDeclaredField("keyStoreSpi");
spiField.setAccessible(true);
KeyStoreSpi spi = (KeyStoreSpi) spiField.get(browser);
Field entriesField = spi.getClass().getSuperclass().getDeclaredField("entries");
entriesField.setAccessible(true);
#SuppressWarnings("rawtypes")
Collection entries = (Collection) entriesField.get(spi);
for (Object entry : entries) {
String alias = (String) invokeGetter(entry, "getAlias");
X509Certificate[] certificateChain = (X509Certificate[]) invokeGetter(entry, "getCertificateChain");
for (X509Certificate current : certificateChain) {
pubKey1 = this.bASE64Encoder.encode(current.getPublicKey().getEncoded());
if (pubkey.equals(pubKey1) && !pubkey.equals("")) {
privateKey = (PrivateKey) invokeGetter(entry, "getPrivateKey");
return privateKey;
}
}
}
} catch (Exception e) {
e.printStackTrace();
return null;
}
}
return privateKey;
}
You won't find private key in certificate because it must be in your keystore, of course, if you generated your cert with its CSR :)
As a tip, I may ask is the cert expired for example?
Anyway, the question is pretty unclear :( If you have cert you must have the keystore which was used to sign your app... It would be better you give more details...

Write certificate chain to a PEM file

I am having a certificate chain where it may contain single certificate or certificate along with intermediate CA's certificate. Now I want to write this into a PEM format file. Is it possible to achieve with existing Java libraries without any third party libraries? Below is my code for certificate chain,
final Collection<? extends Certificate> c =
(Collection<? extends Certificate>) certFactory.generateCertificates(
new ByteArrayInputStream(certificateString.getBytes()));
final Certificate[] certs = (Certificate[]) c.toArray(new Certificate[] {});
How can I write this certs into a PEM file?
try this:
BASE64Encoder encoder = new BASE64Encoder();
out.println(X509Factory.BEGIN_CERT);
encoder.encodeBuffer(cert.getEncoded(), out);
out.println(X509Factory.END_CERT);
or try this
import javax.xml.bind.DatatypeConverter;
x509cert.encode();
try {
System.out.println("---BEGIN CERTIFICATE---");
System.out.println(DatatypeConverter.printBase64Binary(x509cert.getEncoded()));
System.out.println("---END CERTIFICATE---");
} catch (CertificateEncodingException e) {
e.printStackTrace();
}

Categories

Resources