Skip to content

Commit

Permalink
Merge pull request #21 from eclipxe13/version-1.2.2
Browse files Browse the repository at this point in the history
Corregir el número de serie en mayúsculas y mantenimiento (versión 1.2.2)
  • Loading branch information
eclipxe13 committed Jun 7, 2024
2 parents f1d8fa6 + 261fe93 commit 1a8c0a0
Show file tree
Hide file tree
Showing 14 changed files with 143 additions and 56 deletions.
3 changes: 3 additions & 0 deletions .gitattributes
Original file line number Diff line number Diff line change
Expand Up @@ -17,3 +17,6 @@
/phpstan.neon.dist export-ignore
/phpunit.xml.dist export-ignore
/sonar-project.properties export-ignore

# Do not count these files on github code language
/tests/_files/** linguist-detectable=false
27 changes: 13 additions & 14 deletions .github/workflows/build.yml
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
name: build
on:
workflow_dispatch:
pull_request:
branches: [ "main" ]
push:
Expand All @@ -17,11 +18,11 @@ jobs:
runs-on: "ubuntu-latest"
steps:
- name: Checkout
uses: actions/checkout@v3
uses: actions/checkout@v4
- name: Setup PHP
uses: shivammathur/setup-php@v2
with:
php-version: '8.2'
php-version: '8.3'
coverage: none
tools: composer-normalize
env:
Expand All @@ -34,11 +35,11 @@ jobs:
runs-on: "ubuntu-latest"
steps:
- name: Checkout
uses: actions/checkout@v3
uses: actions/checkout@v4
- name: Setup PHP
uses: shivammathur/setup-php@v2
with:
php-version: '8.2'
php-version: '8.3'
coverage: none
tools: cs2pr, phpcs
env:
Expand All @@ -51,30 +52,28 @@ jobs:
runs-on: "ubuntu-latest"
steps:
- name: Checkout
uses: actions/checkout@v3
uses: actions/checkout@v4
- name: Setup PHP
uses: shivammathur/setup-php@v2
with:
php-version: '8.2'
php-version: '8.3'
coverage: none
tools: cs2pr, php-cs-fixer
env:
fail-fast: true
- name: Code style (php-cs-fixer)
run: php-cs-fixer fix --dry-run --format=checkstyle | cs2pr
env:
PHP_CS_FIXER_IGNORE_ENV: 1

phpstan:
name: Code analysis (phpstan)
runs-on: "ubuntu-latest"
steps:
- name: Checkout
uses: actions/checkout@v3
uses: actions/checkout@v4
- name: Setup PHP
uses: shivammathur/setup-php@v2
with:
php-version: '8.2'
php-version: '8.3'
coverage: none
tools: composer:v2, phpstan
env:
Expand All @@ -83,7 +82,7 @@ jobs:
id: composer-cache
run: echo "dir=$(composer config cache-files-dir)" >> $GITHUB_OUTPUT
- name: Cache dependencies
uses: actions/cache@v3
uses: actions/cache@v4
with:
path: ${{ steps.composer-cache.outputs.dir }}
key: ${{ runner.os }}-composer-${{ hashFiles('**/composer.json') }}
Expand All @@ -98,10 +97,10 @@ jobs:
runs-on: "ubuntu-latest"
strategy:
matrix:
php-versions: ['7.3', '7.4', '8.0', '8.1', '8.2']
php-versions: ['7.3', '7.4', '8.0', '8.1', '8.2', '8.3']
steps:
- name: Checkout
uses: actions/checkout@v3
uses: actions/checkout@v4
- name: Setup PHP
uses: shivammathur/setup-php@v2
with:
Expand All @@ -114,7 +113,7 @@ jobs:
id: composer-cache
run: echo "dir=$(composer config cache-files-dir)" >> $GITHUB_OUTPUT
- name: Cache dependencies
uses: actions/cache@v3
uses: actions/cache@v4
with:
path: ${{ steps.composer-cache.outputs.dir }}
key: ${{ runner.os }}-composer-${{ hashFiles('**/composer.json') }}
Expand Down
17 changes: 9 additions & 8 deletions .github/workflows/coverage.yml
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
name: coverage
on:
workflow_dispatch:
push:
branches: [ "main" ]

