Generate or create p12 file from Android - java

I have a problem when generating a .pfx certificate that I get from an api. as can be seen in the image.
{
"type": "success",
"code": 0,
"message": "Certificado descargado exitosamente",
"detalle_mensaje": "55g/bNoVn........fHlukJDHhj4=",
"pass": "oz7FkVw1zrHC/Nt+2NQR3arg4Keo409MRbKC6MM3GoE=",
"excepcion": null,
"extension": ".pfx"
}
This is an encrypted code, where the original result is a private key:
MIIM/AIBAzCCDMIGCSqGSIb3DQEHAaCCDLMEggyvMIIMqzCCBzcGCSqGSIb3DQEHBqCCBygwggckAgEAMIIHHQYJKoZI....
So I need to generate this .pfx or .p12 file on the device storage.
I have this code to generate p12 but I don't know where to use the key (MIIM/AIBAzCCD ...)
It is worth mentioning that with this code it generates the .p12 file but when I open the certificate, I copy the password and it cannot open
String storeName = "ruta_del_dispositivo.p12";
KeyPairGenerator keyPairGenerator = KeyPairGenerator.getInstance("RSA");
keyPairGenerator.initialize(1024);
KeyPair keyPair = keyPairGenerator.generateKeyPair();
PublicKey publicKey = keyPair.getPublic();
PrivateKey privateKey = keyPair.getPrivate();
Certificate trustCert = createCertificate("CN=CA", "CN=CA", publicKey, privateKey);
Certificate[] outChain = { createCertificate("CN=Client", "CN=CA", publicKey, privateKey), trustCert };
KeyStore ks = KeyStore.getInstance("pkcs12");
ks.load(null);
// GUARDAR archivo p12
OutputStream outputStream = new FileOutputStream(storeName);
ks.store(outputStream, password.toCharArray());
outputStream.flush();
outputStream.close();
--
private static java.security.cert.X509Certificate createCertificate(String dn, String issuer, PublicKey publicKey, PrivateKey privateKey) throws Exception {
X509V3CertificateGenerator certGenerator = new X509V3CertificateGenerator();
certGenerator.setSerialNumber(BigInteger.valueOf(Math.abs(new Random().nextLong())));
certGenerator.setSubjectDN(new X509Name(dn));
certGenerator.setIssuerDN(new X509Name(issuer)); // Set issuer!
certGenerator.setNotBefore(Calendar.getInstance().getTime());
certGenerator.setNotAfter(Calendar.getInstance().getTime());
certGenerator.setPublicKey(publicKey);
certGenerator.setSignatureAlgorithm("SHA1withRSA");
X509Certificate certificate = certGenerator.generate(privateKey, "BC");
return certificate;
}

Related

Bug in Keycloak package? ( ECDSA KeyFactory not available )

When running my keycloak application, the following error appears:
java.security.NoSuchAlgorithmException: ECDSA KeyFactory not available
at java.base/java.security.KeyFactory.<init>(KeyFactory.java:138) ~[na:na]
at java.base/java.security.KeyFactory.getInstance(KeyFactory.java:183) ~[na:na]
at org.keycloak.jose.jwk.JWKParser.createECPublicKey(JWKParser.java:107) ~[keycloak-core-15.0.2.jar:15.0.2]
... 61 common frames omitted
After doing some digging, found out KeyFactory cannot settle "ECDSA" as an algorithm and therefore I should use the "EC" algorithm to generate public key.
But if KeyFactory doesnt support "ECDSA" as an algorithm, why does Keycloak-15.0.2 JWKParser class' createECPublicKey func remain trying to generate a public key with ECDSA?
try {
ECNamedCurveParameterSpec spec = ECNamedCurveTable.getParameterSpec(name);
ECNamedCurveSpec params = new ECNamedCurveSpec("prime256v1", spec.getCurve(), spec.getG(), spec.getN());
ECPoint point = new ECPoint(x, y);
ECPublicKeySpec pubKeySpec = new ECPublicKeySpec(point, params);
KeyFactory kf = KeyFactory.getInstance("ECDSA");
return kf.generatePublic(pubKeySpec);
} catch (Exception e) {
throw new RuntimeException(e);
}
Is this a bug? or am I juts completely missing something?
Your main problem is that you forgot to tell KeyFactory.getInstance that ECDSA is from BouncyCastle provider.
You add this, KeyFactory.getInstance("ECDSA"); will work:
public static final Provider BC = new BouncyCastleProvider();
...
public static void myMethod() {
...
KeyFactory kf = KeyFactory.getInstance("ECDSA", BC);
}
Alternatively you can add BouncyCastleProvider to your list of providers:
Security.addProvider(new BouncyCastleProvider());

