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..
Related
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;
}
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 have a Diffie–Hellman security class like this:
public class AESSecurityCap {
private PublicKey publicKey;
KeyAgreement keyAgreement;
byte[] sharedsecret;
AESSecurityCap() {
makeKeyExchangeParams();
}
private void makeKeyExchangeParams() {
KeyPairGenerator kpg = null;
try {
kpg = KeyPairGenerator.getInstance("EC");
kpg.initialize(128);
KeyPair kp = kpg.generateKeyPair();
publicKey = kp.getPublic();
keyAgreement = KeyAgreement.getInstance("ECDH");
keyAgreement.init(kp.getPrivate());
} catch (NoSuchAlgorithmException | InvalidKeyException e) {
e.printStackTrace();
}
}
public void setReceiverPublicKey(PublicKey publickey) {
try {
keyAgreement.doPhase(publickey, false); // <--- Error on this line
sharedsecret = keyAgreement.generateSecret();
} catch (InvalidKeyException e) {
e.printStackTrace();
}
}
}
and implemented this class:
public class Node extends AESSecurityCap {
}
Sometimes I need to reinitialize DH keyAgreement:
public class TestMainClass {
public static void main(String[] args) {
Node server = new Node();
Node client = new Node();
server.setReceiverPublicKey(client.getPublicKey());
client.setReceiverPublicKey(server.getPublicKey());
// My problem is this line ,
// Second time result exception
server.setReceiverPublicKey(client.getPublicKey());
}
}
but receive this exception:
Exception in thread "main" java.lang.IllegalStateException: Phase already executed
at jdk.crypto.ec/sun.security.ec.ECDHKeyAgreement.engineDoPhase(ECDHKeyAgreement.java:91)
at java.base/javax.crypto.KeyAgreement.doPhase(KeyAgreement.java:579)
at ir.moke.AESSecurityCap.setReceiverPublicKey(AESSecurityCap.java:37)
at ir.moke.TestMainClass.main(TestMainClass.java:13)
Is there any way to reinitialize ECDH KeyAgreement multiple time?
This is my test case:
Client initialize DH and generate public key.
Client sent public key to server.
Server initialize DH with client key and generate own public key and generate shared secret key.
Server send public key to client.
Client generate shared secret key with server public key.
In this step client and server has public keys and shared secret.
My problem is client disconnected() and KeyAgreement initialized by singleton object and don't reinitialized second time.
Sometimes I need to do this subject.
Please guide me to fix this problem.
The IllegalStateException (Phase already executed) seems to be especially caused by the ECDH-implementation of the SunEC-provider. The exception doesn't occur if an (additional) init is executed immediately before the doPhase. However, this init-call shouldn't be necessary, since after the doPhase-call generateSecret is executed, which should reset the KeyAgreement-instance to the state after the init-call, at least according to the generateSecret-documentation:
This method resets this KeyAgreement object to the state that it was in after the most recent call to one of the init methods...
Possibly it's a bug in the SunEC-provider. If DH is used instead of ECDH (and the SunJCE-provider instead of the SunEC-provider) the behavior is as expected, i.e. repeated doPhase-calls are possible (without additional init-calls). The same applies to ECDH using the BouncyCastle-provider. Therefore, you could take the BouncyCastle-provider instead of the SunEC-provider to run ECDH with your code.
Note: The second parameter (lastPhase) in doPhase should be set to true, otherwise an IllegalStateException (Only two party agreement supported, lastPhase must be true) is generated (at least for ECDH).
EDIT:
The bug is already known and fixed in JDK 12, see JDK-8205476: KeyAgreement#generateSecret is not reset for ECDH based algorithmm.
I am writing and sns http end point. I have to verify the SNS message using verifyMessageSignature method of SignatureChecker. How can I get the publicKey attribute from message. Is there util method for it as well. Please help.
The SNS message from Amazon includes a field SigningCertURL. Fetch the bytes at this location into a string cert, then make a public key from it thusly:
/**
* Build a PublicKey object from a cert
*
* #param cert the cert body
* #return a public key
*/
private PublicKey makePublicKey(String cert) {
try {
CertificateFactory fact = CertificateFactory.getInstance("X.509");
InputStream stream = new ByteArrayInputStream(cert.getBytes(StandardCharsets.UTF_8));
X509Certificate cer = (X509Certificate) fact.generateCertificate(stream);
return cer.getPublicKey();
} catch (Exception e) {
LOGGER.error("Failed to make a public key from Amazon cert", e);
return null;
}
}
Then you can call signatureChecker.verifySignature with it as the second argument.
I am trying to verify the public key of a certificate. The certificate has been imported into a keystore using this command:
keytool -importcert -file cert.cer -keystore kstore.jks -alias mycert -storepass changeit
This is the java code I use to verify the public key:
File keyStore = new File("kstore.jks");
String keyStorePassword = "changeit";
KeyStore ks = null;
try {
ks = KeyStore.getInstance("jks");
ks.load(keyStore.toURI().toURL().openStream(), keyStorePassword.toCharArray());
} catch (Exception e) {
e.printStackTrace();
}
try {
Certificate cert = ks.getCertificate("mycert");
PublicKey pk = cert.getPublicKey();
cert.verify(pk);
//cert.verify(pk, "SunRsaSign");
System.out.println("Keys verified");
} catch (Exception e) {
e.printStackTrace();
}
The exception I get is:
java.security.SignatureException: Signature does not match.
at sun.security.x509.X509CertImpl.verify(X509CertImpl.java:446)
at sun.security.x509.X509CertImpl.verify(X509CertImpl.java:389)
at VerifyEBXMLSignature.runIt3(VerifyEBXMLSignature.java:62)
at VerifyEBXMLSignature.main(VerifyEBXMLSignature.java:41)
The certificate contains a public key and I do not have access to the private key.
Is it at all possible to verify the public key against this certificate that I import into a keystore? The public key comes from the certificate itself, so it should be correct.
What more should I look for with the certificate?
I just got some more iformation about the certificate: It is exported from the private key. Is there anything in that process that may have be done wrong?
You shouldn't be passing in the public key that you extracted from the certificate. You should be passing in the public key of the issuer's certificate to verify the signature.
So, as Robert pointed out in comments, your above code only works if it's a self-signed certificate (the certificate is signed with itself).
The public key verify method internally uses X509 Certificate implementation.
So it can only verify those certificates which are generated as per X509 standards.
For more info Visit http://en.wikipedia.org/wiki/X.509