Expand All @@ -14,11 +15,11 @@ jobs:
runs-on: "ubuntu-latest"
steps:
- name: Checkout
uses: actions/checkout@v3
uses: actions/checkout@v4
- name: Setup PHP
uses: shivammathur/setup-php@v2
with:
php-version: '8.2'
php-version: '8.3'
coverage: xdebug
tools: composer:v2
env:
Expand All @@ -27,7 +28,7 @@ jobs:
id: composer-cache
run: echo "dir=$(composer config cache-files-dir)" >> $GITHUB_OUTPUT
- name: Cache dependencies
uses: actions/cache@v3
uses: actions/cache@v4
with:
path: ${{ steps.composer-cache.outputs.dir }}
key: ${{ runner.os }}-composer-${{ hashFiles('**/composer.json') }}
Expand All @@ -37,7 +38,7 @@ jobs:
- name: Create code coverage
run: vendor/bin/phpunit --testdox --verbose --coverage-xml=build/coverage --coverage-clover=build/coverage/clover.xml --log-junit=build/coverage/junit.xml
- name: Store code coverage
uses: actions/upload-artifact@v3
uses: actions/upload-artifact@v4
with:
name: code-coverage
path: build/coverage
Expand Down Expand Up @@ -72,28 +73,28 @@ jobs:
runs-on: "ubuntu-latest"
steps:
- name: Checkout
uses: actions/checkout@v3
uses: actions/checkout@v4
- name: Unshallow clone to provide blame information
run: git fetch --unshallow
- name: Setup PHP
uses: shivammathur/setup-php@v2
with:
php-version: '8.2'
php-version: '8.3'
coverage: none
tools: composer:v2
- name: Get composer cache directory
id: composer-cache
run: echo "dir=$(composer config cache-files-dir)" >> $GITHUB_OUTPUT
- name: Cache dependencies
uses: actions/cache@v3
uses: actions/cache@v4
with:
path: ${{ steps.composer-cache.outputs.dir }}
key: ${{ runner.os }}-composer-${{ hashFiles('**/composer.json') }}
restore-keys: ${{ runner.os }}-composer-
- name: Install project dependencies
run: composer upgrade --no-interaction --no-progress --prefer-dist
- name: Obtain code coverage
uses: actions/download-artifact@v3
uses: actions/download-artifact@v4
with:
name: code-coverage
path: build/coverage
Expand Down
10 changes: 5 additions & 5 deletions .phive/phars.xml
Original file line number Diff line number Diff line change
@@ -1,8 +1,8 @@
<?xml version="1.0" encoding="UTF-8"?>
<phive xmlns="https://phar.io/phive">
<phar name="php-cs-fixer" version="^3.17.0" installed="3.17.0" location="./tools/php-cs-fixer" copy="false"/>
<phar name="phpcs" version="^3.7.2" installed="3.7.2" location="./tools/phpcs" copy="false"/>
<phar name="phpcbf" version="^3.7.2" installed="3.7.2" location="./tools/phpcbf" copy="false"/>
<phar name="phpstan" version="^1.10.15" installed="1.10.15" location="./tools/phpstan" copy="false"/>
<phar name="composer-normalize" version="^2.31.0" installed="2.31.0" location="./tools/composer-normalize" copy="false"/>
<phar name="php-cs-fixer" version="^3.58.1" installed="3.58.1" location="./tools/php-cs-fixer" copy="false"/>
<phar name="phpcs" version="^3.10.1" installed="3.10.1" location="./tools/phpcs" copy="false"/>
<phar name="phpcbf" version="^3.10.1" installed="3.10.1" location="./tools/phpcbf" copy="false"/>
<phar name="phpstan" version="^1.11.4" installed="1.11.4" location="./tools/phpstan" copy="false"/>
<phar name="composer-normalize" version="^2.42.0" installed="2.42.0" location="./tools/composer-normalize" copy="false"/>
</phive>
2 changes: 1 addition & 1 deletion .php-cs-fixer.dist.php
Original file line number Diff line number Diff line change
Expand Up @@ -22,7 +22,7 @@
'whitespace_after_comma_in_array' => true,
'no_empty_statement' => true,
'no_extra_blank_lines' => true,
'function_typehint_space' => true,
'type_declaration_spaces' => true,
'trailing_comma_in_multiline' => ['after_heredoc' => true, 'elements' => ['arrays']],
'no_blank_lines_after_phpdoc' => true,
'object_operator_without_whitespace' => true,
Expand Down
2 changes: 1 addition & 1 deletion LICENSE
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
The MIT License (MIT)