I need to read a public and private key in java which is already given. I am not able to please advise

The public key is in .csr and private key is in .key extension. The exception I receive is
Exception in thread "main" java.security.cert.CertificateParsingException: java.io.IOException:
ObjectIdentifier() -- data isn't an object ID (tag = 49)
at sun.security.x509.X509CertInfo.<init>(Unknown Source)
at sun.security.x509.X509CertImpl.parse(Unknown Source)
at sun.security.x509.X509CertImpl.<init>(Unknown Source)
at sun.security.provider.X509Factory.engineGenerateCertificate(Unknown Source)
at java.security.cert.CertificateFactory.generateCertificate(Unknown Source)
at com.ebao.gimo.integration.security.RSAEnc.getPublicKey(RSAEnc.java:208)
at com.ebao.gimo.integration.security.RSAEnc.main(RSAEnc.java:37)
Caused by: java.io.IOException: ObjectIdentifier() -- data isn't an object ID (tag = 49)
at sun.security.util.ObjectIdentifier.<init>(Unknown Source)
at sun.security.util.DerInputStream.getOID(Unknown Source)
at sun.security.x509.AlgorithmId.parse(Unknown Source)
at sun.security.x509.CertificateAlgorithmId.<init>(Unknown Source)
at sun.security.x509.X509CertInfo.parse(Unknown Source)
The code I have tried is :
public static PublicKey getPublicKey(String fileName) throws Exception {
FileInputStream fis = new FileInputStream(fileName);
CertificateFactory cf = CertificateFactory.getInstance("X509");
X509Certificate xCert = (X509Certificate)cf.generateCertificate(fis);
PublicKey pubKeyVal = xCert.getPublicKey();
return pubKeyVal;
}
Reading Private Key
public static Key getPrivateKey(String filename) throws Exception {
PemReader pemReader = new PemReader(new FileReader(filename));
PemObject pemObject = pemReader.readPemObject();
byte[] der = pemObject.getContent();
KeyFactory keyFactory = KeyFactory.getInstance("RSA");
PKCS8EncodedKeySpec ks = new PKCS8EncodedKeySpec(der);
RSAPrivateKey privKey = (RSAPrivateKey)keyFactory.generatePrivate(ks);
return privKey;
}
Kindly help
The public key is
-----BEGIN CERTIFICATE REQUEST-----
MIICwDCCAagCAQAwezELMAkGA1UEBhMCSU4xFDASBgNVBAgMC01haGFyYXNodHJh
MQ8wDQYDVQQHDAZNdW1iYWkxHzAdBgNVBAoMFkViYW90ZWNoIEluZGlhIFB2dCBM
dGQxJDAiBgNVBAsMG0luZGlhIENsb3VkIFJlZmluZW1lbnQgU0JJRzCCASIwDQYJ
KoZIhvcNAQEBBQADggEPADCCAQoCggEBALhcoHMrt4QroPYeIyr0oYiE1Kjrs7xo
L5eryiOVgJp4ddWGtKy9vVjJJcDhs5D+d78d9wu/4u9ET2LtgOX/99JhbPz6UuVX
UP0vdrfVyWJvbwCrfWPUzW2/vmeP0wXIISzvXghzy/5DhRYfaOlhRHGDl0lRvHgU
DxIWZtl9sNoWO3CRZAO0D5QX0Cq/S+uc9WPXFchdve32DsQ+YFMoBYwli8uH//rB
ZFc95oHo1WEHCrCHHNE6EAgFG/zgBGyEqgSMfOBVt28r89lgZCIoM5zAuVvI/VM2
ROy8pOMoGKNfXlg8rYq/1OljQ1XIIOt/Ir/0T5YDcs771SgNTiVCUicCAwEAAaAA
MA0GCSqGSIb3DQEBBQUAA4IBAQBrz0KUgI3CG8xFVVtiqDOeTqutrvOoRRz5ziiE
uMeGrkN7jlF/EyurReO0TIGzYiQbVnl/XKOhpKIPf8EKI8nN/Idr2dA8z9NrH9gM
Iat6wuSACC6Txb+RbGSYo66FAaJZQU1OJTFtfIP7LfM9mZPA2gi3aKb0sM+VuCph
WpMm0Kjbp9m665hRbJ//nck+os2CxWhRTyuxRbK007IDi/4FNMnlV/2cxMi644m/
++hbFoF0ihZzq+npezh7URU0Oj9aW7YBVXy9110XBX8JfgOJ5pfZxjU6ID+HQdi/
SciHqqv15tKsxxlKOi8Ju2y3g8vW5dcPJOS4/G5QsQqZsPn9
-----END CERTIFICATE REQUEST-----
Let me know if the extension of key affects it
The issue was how the keys were created. Once my keys were exported to .der format the code worked. Below link helped me out.
Load RSA public key from file

