From 35c5e6d3d598f821252133c4668507a839fe4abb Mon Sep 17 00:00:00 2001 From: bhashinee Date: Fri, 20 Oct 2023 09:49:46 +0530 Subject: [PATCH 1/8] [Automated] Update the native jar versions --- ballerina/Ballerina.toml | 6 +++--- ballerina/Dependencies.toml | 2 +- 2 files changed, 4 insertions(+), 4 deletions(-) diff --git a/ballerina/Ballerina.toml b/ballerina/Ballerina.toml index 9d2ec160..6a754c31 100644 --- a/ballerina/Ballerina.toml +++ b/ballerina/Ballerina.toml @@ -1,7 +1,7 @@ [package] org = "ballerina" name = "crypto" -version = "2.5.0" +version = "2.5.1" authors = ["Ballerina"] keywords = ["security", "hash", "hmac", "sign", "encrypt", "decrypt", "private key", "public key"] repository = "https://github.com/ballerina-platform/module-ballerina-crypto" @@ -15,8 +15,8 @@ graalvmCompatible = true [[platform.java17.dependency]] groupId = "io.ballerina.stdlib" artifactId = "crypto-native" -version = "2.5.0" -path = "../native/build/libs/crypto-native-2.5.0.jar" +version = "2.5.1" +path = "../native/build/libs/crypto-native-2.5.1-SNAPSHOT.jar" [[platform.java17.dependency]] groupId = "org.bouncycastle" diff --git a/ballerina/Dependencies.toml b/ballerina/Dependencies.toml index b5e5422c..70b6b292 100644 --- a/ballerina/Dependencies.toml +++ b/ballerina/Dependencies.toml @@ -10,7 +10,7 @@ distribution-version = "2201.8.0" [[package]] org = "ballerina" name = "crypto" -version = "2.5.0" +version = "2.5.1" dependencies = [ {org = "ballerina", name = "jballerina.java"}, {org = "ballerina", name = "test"}, From 54eb8de76551586266748af8085605a48bf1a5d5 Mon Sep 17 00:00:00 2001 From: bhashinee Date: Tue, 28 Nov 2023 10:10:53 +0530 Subject: [PATCH 2/8] [Automated] Update the native jar versions --- ballerina/Ballerina.toml | 6 +++--- ballerina/Dependencies.toml | 2 +- 2 files changed, 4 insertions(+), 4 deletions(-) diff --git a/ballerina/Ballerina.toml b/ballerina/Ballerina.toml index 6a754c31..33481ac7 100644 --- a/ballerina/Ballerina.toml +++ b/ballerina/Ballerina.toml @@ -1,7 +1,7 @@ [package] org = "ballerina" name = "crypto" -version = "2.5.1" +version = "2.6.0" authors = ["Ballerina"] keywords = ["security", "hash", "hmac", "sign", "encrypt", "decrypt", "private key", "public key"] repository = "https://github.com/ballerina-platform/module-ballerina-crypto" @@ -15,8 +15,8 @@ graalvmCompatible = true [[platform.java17.dependency]] groupId = "io.ballerina.stdlib" artifactId = "crypto-native" -version = "2.5.1" -path = "../native/build/libs/crypto-native-2.5.1-SNAPSHOT.jar" +version = "2.6.0" +path = "../native/build/libs/crypto-native-2.6.0-SNAPSHOT.jar" [[platform.java17.dependency]] groupId = "org.bouncycastle" diff --git a/ballerina/Dependencies.toml b/ballerina/Dependencies.toml index 70b6b292..c0151c26 100644 --- a/ballerina/Dependencies.toml +++ b/ballerina/Dependencies.toml @@ -10,7 +10,7 @@ distribution-version = "2201.8.0" [[package]] org = "ballerina" name = "crypto" -version = "2.5.1" +version = "2.6.0" dependencies = [ {org = "ballerina", name = "jballerina.java"}, {org = "ballerina", name = "test"}, From 88427be1f72c2387bdfb197ad8d0c8376e1b3d8a Mon Sep 17 00:00:00 2001 From: bhashinee Date: Tue, 28 Nov 2023 11:06:10 +0530 Subject: [PATCH 3/8] Introduce new APIs to read EC private keys and public keys --- ballerina/private_public_key.bal | 35 +++++ ballerina/sign_verify.bal | 44 ++++++ ballerina/tests/resources/ec-keystore.pkcs12 | Bin 0 -> 1196 bytes ballerina/tests/sign_verify_test.bal | 13 ++ ballerina/tests/test_utils.bal | 1 + gradle.properties | 2 +- .../io/ballerina/stdlib/crypto/Constants.java | 5 + .../stdlib/crypto/nativeimpl/Decode.java | 131 ++++++++++++++---- .../stdlib/crypto/nativeimpl/Sign.java | 14 ++ 9 files changed, 215 insertions(+), 30 deletions(-) create mode 100644 ballerina/tests/resources/ec-keystore.pkcs12 diff --git a/ballerina/private_public_key.bal b/ballerina/private_public_key.bal index 53447a1a..a667388f 100644 --- a/ballerina/private_public_key.bal +++ b/ballerina/private_public_key.bal @@ -96,6 +96,24 @@ public isolated function decodeRsaPrivateKeyFromKeyStore(KeyStore keyStore, stri 'class: "io.ballerina.stdlib.crypto.nativeimpl.Decode" } external; +# Decodes the EC private key from the given PKCS#12 archive file. +# ```ballerina +# crypto:KeyStore keyStore = { +# path: "/path/to/keyStore.p12", +# password: "keyStorePassword" +# }; +# crypto:PrivateKey privateKey = check crypto:decodeEcPrivateKeyFromKeyStore(keyStore, "keyAlias", "keyPassword"); +# ``` +# +# + keyStore - KeyStore configurations +# + keyAlias - Key alias +# + keyPassword - Key password +# + return - Reference to the private key or else a `crypto:Error` if the private key was unreadable +public isolated function decodeEcPrivateKeyFromKeyStore(KeyStore keyStore, string keyAlias, string keyPassword) + returns PrivateKey|Error = @java:Method { + 'class: "io.ballerina.stdlib.crypto.nativeimpl.Decode" +} external; + # Decodes the RSA private key from the given private key and private key password. # ```ballerina # string keyFile = "/path/to/private.key"; @@ -127,6 +145,23 @@ public isolated function decodeRsaPublicKeyFromTrustStore(TrustStore trustStore, 'class: "io.ballerina.stdlib.crypto.nativeimpl.Decode" } external; +# Decodes the EC public key from the given PKCS#12 archive file. +# ```ballerina +# crypto:TrustStore trustStore = { +# path: "/path/tp/truststore.p12", +# password: "truststorePassword" +# }; +# crypto:PublicKey publicKey = check crypto:decodeEcPublicKeyFromTrustStore(trustStore, "keyAlias"); +# ``` +# +# + trustStore - TrustStore configurations +# + keyAlias - Key alias +# + return - Reference to the public key or else a `crypto:Error` if the public key was unreadable +public isolated function decodeEcPublicKeyFromTrustStore(TrustStore trustStore, string keyAlias) + returns PublicKey|Error = @java:Method { + 'class: "io.ballerina.stdlib.crypto.nativeimpl.Decode" +} external; + # Decodes the RSA public key from the given public certificate file. # ```ballerina # string certFile = "/path/to/public.cert"; diff --git a/ballerina/sign_verify.bal b/ballerina/sign_verify.bal index 11b1e456..8336d181 100644 --- a/ballerina/sign_verify.bal +++ b/ballerina/sign_verify.bal @@ -116,6 +116,26 @@ public isolated function signRsaSha512(byte[] input, PrivateKey privateKey) retu 'class: "io.ballerina.stdlib.crypto.nativeimpl.Sign" } external; +# Returns the SHA384withECDSA based signature value for the given data. +# ```ballerina +# string input = "Hello Ballerina"; +# byte[] data = input.toBytes(); +# crypto:KeyStore keyStore = { +# path: "/path/to/keyStore.p12", +# password: "keyStorePassword" +# }; +# crypto:PrivateKey privateKey = check crypto:decodeRsaPrivateKeyFromKeyStore(keyStore, "keyAlias", "keyPassword"); +# byte[] signature = check crypto:signSha384withEcdsa(data, privateKey); +# ``` +# +# + input - The content to be signed +# + privateKey - Private key used for signing +# + return - The generated signature or else a `crypto:Error` if the private key is invalid +public isolated function signSha384withEcdsa(byte[] input, PrivateKey privateKey) returns byte[]|Error = @java:Method { + name: "signSha384withEcdsa", + 'class: "io.ballerina.stdlib.crypto.nativeimpl.Sign" +} external; + # Verifies the RSA-MD5 based signature. # ```ballerina # string input = "Hello Ballerina"; @@ -235,3 +255,27 @@ public isolated function verifyRsaSha512Signature(byte[] data, byte[] signature, name: "verifyRsaSha512Signature", 'class: "io.ballerina.stdlib.crypto.nativeimpl.Sign" } external; + +# Verifies the SHA384withECDSA based signature. +# ```ballerina +# string input = "Hello Ballerina"; +# byte[] data = input.toBytes(); +# crypto:KeyStore keyStore = { +# path: "/path/to/keyStore.p12", +# password: "keyStorePassword" +# }; +# crypto:PrivateKey privateKey = check crypto:decodeRsaPrivateKeyFromKeyStore(keyStore, "keyAlias", "keyPassword"); +# byte[] signature = check crypto:signRsaSha512(data, privateKey); +# crypto:PublicKey publicKey = check crypto:decodeRsaPublicKeyFromTrustStore(keyStore, "keyAlias"); +# boolean validity = check crypto:verifySha384withEcdsaSignature(data, signature, publicKey); +# ``` +# +# + data - The content to be verified +# + signature - Signature value +# + publicKey - Public key used for verification +# + return - Validity of the signature or else a `crypto:Error` if the public key is invalid +public isolated function verifySha384withEcdsaSignature(byte[] data, byte[] signature, PublicKey publicKey) + returns boolean|Error = @java:Method { + name: "verifySha384withEcdsaSignature", + 'class: "io.ballerina.stdlib.crypto.nativeimpl.Sign" +} external; diff --git a/ballerina/tests/resources/ec-keystore.pkcs12 b/ballerina/tests/resources/ec-keystore.pkcs12 new file mode 100644 index 0000000000000000000000000000000000000000..a4c371763e97bb84f6012713a31201ced224c715 GIT binary patch literal 1196 zcmXqLVp+k&$ZXKW62!)-)#lOmotKfFaX}M{GfNYTJy6)%po!54MT$|GrHN4Qx<;UmX||iyZM`~ zGnkmv1(+BW40zZ$Am%Y~vN9ORvT-J~c`&9jvoLD02%KJj=F!*LwdZ(~`c@k0#O+}T z_}7sC-n!u4#J7tUyh%6cXJ?r%cEbMuPV*I-2_K4*S8%E9ImB6Ix#l>RrIE1hEA2fS z9Udt^KOSmv_MrS{5e9LWSh;pF!V4`hC7y1#86C5gdvq7nL(E! z8%S3&6fh(*WHJ;PC?XVz8j7$8g=FTYDj1p@7#f&c8km?E8yFchF*Cvyv9m2`V)|sz z#Pp7dkpW0ON60V|3ak zTWz;ECD6Xr)z5QG1Gs=`t(#)E3_RD^`Zwz2m8E@W5Tq&eV~4x>J^T=7d{+ zS{!zE9pTAlbeOpP&%7i1T2!w6v);R>xT9f;tQ8+_aAjc`|F}!jSwW9Sjm3!Ll54!e(`FipXzl!9%@M-WXL*3`*|Mu_*5TyhFiyErZp+ zH?Ezxrp;aJ^PoQI;vSLTtUGf|BNwr9op(Bas@dJ@|B@&BuEw}*bJ*K-*g!eywkMzE z!n=74t2=*rZWc57rG0XZ!MZ1mN3N_6iHs|0USfZ%qu1~0>ZQ52mw#rpxvR|4QhiQi z;e}GwE8L+ASDQTkG+ArI4n};^XWj*Kv~JP(pQgES*D8;iwPh3Ewc2$yEMyV+5+gM2((> keyStoreRecord, BString keyAlias, BString keyPassword) { + Object decodedPrivateKey = getPrivateKey(keyStoreRecord, keyAlias, keyPassword); + if (decodedPrivateKey instanceof PrivateKey) { + PrivateKey privateKey = (PrivateKey) decodedPrivateKey; + return buildRsPrivateKeyRecord(privateKey); + } else { + return decodedPrivateKey; + } + } + + public static Object decodeEcPrivateKeyFromKeyStore(BMap keyStoreRecord, BString keyAlias, + BString keyPassword) { + + Object decodedPrivateKey = getPrivateKey(keyStoreRecord, keyAlias, keyPassword); + if (decodedPrivateKey instanceof PrivateKey) { + PrivateKey privateKey = (PrivateKey) decodedPrivateKey; + return buildEcPrivateKeyRecord(privateKey); + } else { + return decodedPrivateKey; + } + } + + private static Object getPrivateKey(BMap keyStoreRecord, BString keyAlias, BString keyPassword) { File keyStoreFile = new File(keyStoreRecord.get(Constants.KEY_STORE_RECORD_PATH_FIELD).toString()); try (FileInputStream fileInputStream = new FileInputStream(keyStoreFile)) { KeyStore keyStore = KeyStore.getInstance(Constants.KEYSTORE_TYPE_PKCS12); @@ -84,11 +107,11 @@ public static Object decodeRsaPrivateKeyFromKeyStore(BMap keyS } PrivateKey privateKey = (PrivateKey) keyStore.getKey(keyAlias.getValue(), - keyPassword.getValue().toCharArray()); + keyPassword.getValue().toCharArray()); if (privateKey == null) { return CryptoUtils.createError("Key cannot be recovered by using given key alias: " + keyAlias); } - return buildPrivateKeyRecord(privateKey); + return privateKey; } catch (FileNotFoundException e) { return CryptoUtils.createError("PKCS12 KeyStore not found at: " + keyStoreFile.getAbsoluteFile()); } catch (KeyStoreException | CertificateException | IOException e) { @@ -133,7 +156,7 @@ public static Object decodeRsaPrivateKeyFromKeyFile(BString keyFilePath, Object keyFilePath.getValue()); } PrivateKey privateKey = converter.getPrivateKey(privateKeyInfo); - return buildPrivateKeyRecord(privateKey); + return buildRsPrivateKeyRecord(privateKey); } catch (FileNotFoundException e) { return CryptoUtils.createError("Key file not found at: " + privateKeyFile.getAbsoluteFile()); } catch (PKCSException | IOException e) { @@ -141,20 +164,48 @@ public static Object decodeRsaPrivateKeyFromKeyFile(BString keyFilePath, Object } } - private static Object buildPrivateKeyRecord(PrivateKey privateKey) { + private static Object buildRsPrivateKeyRecord(PrivateKey privateKey) { if (privateKey.getAlgorithm().equals(Constants.RSA_ALGORITHM)) { - BMap privateKeyRecord = ValueCreator. - createRecordValue(ModuleUtils.getModule(), Constants.PRIVATE_KEY_RECORD); - privateKeyRecord.addNativeData(Constants.NATIVE_DATA_PRIVATE_KEY, privateKey); - privateKeyRecord.put(StringUtils.fromString(Constants.PRIVATE_KEY_RECORD_ALGORITHM_FIELD), - StringUtils.fromString(privateKey.getAlgorithm())); - return privateKeyRecord; + return getPrivateKeyRecord(privateKey); } else { return CryptoUtils.createError("Not a valid RSA key."); } } + private static Object getPrivateKeyRecord(PrivateKey privateKey) { + BMap privateKeyRecord = ValueCreator. + createRecordValue(ModuleUtils.getModule(), Constants.PRIVATE_KEY_RECORD); + privateKeyRecord.addNativeData(Constants.NATIVE_DATA_PRIVATE_KEY, privateKey); + privateKeyRecord.put(StringUtils.fromString(Constants.PRIVATE_KEY_RECORD_ALGORITHM_FIELD), + StringUtils.fromString(privateKey.getAlgorithm())); + return privateKeyRecord; + } + + private static Object buildEcPrivateKeyRecord(PrivateKey privateKey) { + if (privateKey.getAlgorithm().equals(Constants.EC_ALGORITHM)) { + return getPrivateKeyRecord(privateKey); + } else { + return CryptoUtils.createError("Not a valid EC key."); + } + } + public static Object decodeRsaPublicKeyFromTrustStore(BMap trustStoreRecord, BString keyAlias) { + Object certificate = getPublicKey(trustStoreRecord, keyAlias); + if (certificate instanceof Certificate) { + return buildRsaPublicKeyRecord((Certificate) certificate); + } + return certificate; + } + + public static Object decodeEcPublicKeyFromTrustStore(BMap trustStoreRecord, BString keyAlias) { + Object certificate = getPublicKey(trustStoreRecord, keyAlias); + if (certificate instanceof Certificate) { + return buildEcPublicKeyRecord((Certificate) certificate); + } + return certificate; + } + + private static Object getPublicKey(BMap trustStoreRecord, BString keyAlias) { File keyStoreFile = new File(trustStoreRecord.get(Constants.KEY_STORE_RECORD_PATH_FIELD).toString()); try (FileInputStream fileInputStream = new FileInputStream(keyStoreFile)) { KeyStore keyStore = KeyStore.getInstance(Constants.KEYSTORE_TYPE_PKCS12); @@ -169,7 +220,7 @@ public static Object decodeRsaPublicKeyFromTrustStore(BMap tru if (certificate == null) { return CryptoUtils.createError("Certificate cannot be recovered by using given key alias: " + keyAlias); } - return buildPublicKeyRecord(certificate); + return certificate; } catch (FileNotFoundException e) { return CryptoUtils.createError("PKCS12 KeyStore not found at: " + keyStoreFile.getAbsoluteFile()); } catch (KeyStoreException | CertificateException | IOException e) { @@ -182,7 +233,7 @@ public static Object decodeRsaPublicKeyFromCertFile(BString certFilePath) { try (FileInputStream fileInputStream = new FileInputStream(certFile)) { CertificateFactory certificateFactory = CertificateFactory.getInstance(Constants.CERTIFICATE_TYPE_X509); X509Certificate certificate = (X509Certificate) certificateFactory.generateCertificate(fileInputStream); - return buildPublicKeyRecord(certificate); + return buildRsaPublicKeyRecord(certificate); } catch (FileNotFoundException e) { return CryptoUtils.createError("Certificate file not found at: " + certFile.getAbsolutePath()); } catch (CertificateException | IOException e) { @@ -190,7 +241,40 @@ public static Object decodeRsaPublicKeyFromCertFile(BString certFilePath) { } } - private static Object buildPublicKeyRecord(Certificate certificate) { + private static Object buildRsaPublicKeyRecord(Certificate certificate) { + BMap certificateBMap = enrichPublicKeyInfo(certificate); + PublicKey publicKey = certificate.getPublicKey(); + if (publicKey.getAlgorithm().equals(Constants.RSA_ALGORITHM)) { + return getPublicKeyRecord(certificate, certificateBMap, publicKey); + } + return CryptoUtils.createError("Not a valid RSA key."); + } + + private static Object buildEcPublicKeyRecord(Certificate certificate) { + BMap certificateBMap = enrichPublicKeyInfo(certificate); + PublicKey publicKey = certificate.getPublicKey(); + if (publicKey.getAlgorithm().equals(Constants.EC_ALGORITHM)) { + return getPublicKeyRecord(certificate, certificateBMap, publicKey); + } + return CryptoUtils.createError("Not a valid EC key."); + } + + private static Object getPublicKeyRecord(Certificate certificate, BMap certificateBMap, + PublicKey publicKey) { + BMap publicKeyMap = ValueCreator. + createRecordValue(ModuleUtils.getModule(), Constants.PUBLIC_KEY_RECORD); + publicKeyMap.addNativeData(Constants.NATIVE_DATA_PUBLIC_KEY, publicKey); + publicKeyMap.addNativeData(Constants.NATIVE_DATA_PUBLIC_KEY_CERTIFICATE, certificate); + publicKeyMap.put(StringUtils.fromString(Constants.PUBLIC_KEY_RECORD_ALGORITHM_FIELD), + StringUtils.fromString(publicKey.getAlgorithm())); + if (certificateBMap.size() > 0) { + publicKeyMap.put(StringUtils.fromString(Constants.PUBLIC_KEY_RECORD_CERTIFICATE_FIELD), + certificateBMap); + } + return publicKeyMap; + } + + private static BMap enrichPublicKeyInfo(Certificate certificate) { BMap certificateBMap = ValueCreator. createRecordValue(ModuleUtils.getModule(), Constants.CERTIFICATE_RECORD); if (certificate instanceof X509Certificate) { @@ -214,22 +298,7 @@ private static Object buildPublicKeyRecord(Certificate certificate) { certificateBMap.put(StringUtils.fromString(Constants.CERTIFICATE_RECORD_SIGNATURE_ALG_FIELD), StringUtils.fromString(x509Certificate.getSigAlgName())); } - PublicKey publicKey = certificate.getPublicKey(); - if (publicKey.getAlgorithm().equals(Constants.RSA_ALGORITHM)) { - BMap publicKeyMap = ValueCreator. - createRecordValue(ModuleUtils.getModule(), Constants.PUBLIC_KEY_RECORD); - publicKeyMap.addNativeData(Constants.NATIVE_DATA_PUBLIC_KEY, publicKey); - publicKeyMap.addNativeData(Constants.NATIVE_DATA_PUBLIC_KEY_CERTIFICATE, certificate); - publicKeyMap.put(StringUtils.fromString(Constants.PUBLIC_KEY_RECORD_ALGORITHM_FIELD), - StringUtils.fromString(publicKey.getAlgorithm())); - if (certificateBMap.size() > 0) { - publicKeyMap.put(StringUtils.fromString(Constants.PUBLIC_KEY_RECORD_CERTIFICATE_FIELD), - certificateBMap); - } - return publicKeyMap; - } else { - return CryptoUtils.createError("Not a valid RSA key."); - } + return certificateBMap; } public static Object buildRsaPublicKey(BString modulus, BString exponent) { @@ -253,4 +322,8 @@ public static Object buildRsaPublicKey(BString modulus, BString exponent) { return CryptoUtils.createError("Algorithm of the key factory is not found: " + e.getMessage()); } } + + public static Object generateBCryptHash(BArray input, int cost) { + return new Object(); + } } diff --git a/native/src/main/java/io/ballerina/stdlib/crypto/nativeimpl/Sign.java b/native/src/main/java/io/ballerina/stdlib/crypto/nativeimpl/Sign.java index 61377d3e..c0cb38cc 100644 --- a/native/src/main/java/io/ballerina/stdlib/crypto/nativeimpl/Sign.java +++ b/native/src/main/java/io/ballerina/stdlib/crypto/nativeimpl/Sign.java @@ -59,6 +59,12 @@ public static Object signRsaSha384(BArray inputValue, BMap privateKey) { return CryptoUtils.sign("SHA384withRSA", key, input); } + public static Object signSha384withEcdsa(BArray inputValue, BMap privateKey) { + byte[] input = inputValue.getBytes(); + PrivateKey key = (PrivateKey) privateKey.getNativeData(Constants.NATIVE_DATA_PRIVATE_KEY); + return CryptoUtils.sign("SHA384withECDSA", key, input); + } + public static Object signRsaSha512(BArray inputValue, BMap privateKey) { byte[] input = inputValue.getBytes(); PrivateKey key = (PrivateKey) privateKey.getNativeData(Constants.NATIVE_DATA_PRIVATE_KEY); @@ -104,4 +110,12 @@ public static Object verifyRsaSha512Signature(BArray dataValue, BArray signature PublicKey key = (PublicKey) publicKey.getNativeData(Constants.NATIVE_DATA_PUBLIC_KEY); return CryptoUtils.verify("SHA512withRSA", key, data, signature); } + + public static Object verifySha384withEcdsaSignature(BArray dataValue, BArray signatureValue, + BMap publicKey) { + byte[] data = dataValue.getBytes(); + byte[] signature = signatureValue.getBytes(); + PublicKey key = (PublicKey) publicKey.getNativeData(Constants.NATIVE_DATA_PUBLIC_KEY); + return CryptoUtils.verify("SHA384withECDSA", key, data, signature); + } } From eb6aaff4c7af580e2201decc34add9050f7f77ad Mon Sep 17 00:00:00 2001 From: bhashinee Date: Tue, 28 Nov 2023 11:18:06 +0530 Subject: [PATCH 4/8] Refactor the code --- ballerina/sign_verify.bal | 6 +++--- .../java/io/ballerina/stdlib/crypto/nativeimpl/Sign.java | 3 +-- 2 files changed, 4 insertions(+), 5 deletions(-) diff --git a/ballerina/sign_verify.bal b/ballerina/sign_verify.bal index 8336d181..43041aa6 100644 --- a/ballerina/sign_verify.bal +++ b/ballerina/sign_verify.bal @@ -264,9 +264,9 @@ public isolated function verifyRsaSha512Signature(byte[] data, byte[] signature, # path: "/path/to/keyStore.p12", # password: "keyStorePassword" # }; -# crypto:PrivateKey privateKey = check crypto:decodeRsaPrivateKeyFromKeyStore(keyStore, "keyAlias", "keyPassword"); -# byte[] signature = check crypto:signRsaSha512(data, privateKey); -# crypto:PublicKey publicKey = check crypto:decodeRsaPublicKeyFromTrustStore(keyStore, "keyAlias"); +# crypto:PrivateKey privateKey = check crypto:decodeEcPrivateKeyFromKeyStore(keyStore, "keyAlias", "keyPassword"); +# byte[] signature = check crypto:signSha384withEcdsa(data, privateKey); +# crypto:PublicKey publicKey = check crypto:decodeEcPublicKeyFromTrustStore(keyStore, "keyAlias"); # boolean validity = check crypto:verifySha384withEcdsaSignature(data, signature, publicKey); # ``` # diff --git a/native/src/main/java/io/ballerina/stdlib/crypto/nativeimpl/Sign.java b/native/src/main/java/io/ballerina/stdlib/crypto/nativeimpl/Sign.java index c0cb38cc..098c52e7 100644 --- a/native/src/main/java/io/ballerina/stdlib/crypto/nativeimpl/Sign.java +++ b/native/src/main/java/io/ballerina/stdlib/crypto/nativeimpl/Sign.java @@ -111,8 +111,7 @@ public static Object verifyRsaSha512Signature(BArray dataValue, BArray signature return CryptoUtils.verify("SHA512withRSA", key, data, signature); } - public static Object verifySha384withEcdsaSignature(BArray dataValue, BArray signatureValue, - BMap publicKey) { + public static Object verifySha384withEcdsaSignature(BArray dataValue, BArray signatureValue, BMap publicKey) { byte[] data = dataValue.getBytes(); byte[] signature = signatureValue.getBytes(); PublicKey key = (PublicKey) publicKey.getNativeData(Constants.NATIVE_DATA_PUBLIC_KEY); From 8d227080f4433f6e268402c19714dbe6c73cabc5 Mon Sep 17 00:00:00 2001 From: bhashinee Date: Tue, 28 Nov 2023 12:06:00 +0530 Subject: [PATCH 5/8] Update the specification --- ballerina/sign_verify.bal | 2 +- changelog.md | 3 +++ docs/spec/spec.md | 34 ++++++++++++++++++++++++++++++++++ 3 files changed, 38 insertions(+), 1 deletion(-) diff --git a/ballerina/sign_verify.bal b/ballerina/sign_verify.bal index 43041aa6..3f45f2a8 100644 --- a/ballerina/sign_verify.bal +++ b/ballerina/sign_verify.bal @@ -124,7 +124,7 @@ public isolated function signRsaSha512(byte[] input, PrivateKey privateKey) retu # path: "/path/to/keyStore.p12", # password: "keyStorePassword" # }; -# crypto:PrivateKey privateKey = check crypto:decodeRsaPrivateKeyFromKeyStore(keyStore, "keyAlias", "keyPassword"); +# crypto:PrivateKey privateKey = check crypto:decodeEcPrivateKeyFromKeyStore(keyStore, "keyAlias", "keyPassword"); # byte[] signature = check crypto:signSha384withEcdsa(data, privateKey); # ``` # diff --git a/changelog.md b/changelog.md index 80c755e3..3182dadb 100644 --- a/changelog.md +++ b/changelog.md @@ -5,6 +5,9 @@ The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/), ## [Unreleased] +### Added +- [Introduce new APIs to interact with EC private keys and public keys](https://github.com/ballerina-platform/ballerina-library/issues/5821) + ## [2.5.0] - 2023-09-15 ### Changed diff --git a/docs/spec/spec.md b/docs/spec/spec.md index 178d09b4..42ed0851 100644 --- a/docs/spec/spec.md +++ b/docs/spec/spec.md @@ -55,12 +55,14 @@ The conforming implementation of the specification is released and included in t * 6.1.3. [RSA-SHA256](#613-rsa-sha256) * 6.1.4. [RSA-SHA384](#614-rsa-sha384) * 6.1.5. [RSA-SHA512](#615-rsa-sha512) + * 6.1.6. [SHA384withECDSA](#616-sha384withecdsa) * 6.2. [Verify signature](#62-verify-signature) * 6.2.1. [RSA-MD5](#621-rsa-md5) * 6.2.2. [RSA-SHA1](#622-rsa-sha1) * 6.2.3. [RSA-SHA256](#623-rsa-sha256) * 6.2.4. [RSA-SHA384](#624-rsa-sha384) * 6.2.5. [RSA-SHA512](#625-rsa-sha512) + * 6.2.6. [SHA384withECDSA](#626-sha384withecdsa) ## 1. [Overview](#1-overview) @@ -471,6 +473,21 @@ crypto:PrivateKey privateKey = check crypto:decodeRsaPrivateKeyFromKeyStore(keyS byte[] signature = check crypto:signRsaSha512(data, privateKey); ``` +#### 6.1.6. [SHA384withECDSA](#616-sha384withecdsa) + +This API can be used to create the SHA384withECDSA based signature value for the given data. + +```ballerina +string input = "Hello Ballerina"; +byte[] data = input.toBytes(); +crypto:KeyStore keyStore = { + path: "/path/to/keyStore.p12", + password: "keyStorePassword" +}; +crypto:PrivateKey privateKey = check crypto:decodeEcPrivateKeyFromKeyStore(keyStore, "keyAlias", "keyPassword"); +byte[] signature = check crypto:signSha384withEcdsa(data, privateKey); +``` + ### 6.2. [Verify signature](#62-verify-signature) #### 6.2.1. [RSA-MD5](#621-rsa-md5) @@ -557,3 +574,20 @@ byte[] signature = check crypto:signRsaSha512(data, privateKey); crypto:PublicKey publicKey = check crypto:decodeRsaPublicKeyFromTrustStore(keyStore, "keyAlias"); boolean validity = check crypto:verifyRsaSha512Signature(data, signature, publicKey); ``` + +#### 6.2.6. [SHA384withECDSA](#626-sha384withecdsa) + +This API can be used to verify the SHA384withECDSA based signature. + +```ballerina +string input = "Hello Ballerina"; +byte[] data = input.toBytes(); +crypto:KeyStore keyStore = { + path: "/path/to/keyStore.p12", + password: "keyStorePassword" +}; +crypto:PrivateKey privateKey = check crypto:decodeEcPrivateKeyFromKeyStore(keyStore, "keyAlias", "keyPassword"); +byte[] signature = check crypto:signSha384withEcdsa(data, privateKey); +crypto:PublicKey publicKey = check crypto:decodeEcPublicKeyFromTrustStore(keyStore, "keyAlias"); +boolean validity = check crypto:verifySha384withEcdsaSignature(data, signature, publicKey); +``` From 0a1b6542ed5aa5078d22adb6c08d1482e8ca6d8d Mon Sep 17 00:00:00 2001 From: bhashinee Date: Tue, 28 Nov 2023 13:00:07 +0530 Subject: [PATCH 6/8] Add negative test cases --- ballerina/tests/sign_verify_test.bal | 56 +++++++++++++++++++ .../stdlib/crypto/nativeimpl/Decode.java | 4 +- 2 files changed, 58 insertions(+), 2 deletions(-) diff --git a/ballerina/tests/sign_verify_test.bal b/ballerina/tests/sign_verify_test.bal index deea7d86..2f899580 100644 --- a/ballerina/tests/sign_verify_test.bal +++ b/ballerina/tests/sign_verify_test.bal @@ -243,3 +243,59 @@ isolated function testVerifySha384withEcdsa() returns Error? { byte[] sha384withEcdsaSignature = check signSha384withEcdsa(payload, privateKey); test:assertTrue(check verifySha384withEcdsaSignature(payload, sha384withEcdsaSignature, publicKey)); } + +@test:Config {} +isolated function testDecodeRsaPrivateKeyError() returns Error? { + KeyStore keyStore = { + path: EC_KEYSTORE_PATH, + password: "ballerina" + }; + PrivateKey|Error privateKey = decodeRsaPrivateKeyFromKeyStore(keyStore, "ec-keypair", "ballerina"); + if privateKey is Error { + test:assertEquals(privateKey.message(), "Not a valid RSA key."); + } else { + test:assertFail("Expected error not found."); + } +} + +@test:Config {} +isolated function testDecodeEcPrivateKeyError() returns Error? { + KeyStore keyStore = { + path: KEYSTORE_PATH, + password: "ballerina" + }; + PrivateKey|Error privateKey = decodeEcPrivateKeyFromKeyStore(keyStore, "ballerina", "ballerina"); + if privateKey is Error { + test:assertEquals(privateKey.message(), "Not a valid EC key."); + } else { + test:assertFail("Expected error not found."); + } +} + +@test:Config {} +isolated function testDecodeEcPublicKeyError() returns Error? { + KeyStore keyStore = { + path: KEYSTORE_PATH, + password: "ballerina" + }; + PublicKey|Error publicKey = decodeEcPublicKeyFromTrustStore(keyStore, "ballerina"); + if publicKey is Error { + test:assertEquals(publicKey.message(), "Not a valid EC public key."); + } else { + test:assertFail("Expected error not found"); + } +} + +@test:Config {} +isolated function testDecodeRsaPublicKeyError() returns Error? { + KeyStore keyStore = { + path: EC_KEYSTORE_PATH, + password: "ballerina" + }; + PublicKey|Error publicKey = decodeRsaPublicKeyFromTrustStore(keyStore, "ec-keypair"); + if publicKey is Error { + test:assertEquals(publicKey.message(), "Not a valid RSA public key."); + } else { + test:assertFail("Expected error not found"); + } +} diff --git a/native/src/main/java/io/ballerina/stdlib/crypto/nativeimpl/Decode.java b/native/src/main/java/io/ballerina/stdlib/crypto/nativeimpl/Decode.java index b662bd25..fe5fef28 100644 --- a/native/src/main/java/io/ballerina/stdlib/crypto/nativeimpl/Decode.java +++ b/native/src/main/java/io/ballerina/stdlib/crypto/nativeimpl/Decode.java @@ -247,7 +247,7 @@ private static Object buildRsaPublicKeyRecord(Certificate certificate) { if (publicKey.getAlgorithm().equals(Constants.RSA_ALGORITHM)) { return getPublicKeyRecord(certificate, certificateBMap, publicKey); } - return CryptoUtils.createError("Not a valid RSA key."); + return CryptoUtils.createError("Not a valid RSA public key."); } private static Object buildEcPublicKeyRecord(Certificate certificate) { @@ -256,7 +256,7 @@ private static Object buildEcPublicKeyRecord(Certificate certificate) { if (publicKey.getAlgorithm().equals(Constants.EC_ALGORITHM)) { return getPublicKeyRecord(certificate, certificateBMap, publicKey); } - return CryptoUtils.createError("Not a valid EC key."); + return CryptoUtils.createError("Not a valid EC public key."); } private static Object getPublicKeyRecord(Certificate certificate, BMap certificateBMap, From b854d9f4503f7b011d2e83e751bfeb36d481b6f5 Mon Sep 17 00:00:00 2001 From: bhashinee Date: Tue, 28 Nov 2023 13:33:56 +0530 Subject: [PATCH 7/8] Refactor the code --- ballerina/tests/sign_verify_test.bal | 12 ++++++------ .../ballerina/stdlib/crypto/nativeimpl/Decode.java | 14 ++++++-------- 2 files changed, 12 insertions(+), 14 deletions(-) diff --git a/ballerina/tests/sign_verify_test.bal b/ballerina/tests/sign_verify_test.bal index 2f899580..f80b5872 100644 --- a/ballerina/tests/sign_verify_test.bal +++ b/ballerina/tests/sign_verify_test.bal @@ -252,9 +252,9 @@ isolated function testDecodeRsaPrivateKeyError() returns Error? { }; PrivateKey|Error privateKey = decodeRsaPrivateKeyFromKeyStore(keyStore, "ec-keypair", "ballerina"); if privateKey is Error { - test:assertEquals(privateKey.message(), "Not a valid RSA key."); + test:assertEquals(privateKey.message(), "Not a valid RSA key"); } else { - test:assertFail("Expected error not found."); + test:assertFail("Expected error not found"); } } @@ -266,9 +266,9 @@ isolated function testDecodeEcPrivateKeyError() returns Error? { }; PrivateKey|Error privateKey = decodeEcPrivateKeyFromKeyStore(keyStore, "ballerina", "ballerina"); if privateKey is Error { - test:assertEquals(privateKey.message(), "Not a valid EC key."); + test:assertEquals(privateKey.message(), "Not a valid EC key"); } else { - test:assertFail("Expected error not found."); + test:assertFail("Expected error not found"); } } @@ -280,7 +280,7 @@ isolated function testDecodeEcPublicKeyError() returns Error? { }; PublicKey|Error publicKey = decodeEcPublicKeyFromTrustStore(keyStore, "ballerina"); if publicKey is Error { - test:assertEquals(publicKey.message(), "Not a valid EC public key."); + test:assertEquals(publicKey.message(), "Not a valid EC public key"); } else { test:assertFail("Expected error not found"); } @@ -294,7 +294,7 @@ isolated function testDecodeRsaPublicKeyError() returns Error? { }; PublicKey|Error publicKey = decodeRsaPublicKeyFromTrustStore(keyStore, "ec-keypair"); if publicKey is Error { - test:assertEquals(publicKey.message(), "Not a valid RSA public key."); + test:assertEquals(publicKey.message(), "Not a valid RSA public key"); } else { test:assertFail("Expected error not found"); } diff --git a/native/src/main/java/io/ballerina/stdlib/crypto/nativeimpl/Decode.java b/native/src/main/java/io/ballerina/stdlib/crypto/nativeimpl/Decode.java index fe5fef28..db4d41b4 100644 --- a/native/src/main/java/io/ballerina/stdlib/crypto/nativeimpl/Decode.java +++ b/native/src/main/java/io/ballerina/stdlib/crypto/nativeimpl/Decode.java @@ -78,9 +78,8 @@ public static Object decodeRsaPrivateKeyFromKeyStore(BMap keyS if (decodedPrivateKey instanceof PrivateKey) { PrivateKey privateKey = (PrivateKey) decodedPrivateKey; return buildRsPrivateKeyRecord(privateKey); - } else { - return decodedPrivateKey; } + return decodedPrivateKey; } public static Object decodeEcPrivateKeyFromKeyStore(BMap keyStoreRecord, BString keyAlias, @@ -90,9 +89,8 @@ public static Object decodeEcPrivateKeyFromKeyStore(BMap keySt if (decodedPrivateKey instanceof PrivateKey) { PrivateKey privateKey = (PrivateKey) decodedPrivateKey; return buildEcPrivateKeyRecord(privateKey); - } else { - return decodedPrivateKey; } + return decodedPrivateKey; } private static Object getPrivateKey(BMap keyStoreRecord, BString keyAlias, BString keyPassword) { @@ -168,7 +166,7 @@ private static Object buildRsPrivateKeyRecord(PrivateKey privateKey) { if (privateKey.getAlgorithm().equals(Constants.RSA_ALGORITHM)) { return getPrivateKeyRecord(privateKey); } else { - return CryptoUtils.createError("Not a valid RSA key."); + return CryptoUtils.createError("Not a valid RSA key"); } } @@ -185,7 +183,7 @@ private static Object buildEcPrivateKeyRecord(PrivateKey privateKey) { if (privateKey.getAlgorithm().equals(Constants.EC_ALGORITHM)) { return getPrivateKeyRecord(privateKey); } else { - return CryptoUtils.createError("Not a valid EC key."); + return CryptoUtils.createError("Not a valid EC key"); } } @@ -247,7 +245,7 @@ private static Object buildRsaPublicKeyRecord(Certificate certificate) { if (publicKey.getAlgorithm().equals(Constants.RSA_ALGORITHM)) { return getPublicKeyRecord(certificate, certificateBMap, publicKey); } - return CryptoUtils.createError("Not a valid RSA public key."); + return CryptoUtils.createError("Not a valid RSA public key"); } private static Object buildEcPublicKeyRecord(Certificate certificate) { @@ -256,7 +254,7 @@ private static Object buildEcPublicKeyRecord(Certificate certificate) { if (publicKey.getAlgorithm().equals(Constants.EC_ALGORITHM)) { return getPublicKeyRecord(certificate, certificateBMap, publicKey); } - return CryptoUtils.createError("Not a valid EC public key."); + return CryptoUtils.createError("Not a valid EC public key"); } private static Object getPublicKeyRecord(Certificate certificate, BMap certificateBMap, From 6f1b5734ec97d4e3a5a4e254f44fe11bf72a1202 Mon Sep 17 00:00:00 2001 From: bhashinee Date: Tue, 5 Dec 2023 11:00:13 +0530 Subject: [PATCH 8/8] Address review suggestions --- .../stdlib/crypto/nativeimpl/Decode.java | 22 ++++++------------- 1 file changed, 7 insertions(+), 15 deletions(-) diff --git a/native/src/main/java/io/ballerina/stdlib/crypto/nativeimpl/Decode.java b/native/src/main/java/io/ballerina/stdlib/crypto/nativeimpl/Decode.java index db4d41b4..059a2905 100644 --- a/native/src/main/java/io/ballerina/stdlib/crypto/nativeimpl/Decode.java +++ b/native/src/main/java/io/ballerina/stdlib/crypto/nativeimpl/Decode.java @@ -20,7 +20,6 @@ import io.ballerina.runtime.api.creators.ValueCreator; import io.ballerina.runtime.api.utils.StringUtils; -import io.ballerina.runtime.api.values.BArray; import io.ballerina.runtime.api.values.BMap; import io.ballerina.runtime.api.values.BString; import io.ballerina.stdlib.crypto.Constants; @@ -75,8 +74,7 @@ private Decode() {} public static Object decodeRsaPrivateKeyFromKeyStore(BMap keyStoreRecord, BString keyAlias, BString keyPassword) { Object decodedPrivateKey = getPrivateKey(keyStoreRecord, keyAlias, keyPassword); - if (decodedPrivateKey instanceof PrivateKey) { - PrivateKey privateKey = (PrivateKey) decodedPrivateKey; + if (decodedPrivateKey instanceof PrivateKey privateKey) { return buildRsPrivateKeyRecord(privateKey); } return decodedPrivateKey; @@ -86,8 +84,7 @@ public static Object decodeEcPrivateKeyFromKeyStore(BMap keySt BString keyPassword) { Object decodedPrivateKey = getPrivateKey(keyStoreRecord, keyAlias, keyPassword); - if (decodedPrivateKey instanceof PrivateKey) { - PrivateKey privateKey = (PrivateKey) decodedPrivateKey; + if (decodedPrivateKey instanceof PrivateKey privateKey) { return buildEcPrivateKeyRecord(privateKey); } return decodedPrivateKey; @@ -182,23 +179,22 @@ private static Object getPrivateKeyRecord(PrivateKey privateKey) { private static Object buildEcPrivateKeyRecord(PrivateKey privateKey) { if (privateKey.getAlgorithm().equals(Constants.EC_ALGORITHM)) { return getPrivateKeyRecord(privateKey); - } else { - return CryptoUtils.createError("Not a valid EC key"); } + return CryptoUtils.createError("Not a valid EC key"); } public static Object decodeRsaPublicKeyFromTrustStore(BMap trustStoreRecord, BString keyAlias) { Object certificate = getPublicKey(trustStoreRecord, keyAlias); - if (certificate instanceof Certificate) { - return buildRsaPublicKeyRecord((Certificate) certificate); + if (certificate instanceof Certificate publicKey) { + return buildRsaPublicKeyRecord(publicKey); } return certificate; } public static Object decodeEcPublicKeyFromTrustStore(BMap trustStoreRecord, BString keyAlias) { Object certificate = getPublicKey(trustStoreRecord, keyAlias); - if (certificate instanceof Certificate) { - return buildEcPublicKeyRecord((Certificate) certificate); + if (certificate instanceof Certificate publicKey) { + return buildEcPublicKeyRecord(publicKey); } return certificate; } @@ -320,8 +316,4 @@ public static Object buildRsaPublicKey(BString modulus, BString exponent) { return CryptoUtils.createError("Algorithm of the key factory is not found: " + e.getMessage()); } } - - public static Object generateBCryptHash(BArray input, int cost) { - return new Object(); - } }