Copyright (c) 2019 - 2023 PhpCfdi https://www.phpcfdi.com/
Copyright (c) 2019 - 2024 PhpCfdi https://www.phpcfdi.com/

Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal
Expand Down
25 changes: 25 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -119,6 +119,31 @@ Notas de tratamiento de archivos `DER`:
Para entender más de los formatos de llaves privadas se puede consultar la siguiente liga:
<https://github.com/kjur/jsrsasign/wiki/Tutorial-for-PKCS5-and-PKCS8-PEM-private-key-formats-differences>

## Acerca de los números de serie

Los certificados contienen un número de serie expresado en notación hexadecimal, por ejemplo, el número
de serie `27 2B` se refiere al certificado número `10027` expresado en decimal.

Para el SAT, sin embargo, se reconoce el número de serie no como el estándar en hexadecimal.
El SAT pide que el número de serie reflejado sea **la expresión hexadecimal convertida a ASCII**.
Luego entonces, el certificado con número de serie `3330303031303030303030333030303233373038`
lo identifica como `30001000000300023708`.

Esta práctica del SAT no es estándar, y no es comúnmente observada. Sin embargo, así ha decidido que se
interpreten el dato de "número de serie" referido en sus certificados emitidos, por ejemplo en el atributo
`Comprobante@NoCertificado`.

Como ejemplo contrario: En el firmado de documentos XML utilizado en el servicio web de descarga masiva,
sí se utiliza la notación decimal (el número hexadecimal convertido a decimal), en lugar de la notación de bytes.

La notación de bytes es problemática porque no todos los caracteres son imprimibles o
cuentan una representación gráfica. La notación hexadecimal es ligeramente problemática
porque tiene muchas variantes como el uso de mayúsculas y minúsculas o el prefijo `0x`.
La notación decimal no tiene problema, se trata simplemente de un entero muy grande,
tan grande que debe tratarse como una cadena de caracteres.

Espero que en algún futuro el SAT reconsidere y utilice una notación decimal, para referirnos al número de serie.

## Leer y exportar archivos PFX