How to load a certificate from "Credential storage"?

My network code is written in NDK (cURL + OpenSSL) and I'd like to use a certificate from Android's credential storage as a client certificate for a SSL connection. Moreover, I'd like to offer a list of available certificates to the user, so he can choose the certificate for the connection. Unfortunately, I cannot obtain a certificate from the key storage.
I installed a client certificate to "Credential storage" (Settings -> Secutrity -> ...) on my Android device (5.0.2), but I'm not able to access it from Java. I tried to call following code, but the key storage is empy, athough the certificate is installed in the Credential storage:
//KeyStore ks = KeyStore.getInstance(KeyStore.getDefaultType());
KeyStore ks = KeyStore.getInstance("AndroidKeyStore");
ks.load(null);
Enumeration<String> aliases = ks.aliases();
while(aliases.hasMoreElements()) {
String alias = (String)aliases.nextElement();
Log.i("app", "alias name: " + alias);
Certificate certificate = ks.getCertificate(alias);
Log.i("app", certificate.toString());
}
What am I doing wrong?
User credentials installed on the device are available through Android KeyChain, not Android KeyStore
The KeyChain class provides access to private keys and their corresponding certificate chains in credential storage.
Use choosePrivateKeyAlias ​​to prompt the user for selecting the certificate. The system launches an Activity for the user to select the alias and returns it via a callback. Then use getPrivateKey and getCertificate to recover the key and the corresponding certificate chain
KeyChain.choosePrivateKeyAlias(activity, new KeyChainAliasCallback() {
public void alias(String alias) {
//do something with the selected alias
}
},
new String[] { KeyProperties.KEY_ALGORITHM_RSA, "DSA"}, // List of acceptable key types. null for any
null, // issuer, null for any
null, // host name of server requesting the cert, null if unavailable
-1, // port of server requesting the cert, -1 if unavailable
""); // alias to preselect, null if unavailable
PrivateKey privateKey = KeyChain.getPrivateKey(activity, alias);
X509Certificate chain[] = KeyChain.getCertificateChain(activity, alias);
Try something like this:
X509TrustManager manager = null;
FileInputStream fs = null;
TrustManagerFactory trustManagerFactory = TrustManagerFactory.getInstance(TrustManagerFactory.getDefaultAlgorithm());
KeyStore keyStore = KeyStore.getInstance(KeyStore.getDefaultType());
try
{
fs = new FileInputStream(System.getProperty("javax.net.ssl.trustStore"));
keyStore.load(fs, null);
}
finally
{
if (fs != null) { fs.close(); }
}
trustManagerFactory.init(keyStore);
TrustManager[] managers = trustManagerFactory.getTrustManagers();
for (TrustManager tm : managers)
{
if (tm instanceof X509TrustManager)
{
manager = (X509TrustManager) tm;
break;
}
}

ECDH using Android KeyStore generated private key

