Skip to content

Commit

Permalink
Merge pull request #533 from Bhashinee/versionUpdate
Browse files Browse the repository at this point in the history
Introduce new APIs to read EC private keys and public keys
  • Loading branch information
Bhashinee authored Dec 6, 2023
2 parents 0c97dc2 + 6f1b573 commit 1f4311f
Show file tree
Hide file tree
Showing 13 changed files with 302 additions and 35 deletions.
6 changes: 3 additions & 3 deletions ballerina/Ballerina.toml
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
[package]
org = "ballerina"
name = "crypto"
version = "2.5.0"
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"
Expand All @@ -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.6.0"
path = "../native/build/libs/crypto-native-2.6.0-SNAPSHOT.jar"

[[platform.java17.dependency]]
groupId = "org.bouncycastle"
Expand Down
2 changes: 1 addition & 1 deletion ballerina/Dependencies.toml
Original file line number Diff line number Diff line change
Expand Up @@ -10,7 +10,7 @@ distribution-version = "2201.8.0"
[[package]]
org = "ballerina"
name = "crypto"
version = "2.5.0"
version = "2.6.0"
dependencies = [
{org = "ballerina", name = "jballerina.java"},
{org = "ballerina", name = "test"},
Expand Down
35 changes: 35 additions & 0 deletions ballerina/private_public_key.bal
Original file line number Diff line number Diff line change
Expand Up @@ -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";
Expand Down Expand Up @@ -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";
Expand Down
44 changes: 44 additions & 0 deletions ballerina/sign_verify.bal
Original file line number Diff line number Diff line change
Expand Up @@ -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:decodeEcPrivateKeyFromKeyStore(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";
Expand Down Expand Up @@ -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: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);
# ```
#
# + 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;
Binary file added ballerina/tests/resources/ec-keystore.pkcs12
Binary file not shown.
69 changes: 69 additions & 0 deletions ballerina/tests/sign_verify_test.bal
Original file line number Diff line number Diff line change
Expand Up @@ -230,3 +230,72 @@ isolated function testVerifyRsaSha512() returns Error? {
byte[] sha512Signature = check signRsaSha512(payload, privateKey);
test:assertTrue(check verifyRsaSha512Signature(payload, sha512Signature, publicKey));
}

@test:Config {}
isolated function testVerifySha384withEcdsa() returns Error? {
byte[] payload = "Ballerina test".toBytes();
KeyStore keyStore = {
path: EC_KEYSTORE_PATH,
password: "ballerina"
};
PrivateKey privateKey = check decodeEcPrivateKeyFromKeyStore(keyStore, "ec-keypair", "ballerina");
PublicKey publicKey = check decodeEcPublicKeyFromTrustStore(keyStore, "ec-keypair");
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");
}
}
1 change: 1 addition & 0 deletions ballerina/tests/test_utils.bal
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,7 @@
// under the License.

const string KEYSTORE_PATH = "tests/resources/keyStore.p12";
const string EC_KEYSTORE_PATH = "tests/resources/ec-keystore.pkcs12";
const string ENCRYPTED_KEY_PAIR_PATH = "tests/resources/encryptedKeyPair.pem";
const string KEY_PAIR_PATH = "tests/resources/keyPair.pem";
const string ENCRYPTED_PRIVATE_KEY_PATH = "tests/resources/encryptedPrivate.key";
Expand Down
3 changes: 3 additions & 0 deletions changelog.md
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand Down
34 changes: 34 additions & 0 deletions docs/spec/spec.md
Original file line number Diff line number Diff line change
Expand Up @@ -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)

Expand Down Expand Up @@ -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)
Expand Down Expand Up @@ -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);
```
2 changes: 1 addition & 1 deletion gradle.properties
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
org.gradle.caching=true
group=io.ballerina.stdlib
version=2.5.1-SNAPSHOT
version=2.6.0-SNAPSHOT
puppycrawlCheckstyleVersion=10.12.0
bouncycastleVersion=1.74
githubSpotbugsVersion=5.0.14
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -42,6 +42,8 @@ private Constants() {}
// Native data key for private key within the PrivateKey record.
public static final String NATIVE_DATA_PRIVATE_KEY = "NATIVE_DATA_PRIVATE_KEY";

public static final String NATIVE_DATA_EC_PRIVATE_KEY = "NATIVE_DATA_EC_PRIVATE_KEY";

// Native data key for private key within the PublicKey record.
public static final String NATIVE_DATA_PUBLIC_KEY = "NATIVE_DATA_PUBLIC_KEY";

Expand Down Expand Up @@ -97,6 +99,9 @@ private Constants() {}
// RSA key algorithm
public static final String RSA_ALGORITHM = "RSA";

// EC key algorithm
public static final String EC_ALGORITHM = "EC";

// GMT timezone name used for X509 validity times
public static final String TIMEZONE_GMT = "GMT";

Expand Down
Loading

0 comments on commit 1f4311f

Please sign in to comment.