diff --git a/packages/fcl-hash/src/fprsa.pas b/packages/fcl-hash/src/fprsa.pas index d2b0b6305a..be4ed4ee56 100644 --- a/packages/fcl-hash/src/fprsa.pas +++ b/packages/fcl-hash/src/fprsa.pas @@ -377,6 +377,7 @@ begin {$ENDIF} // Encrypt the Block + // Note: the RSA.M was set in RSAInitFromPublicKey Decrypted:=BIImport(RSA.Context,Imported,Size); if Sign then begin diff --git a/packages/fcl-hash/tests/utestpem.pp b/packages/fcl-hash/tests/utestpem.pp index b5dcf9e2e4..a57e7a25df 100644 --- a/packages/fcl-hash/tests/utestpem.pp +++ b/packages/fcl-hash/tests/utestpem.pp @@ -5,26 +5,85 @@ unit utestpem; interface uses - Classes, SysUtils, fpcunit, testregistry, fpasn, fppem, fprsa; + Classes, SysUtils, fpcunit, testregistry, basenenc, + fpTLSBigInt, fppem, fprsa, fphashutils, fpecc; Type { TTestPEM } TTestPEM = Class(TTestCase) + protected + procedure GetPEM_PrivateKeyRSA2048bit(out PrivatePEM, PublicPEM: string); Published Procedure TestLoad; Procedure TestRSA_RS256Verify; + Procedure TestRSA_PrivatePublicPEM_NoPassphrase; end; implementation -uses basenenc, fphashutils, fpecc; +function LinesToText(Lines: array of string): string; +var + i: Integer; +begin + Result:=''; + for i:=low(Lines) to High(Lines) do + Result:=Result+Lines[i]+sLineBreak; +end; { TTestPEM } Const PrivateKeyFile = 'private-key.pem'; +procedure TTestPEM.GetPEM_PrivateKeyRSA2048bit(out PrivatePEM, PublicPEM: string + ); +begin + // generated with + // openssl genrsa -out private.pem 2048 + PrivatePEM:=LinesToText([ + '-----BEGIN RSA PRIVATE KEY-----', + 'MIIEpQIBAAKCAQEAvkRfGW8psCZ3G4+hBA6W/CR/FHhBLB3k3QLypamPbRFlFBxL', + 'tOK2NblBybY22vUiMLZbb5x8OoOj/IhOrJAlTqhtbTWLy/0K3qbG09vLm8V40kEK', + '8/p0STrp3UmsxHNkccj9MRSKk7pOyEvxSCY6K5JGK1VTsMuDCS7DCYk6Vqr3zjX7', + 'qedF1PVM+Z5t0B+f//kt3oBETNlic4IooEpG/PN2GUQ0oZpa16DDtfgGu7wT3X3Q', + 'EZFWLJYQTvGc82NpachBIUvqNdIt1npbK38MXU4IPHVrSN/HdK2nQPSMLdKnTV+E', + 'h/HcxpfjBjarg+VjgDqlmqJ9bkosOVn35vsg8wIDAQABAoIBAQCZxVwujB7fFFdS', + '2QPC6Z+w7DYgbwgNBaP/0vAUXzNhbJuKY0v0Rv4H8U9wHGm9EDyvrdG8JHZqPBX+', + 'dJNQ97aPGaRGjO4M0NdGFve+JXcqz6/UDWkywYnV3V1A0NhmdPQK2et3DSjqN7qQ', + 'OoAoVWzR5gf74Zwf2Hpwo3BRdqzFeUYVDOH7e7q1SOf2QeU54kVUG21saJR0wsyH', + 'oSX8BMU2kmg1Un8ET4FM5xEwhdTZzgFTJVZhc6EfOKVbQt6cKmW3aER3c9vR7M3l', + 'N6Oq73vqrfmy+jFMwz1SoPObQQ7UAnr7YUowaX0AzxHpYm/afyVm+Toym0qWGrrY', + 'MY/l+vNRAoGBAOsi72pJj30ApfVbSpx8/8QIpweLbEgAD+Ssd41Kgc4O/N7azB61', + 'RjzSOs1BGhpAZNU6muAAbucm9EssfG5WTAjIM2W2LVuZXXEVXqEGkIymPz9NGugf', + 'JaCWLaoibmwHkKa+ZV9kDwasmx/VkbAfAbRWaz49ejdrMmkpCW77lYjHAoGBAM8m', + 'PVJWvFhQrB21xQGSWKd5iSUn2V92gICeDoORqfVtt/UPOaDT915KzXPh4bJeOwg6', + 'Kkx5wX6UwaNSRH39loDSY1rsBYioV8bxW0BpBvEJG7KXRbBvxzr0+TJkCHgmGMns', + 'dhePYUcriCaqpQi1yzf201oLTZ6PlJxkmHQobXJ1AoGBAIgWPg576InmWCa64WHU', + 'joq8nz8kmFTLhGdK0h56IspJrlyksUKMk8wbuGCW7y6GWlV2h7BhT86Eoxrm8lVB', + 'qNvkUqrpVzMOfiA2x//WNs7QYQaX75ysejCI+oDfUJ1Be5yl0TH2TSQFvfoctycB', + 'qxDee08YcaWlaxWl5InRHeh9AoGABm3XZWDPw6XtUZa8oIncOoZpHUAZXP8eid9d', + '7/NrZPScyvxH+5fYi5Kiwb/280Q9bMnxWiJFQRp40ArTmV1veFwPPVkp6s3eu4vu', + 'GxenYX+43lgXj5xIgKntugSkxqXYCxxNpfmLOVw+g4S0Torl3bzJXngPVqZ6JEhy', + '+tfuXakCgYEA19/JCD/5pVPJtwyDDAYnUUESK+JfBPq1cTbsxcOq01mp5ntsqR4y', + 'dtOAmxMASvsqud3XIM5fO5m3Jpl1phiGhCw4nvVLcYzVWxYY+oWoeCSyECgu5tmT', + 'Fo8vn4EEXCkEAA2YPiEuVcrcYsWkLivCTC19lJDfUNMmpwSdiGz/tDU=', + '-----END RSA PRIVATE KEY-----' ]); + + // public key extracted with + // openssl rsa -in private.pem -outform PEM -pubout -out public.pem + PublicPEM:=LinesToText([ + '-----BEGIN PUBLIC KEY-----', + 'MIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEAvkRfGW8psCZ3G4+hBA6W', + '/CR/FHhBLB3k3QLypamPbRFlFBxLtOK2NblBybY22vUiMLZbb5x8OoOj/IhOrJAl', + 'TqhtbTWLy/0K3qbG09vLm8V40kEK8/p0STrp3UmsxHNkccj9MRSKk7pOyEvxSCY6', + 'K5JGK1VTsMuDCS7DCYk6Vqr3zjX7qedF1PVM+Z5t0B+f//kt3oBETNlic4IooEpG', + '/PN2GUQ0oZpa16DDtfgGu7wT3X3QEZFWLJYQTvGc82NpachBIUvqNdIt1npbK38M', + 'XU4IPHVrSN/HdK2nQPSMLdKnTV+Eh/HcxpfjBjarg+VjgDqlmqJ9bkosOVn35vsg', + '8wIDAQAB', + '-----END PUBLIC KEY-----' ]); +end; + procedure TTestPEM.TestLoad; Const @@ -77,6 +136,63 @@ begin Fail('TestRS256Verify'); end; +procedure TTestPEM.TestRSA_PrivatePublicPEM_NoPassphrase; +const + SecretMsg = 'FreePascal RSA Test'; +var + PrivatePEM, PublicPEM, Original, Encrypted, Decrypted: string; + DER: TBytes; + PrivateRSA, PublicRSA: TRSA; + EncryptedLen, DecryptedLen: Integer; +begin + GetPEM_PrivateKeyRSA2048bit(PrivatePEM, PublicPEM); + + RSACreate(PublicRSA); + RSACreate(PrivateRSA); + try + // load public key + DER:=PemToDER(PublicPEM,_BEGIN_PUBLIC_KEY,_END_PUBLIC_KEY); + if length(DER)=0 then + Fail('PemToDER public key'); + RSAInitFromPublicKeyDER(PublicRSA,DER); + + // load private key + DER:=PemToDER(PrivatePEM,_BEGIN_RSA_PRIVATE_KEY,_END_RSA_PRIVATE_KEY); + if length(DER)=0 then + Fail('PemToDER private rsa key'); + RSAInitFromPrivateKeyDER(PrivateRSA,DER); + + AssertEquals('PublicRSA.ModulusLen=PrivateRSA.ModulusLen',PublicRSA.ModulusLen,PrivateRSA.ModulusLen); + if BICompare(PublicRSA.M,PrivateRSA.M)<>0 then + Fail('PublicRSA.M = PrivateRSA.M'); + + if BICompare(PublicRSA.E,PrivateRSA.E)<>0 then + Fail('PublicRSA.E = PrivateRSA.E'); + + Original:=SecretMsg; + + // encrypt + SetLength(Encrypted{%H-},PublicRSA.ModulusLen); + EncryptedLen:=RSAEncryptSign(PublicRSA,@Original[1],length(Original),@Encrypted[1],false); + if EncryptedLenlength(Original) then + AssertEquals('DecryptedLen = length(Original)',DecryptedLen,length(Original)); + SetLength(Decrypted,DecryptedLen); + if Decrypted<>Original then + Fail('Decrypted = Original'); + + finally + RSAFree(PublicRSA); + RSAFree(PrivateRSA); + end; +end; + initialization RegisterTest(TTestPEM); end.