Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
11 changes: 11 additions & 0 deletions resources/certificates/ecdsa_p256.crt
Original file line number Diff line number Diff line change
@@ -0,0 +1,11 @@
-----BEGIN CERTIFICATE-----
MIIBlTCCATugAwIBAgIUWHvNlHco/kXqoHF43TZjwf4F2QwwCgYIKoZIzj0EAwIw
IDEeMBwGA1UEAwwVWW91ciBTQU1MIEVudGl0eSBOYW1lMB4XDTI2MTAzMDIwNTY0
NloXDTM2MTAyNzIwNTY0NlowIDEeMBwGA1UEAwwVWW91ciBTQU1MIEVudGl0eSBO
YW1lMFkwEwYHKoZIzj0CAQYIKoZIzj0DAQcDQgAEBm8ZNWwOcn0uQC0FygoZh6JD
jDDVQIzWiXtlL7Hh8RdiVxdPGelxSuSxickteg6ARgFkA/ITHy3T3grfgR/pL6NT
MFEwHQYDVR0OBBYEFGcpb3FbXgL0mMVfTS/fvIg9h+ibMB8GA1UdIwQYMBaAFGcp
b3FbXgL0mMVfTS/fvIg9h+ibMA8GA1UdEwEB/wQFMAMBAf8wCgYIKoZIzj0EAwID
SAAwRQIgPPpZwGxAgjE18UnxZuYG9gGRBqVN/ZIws3b6yxaSVGoCIQCwapQL096j
+OfTgLNl3u7qppz6k+gJiPA+lTmPxoEdKg==
-----END CERTIFICATE-----
5 changes: 5 additions & 0 deletions resources/keys/ecdsa_p256.key
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
-----BEGIN PRIVATE KEY-----
MIGHAgEAMBMGByqGSM49AgEGCCqGSM49AwEHBG0wawIBAQQgr4ZWUPx/u4A/pnZA
ys8xMPj8pW2SZt9Cp3rrOK6fKkOhRANCAAQGbxk1bA5yfS5ALQXKChmHokOMMNVA
jNaJe2UvseHxF2JXF08Z6XFK5LGJyS16DoBGAWQD8hMfLdPeCt+BH+kv
-----END PRIVATE KEY-----
4 changes: 4 additions & 0 deletions resources/keys/ecdsa_p256.pub
Original file line number Diff line number Diff line change
@@ -0,0 +1,4 @@
-----BEGIN PUBLIC KEY-----
MFkwEwYHKoZIzj0CAQYIKoZIzj0DAQcDQgAEBm8ZNWwOcn0uQC0FygoZh6JDjDDV
QIzWiXtlL7Hh8RdiVxdPGelxSuSxickteg6ARgFkA/ITHy3T3grfgR/pLw==
-----END PUBLIC KEY-----
39 changes: 39 additions & 0 deletions src/Alg/Signature/ECDSA.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,39 @@
<?php

declare(strict_types=1);

namespace SimpleSAML\XMLSecurity\Alg\Signature;

use SimpleSAML\XMLSecurity\Constants as C;
use SimpleSAML\XMLSecurity\Key\AsymmetricKey;

