1

A brief context: in mexico there is the so called "electronic invoice". The IRS equivalent, emits a certificate/key pair (.cer, .key) in DER format, named Digital Sign Certificate to each tax payer. This certificate is used to sign an horrible XML file that is in fact, the invoice. The private key is PCKS8 with des-ede3-cbc encryption.

Invoicing software use this certificate to sign the XML file and send it (typically invoking a web service) to a so called PAC to add a second signature.

We created a system to do such invoicing using Delphi 10 and OpenSLL (libeay32.dll and ssleay32.dll) The user uploads his/her .cer,.key pair and the program converts to PEM format and stores in a database. This part is important: after that, the program no longer needs (or needed) the original pair and does not have access to it.

Maybe is irrelevant, but this is the code to load the .cer (x509) file and convert it to PEM.

Var
 infile,
 outfile : pBIO;
 x       : pX509;
 n       : Integer;
 PEM,
 fn      : AnsiString; // holds the filename

BIO_read_filename(infile,PansiChar(fn));
x := d2i_X509_bio(infile,Nil);
outfile := BIO_new(BIO_s_mem);
PEM_write_bio_X509(outfile,x);
n := BIO_pending(outfile);
SetLength(PEM,n);
BIO_read(outfile,@PEM[1],n)  // PEM is saved to the database

The code to load .key:

Var
 infile,
 outfile   : pBIO;
 p8        : pX509_SIG;
 p8inf     : pPKCS8_Priv_Key_Info;
 n         : Integer;
 x         : pEVP_PKEY;
 PEM,
 fn        : AnsiString;

infile  := BIO_new(BIO_s_file);
BIO_read_filename(infile,PANSICHAR(fn));
p8 := d2i_PKCS8_bio(infile,Nil);
p8inf := PKCS8_decrypt(p8,_PCHAR(Password),Length(Password)); // Password is received as parameter
x := EVP_PKCS82PKEY(p8inf);
outfile := BIO_new(BIO_s_mem);
PEM_write_bio_PKCS8PrivateKey(outfile,x,Nil,_PCHAR(Password),Length(Password),Nil,Nil);

n := BIO_pending(outfile);
SetLength(PEM,n);
BIO_read(outfile,@PEM[1],n)  // PEM is saved to the database

Due to new requirements, a call to the web service needs the original .cer/.key pair. This didn't seem to be a problem: it could be reconstructed from the PEM stored in the database, so the following code was written (only shown for the private key. The x509 code produces an exact copy of the original):

Var
 infile,
 outfile   : pBIO;
 n         : Integer;
 pRSAKey   : pEVP_PKEY;
 DER       : TArray<Byte>;

infile := BIO_new(BIO_s_mem);
BIO_puts(infile,PAnsiChar(PEM_key_saved));
PEM_read_bio_PrivateKey(infile,pRSAKey,Nil,PAnsiChar(Password));

outfile := BIO_new(BIO_s_mem);
i2d_PKCS8PrivateKey_bio(outfile,pRSAKey,EVP_des_ede3_cbc(),Nil,0,Nil,PAnsiChar(Password)

n := BIO_pending(outfile);
SetLength(DER,n);
BIO_read(outfile,@DER[0],n)  // DER should have a byte to byte copy of the original .key

At this point, we expected DER to have an exact copy of the original (let's call it) original.key file, but it is not. The contents of DER were saved to new.key. This is what happened:

  1. Both original.key and new.key are the same size but contents are distinct
  2. The command openssl pkcs8 -in (file).key -inform der outputs exactly the same "-----BEGIN RSA PRIVATE KEY-----..." text
  3. The command openssl asn1parse -inform der -in (file).key outputs distinct results for each file
  4. The web service rejects new.key but if original.key is sent, the call succeeds.

We ran on both files:

openssl asn1parse -inform der -in (file).key

The results:

original.key
0:d=0 hl=4 l=1294 cons: SEQUENCE
4:d=1 hl=2 l= 64 cons: SEQUENCE
6:d=2 hl=2 l= 9 prim: OBJECT :PBES2
17:d=2 hl=2 l= 51 cons: SEQUENCE
19:d=3 hl=2 l= 27 cons: SEQUENCE
21:d=4 hl=2 l= 9 prim: OBJECT :PBKDF2
32:d=4 hl=2 l= 14 cons: SEQUENCE
34:d=5 hl=2 l= 8 prim: OCTET STRING [HEX DUMP]:0201000282010100
44:d=5 hl=2 l= 2 prim: INTEGER :0800
48:d=3 hl=2 l= 20 cons: SEQUENCE
50:d=4 hl=2 l= 8 prim: OBJECT :des-ede3-cbc
60:d=4 hl=2 l= 8 prim: OCTET STRING [HEX DUMP]:308204BD02010030
70:d=1 hl=4 l=1224 prim: OCTET STRING [HEX DUMP]:D2268F527FDA1E5B97...

new.key
0:d=0 hl=4 l=1294 cons: SEQUENCE
4:d=1 hl=2 l= 64 cons: SEQUENCE
6:d=2 hl=2 l= 9 prim: OBJECT :PBES2
17:d=2 hl=2 l= 51 cons: SEQUENCE
19:d=3 hl=2 l= 27 cons: SEQUENCE
21:d=4 hl=2 l= 9 prim: OBJECT :PBKDF2
32:d=4 hl=2 l= 14 cons: SEQUENCE
34:d=5 hl=2 l= 8 prim: OCTET STRING [HEX DUMP]:22001506BC2D25ED
44:d=5 hl=2 l= 2 prim: INTEGER :0800
48:d=3 hl=2 l= 20 cons: SEQUENCE
50:d=4 hl=2 l= 8 prim: OBJECT :des-ede3-cbc
60:d=4 hl=2 l= 8 prim: OCTET STRING [HEX DUMP]:ADBF5AE2FA728F54
70:d=1 hl=4 l=1224 prim: OCTET STRING [HEX DUMP]:EC2876939E0CC1A1...

As noted, the are a differences, specially in the last OCTECT STRING that for some reason, makes the web service fail (it's from an external company so we can not modify it in any way) despite containing the same private key

The question is: are we missing something during the PEM to DER conversion? Should some fields in pRSAKey need to be set? As mentioned, the code for reconstructing the .cer file works fine and produces and exact copy of the original.

13
  • Something is wrong here: you say the key is pkcs#8 but the begin rsa private key indicates it is pkcs#1 stackoverflow.com/q/20065304/150978 Commented Aug 10 at 7:40
  • Please tell me they don't send you your private key. This is a fundamental use of PKI if true. You should be generating your own keypair and CSR and submitting it to them. Commented Aug 10 at 8:19
  • 1
    Last line does not seem correct to me. You should copy to the first element of the array: BIO_read(outfile,@DER[0],n) Commented Aug 10 at 10:08
  • 1
    I didn't read all that code, but if you are decrypting the original and re-encrypting the new with 3DES in CBC mode, OpenSSL will have generated a new random IV, and the ciphertext will be different. The important thing is that the plain text will be the same. Commented Aug 10 at 14:31
  • 2
    Function i2d_PKCS8PrivateKey_bio, the password param is shifted: bio, privkey, cipher, PASSWORD, passlength, password_callback(nil for you), dataforcallback Commented Aug 10 at 21:34

0

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.