I have two certificate. One certificate is issuer of another certificate.
how can I see with java code that, my issuer certificate is really issuer?
I know that AuthorityKeyIdentifier of my certificate and SubjectKeyIdentifie of issuer certifiactes must be the same. I checked and they are the same.
but with java code I have that result:
CertificateFactory certFactory = CertificateFactory.getInstance("X.509");
InputStream usrCertificateIn = new FileInputStream("/usr.cer");
X509Certificate cert = (X509Certificate) certFactory.generateCertificate(usrCertificateIn);
InputStream SiningCACertificateIn = new FileInputStream("/siningCA.cer");
X509Certificate issuer = (X509Certificate) certFactory.generateCertificate(SiningCACertificateIn);
byte[] octets = (ASN1OctetString.getInstance(cert.getExtensionValue("2.5.29.35")).getOctets());
System.out.println(Arrays.toString(octets) + " bouncycastle, AuthorityKeyIdentifier");
System.out.println(Arrays.toString(cert.getExtensionValue("2.5.29.35")) + "java.security, AuthorityKeyIdentifier");
octets = ASN1OctetString.getInstance(issuer.getExtensionValue("2.5.29.14")).getOctets();
System.out.println((Arrays.toString(octets) + "bouncycastle, SubjectKeyIdentifie "));
System.out.println(Arrays.toString(issuer.getExtensionValue("2.5.29.14")) + "java.security, SubjectKeyIdentifie ");
adn the result is that:
[48, 22, -128, 20, 52, -105, 49, -70, -24, 78, 127, -113, -25, 55, 39, 99, 46, 6, 31, 66, -55, -86, -79, 113] bouncycastle, AuthorityKeyIdentifier
[4, 24, 48, 22, -128, 20, 52, -105, 49, -70, -24, 78, 127, -113, -25, 55, 39, 99, 46, 6, 31, 66, -55, -86, -79, 113]java.security, AuthorityKeyIdentifier
and another byte array that MUST BE THE SAME, BUT IT IS NOT another bytes is added at the beginning of the array.
[4, 20, 52, -105, 49, -70, -24, 78, 127, -113, -25, 55, 39, 99, 46, 6, 31, 66, -55, -86, -79, 113]bouncycastle, SubjectKeyIdentifie
[4, 22, 4, 20, 52, -105, 49, -70, -24, 78, 127, -113, -25, 55, 39, 99, 46, 6, 31, 66, -55, -86, -79, 113]java.security, SubjectKeyIdentifie
question 1)
Can I calculates the key Identifiers to get the same Arrays?
question 2)
is there another way in order to prove that one certificate is issuer of another certificates.
AuthorityKeyIdentifier and SubjectKeyIdentifier are defined differently:
AuthorityKeyIdentifier ::= SEQUENCE {
keyIdentifier [0] KeyIdentifier OPTIONAL,
authorityCertIssuer [1] GeneralNames OPTIONAL,
authorityCertSerialNumber [2] CertificateSerialNumber OPTIONAL }
SubjectKeyIdentifier ::= KeyIdentifier
KeyIdentifier ::= OCTET STRING
(sections 4.2.1.1 and 4.2.1.2 of RFC 5280)
Thus, merely comparing the extension values cannot work, instead you have to extract the KeyIdentifier contents and compare them, e.g. using BouncyCastle ASN.1 helper classes.
BTW, the actual key identifier bytes are only
52, -105, 49, -70, -24, 78, 127, -113, -25, 55, 39, 99, 46, 6, 31, 66, -55, -86, -79, 113
The 4, 20 before that indicates an OCTET STRING, 20 bytes long. In the AuthorityKeyIdentifier the 4 is replaced by the tag [0] (byte -128) due to implicit tagging.
The 48, 22 before that in your AuthorityKeyIdentifier means (constructed) SEQUENCE, 22 bytes long.
Etc. etc.
Thus,
Can I calculates the key Identifiers to get the same Arrays?
Yes, drill down to the actual KeyIdentifier OCTET STRING values.
is there another way in order to prove that one certificate is issuer of another certificates
Well, you can check whether the signature in the certificate is signed by the private key associated with the assumed issuer certificate by verifying against that certificate's public key.
PS: Concerning the question in the comments
is key identifier's length always 20? is it fixed? may be no, is not it?
No, it is not. The before mentioned RFC 5280 says:
For CA certificates, subject key identifiers SHOULD be derived from
the public key or a method that generates unique values. Two common
methods for generating key identifiers from the public key are:
(1) The keyIdentifier is composed of the 160-bit SHA-1 hash of the
value of the BIT STRING subjectPublicKey (excluding the tag,
length, and number of unused bits).
(2) The keyIdentifier is composed of a four-bit type field with
the value 0100 followed by the least significant 60 bits of
the SHA-1 hash of the value of the BIT STRING
subjectPublicKey (excluding the tag, length, and number of
unused bits).
Other methods of generating unique numbers are also acceptable.
I assume your CA uses method 1 (160 bit = 20 bytes) but this is merely a common method, not even something explicitly recommended, let alone required. Thus, no, you cannot count on the identifier being 20 bytes long.
PPS: Concerning the question in the comments
Isn't the signature the only way to truly prove one cert is issued by another?
That is no prove of the issuer-issuedBy relation either, it merely proves (at least to a certain degree) that the private key associated with the assumed issuer certificate signed the inspected certificate but there are situations where the same private-public key pair is used in multiple certificates.
In essence you need to do multiple complementary test and even then have to trust the CA not to do weird stuff. Not long ago e.g. the Swisscom changed one of their CA certificates to include an additional extension or critical attribute (I'd have to look up the details; I think someone certifying them demanded that change), and by the certificate signature verification test old signer certificates now seem to be issued by the new CA certificate even though the owners of the signer certificates might not be aware of that new extension/critical attribute.
So eventually real life simply is not as simple as one would like it to be...
To prove one certificate was issued by another, you should prove it was signed by the private key corresponding to the public key in the issuing certificate.
Let's call 2 certificates caCert and issuedCert. These are of type X509Certificate.
The Java code to prove issuedCert is signed by the entity represented by caCert is quite simple.
PublicKey caPubKey = caCert.getPublicKey();
issuedCert.verify(caPubKey);
If the verify method returns without throwing an exception, then issuedCert was signed by the private key corresponding to the public key in caCert.
Related
I am trying to transfer my Delphi 7 code to Delphi 11.
The following code worked fine in Delphi 7, but in Delphi 11 I get a compile-time error
[dcc32 Error] VELOS.PAS(44): E2197 Constant object cannot be passed as var parameter
Is there a way to make Delphi 11 compile my code?
I can make the initialized constants be global initialized var, but changing all similar code to comply with Delphi 11 is a lot of work because I use similar stuff a lot.
My code:
function Kt_f25(Mrd,Md,aro,dh:real; var k:real):real;
const
vmax=9;
type
Atype=array [1..vmax] of real;
const
arA:Atype=( 10, 15, 20, 25, 30, 40, 60, 100, 200);
v0a08: Atype=( 235, 170, 135, 115, 100, 82, 63, 46, 33);
v1a08: Atype=( 135, 102, 84, 74, 67, 57, 47, 37, 30);
v0a09: Atype=( 166, 118, 95, 80, 70, 57, 44, 33, 23);
v1a09: Atype=( 99, 76, 63, 56, 50, 43, 36, 29, 22);
v0a10: Atype=( 120, 86, 70, 59, 51, 42, 32, 24, 17);
v1a10: Atype=( 77, 59, 50, 44, 40, 34, 28, 22, 16);
dhA:array [1..3] of real=(8,9,10);
var
v0,v1,v2,kt:real;
res:array [1..3] of real;
begin
if Md>0 then k:=Mrd/Md else if Mrd=0 then k:=1 else k:=10;
if k>1.3 then k:=1.3;
if k<0 then k:=0;
v0:=LinearApr(arA,v0a08,aro*1000,vmax); v1:=LinearApr(arA,v1a08,aro*1000,vmax); v2:=20;
if k>=1 then res[1]:=v2+k*(v1-v2)/0.2 else res[1]:=v1+k*(v0-v1)/1.0;
v0:=LinearApr(arA,v0a09,aro*1000,vmax); v1:=LinearApr(arA,v1a09,aro*1000,vmax); v2:=20;
if k>=1 then res[2]:=v2+k*(v1-v2)/0.2 else res[2]:=v1+k*(v0-v1)/1.0;
v0:=LinearApr(arA,v0a10,aro*1000,vmax); v1:=LinearApr(arA,v1a10,aro*1000,vmax); v2:=20;
if k>=1 then res[3]:=v2+k*(v1-v2)/0.2 else res[3]:=v1+k*(v0-v1)/1.0;
kt:=LinearApr(dhA,res,dh*10,3);
Result:=kt/10;
end;
function LinearApr(var ap1,AV1; r:real; Vmax:integer):real;
const gmax=100;
type
Atype=array [1..gmax] of real;
var
i,j:integer;
ap2:Atype absolute ap1;
AV2:Atype absolute AV1;
ap,AV:Atype;
begin
...
end;
The error is self-explanatory. You are passing typed constants where variable references are expected.
You are encountering the following change in behavior between Delphi 7 and 11:
Writeable typed constants (Delphi)
The $J directive controls whether typed constants can be modified or not. In the {$J+} state, typed constants can be modified, and are in essence initialized variables. In the {$J-} state, typed constants are truly constant, and any attempt to modify a typed constant causes the compiler to report an error.
...
In early versions of Delphi and Object Pascal, typed constants were always writeable, corresponding to the {$J+} state. Old source code that uses writeable typed constants must be compiled in the {$J+} state, but for new applications it is recommended that you use initialized variables and compile your code in the {$J-} state.
The default state is {$J-} in modern Delphi versions. So, simply add an explicit {$J+} or {$WRITEABLECONST ON} directive to your existing code to get the old behavior.
So I was given a key and a hash to decrypt with PowerShell.
key: (165, 49, 50, 151, 4, 58, 80, 217, 250, 19, 249, 150, 185, 102, 202, 113)
hash:
76492d1116743f0423413b16050a5345MgB8AEsAVQBkAGkAWQBQAGYAcgA2ADQASwBCAGQ
AcwBkAFEATQByAFQATABqAFEAPQA9AHwAOQBkAGQAYQA2AGQAYgA4ADQAZQBmAGUAMgBiADQ
ANgA1ADkANAA5AGYAMQBlADIAYgA5ADIAMQBmADUANgA0ADgANgBiADYANwBmAGMANgBjADAA
ZgA5AGUAZQAxAGQANgA3ADUANwA3ADUAZgA1AGYAOABlADcAOQA0AGMAOQA4ADMAZgBmADQ
AMQBkADcAYgAyADkAZQBhADEANwA2AGEAMQBhADMAZQBiADEAOQBkAGMANABiAGIANQBhAGM
AOQA0ADQAMwBhADAAZQAzAGQAZQAwADIANwA2ADUANwAzADcAMABhAGMAMwAxAGYAYwBjA
DYANAA5ADYAZgBlAGQANAA3ADIAMAA2AGUAOQA0ADEAMQA2ADEAYgA2ADMAYgAwADUANQA1A
GQAYQBiADQAOABkAGMANQAyADUAZABkADIANQA5AGQANQA3ADIANgA4ADUAYQA3ADcAMgAxAD
AAOQA2AGUAYQA4ADUAZQAyADgAZABhADMANABlADkANwA0ADIAZQBiADMAMwAzADMAMwAwA
DIAZAA4ADIAYgAwADkAZgBjADUAMgA4AGQANQBkADUAOQA0ADgAMwA4ADEAYgBmAGIAMwBjAGU
AYQAyADUAZgA3ADgAOQA0AGUAZABiAGMAYgBkADcANQAyADQAOQA0ADkANwAxADgAOQAxAGMA
ZAA5ADcAMQA2AGQAOABjAGQAOQAyAGIANQA0AGUAZgAzADcANwA0ADkAOQAxAGIANQA2AGUANw
AyADIAMAA5ADcAOQA3ADIANwAyADAAZgAyAGUAMgA2AGQAMwA1ADMAMAA1AGUAYwAxADcAYw
A2AGQAMwA2AGQAZQBjADgANwAyADYAYwBkADgANQA5AGYANwAwAGUAMQA3AGUANwBkADIAMQ
A4ADgANAA2ADIANAAwAGIANAA5ADgAMwBiADMAOAA1ADUAYgBiAGUAMABhAGIANgAxAGEAMwB
mADYAYQA5ADUANQBmAGUAZQAyADAAZQBhADAAZQAxADUAOAAxAGIAZQA1ADMAMAA5AGUAYQ
BiADkANwAwAGQAMQBmAGQAYgAxAGIANQA0ADAAYwAwADMAOQA5ADcANQA1ADcAOABjADUANg
AyADAAMAA2ADEAZAAyAGQAYgA0ADYAZAA2AGUAOABhADUANQA4ADgAYQA0AGMAZgAwADQANwB
lAGMAZABiAGQAYgBjADQANwA1AGQAYQA2ADQAYgA4ADcAMAAzADIAMgBhADIAZgA1ADAANgA4AGU
AYwBlADgAMgA4AGEAMwAzAGYAMAAzADkANQBlADkAMgBkADIAMgA1ADAAOAA4AGIAOQA3AGUAYg
AzADIAZQA3ADMAMQA4AA==
My code:
$Key = (165, 49, 50, 151, 4, 58, 80, 217, 250, 19, 249, 150, 185, 102, 202, 113)
ConvertFrom-SecureString (ConvertTo-SecureString
"abovehash" -AsPlainText -Force) -Key $Key
My theory is that it is already a secure string but to flip it to plaintext I would need to convertto and convertfrom to translate it. I used a website which decoded it perfectly
https://www.wietzebeukema.nl/powershell-securestring-decoder/#
I get the hash re-hashed again.
I know how to make my own secure key and decrypt it but how would you decrypt a hash if you were given the hash and the key.
I remember trying to do this 4 or 5 years ago to store random stuff in it. What I found out is for some reason the ConvertFrom-SecureString never worked. After some reasearch I found out converting to BTSR using the System.Runtime.InteropServices.Marshal SecureStringtoBTSR from a SecureString and then using the PtrToStringAuto will decrypt it properly. I will try to locate the reasoning and post it. But this should work and provide you the message you want:
$Key = (165, 49, 50, 151, 4, 58, 80, 217, 250, 19, 249, 150, 185, 102, 202, 113)
$hash = "76492d1116743f0423413b16050a5345MgB8AEsAVQBkAGkAWQBQAGYAcgA2ADQASwBCAGQAcwBkAFEATQByAFQATABqAFEAPQA9AHwAOQBkAGQAYQA2AGQAYgA4ADQAZQBmAGUAMgBiADQANgA1ADkANAA5AGYAMQBlADIAYgA5ADIAMQBmADUANgA0ADgANgBiADYANwBmAGMANgBjADAAZgA5AGUAZQAxAGQANgA3ADUANwA3ADUAZgA1AGYAOABlADcAOQA0AGMAOQA4ADMAZgBmADQAMQBkADcAYgAyADkAZQBhADEANwA2AGEAMQBhADMAZQBiADEAOQBkAGMANABiAGIANQBhAGMAOQA0ADQAMwBhADAAZQAzAGQAZQAwADIANwA2ADUANwAzADcAMABhAGMAMwAxAGYAYwBjADYANAA5ADYAZgBlAGQANAA3ADIAMAA2AGUAOQA0ADEAMQA2ADEAYgA2ADMAYgAwADUANQA1AGQAYQBiADQAOABkAGMANQAyADUAZABkADIANQA5AGQANQA3ADIANgA4ADUAYQA3ADcAMgAxADAAOQA2AGUAYQA4ADUAZQAyADgAZABhADMANABlADkANwA0ADIAZQBiADMAMwAzADMAMwAwADIAZAA4ADIAYgAwADkAZgBjADUAMgA4AGQANQBkADUAOQA0ADgAMwA4ADEAYgBmAGIAMwBjAGUAYQAyADUAZgA3ADgAOQA0AGUAZABiAGMAYgBkADcANQAyADQAOQA0ADkANwAxADgAOQAxAGMAZAA5ADcAMQA2AGQAOABjAGQAOQAyAGIANQA0AGUAZgAzADcANwA0ADkAOQAxAGIANQA2AGUANwAyADIAMAA5ADcAOQA3ADIANwAyADAAZgAyAGUAMgA2AGQAMwA1ADMAMAA1AGUAYwAxADcAYwA2AGQAMwA2AGQAZQBjADgANwAyADYAYwBkADgANQA5AGYANwAwAGUAMQA3AGUANwBkADIAMQA4ADgANAA2ADIANAAwAGIANAA5ADgAMwBiADMAOAA1ADUAYgBiAGUAMABhAGIANgAxAGEAMwBmADYAYQA5ADUANQBmAGUAZQAyADAAZQBhADAAZQAxADUAOAAxAGIAZQA1ADMAMAA5AGUAYQBiADkANwAwAGQAMQBmAGQAYgAxAGIANQA0ADAAYwAwADMAOQA5ADcANQA1ADcAOABjADUANgAyADAAMAA2ADEAZAAyAGQAYgA0ADYAZAA2AGUAOABhADUANQA4ADgAYQA0AGMAZgAwADQANwBlAGMAZABiAGQAYgBjADQANwA1AGQAYQA2ADQAYgA4ADcAMAAzADIAMgBhADIAZgA1ADAANgA4AGUAYwBlADgAMgA4AGEAMwAzAGYAMAAzADkANQBlADkAMgBkADIAMgA1ADAAOAA4AGIAOQA3AGUAYgAzADIAZQA3ADMAMQA4AA=="
$Secured = $hash | ConvertTo-SecureString -key $key
$BTSR = [System.Runtime.InteropServices.Marshal]::SecureStringToBSTR($Secured)
[System.Runtime.InteropServices.Marshal]::PtrToStringAuto($BTSR)
And of course in pure Microsoft fashion the explanation article is no longer where it used to be. Once I have the context reasoning I will update.
Update
Here is an SO Answer giving some context to it: Are you able to use PtrToStringAuto to decrypt a secure string in Powershell 7 on macOS?
When I connect from client java application to wildFly remote ejb over SSL, getting this error.
The client connects to EJB via remoting. The remoting in WildFly is configured with SSLRealm and the HTTPS listener also set with SSLRealm.
The same worked with WF 8.2 and this issue seen after migrating to WF-16.
SSL debug log:
%% No cached client session update handshake state: client_hello[1]
upcoming handshake states: server_hello[2]
* ClientHello, TLSv1.2 RandomCookie: GMT: 1583221271 bytes = { 169, 19, 89, 86, 88, 131, 155, 237, 237, 142, 227, 16, 104, 162, 145, 10,
46, 109, 215, 68, 16, 53, 154, 91, 112, 216, 168, 160 } Session ID:
{} Cipher Suites: [TLS_ECDHE_ECDSA_WITH_AES_256_CBC_SHA384,
TLS_ECDHE_RSA_WITH_AES_256_CBC_SHA384,
TLS_RSA_WITH_AES_256_CBC_SHA256, TLS_DHE_RSA_WITH_AES_128_GCM_SHA256,
TLS_DHE_DSS_WITH_AES_128_GCM_SHA256,
TLS_EMPTY_RENEGOTIATION_INFO_SCSV] Compression Methods: { 0 }
Extension elliptic_curves, curve names: {secp256r1, secp384r1,
secp521r1, sect283k1, sect283r1, sect409k1, sect409r1, sect571k1,
sect571r1, secp256k1} Extension ec_point_formats, formats:
[uncompressed] Extension signature_algorithms, signature_algorithms:
SHA512withECDSA, SHA512withRSA, SHA384withECDSA, SHA384withRSA,
SHA256withECDSA, SHA256withRSA, SHA256withDSA, SHA224withECDSA,
SHA224withRSA, SHA224withDSA, SHA1withECDSA, SHA1withRSA, SHA1withDSA
Extension extended_master_secret
XNIO-1 I/O-1, WRITE: TLSv1.2 Handshake, length = 199 XNIO-1 I/O-1, READ: TLSv1.2 Handshake, length = 1035 check handshake state:
server_hello[2]
ServerHello, TLSv1.2 RandomCookie: GMT: 1583221271 bytes = { 107, 141, 20, 188, 78, 97, 175, 228, 80, 217, 148, 35, 196, 141, 120, 88,
110, 219, 94, 135, 8, 172, 103, 78, 85, 107, 177, 129 } Session ID:
{94, 94, 10, 23, 107, 225, 45, 207, 234, 219, 71, 87, 112, 37, 218,
175, 226, 249, 235, 229, 43, 149, 49, 236, 27, 116, 133, 118, 174, 68,
89, 148} Cipher Suite: TLS_ECDHE_RSA_WITH_AES_256_CBC_SHA384
Compression Method: 0 Extension renegotiation_info,
renegotiated_connection: Extension extended_master_secret
* %% Initialized: [Session-3, TLS_ECDHE_RSA_WITH_AES_256_CBC_SHA384]
** TLS_ECDHE_RSA_WITH_AES_256_CBC_SHA384 update handshake state: server_hello[2] upcoming handshake states: server certificate[11]
upcoming handshake states: server_key_exchange12 upcoming
handshake states: certificate_request13 upcoming handshake
states: server_hello_done[14] upcoming handshake states: client
certificate11 upcoming handshake states:
client_key_exchange[16] upcoming handshake states:
certificate_verify15 upcoming handshake states: client
change_cipher_spec[-1] upcoming handshake states: client finished[20]
upcoming handshake states: server change_cipher_spec[-1] upcoming
handshake states: server finished[20] check handshake state:
certificate[11] update handshake state: certificate[11] upcoming
handshake states: server_key_exchange12 upcoming handshake
states: certificate_request13 upcoming handshake states:
server_hello_done[14] upcoming handshake states: client
certificate11 upcoming handshake states:
client_key_exchange[16] upcoming handshake states:
certificate_verify15 upcoming handshake states: client
change_cipher_spec[-1] upcoming handshake states: client finished[20]
upcoming handshake states: server change_cipher_spec[-1] upcoming
handshake states: server finished[20]
*** Certificate chain chain [0] = [ [ Version: V3 Subject: EMAILADDRESS=app-webserver#appdev.com, CN=app-webserver-commonName,
OU=app Demo, O=app cert, ST=CA, C=US Signature Algorithm:
SHA1withRSA, OID = 1.2.840.113549.1.1.5
Key: Sun RSA public key, 1024 bits modulus:
10594925822321141887721258456061864128740466833580453489554475888747706649063995418909414161
public exponent: 65537 Validity: [From: Sat Feb 14 05:00:36 IST
2009,
To: Tue Oct 23 06:47:16 IST 2040] Issuer: EMAILADDRESS=app-webserver#appdev.com, CN=app-webserver-commonName,
OU=app Demo, O=app cert, ST=CA, C=US SerialNumber: [ 7cd0a83e
8bdfd29d]
] Algorithm: [SHA1withRSA] Signature: 0060: 2A 78 FB 9B 2E EA 22
F5 A9 42 04 72 E3 45 4F 76 *x...."..B.r.EOv 0070: D9 38 F2 54 57 FA
AE 5F 42 CA FE 8C 5E 05 3E CE .8.TW.._B...^.>.
]
XNIO-1 I/O-1, fatal error: 46: General SSLEngine problem
sun.security.validator.ValidatorException: PKIX path building failed: sun.security.provider.certpath.SunCertPathBuilderException: unable to find valid certification path to requested target
%% Invalidated: [Session-1, TLS_ECDHE_RSA_WITH_AES_256_CBC_SHA384]
XNIO-1 I/O-1, SEND TLSv1.2 ALERT: fatal, description = certificate_unknown
XNIO-1 I/O-1, WRITE: TLSv1.2 Alert, length = 2
XNIO-1 I/O-1, fatal: engine already closed. Rethrowing javax.net.ssl.SSLHandshakeException: General SSLEngine problem
XNIO-1 I/O-1, called closeOutbound()
XNIO-1 I/O-1, closeOutboundInternal()
org.jboss.ejb.client.RequestSendFailedException: EJBCLIENT000409: No more destinations are available
at org.jboss.ejb.client.EJBClientInvocationContext.getResult(EJBClientInvocationContext.java:592)
Any idea?
It looks like you are not using a valid certificate. Do you get a warning about the cert if you goto the server in your browser?
If you can't use a cert issued from a proper CA, then you will need to use a truststore with your client
I'm playing with WebCrypto and I'm getting a confusing output.
The following test case encrypts a random 16byte (128bit) plain text with a newly generated 128bit key and 128bit random IV but is outputting a 32byte (256bit) output.
If I remember the details of AES-CBC it should output 128bit blocks.
function test() {
var data = new Uint8Array(16);
window.crypto.getRandomValues(data);
console.log(data)
window.crypto.subtle.generateKey(
{
name: "AES-CBC",
length: 128,
},
false,
["encrypt", "decrypt"]
)
.then(function(key){
//returns a key object
console.log(key);
window.crypto.subtle.encrypt(
{
name: "AES-CBC",
iv: window.crypto.getRandomValues(new Uint8Array(16)),
},
key,
data
)
.then(function(encrypted){
console.log(new Uint8Array(encrypted));
})
.catch(function(err){
console.error(err);
});
})
.catch(function(err){
console.error(err);
});
}
Example output:
Uint8Array(16) [146, 207, 22, 56, 56, 151, 125, 174, 137, 69, 133, 36, 218, 114, 143, 174]
CryptoKey {
algorithm: {name: "AES-CBC", length: 128}
extractable: false
type: "secret"
usages: (2) ["encrypt", "decrypt"]
__proto__: CryptoKey
Uint8Array(32) [81, 218, 52, 158, 115, 105, 57, 230, 45, 253, 153, 54, 183, 19, 137, 240, 183, 229, 241, 75, 182, 19, 237, 8, 238, 5, 108, 107, 123, 84, 230, 209]
Any idea what I've got wrong.
(Open to moving to crypto.stackexchange.com if more suitable)
I'm testing on Chrome 71 on MacOS at the moment.
Yes. The extra 16 bytes is the padding. Even when the message text is a multiple of the block size, padding is added, otherwise the decryption logic doesn't know when to look for padding.
The Web Cryptography API Specification says:
When operating in CBC mode, messages that are not exact multiples of
the AES block size (16 bytes) can be padded under a variety of padding
schemes. In the Web Crypto API, the only padding mode that is
supported is that of PKCS#7, as described by Section 10.3, step 2, of
[RFC2315].
This means unlike other language implementations (like Java) where you can specify NoPadding when you know that your input message text is always going to be a multiple of block size (128 bits for AES), Web Cryptography API forces you to have PKCS#7 padding.
If we look into RFC2315:
Some content-encryption algorithms assume the input length is a
multiple of k octets, where k > 1, and let the application define a
method for handling inputs whose lengths are not a multiple of k
octets. For such algorithms, the method shall be to pad the input at
the trailing end with k - (l mod k) octets all having value k - (l mod
k), where l is the length of the input. In other words, the input is
padded at the trailing end with one of the following strings:
01 -- if l mod k = k-1
02 02 -- if l mod k = k-2
.
.
.
k k ... k k -- if l mod k = 0
The padding can be removed unambiguously since all input is padded and
no padding string is a suffix of another. This padding method is
well-defined if and only if k < 256; methods for larger k are an open
issue for further study.
Note: k k ... k k -- if l mod k = 0
If you refer to the subtle.encrypt signature, you have no way to specify the padding mode. This means, the decryption logic always expects the padding.
However, in your case, if you use the Web Cryptography API only for encryption and your Python app (with NoPadding) only for decryption, I think you can simply strip off the last 16 bytes from the cipher text before feeding it to the Python app. Here is the code sample just for demonstration purpose:
function test() {
let plaintext = 'GoodWorkGoodWork';
let encoder = new TextEncoder('utf8');
let dataBytes = encoder.encode(plaintext);
window.crypto.subtle.generateKey(
{
name: "AES-CBC",
length: 128,
},
true,
["encrypt", "decrypt"]
)
.then(function(key){
crypto.subtle.exportKey('raw', key)
.then(function(expKey) {
console.log('Key = ' + btoa(String.
fromCharCode(...new Uint8Array(expKey))));
});
let iv = new Uint8Array(16);
window.crypto.getRandomValues(iv);
let ivb64 = btoa(String.fromCharCode(...new Uint8Array(iv)));
console.log('IV = ' + ivb64);
window.crypto.subtle.encrypt(
{
name: "AES-CBC",
iv: iv,
},
key,
dataBytes
)
.then(function(encrypted){
console.log('Cipher text = ' +
btoa(String.fromCharCode(...new Uint8Array(encrypted))));
})
.catch(function(err){
console.error(err);
});
})
.catch(function(err){
console.error(err);
});
}
The output of the above is:
IV = qW2lanfRo2H/3aSLzxIecA==
Key = 0LDBq5iz243HBTUE/lrM+A==
Cipher text = Wa4nIF0tt4PEBUChiH1KCkSOg6L2daoYdboEEf+Oh6U=
Now, I use take these as input, strip off the last 16 bytes of the cipher text and still get the same message text after decryption using the following Java code:
package com.sapbasu.javastudy;
import java.nio.charset.StandardCharsets;
import java.util.Base64;
import javax.crypto.Cipher;
import javax.crypto.SecretKey;
import javax.crypto.spec.IvParameterSpec;
import javax.crypto.spec.SecretKeySpec;
public class EncryptCBC {
public static void main(String[] arg) throws Exception {
SecretKey key = new SecretKeySpec(Base64.getDecoder().decode(
"0LDBq5iz243HBTUE/lrM+A=="),
"AES");
IvParameterSpec ivSpec = new IvParameterSpec(Base64.getDecoder().decode(
"qW2lanfRo2H/3aSLzxIecA=="));
Cipher cipher = Cipher.getInstance("AES/CBC/NoPadding");
cipher.init(Cipher.DECRYPT_MODE, key, ivSpec);
byte[] cipherTextWoPadding = new byte[16];
System.arraycopy(Base64.getDecoder().decode(
"Wa4nIF0tt4PEBUChiH1KCkSOg6L2daoYdboEEf+Oh6U="),
0, cipherTextWoPadding, 0, 16);
byte[] decryptedMessage = cipher.doFinal(cipherTextWoPadding);
System.out.println(new String(decryptedMessage, StandardCharsets.UTF_8));
}
}
In ZXing i'm creating a string of a binary data using the encode "ISO-8859-1"
but somehow negative bytes in the data get truncated to byte 63 when reading the produced QR code
Example: String before QR code (as bytes)
-78, 99, -86, 15, -123, 31, -11, -64, 77, -91, 26, -126, -68, 33
String read from QR code:
63, 99, 63, 15, 63, 31, 63, 63, 77, 63, 26, 63, 63, 33
How do I prevent that without using base64?
For some reason ZXing assembles the QR matrix with the correct data, it's the reading that truncates the bytes. I ended up sidestepping the problem by encoding my binary data to base64 and dealing with the increased message size