I'm trying to implement ECDH in Android using a private generated by Android KeyStore Provider.
public byte[] ecdh(PublicKey otherPubKey) throws Exception {
try {
ECPublicKey ecPubKey = (ECPublicKey) otherPubKey;
KeyAgreement keyAgreement = KeyAgreement.getInstance("ECDH");
PrivateKey pk = (PrivateKey) LoadPrivateKey("Backend");
keyAgreement.init(pk);
keyAgreement.doPhase(ecPubKey, true);
return (keyAgreement.generateSecret());
}
catch (Exception e)
{
Log.e("failure", e.toString());
return null;
}
}
However, this exception is catched in keyAgreement.init(pk) :
E/failure: java.security.InvalidKeyException: cannot identify EC private key: java.security.InvalidKeyException: no encoding for EC private key
I generated before successfully the "Backend" Public/Private key pair using:
public void GenerateNewKeyPair(String alias)
throws Exception {
if (!keyStore.containsAlias(alias)) {
// use the Android keystore
KeyPairGenerator keyGen = KeyPairGenerator.getInstance(KeyProperties.KEY_ALGORITHM_EC, ANDROID_KEYSTORE);
keyGen.initialize(
new KeyGenParameterSpec.Builder(
alias,
KeyProperties.PURPOSE_SIGN | KeyProperties.PURPOSE_VERIFY | KeyProperties.PURPOSE_ENCRYPT | KeyProperties.PURPOSE_DECRYPT)
.setAlgorithmParameterSpec(new ECGenParameterSpec("secp256r1"))
.setDigests(KeyProperties.DIGEST_SHA256,
KeyProperties.DIGEST_SHA384,
KeyProperties.DIGEST_SHA512)
.setRandomizedEncryptionRequired(true)
.build());
// generates the keypair
KeyPair keyPair = keyGen.generateKeyPair();
}
}
And I load the private key using:
public PrivateKey LoadPrivateKey(String alias) throws Exception {
PrivateKey key = (PrivateKey) keyStore.getKey(alias, null);
return key;
}
Anyone has an idea what is happening and can help me to understand how to fix it? Thanks!
As far as I know through research and trial and error, this is not currently supported.
I believe the best you can do is sign the public key of an EC key pair you generate outside of the AndroidKeyStore with an EC key pair that is stored in AndroidKeyStore. You can then send this signed public key over to the other party with your signing key certificate, generate a shared secret (outside of AndroidKeyStore), then store the SecretKey that is derived using a KDF on the generated secret. I recommend using this non-AndroidKeyStore generated key pair once (so only for the purpose of deriving the secret) and repeating this process to re-key when deemed necessary.
EDIT: When I said 'store the SecretKey', I meant in AndroidKeyStore. That key will initially be in what is called 'normal world' in this context, but its the best you can do for now.
ECDH is supported in API level 23. Please refer android documentation on Android Keystore System
Sample code is also available in this link..

Unable to add certificates generated form String to my custom TrustManagers

I'm trying to create an array of TrustManagers populated with CA certificates decoded from Base64-encoded PEM Strings to pass it in SSLSocketFactory. Here is my code:
public static TrustManager[] getCustomTrustManagers(List<CertObject> certObjects)
{
TrustManagerFactory tmf;
try
{
tmf = TrustManagerFactory.getInstance(TrustManagerFactory.getDefaultAlgorithm());
if (AndroidBuild.getSdkVersion() >= android.os.Build.VERSION_CODES.ICE_CREAM_SANDWICH)
{
KeyStore trustStore;
try
{
trustStore = KeyStore.getInstance("BKS", "BC");
trustStore.load (null, null);
CertificateFactory cf = CertificateFactory.getInstance("X.509");
for (CertObject certObject : certObjects)
{
byte[] decoded = org.apache.commons.codec.binary.Base64.decodeBase64(certObject.getData().getBytes());
Certificate cert = cf.generateCertificate(new ByteArrayInputStream(decoded));
trustStore.setCertificateEntry(certObject.getName(), cert);
}
}
catch (KeyStoreException | NoSuchAlgorithmException | IOException e)
{
trustStore = null;
}
tmf.init(trustStore);
}
else
{
tmf.init((KeyStore) null);
}
}
catch (NoSuchAlgorithmException | KeyStoreException e)
{
Log.e(TAG, e.getMessage);
}
return tmf.getTrustManagers();
}
The certificate is successfully generated, I see the valid instance of X509Certificate, but then it fails in
trustStore.setCertificateEntry(certObject.getName(), cert);
I have the following error:
java.lang.NullPointerException: Attempt to invoke virtual method 'int java.lang.Object.hashCode()' on a null object reference
at java.util.Collections.secondaryHash(Collections.java:3405)
at java.util.Hashtable.get(Hashtable.java:265)
at com.android.org.bouncycastle.jcajce.provider.keystore.bc.BcKeyStoreSpi.engineSetCertificateEntry(BcKeyStoreSpi.java:638)
at java.security.KeyStore.setCertificateEntry(KeyStore.java:393)
at com.xxx.app.core.Utils.getCustomTrustManagers(Utils.java:58)
Any ideas what is wrong? How should I add certificates to the keystore? certObject.getName() and certObject.getData() are both non-empty Strings. certObject.getData() contains PEM certificate data encoded with Base64 without -----BEGIN CERTIFICATE----- and -----END CERTIFICATE----- Lines.
Thanks in advance!
Try using a different alias looks like certObject.getName() is null.
trustStore.setCertificateEntry("MyAlias", cert);
MyAlias you have to replace with some dynamic String. I just used as an example.

Categories