I have a certificate chain in server:
Certificate chain
0 s:/******/O=Foobar International BV/OU****
i:/C=US/O=Symantec Corporation/OU=Symantec Trust Network/****
1 s:/C=US/O=Symantec Corporation/OU=Symantec Trust Network/****
i:/C=US/O=VeriSign, Inc./OU=VeriSign Trust Network/OU=**** - G5
2 s:/C=US/O=VeriSign, Inc./OU=VeriSign Trust Network/OU=**** - G5
i:/C=US/O=VeriSign, Inc./OU=Class 3 Public Primary Certification Authority
And my local root CA certificate is:
s:/C=US/O=Symantec Corporation/OU=Symantec Trust Network/****
i:/C=US/O=VeriSign, Inc./OU=VeriSign Trust Network/OU=**** - G5
And I am using this snippet to verify the certificate:
//gcc -lssl -lcrypto -o certverify certverify.c
#include <openssl/ssl.h>
#include <openssl/asn1.h>
#include <openssl/bio.h>
#include <openssl/x509.h>
#include <openssl/x509_vfy.h>
#include <openssl/pem.h>
#include <openssl/x509v3.h>
#include <openssl/err.h>
#include <openssl/conf.h>
#include <string.h>
int main() {
const char ca_bundlestr[] = "./ca-bundle.pem";
const char cert_filestr[] = "./cert-file.pem";
BIO *certbio = NULL;
BIO *outbio = NULL;
X509 *error_cert = NULL;
X509 *cert = NULL;
X509_NAME *certsubject = NULL;
X509_STORE *store = NULL;
X509_STORE_CTX *vrfy_ctx = NULL;
int ret;
/* ---------------------------------------------------------- *
* These function calls initialize openssl for correct work. *
* ---------------------------------------------------------- */
OpenSSL_add_all_algorithms();
ERR_load_BIO_strings();
ERR_load_crypto_strings();
/* ---------------------------------------------------------- *
* Create the Input/Output BIO's. *
* ---------------------------------------------------------- */
certbio = BIO_new(BIO_s_file());
outbio = BIO_new_fp(stdout, BIO_NOCLOSE);
/* ---------------------------------------------------------- *
* Initialize the global certificate validation store object. *
* ---------------------------------------------------------- */
if (!(store=X509_STORE_new()))
BIO_printf(outbio, "Error creating X509_STORE_CTX object\n");
/* ---------------------------------------------------------- *
* Create the context structure for the validation operation. *
* ---------------------------------------------------------- */
vrfy_ctx = X509_STORE_CTX_new();
/* ---------------------------------------------------------- *
* Load the certificate and cacert chain from file (PEM). *
* ---------------------------------------------------------- */
ret = BIO_read_filename(certbio, cert_filestr);
if (! (cert = PEM_read_bio_X509(certbio, NULL, 0, NULL))) {
BIO_printf(outbio, "Error loading cert into memory\n");
exit(-1);
}
ret = X509_STORE_load_locations(store, ca_bundlestr, NULL);
if (ret != 1)
BIO_printf(outbio, "Error loading CA cert or chain file\n");
/* ---------------------------------------------------------- *
* Initialize the ctx structure for a verification operation: *
* Set the trusted cert store, the unvalidated cert, and any *
* potential certs that could be needed (here we set it NULL) *
* ---------------------------------------------------------- */
X509_STORE_CTX_init(vrfy_ctx, store, cert, NULL);
/* ---------------------------------------------------------- *
* Check the complete cert chain can be build and validated. *
* Returns 1 on success, 0 on verification failures, and -1 *
* for trouble with the ctx object (i.e. missing certificate) *
* ---------------------------------------------------------- */
ret = X509_verify_cert(vrfy_ctx);
BIO_printf(outbio, "Verification return code: %d\n", ret);
if(ret == 0 || ret == 1)
BIO_printf(outbio, "Verification result text: %s\n",
X509_verify_cert_error_string(vrfy_ctx->error));
/* ---------------------------------------------------------- *
* The error handling below shows how to get failure details *
* from the offending certificate. *
* ---------------------------------------------------------- */
if(ret == 0) {
/* get the offending certificate causing the failure */
error_cert = X509_STORE_CTX_get_current_cert(vrfy_ctx);
certsubject = X509_NAME_new();
certsubject = X509_get_subject_name(error_cert);
BIO_printf(outbio, "Verification failed cert:\n");
X509_NAME_print_ex(outbio, certsubject, 0, XN_FLAG_MULTILINE);
BIO_printf(outbio, "\n");
}
/* ---------------------------------------------------------- *
* Free up all structures *
* ---------------------------------------------------------- */
X509_STORE_CTX_free(vrfy_ctx);
X509_STORE_free(store);
X509_free(cert);
BIO_free_all(certbio);
BIO_free_all(outbio);
exit(0);
}
But this code return following output:
Verification return code: 0
Verification result text: unable to get issuer certificate
Verification failed cert:
countryName = US
organizationName = Symantec Corporation
organizationalUnitName = Symantec Trust Network
commonName = Symantec Class 3 Secure Server CA - G4
What's wrong here?
Your root CA uses probably the same public key as the first intermediate CA in chain (below the host certificate) and you have probably no root-CA which can be used to trust the last chain certificate. Such setups are not very common, but actually happen. Unfortunately OpenSSL has problems with this setup and will only try to verify the longest chain, even if a shorter chain provides already the necessary trust.
There is a bug entry for this OpenSSL problem, but nobody from the OpenSSL developers ever took care of it. You can also find a patch if you are looking for X509_V_FLAG_TRUSTED_FIRST. It looks like that OpenSSL 1.0.2 (not yet released) will have this option too.
From my understanding only OpenSSL has this kind of problem, i.e. neither NSS (Firefox, Chrome on Desktop) nor SChannel (Microsoft).
I think Steffen probably helped you solve the problem. But here's a small nitpick that may have side stepped the bug you are experiencing and improved your security posture.
const char ca_bundlestr[] = "./ca-bundle.pem";
You don't need the CA bundle. You only need Verisign's Class 3 Public Primary Certification Authority (G5). You can get the one CA cert needed from Verisign at Use of Root Certificates.
Its an improvement in your security posture because you're allowing any CA to certify the server's certificate (even wrong ones), and not using the one known to certify the server's certificate (Verisign).
And I am using this snippet to verify the certificate ...
If you want to see an example of a simple TLS client, then check out SSL/TLS Client on the OpenSSL wiki. It provides an example of fetching random numbers from random.org. It won't take much work to change it to example.com.
Note well: OpenSSL does not perform hostname matching during validation. You still need to do it yourself if you are using OpenSSL 1.0.2, 1.0.1, 1.0.0 and lesser versions. OpenSSL provides hostname matching in 1.1.0, but its not available yet.
The sample code to extract the hostnames from the Common Name (CN) and Subject Alt Names (SAN) in the X.509 certificate is provided in the SSL/TLS Client, but you will have to provided the actual matching code.
Based on the information in the comments, you need the certificate: "Symantec Class 3 Secure Server CA - G5". Below is what it looks like when providing the proper anchor - it ends in a Verify return code: 0 (ok) (and not the error 20).
The "Symantec Class 3 Secure Server CA - G5" is the one with the fingerprint 4e b6 d5 78 49 9b 1c cf 5f 58 1e ad 56 be 3d 9b 67 44 a5 e5. You can fetch it from Verisign's Use of Root Certificates.
The CAfile option being used by s_client (below) is set inside s_client.c with a call to SSL_CTX_load_verify_locations. Its set to the one CA needed to certify the server's certificate, and not the CA Zoo (i.e., cacerts.pem).
You can check the Subject Alternate Names (SAN) in the certificate with $ openssl s_client -connect www.smartbabymonitor.ugrow.example.com:443 | openssl x509 -text -noout. You will be OK because the host www.smartbabymonitor.ugrow.example.com is listed in the SAN. You could even add the -servername option to the command to use Server Name Indication (SNI).
$ openssl s_client -showcerts -connect www.smartbabymonitor.ugrow.example.com:443 -CAfile VeriSign-Class\ 3-Public-Primary-Certification-Authority-G5.pem
CONNECTED(00000003)
depth=3 C = US, O = "VeriSign, Inc.", OU = Class 3 Public Primary Certification Authority
verify return:1
depth=2 C = US, O = "VeriSign, Inc.", OU = VeriSign Trust Network, OU = "(c) 2006 VeriSign, Inc. - For authorized use only", CN = VeriSign Class 3 Public Primary Certification Authority - G5
verify return:1
depth=1 C = US, O = Symantec Corporation, OU = Symantec Trust Network, CN = Symantec Class 3 Secure Server CA - G4
verify return:1
depth=0 C = NL, ST = Netherlands, L = Eindhoven, O = Example International BV, OU = Consumer Lifestyle, CN = smartbabymonitor.ugrow.example.com
verify return:1
---
Certificate chain
0 s:/C=NL/ST=Netherlands/L=Eindhoven/O=Example International BV/OU=Consumer Lifestyle/CN=smartbabymonitor.ugrow.example.com
i:/C=US/O=Symantec Corporation/OU=Symantec Trust Network/CN=Symantec Class 3 Secure Server CA - G4
-----BEGIN CERTIFICATE-----
MIIF+DCCBOCgAwIBAgIQa0fyuH2bp1ucngiNHVoV4jANBgkqhkiG9w0BAQsFADB+
MQswCQYDVQQGEwJVUzEdMBsGA1UEChMUU3ltYW50ZWMgQ29ycG9yYXRpb24xHzAd
...
+eGxGqm8e1jgxB/fQePrh1vG4V40nr0cBKh6t52HmksBCfM0wOlMMJyUYiO0p44W
s4nxNrvMJS6e4bwdECI0UNhJznWr0tAu+ilFoTsfOlQpngCBDJEkZYr3mRjpIjX8
Sz4+hGzIhZVyjDvbcVCrsvCpM67cU2rQpJ2nkYM4ol/z6VDRs/G5aPiXe7o=
-----END CERTIFICATE-----
1 s:/C=US/O=Symantec Corporation/OU=Symantec Trust Network/CN=Symantec Class 3 Secure Server CA - G4
i:/C=US/O=VeriSign, Inc./OU=VeriSign Trust Network/OU=(c) 2006 VeriSign, Inc. - For authorized use only/CN=VeriSign Class 3 Public Primary Certification Authority - G5
-----BEGIN CERTIFICATE-----
MIIFODCCBCCgAwIBAgIQUT+5dDhwtzRAQY0wkwaZ/zANBgkqhkiG9w0BAQsFADCB
yjELMAkGA1UEBhMCVVMxFzAVBgNVBAoTDlZlcmlTaWduLCBJbmMuMR8wHQYDVQQL
...
QGX0InLNmfiIEfXzf+YzguaoxX7+0AjiJVgIcWjmzaLmFN5OUiQt/eV5E1PnXi8t
TRttQBVSK/eHiXgSgW7ZTaoteNTCLD0IX4eRnh8OsN4wUmSGiaqdZpwOdgyA8nTY
Kvi4Os7X1g8RvmurFPW9QaAiY4nxug9vKWNmLT+sjHLF+8fk1A/yO0+MKcc=
-----END CERTIFICATE-----
2 s:/C=US/O=VeriSign, Inc./OU=VeriSign Trust Network/OU=(c) 2006 VeriSign, Inc. - For authorized use only/CN=VeriSign Class 3 Public Primary Certification Authority - G5
i:/C=US/O=VeriSign, Inc./OU=Class 3 Public Primary Certification Authority
-----BEGIN CERTIFICATE-----
MIIE0DCCBDmgAwIBAgIQJQzo4DBhLp8rifcFTXz4/TANBgkqhkiG9w0BAQUFADBf
MQswCQYDVQQGEwJVUzEXMBUGA1UEChMOVmVyaVNpZ24sIEluYy4xNzA1BgNVBAsT
...
A4GBABMC3fjohgDyWvj4IAxZiGIHzs73Tvm7WaGY5eE43U68ZhjTresY8g3JbT5K
lCDDPLq9ZVTGr0SzEK0saz6r1we2uIFjxfleLuUqZ87NMwwq14lWAyMfs77oOghZ
tOxFNfeKW/9mz1Cvxm1XjRl4t7mi0VfqH5pLr7rJjhJ+xr3/
-----END CERTIFICATE-----
---
Server certificate
subject=/C=NL/ST=Netherlands/L=Eindhoven/O=Example International BV/OU=Consumer Lifestyle/CN=smartbabymonitor.ugrow.example.com
issuer=/C=US/O=Symantec Corporation/OU=Symantec Trust Network/CN=Symantec Class 3 Secure Server CA - G4
---
No client certificate CA names sent
---
SSL handshake has read 4805 bytes and written 434 bytes
---
New, TLSv1/SSLv3, Cipher is ECDHE-RSA-AES256-GCM-SHA384
Server public key is 2048 bit
Secure Renegotiation IS supported
Compression: NONE
Expansion: NONE
SSL-Session:
Protocol : TLSv1.2
Cipher : ECDHE-RSA-AES256-GCM-SHA384
Session-ID: F1B9C9DFA3CFC6CB3F958FAD4ECBBAFA0E72EA8A86F6AC9601CF8204819DB0F0
Session-ID-ctx:
Master-Key: EC4C5B32E60B5A0458BC85CC02529EA18DE61AFB8583D85D275C2822AC84E0E5E0C5B5E2C3C2D90F8B6E0EBB518EAA99
Key-Arg : None
PSK identity: None
PSK identity hint: None
SRP username: None
TLS session ticket lifetime hint: 300 (seconds)
TLS session ticket:
0000 - 27 e0 fb b8 dd c9 9f 29-31 85 2b 6c d1 5a b3 d1 '......)1.+l.Z..
0010 - 55 d6 e4 8a 4d f5 ef 2e-51 95 21 90 47 9d b6 0a U...M...Q.!.G...
0020 - df a5 d2 10 3d 03 e5 07-41 81 92 09 30 0e 08 3d ....=...A...0..=
0030 - fc ea 24 93 29 ed 60 9a-d0 d9 57 88 e4 4d 18 e3 ..$.).`...W..M..
0040 - ba aa 97 ee bf 39 9e 5b-76 5b 76 f7 81 c4 03 08 .....9.[v[v.....
0050 - fb b9 a3 4f 11 b0 99 4c-8c f2 a6 8a 9a e4 fe c6 ...O...L........
0060 - 0d 7b 6d a7 5b 53 b5 33-15 4f c4 ab 6b 29 7b 8f .{m.[S.3.O..k){.
0070 - ec 00 7f b2 6f 91 e4 ca-63 45 58 73 3a 78 8b 29 ....o...cEXs:x.)
0080 - 44 fc d5 e8 ad 4d dd 9c-22 df 50 eb d5 bf b9 90 D....M..".P.....
0090 - d8 6a 7d 6d bd 61 f2 63-07 75 8b d0 fc 40 64 76 .j}m.a.c.u...#dv
00a0 - 2b 97 53 aa 47 bc 3d d1-76 aa 8a 07 e1 60 14 d1 +.S.G.=.v....`..
00b0 - f7 88 8f f6 d9 b9 6b 0c-64 96 b5 f0 46 73 27 d6 ......k.d...Fs'.
Start Time: 1419835334
Timeout : 300 (sec)
Verify return code: 0 (ok)
---
Related
I have an archive encoded with gzip 1.5. I'm unable to decode it using the C zlib library. zlib inflate() return EC -3 stream.msg = "unknown compression method".
$ gzip --list --verbose vmlinux.z
method crc date time compressed uncompressed ratio uncompressed_name
defla 12169518 Apr 29 13:00 4261643 9199404 53.7% vmlinux
The first 32 bytes of the file are:
00000000 1f 8b 08 08 29 f4 8a 60 00 03 76 6d 6c 69 6e 75 |....)..`..vmlinu|
00000010 78 00 ec 9a 7f 54 1c 55 96 c7 6f 75 37 d0 fc 70 |x....T.U..ou7..p|
I see the first 18 bytes are the RFC-1952 gzip header.
After the NULL, I expect the next byte to be RFC-1951 deflate or RFC-1950 zlib (I'm not sure which)
So, I pass zlib inflate() a z_stream:next_in pointing to to the byte #0x12.
If this were deflate encoded, then I would expect the next byte #0x12 to be 0aabbbbb (BFINAL=0 and BTYPE=some compression)
If this were zlib encoded, I would expect the next byte #0x12 to take the form 0aaa1000 bbbccccc
Instead, I see #0x12 EC = 1110 1100 Which fits neither of those.
For my code, I took the uncompress() code and modified it slightly with allocators appropriate to my environment and several different experiments with the window bits (including 15+16, -MAX_WBITS, and MAX_WBITS).
int ZEXPORT unzip (dest, destLen, source, sourceLen)
Bytef *dest;
uLongf *destLen;
const Bytef *source;
uLong sourceLen;
{
z_stream stream;
int err;
stream.next_in = (Bytef*)source;
stream.avail_in = (uInt)sourceLen;
/* Check for source > 64K on 16-bit machine: */
if ((uLong)stream.avail_in != sourceLen) return Z_BUF_ERROR;
stream.next_out = dest;
stream.avail_out = (uInt)*destLen;
if ((uLong)stream.avail_out != *destLen) return Z_BUF_ERROR;
stream.zalloc = (alloc_func)my_alloc;
stream.zfree = (free_func)my_free;
/*err = inflateInit(&stream);*/
err = inflateInit2(&stream, 15 + 16);
if (err != Z_OK) return err;
err = inflate(&stream, Z_FINISH);
if (err != Z_STREAM_END) {
inflateEnd(&stream);
return err == Z_OK ? Z_BUF_ERROR : err;
}
*destLen = stream.total_out;
err = inflateEnd(&stream);
return err;
}
How can I correct my decoding of this file?
That should work fine, assuming that my_alloc and my_free do what they need to do. You should verify that you are actually giving unzip() the data that you think you are giving it. The data you give it needs to start with the 1f 8b.
(Side comment: "unzip" is a lousy name for the function. It does not unzip, since zip is an entirely different format than either gzip or zlib. "gunzip" or "ungzip" would be appropriate.)
You are manually reading the bits in the deflate stream in the wrong order. The least significant bits are first. The low three bits of ec are 100, indicating a non-last dynamic block. 0 for non-last, then 10 for dynamic.
You can use infgen to disassemble a deflate stream. Its output for the 14 bytes provided is this initial portion of a dynamic block:
dynamic
count 286 27 16
code 0 5
code 2 7
code 3 7
code 4 5
code 5 5
code 6 4
code 7 4
code 8 2
code 9 3
code 10 2
code 11 4
code 12 4
code 16 7
code 17 7
lens 4 6 7 7 7 8 8 8 7 8
repeat 3
lens 10
I have this command as reference:
XX 2C 03 XX
When I send the command: "00 2C 03 01 00" I'm getting error 6d00 (Instruction code not supported or invalid)
Important: I am in a test environment, I am studying the APDU commands.
After testing questions I sent the command to verify the PIN containing an invalid PIN 3 times in a row.
Original PIN: 1574
P1 = 0x15
P2 = 0x15
Commands:
>> 0x00,0x20,0x00,0x80,0x08,0x24,p1,p2,0xFF,0xFF,0xFF,0xFF,0xFF
<< 63c2
>> 0x00,0x20,0x00,0x80,0x08,0x24,p1,p2,0xFF,0xFF,0xFF,0xFF,0xFF
<< 63c1
>> 0x00,0x20,0x00,0x80,0x08,0x24,p1,p2,0xFF,0xFF,0xFF,0xFF,0xFF
<< 63c0
After that, I run the command again:
>> 0x00,0x20,0x00,0x80,0x08,0x24,p1,p2,0xFF,0xFF,0xFF,0xFF,0xFF
<< 6983
I want to reset the counter (Reset Retry Counter), so that I can verify the PIN again, for this purpose I am executing the following command:
>> 00 2C 03 00
<< 6d00
Why am I getting this error: "6d00"?
I was forgetting the PUK code, problem solved!
The correct is:
CL ='00' -
INS='2C' - RESET RETRY COUNTER
P1 = either
'00' - Data contains PUK and new PIN
'01' - Data contains PUK only
P2 ='01' - Key Reference of the PIN (as <01>)
Data = either
PUK | NewPIN, if P1='00'
PUK , if P1='01'
By using an online tool and wikipedia I found out that every sha-256 encrypted string is 64 chars longs containing numbers and characters. Hence I assumed that there are 34^36 combinations ( 2^216 simplified by an algebra calculator ).
After doing some research I found out that most people said there are 2^256 combinations. Could someone explain ? To make the context clear, I write a paper about cryptocurrencies and try to explain how many different combinations there are to encrypt and how long this could take ( therefore how many guesses it could take) and compare this to the amount of total atoms in the universe (roughly 10^85).
SHA-256 produces 256 bits which is 32 bytes, not characters, each byte has 256 possible values.
There are 256 bits and each bit has 2 values (0 or 1), thus 2^256.
There are 32 bytes and each byte has 256 values, thus 256^32.
Note: 2^256 == 256^32 ~= 10^77.
The 32 bytes can be encoded many ways, in hexadecimal it would be 64 characters, in Base64 it would be 44 characters.
Total combinations of SHA-256 is
115,792,089,237,316,195,423,570,985,008,687,907,853,269,984,665,640,564,039,457,584,007,913,129,639,936
A sha-256 hash has 64 characters, 32 hex combinations, because a hex has 2 characters.
3a 7b d3 e2 36 0a 3d 29 ee a4 36 fc fb 7e 44 c7 35 d1 17 c4 2d 1c 18 35 42 0b 6b 99 42 dd 4f 1b
Above is a hash where the hex combinations are separated so you can count 32.
There are 16 characters available to hex 0-9&a-f and 16^2 or 256 combinations in hex.
With 32 slots for a hex in a sha-256 you use 256^32 to get:
115792089237316195423570985008687907853269984665640564039457584007913129639936
Available sha-256 hashes.
I am trying to port a program which queries an LDAP server from Perl to Go, and with the Go version I am receiving a response that the filter is malformed:
00000057: LdapErr: DSID-0C0C0968, comment: The server was unable to decode a search request filter, data 0, v1db1\x00
I have used tcpdump to capture the data transmitted to the server with both the Perl and Go versions of my program, and have found that they are sending slightly different filter packets. This question is not about any possible bugs in the Go program, but simply about understanding the contents of the LDAP filter packets.
The encoded filter is:
(objectClass=*)
And the Perl-generated packet (which the server likes) looks like this:
ASCII . . o b j e c t C l a s s
Hex 87 0b 6f 62 6a 65 63 74 43 6c 61 73 73
Byte# 0 1 2 3 4 5 6 7 8 9 10 11 12
The Go-generated packet (which the server doesn't like) looks like this:
ASCII . . . . o b j e c t C l a s s
Hex a7 0d 04 0b 6f 62 6a 65 63 74 43 6c 61 73 73
Byte# 0 1 2 3 4 5 6 7 8 9 10 11 12 13 14
This is my own breakdown of the packets:
##Byte 0: Tag
When I dissect Byte 0 from both packets, I see they are identical, except for the Primitive/Constructed bit, which is set to Primitive in the Perl version, and Constructed in the Go version. See DER encoding for details.
Bit# 87 6 54321
Perl 10 0 00111
Go 10 1 00111
Bits 87: In both packets, 10 = Context Specific
Bit 6: In the Perl version 0 = Primitive, in the Go version 1 = Constructed
Bits 54321: 00111 = 7 = Object descriptor
##Byte 1: Length
11 bytes for the Perl version, 13 for the Go version
##Bytes 2-3 for the Go version
Byte 2: Tag 04: Substring Filter (See section 4.5.1 of RFC 4511)
Byte 3: Length of 11 bytes
##Remainder: Payload
For both packets this is simply the ASCII text objectClass
My reading of RFC 4511 section 4.5.1 suggests that the Go version is "more" correct, yet the Perl version is the one that works with the server. What gives?
Wireshark is able to parse both packets, and interprets them both equally.
The Perl version is correct, and the Go version is incorrect.
As you point out, RFC 4511 section 4.5.1 specifies encoding for the filter elements, like:
Filter ::= CHOICE {
and [0] SET SIZE (1..MAX) OF filter Filter,
or [1] SET SIZE (1..MAX) OF filter Filter,
not [2] Filter,
equalityMatch [3] AttributeValueAssertion,
substrings [4] SubstringFilter,
greaterOrEqual [5] AttributeValueAssertion,
lessOrEqual [6] AttributeValueAssertion,
present [7] AttributeDescription,
approxMatch [8] AttributeValueAssertion,
extensibleMatch [9] MatchingRuleAssertion,
... }
And in this case, the relevant portion is:
present [7] AttributeDescription,
The AttributeDescription element is defined in section 4.1.4 of the same specification:
AttributeDescription ::= LDAPString
-- Constrained to <attributedescription>
-- [RFC4512]
And from section 4.1.2:
LDAPString ::= OCTET STRING -- UTF-8 encoded,
-- [ISO10646] characters
So this means that the present filter component is an octet string, which is a primitive element. Go is incorrectly converting it to a constructed element, and the directory server is correctly rejecting that malformed request.
I am trying to parse an LDAP bind request using the Apache Harmony ASN.1/BER classes (could use another library, I just chose that as it has an Apache License).
My question is on the encoding specifically of a "CHOICE" in ASN.1. The RFC that defines the LDAP ASN.1 schema (http://www.rfc-editor.org/rfc/rfc2251.txt) gives the following as part a bind request:
BindRequest ::= [APPLICATION 0] SEQUENCE {
version INTEGER (1 .. 127),
name LDAPDN,
authentication AuthenticationChoice }
AuthenticationChoice ::= CHOICE {
simple [0] OCTET STRING,
-- 1 and 2 reserved
sasl [3] SaslCredentials }
SaslCredentials ::= SEQUENCE {
mechanism LDAPString,
credentials OCTET STRING OPTIONAL }
How is that CHOICE there actually encoded?
I generated a sample bind request using JXplorer and captured the raw data that was sent. It looks like this:
00000000 30 31 02 01 01 60 2c 02 01 03 04 1b 75 69 64 3d |01...`,.....uid=|
00000010 74 65 73 74 75 73 65 72 2c 64 63 3d 74 65 73 74 |testuser,dc=test|
00000020 2c 64 63 3d 63 6f 6d 80 0a 74 65 73 74 69 6e 67 |,dc=com..testing|
00000030 31 32 33 |123|
The 80 there (at offset 0x27) seems to represent that choice. Fair enough - and I get that (per http://en.wikipedia.org/wiki/Basic_Encoding_Rules#BER_encoding) the last bit is set in order to indicate that it's "context specific" (i.e. defined by this application/protocol) But how would I know if this is a "simple" or "sasl" auth? What indicates which option of the choice is being used? In this case it looks like the next byte (0x0a) is the length of the string - so this could be an OctetString or something of the sort - but I don't see anything here that indicates what the actual is other than 0x80...
I'm also not sure what the [0] and [3] mean in the CHOICE section above. Is that saying there are four options but only options numbered 0 and 3 are in use?
Below you can see output of openssl asn1parse command. The CHOICE members are encoded using so called context specific tags - which means normal tag value is replaced with the one specified in ASN.1 definition for respective item in the CHOICE. The tag has value 0 which implicates the first item in CHOICE is selected. The first choice item is of type OCTET STRING. The value 0 of context specific tag gives you the information about the value type. If there was no context tag, normal OCTET STRING tag would be used.
0:d=0 hl=2 l= 49 cons: SEQUENCE
2:d=1 hl=2 l= 1 prim: INTEGER :01
5:d=1 hl=2 l= 44 cons: appl [ 0 ]
7:d=2 hl=2 l= 1 prim: INTEGER :03
10:d=2 hl=2 l= 27 prim: OCTET STRING :uid=testuser,dc=test,dc=com
39:d=2 hl=2 l= 10 prim: cont [ 0 ]
The '80'H in the encoded message above is called the "identifier octets" (in general it may be more than one octet). This value of the identifier octet(s) indicates that the selected alternative of the CHOICE is "simple", because the five low-order bits of '80'H are '00000'B, which matches the tag number of the tag of "simple" ([0]).
If the sender had selected the "sasl" alternative, the identifier octet would be 'A3'H instead of '80'H. The '3'H in 'A3'H (the five low-order bits) is the tag number of the tag of "sasl" ([3]). The two highest-order bits of the identifier octet are set to '10'B for both alternatives because both [0] and [3] are "context-specific" tags (this just means that these tags don't contain the APPLICATION keyword or the PRIVATE keyword). The next bit of the identifier octet (the "constructed" bit) is set to '0' for "simple" but is set to '1' for "sasl", because the encoding of "sasl" contains nested tags whereas the encoding of "simple" does not contain any nested tags.