Esta librería soporta obtener el objeto `Credential` desde un archivo PFX (PKCS #12) y vicerversa.
Expand Down
31 changes: 31 additions & 0 deletions docs/CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,37 @@ versión, aunque sí su incorporación en la rama principal de trabajo. Generalm

## Listado de cambios

### Versión 1.2.2 2024-06-06

Se corrigió el problema de no crear correctamente el número de serie cuando incluía caracteres en mayúsculas.
Anteriormente, se hacía una conversión a minúsculas, ahora se expresa en mayúsculas.

Se agrega el método `SerialNumber::bytesArePrintable(): bool` para identificar que el número de serie de un certificado
contiene solamente caracteres imprimibles en su representación como *bytes*, como en el caso de los números de serie
utilizados por el SAT.

Se refactorizan los métodos `SerialNumber::createFromBytes()` y `SerialNumber::bytes()` para usar las funciones
de PHP `bin2hex` y `hex2bin` respectivamente.

Se agrega documentación en el archivo `README.md` explicando la interpretación del número de serie como hexadecimal,
decimal y *bytes*. Así como el uso específico del SAT.

Se actualiza el año de licencia a 2024.

Se garantiza la compatibilidad con PHP 8.3.

Adicionalmente, se hacen los siguientes cambios internos:

- Se remueven los archivos `test/_files` de la detección de lenguaje de GitHub.
- En los flujos de trabajo de GitHub.
- Se permite la ejecución manual.
- Se agrega PHP 8.3 a la matriz de pruebas.
- Se ejecutan los trabajos en PHP 8.3.
- Se actualizan las acciones de GitHub a la versión 4.
- En el trabajo `php-cs-fixer` se remueve la variable de entorno `PHP_CS_FIXER_IGNORE_ENV`.
- Se corrige `.php-cs-fixer.dist.php` sustituyendo `function_typehint_space` por `type_declaration_spaces`.
- Se actualizan las herramientas de desarrollo.

### Versión 1.2.1 2023-05-24

PHPStan detectó un uso inapropiado de conversión de objeto a cadena de caracteres.
Expand Down
3 changes: 0 additions & 3 deletions docs/Certificados.md
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,3 @@ Los certificados están vinculados con un creador, llamado emisor o entidad cert
Con un certificado se pueden realizar pocas acciones, en específico:
- extraer información de quien esté relacionado con el certificado
- obtener la llave pública (para poder verificar un mensaje)



2 changes: 1 addition & 1 deletion sonar-project.properties
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,7 @@ sonar.sourceEncoding=UTF-8
sonar.language=php
sonar.sources=src
sonar.tests=tests
sonar.exclusions=vendor/,tools/,build/,tests/_files/
sonar.test.exclusions=tests/_files/**/*
sonar.working.directory=build/.scannerwork
sonar.php.tests.reportPath=build/sonar-junit.xml
sonar.php.coverage.reportPaths=build/sonar-coverage.xml
2 changes: 1 addition & 1 deletion src/Internal/BaseConverter.php
Original file line number Diff line number Diff line change
Expand Up @@ -27,7 +27,7 @@ public function __construct(BaseConverterSequence $sequence)

public static function createBase36(): self
{
return new self(new BaseConverterSequence('0123456789abcdefghijklmnopqrstuvwxyz'));
return new self(new BaseConverterSequence('0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZ'));
}

public function sequence(): BaseConverterSequence
Expand Down
21 changes: 9 additions & 12 deletions src/SerialNumber.php
Original file line number Diff line number Diff line change
Expand Up @@ -25,7 +25,8 @@ public function __construct(string $hexadecimal)
if (0 === strcasecmp('0x', substr($hexadecimal, 0, 2))) {
$hexadecimal = substr($hexadecimal, 2);
}
if (! boolval(preg_match('/^[0-9a-f]*$/', $hexadecimal))) {
$hexadecimal = strtoupper($hexadecimal);
if (! preg_match('/^[0-9A-F]*$/', $hexadecimal)) {
throw new UnexpectedValueException('The hexadecimal string contains invalid characters');
}
$this->hexadecimal = $hexadecimal;
Expand All @@ -44,14 +45,7 @@ public static function createFromDecimal(string $decString): self

public static function createFromBytes(string $input): self
{
/** @noinspection PhpRedundantOptionalArgumentInspection */
$hexadecimal = implode('', array_map(
function (string $value): string {
return dechex(ord($value));
},
str_split($input, 1)
));
return new self($hexadecimal);
return new self(bin2hex($input));
}

public function hexadecimal(): string
Expand All @@ -61,13 +55,16 @@ public function hexadecimal(): string

public function bytes(): string
{
return implode('', array_map(function (string $value): string {
return chr(intval(hexdec($value)));
}, str_split($this->hexadecimal, 2)));
return (string) hex2bin($this->hexadecimal);
}

public function decimal(): string
{
return BaseConverter::createBase36()->convert($this->hexadecimal(), 16, 10);
}

public function bytesArePrintable(): bool
{
return (bool) preg_match('/^[[:print:]]*$/', $this->bytes());
}
}
28 changes: 28 additions & 0 deletions tests/Unit/SerialNumberTest.php
Original file line number Diff line number Diff line change
Expand Up @@ -29,6 +29,7 @@ public function testCreateFromHexadecimal(string $prefix): void
$this->assertSame(self::SERIAL_HEXADECIMAL, $serial->hexadecimal());
$this->assertSame(self::SERIAL_DECIMAL, $serial->decimal());
$this->assertSame(self::SERIAL_BYTES, $serial->bytes());
$this->assertTrue($serial->bytesArePrintable());
}

public function testCreateHexadecimalEmpty(): void
Expand Down Expand Up @@ -63,4 +64,31 @@ public function testCreateFromBytes(): void
$serial = SerialNumber::createFromBytes(self::SERIAL_BYTES);
$this->assertSame(self::SERIAL_HEXADECIMAL, $serial->hexadecimal());
}

/** @return array<string, array{string, string, string, bool}> */
public function providerSerialNumbersNotIssuedFromSat(): array
{
return [
'Mifiel pruebas' => ['272B', '10027', "'+", true],
'SN Letsencrypt' => [
'045E9B96CBBA0057885950B3B59A5B2B98FB',
'380642499533550337925875167187989405866235',
(string) hex2bin('045E9B96CBBA0057885950B3B59A5B2B98FB'),
false,
],
];
}

/** @dataProvider providerSerialNumbersNotIssuedFromSat */
public function testSerialNumbersNotIssuedFromSat(
string $hexadecimalInput,
string $expectedDecimal,
string $expectedBytes,
bool $expectedBytesArePrintable
): void {
$serial = SerialNumber::createFromHexadecimal($hexadecimalInput);
$this->assertSame($expectedDecimal, $serial->decimal());
$this->assertSame($expectedBytes, $serial->bytes());
$this->assertSame($expectedBytesArePrintable, $serial->bytesArePrintable());
}
}
Loading

0 comments on commit 1a8c0a0

Please sign in to comment.