/**
* Class implementing the ECDSA signature algorithm.
*
* @package simplesamlphp/xml-security
*/
final class ECDSA extends AbstractSigner implements SignatureAlgorithmInterface
{
/**
* ECDSA constructor.
*
* @param \SimpleSAML\XMLSecurity\Key\AsymmetricKey $key The asymmetric key (either public or private) to use.
* @param string $algId The identifier of this algorithm.
*/
public function __construct(
#[\SensitiveParameter]
AsymmetricKey $key,
string $algId,
) {
parent::__construct($key, $algId, C::$ECDSA_DIGESTS[$algId]);
}


/**
* @return string[]
*/
public static function getSupportedAlgorithms(): array
{
return array_keys(C::$ECDSA_DIGESTS);
}
}
4 changes: 3 additions & 1 deletion src/Alg/Signature/SignatureAlgorithmFactory.php
Original file line number Diff line number Diff line change
Expand Up @@ -25,11 +25,12 @@ final class SignatureAlgorithmFactory
/**
* An array of blacklisted algorithms.
*
* Defaults to RSA-SHA1, RSAPSS-SHA1 & HMAC-SHA1 due to the weakness of SHA1.
* Defaults to ECDSA-SHA1, RSA-SHA1, RSAPSS-SHA1 & HMAC-SHA1 due to the weakness of SHA1.
*
* @var string[]
*/
public const array DEFAULT_BLACKLIST = [
C::SIG_ECDSA_SHA1,
C::SIG_RSA_SHA1,
C::SIG_RSA_PSS_SHA1,
C::SIG_HMAC_SHA1,
Expand All @@ -41,6 +42,7 @@ final class SignatureAlgorithmFactory
* @var class-string[]
*/
private const array SUPPORTED_DEFAULTS = [
ECDSA::class,
RSA::class,
HMAC::class,
];
Expand Down
7 changes: 7 additions & 0 deletions src/Backend/OpenSSL.php
Original file line number Diff line number Diff line change
Expand Up @@ -204,6 +204,7 @@ public function sign(
if (!openssl_sign($plaintext, $signature, $key->getMaterial(), $this->digest, $this->sig_padding)) {
throw new OpenSSLException('Cannot sign data');
}

return $signature;
}

Expand Down Expand Up @@ -308,6 +309,12 @@ public function setOAEPParams(?string $digestAlg = null, ?string $mgf = null): v
public function setSignaturePadding(string $algId): void
{
$padding = match ($algId) {
C::SIG_ECDSA_SHA1 => 0,
C::SIG_ECDSA_SHA224 => 0,
C::SIG_ECDSA_SHA256 => 0,
C::SIG_ECDSA_SHA384 => 0,
C::SIG_ECDSA_SHA512 => 0,

C::SIG_RSA_PSS_SHA1 => OPENSSL_PKCS1_PSS_PADDING,
C::SIG_RSA_PSS_SHA224 => OPENSSL_PKCS1_PSS_PADDING,
C::SIG_RSA_PSS_SHA256 => OPENSSL_PKCS1_PSS_PADDING,
Expand Down
22 changes: 22 additions & 0 deletions src/Constants.php
Original file line number Diff line number Diff line change
Expand Up @@ -116,6 +116,19 @@ class Constants extends \SimpleSAML\XML\Constants

public const string SIG_RSA_RIPEMD160 = 'http://www.w3.org/2001/04/xmldsig-more#rsa-ripemd160';

/**
* ECDSA Signature algorithms
*/
public const string SIG_ECDSA_SHA1 = 'http://www.w3.org/2001/04/xmldsig-more#ecdsa-sha1';

public const string SIG_ECDSA_SHA224 = 'http://www.w3.org/2001/04/xmldsig-more#ecdsa-sha224';

public const string SIG_ECDSA_SHA256 = 'http://www.w3.org/2001/04/xmldsig-more#ecdsa-sha256';

public const string SIG_ECDSA_SHA384 = 'http://www.w3.org/2001/04/xmldsig-more#ecdsa-sha384';

public const string SIG_ECDSA_SHA512 = 'http://www.w3.org/2001/04/xmldsig-more#ecdsa-sha512';

/**
* HMAC Signature algorithms
*/
Expand Down Expand Up @@ -240,6 +253,15 @@ class Constants extends \SimpleSAML\XML\Constants
self::BLOCK_ENC_AES256_GCM => 16,
];

/** @var array<string, string> */
public static array $ECDSA_DIGESTS = [
self::SIG_ECDSA_SHA1 => self::DIGEST_SHA1,
self::SIG_ECDSA_SHA224 => self::DIGEST_SHA224,
self::SIG_ECDSA_SHA256 => self::DIGEST_SHA256,
self::SIG_ECDSA_SHA384 => self::DIGEST_SHA384,
self::SIG_ECDSA_SHA512 => self::DIGEST_SHA512,
];

/** @var array<string, string> */
public static array $RSA_DIGESTS = [
self::SIG_RSA_SHA1 => self::DIGEST_SHA1,
Expand Down
6 changes: 6 additions & 0 deletions src/TestUtils/PEMCertificatesMock.php
Original file line number Diff line number Diff line change
Expand Up @@ -42,6 +42,12 @@ class PEMCertificatesMock

public const string SELFSIGNED_PRIVATE_KEY = 'selfsigned.simplesamlphp.org.key';

public const string ECDSA_CERTIFICATE = 'ecdsa_p256.crt';

public const string ECDSA_PUBLIC_KEY = 'ecdsa_p256.pub';

public const string ECDSA_PRIVATE_KEY = 'ecdsa_p256.key';

public const string BROKEN_CERTIFICATE = 'broken.simplesamlphp.org.crt';

public const string BROKEN_PUBLIC_KEY = 'broken.simplesamlphp.org.pub';
Expand Down
1 change: 1 addition & 0 deletions src/XML/ds/SignatureMethod.php
Original file line number Diff line number Diff line change
Expand Up @@ -51,6 +51,7 @@ public function __construct(
Assert::oneOf(
$Algorithm->getValue(),
array_merge(
array_keys(C::$ECDSA_DIGESTS),
array_keys(C::$RSA_DIGESTS),
array_keys(C::$HMAC_DIGESTS),
),
Expand Down
56 changes: 53 additions & 3 deletions tests/XML/SignedElementTest.php
Original file line number Diff line number Diff line change
Expand Up @@ -22,9 +22,11 @@
use SimpleSAML\XMLSecurity\XML\ds\X509Certificate as X509;
use SimpleSAML\XMLSecurity\XML\ds\X509Data;

use function array_keys;
use function base64_encode;
use function chunk_split;
use function dirname;
use function in_array;
use function strval;
use function trim;

Expand All @@ -38,6 +40,9 @@ final class SignedElementTest extends TestCase
/** @var \SimpleSAML\XMLSecurity\CryptoEncoding\PEM */
private PEM $certificate;

/** @var \SimpleSAML\XMLSecurity\CryptoEncoding\PEM */
private PEM $ecdsa_certificate;

/** @var \SimpleSAML\XMLSecurity\CryptoEncoding\PEM */
private PEM $wrong_certificate;

Expand Down Expand Up @@ -68,6 +73,10 @@ public function setUp(): void
PEMCertificatesMock::getPlainCertificate(PEMCertificatesMock::SELFSIGNED_CERTIFICATE),
);

$this->ecdsa_certificate = PEM::fromString(
PEMCertificatesMock::getPlainCertificate(PEMCertificatesMock::ECDSA_CERTIFICATE),
);

$this->wrong_certificate = PEM::fromString(
PEMCertificatesMock::getPlainCertificate(PEMCertificatesMock::OTHER_CERTIFICATE),
);
Expand Down Expand Up @@ -328,6 +337,41 @@ public function testSuccessfulVerifyingRsaPssSignature(): void
}


/**
* Verify an ECDSA signature
*/
public function testSuccessfulVerifyingEcdsaSignature(): void
{
// sign using ECDSA_SHA256
$customSignable = $this->sign(C::SIG_ECDSA_SHA256);
// No-op to _actually_ sign the element and set the signature-property.
$customSignable = CustomSignable::fromXML($customSignable->toXML());

$signature = $customSignable->getSignature();

$this->assertEquals(
C::SIG_ECDSA_SHA256,
$signature
->getSignedInfo()
->getSignatureMethod()
->getAlgorithm()
->getValue(),
);

$certificate = new X509Certificate($this->ecdsa_certificate);

$factory = new SignatureAlgorithmFactory();
$verifier = $factory->getAlgorithm(
C::SIG_ECDSA_SHA256,
$certificate->getPublicKey(),
);

$verified = $customSignable->verify($verifier);

$this->assertInstanceOf(CustomSignable::class, $verified);
}


/**
* Negative: verify a RSA-PSS signature using PKCS#1 v1.5
*/
Expand Down Expand Up @@ -382,9 +426,15 @@ private function sign(string $algorithm): CustomSignable

$customSignable = CustomSignable::fromXML($xml->documentElement);

$private = PEMCertificatesMock::getPrivateKey(
PEMCertificatesMock::SELFSIGNED_PRIVATE_KEY,
);
if (in_array($algorithm, array_keys(C::$ECDSA_DIGESTS))) {
$private = PEMCertificatesMock::getPrivateKey(
PEMCertificatesMock::ECDSA_PRIVATE_KEY,
);
} else {
$private = PEMCertificatesMock::getPrivateKey(
PEMCertificatesMock::SELFSIGNED_PRIVATE_KEY,
);
}

$factory = new SignatureAlgorithmFactory();
$signer = $factory->getAlgorithm(
Expand Down
Loading