What does plus (+) sign mean in openssl output Subject line? - ssl

I'm trying to self-sign an SSL certificate for testing purpose, where I need to match the subject of a real world certificate with a special subject line.
The certificates have a special UID attribute in their subject. For example if I inspect the certificate with openssl:
openssl x509 -in customer/1.user_cert.der -inform der -noout -text
Then in the output I can see a subject line like this:
Subject: C = XX, CN = JOHN SMITH + UID = 123
Normally in the Subject, the comma , separates different fields, but here the CN and UID are separated by +.
I think the + is not part of CN because, if I try to self-create a certificate with CN = JOHN + UID=123 then the -text output would show quotes (") to indicate the boundaries of the CN like CN = "JOHN + UID=123".
So what does this + mean in the subject line? How is it entered into a certificate request with openssl?

It's a "Multivalue-RDN".
When creating a certificate request with multivalued RDN, -multivalue-rdn parameter must be given to the openssl command.
Example:
openssl req -new -sha256 -key user.key -multivalue-rdn -subj '/C=XX/CN=JOHN SMITH+UID=123/' -out user.csr
Reference:
https://www.openssl.org/docs/man1.0.2/man1/req.html
-multivalue-rdn
this option causes the -subj argument to be interpreted with full support for multivalued RDNs. Example:
/DC=org/DC=OpenSSL/DC=users/UID=123456+CN=John Doe
If -multi-rdn is not used then the UID value is 123456+CN=John Doe.

Related

Generate weak X509 certificate

I need to generate weak certificate for CTF challenge using RSA and small modulus so it's factorable. It should be about 64 bits.
I've tried to generate that using OpenSSL as I would the normal one, but it forbids creating certificate with modulus lower than 512 bits for security reasons. So I've changed the source files of OpenSSL so it doesn't checks the bit length and recompiled that again. I was then able to create private key using smaller modulus, but trying to create certificate using that key (or new small one) evoked new error which I don't fully understand. I even wanted to evade the OpenSSL problem at all using Python, but that just showed that it uses OpenSSL too and had exactly same problems.
Generating small private key:
$ openssl genrsa -out acn.pem 64
-----BEGIN PRIVATE KEY-----
MFQCAQAwDQYJKoZIhvcNAQEBBQAEQDA+AgEAAgkAlz0xJ3uUx5UCAwEAAQIIR1Zs
1Wo4EQECBQDHPJNVAgUAwlPjQQIFAKqNunkCBClt4QECBHlHx1Q=
-----END PRIVATE KEY-----
Generating certificate:
$ openssl req -key acn.pem -new -out domain.csr
...
140561480598848:error:04075070:rsa routines:RSA_sign:digest too big for rsa key:../crypto/rsa/rsa_sign.c:100:
140561480598848:error:0D0DC006:asn1 encoding routines:ASN1_item_sign_ctx:EVP lib:../crypto/asn1/a_sign.c:224:
I found that this thread could be helpful as I could even choose my own numbers, but the method didn't worked for me:
How to Generate rsa keys using specific input numbers in openssl?
Here's sample certificate from PicoCTF. I would like to create similar one.
-----BEGIN CERTIFICATE-----
MIIB6zCB1AICMDkwDQYJKoZIhvcNAQECBQAwEjEQMA4GA1UEAxMHUGljb0NURjAe
Fw0xOTA3MDgwNzIxMThaFw0xOTA2MjYxNzM0MzhaMGcxEDAOBgNVBAsTB1BpY29D
VEYxEDAOBgNVBAoTB1BpY29DVEYxEDAOBgNVBAcTB1BpY29DVEYxEDAOBgNVBAgT
B1BpY29DVEYxCzAJBgNVBAYTAlVTMRAwDgYDVQQDEwdQaWNvQ1RGMCIwDQYJKoZI
hvcNAQEBBQADEQAwDgIHEaTUUhKxfwIDAQABMA0GCSqGSIb3DQEBAgUAA4IBAQAH
al1hMsGeBb3rd/Oq+7uDguueopOvDC864hrpdGubgtjv/hrIsph7FtxM2B4rkkyA
eIV708y31HIplCLruxFdspqvfGvLsCynkYfsY70i6I/dOA6l4Qq/NdmkPDx7edqO
T/zK4jhnRafebqJucXFH8Ak+G6ASNRWhKfFZJTWj5CoyTMIutLU9lDiTXng3rDU1
BhXg04ei1jvAf0UrtpeOA6jUyeCLaKDFRbrOm35xI79r28yO8ng1UAzTRclvkORt
b8LMxw7e+vdIntBGqf7T25PLn/MycGPPvNXyIsTzvvY/MXXJHnAqpI5DlqwzbRHz
q16/S1WLvzg4PsElmv1f
-----END CERTIFICATE-----
I need to generate weak certificate for CTF challenge using RSA and small modulus so it's factorable. It should be about 64 bits.
It's impossible to do that as a self-signed certificate, because proper RSA signing can't work with keys that small.
RSA-SSA-PKCS1_v1.5 is the shortest structured RSA signature padding, and it's structured as (per https://datatracker.ietf.org/doc/html/rfc8017#section-9.2):
DigestInfo ::= SEQUENCE {
digestAlgorithm AlgorithmIdentifier,
digest OCTET STRING
}
The shortest possible encoding for that structure is 9 bytes... and that's with a 0-byte hash algorithm:
30 07 // SEQUENCE (7 content bytes)
30 03 // digestAlgorithm: SEQUENCE (3 bytes)
06 01 00 // OBJECT IDENTIFIER 0.0 ({itu-t(0) recommendation(0)})
// omit implicit NULL
04 00 // digest (empty)
So, with our empty hash we need to keep going:
If emLen < tLen + 11, output "intended encoded message length too short" and stop.
emLen is the length of the modulus (in bytes) and tLen is the length of our encoded structure (9 bytes+).
That makes 20 bytes (160 bits) the shortest possible RSA key to do anything that might stand a chance of being regarded as an RSA signature... which produced a pointless signature (since everything collides under a 0-bit hash).
If you are comfortable stomping on a 1-byte OID for your CTF, your RSA key modulus would need to be 20 bytes + the length of the intended hash (in bytes). Since there's no 1-byte OID that identifies an existing hash algorithm (and no defined hash algorithm for use with certificates that's that small), no existing easy-to-use tool can do this for you.
You could invent a new form of RSA signature padding, of course, using something like a 60-bit hash processed directly with your 64-bit key. That'll require even further work on your part.
You're basically reduced to using a DER writer (or writing your own) to hand-craft a certificate.
Here's sample certificate from PicoCTF. I would like to create similar one.
That certificate is not self-signed. It has a 2048-bit certificate signature, from RSA-SSA-PKCS1_v1.5 with MD2. So while it describes a short RSA key (53 bit modulus) it was signed with something "proper". If that's what you're after, the general flow would be something like
Create a normal self-signed cert
Create your small RSA key
Build a new cert, signed by the first cert, containing the small RSA key.
It's hard to encode a CSR for the small key (because it can't self-sign the request), but maybe there's a way to get openssl req -new to do something other than self-sign, which would allow skipping the intermediate CSR. (Or use library tools like .NET's CertificateRequest, or OpenSSL's APIs instead of their CLI tool, or whatever.)

OpenSSL verify fails unexpectedly with "permitted subtree violation" for certificate with Name Constraints

I am trying to verify a PIV certificate from a smart card using a certificate that includes a Name Constraints extension. If I inspect the -text output everything looks like it should be fine but the two values have different case. Is it possible that OpenSSL is doing a case-sensitive comparison even though rfc5280 indicates that it should be case-insensitive? Here are the relevant text outputs:
Root CA (changed actual values to example but preserved casing:
X509v3 Name Constraints: critical
Permitted:
email:example.com
email:.example.com
DNS:example.com
DirName:C = US, O = Example
PIV Certificate (again, changed actual values to example)
Subject: C = us, O = example, OU = people, CN = john.j.doe.123456
Here the Subject C and O values have a different case than those in the DirName constraint.
Here is the full output I'm getting from the openssl verify
$ openssl verify -noCAfile -CAfile root.crt -untrusted intermediate.crt piv-cert.crt
C = us, O = example, OU = people, CN = john.j.doe.123456
error 47 at 0 depth lookup: permitted subtree violation
error piv-cert.crt: verification failed
No, it's not due to case; nc_dn in v3_ncons.c calls the i2d routine which calls x509_name_canon in x_name.c which calls asn1_string_canon which drops unnecessary spaces and converts to lowercase, before comparing.
It's (probably, given your redaction) due to an additional check that CommonName in the leaf cert if it 'looks like' a DNS name must satisfy the DNS constraints, which your example respectively does and doesn't.
Specifically, NAME_CONSTRAINTS_check_CN in v3_ncons.c is called for the leaf/EE cert, and (now) calls cn2dnsid to check for DNS syntax (post-punycoding if applicable): all ASCII letters, digits, plus nonstandard underscore, plus dot not at beginning or end or consecutive (i.e. all DNS labels must be nonempty) plus hyphen not at beginning or end or adjacent to dot. If this passes it applies any DNS constraints.
This looks like it is intended for SSL/TLS server certificates -- and the commit that added an earlier version to 1.1.0 acknowledged 5280 doesn't call for CN check, while this refactoring for 1.1.1 briefly modified it to apply only if no SAN DNS was present (and thus checked), but immediately thereafter that SAN bypass was removed from the code at the same time the documentation was changed to describe it as happening, which looks like a mistake to me -- but the code doesn't attempt to limit it to such server certs (and probably can't, because EKU is optional).

How to Convert Certificate to Decimal Modulus and Exponent

Some libraries require that a public key is represented by an integer modulus and exponent. It's surprisingly hard to quickly convert a certificate or public key into that format. How can I convert that using bash?
Convert Certificate to Modulus, Exponent
The process for taking a certificate or a public key and turning it into integers is a surprisingly tough task as first glance. There's a long chain of decoding and encoding to various format that this page will describe.
1. Certificate to Public Key
$ CERTIFICATE_PATH=/path/to/file.cer
$ OUTPUT_PUBLIC_KEY_PATH=public_key.pem
$ openssl x509 -pubkey -noout -in $CERTIFICATE_PATH > $OUTPUT_PUBLIC_KEY_PATH
Creates the a public key (example output below, don't worry this is a sample cert, not real).
-----BEGIN PUBLIC KEY-----
MIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEArDMy/rsEEUfMQrrCSQmc
Lo9CSTvoL2jijXN1yi8jlx86yorxxm8Njdo0R2N6U0QRBLjglabeJJJZLJxQqPpU
RRFB7euTmmdLsctHxYVn2UDglIXV1Q5CGkSi9nFRupkL7ZMUflP0hqvPFRagUHaG
exJSlsO+32Gy6TxVIQKxAacjoYFJcMDGlDV4oTPelgMJ7enA7s4Bnz2tr6KJyoD3
N9fhcmFR0lGR7W7wqR79ePaxmPcCEl6UGAIbPny8WSQluRGzjsdB56dwsvdIZtlS
ChYr4tzoQYOB7cWc9gnJlQFOhocc/7tFPVXelT2Ff95Kp4eWGUK5JmzoU720B8Yk
EQIDAQAB
-----END PUBLIC KEY-----
2. Get Hexidecimal Modulus from Public Ke
$ HEX_MODULUS=$(openssl rsa -pubin -in $OUTPUT_PUBLIC_KEY_PATH -modulus | grep 'Modulus=' | cut -d'=' -f 2)
Creates the a hexidecimal representation of the modulus (example output below).
DD6DBDF880FAD7DE1B1FA7A32EB202E216F6520A3CBFA642F8CADC93674D60C34F8DC38A001BF1C44B416A69D269E53F218EC50BF82237ADB62C4B55FF7A0372BB9AD3EC96B9569FCB1999C932946F8FC652069F4503DFFDE897F6EAD6BABB482BB5E034614D52360FAB87522503CF87008713F2CA0329169D905746B5F40EAE17C80A4D92ED08A632231171FEF22C44D76C07F30B7B0C4BDD3BB4F737709F51B6884E5D6A057F8D9B667AAB8020FEEE6B97C3497D783BD599970375CE8FBCC5BE9C9AA5121970F9A4BD9627ED2302A7C757C971CF7694A22162F6B81DCA88EE09AD462FB761B32C1513869FA535265A67F437C8E68001490EC7ED61D3CDBCE4F8BE3FC94EF87D9789CE12BCCAB5C6D2E0D9B3683C2E4A9DB45FB853EE503DBFDDD4A28AB6A027AB980CB3B25890E2BCA1ADFFBD8E55310F00BF68E93DA9199AF06D0BA2146AC64CC64EBD6312A50B4D97EB42097953E265AA243470B8C1AB2380E79C6CEDDC82AA3704B8432A3D2AA8CC20FC275D902658F9B714E29EE2C1707397E96B028ED352597B00EC6130F1563F9CC17C05C5B136C81885CF61401F07E8A70687DF9A770BA9647203F693FCE00259C196ECC009423E30A27F1B482FFEE0218F5387250DCBEA49F54A9BD0E35FEE7818E5BA7131A904980FB1AD6752A0F2E39CAB6AFE588484DD073D3294051645159659A0586C180EE37766C7B3F799
3. Convert Hexadecimal Modulus to Decimal
$ ruby<<EOF
p "$HEX_MODULUS".to_i(16).to_s(10)
EOF
Converts the hexadecimal representation of the modulus to decimal (example output below).
"903350200741846433103490275702318092987711221795080780487705515793447642462352293930005029082816456324966429635984183642976753224939100182934586764830322547353276613511778555156314685902120045785748740652102149499802458636594115145098376676532039811405280029484180531080897744797671946602535748106482573745640230125725071851411418858523441305542960197934970282201978169899128421358221136498561735378441078904842496000031288381427214662674876164488503477712353985285783038861704346173846417987800700372136855065513839371230505066979655945991689080601315943347208545322825551901523372801711788730312786616209024413727099938085068143933277702040068400770002392724273077692527618673710507253235031640666038293670466879460643091688898398212470614257430485567355559709027230476557173161015264993692101931219217542706455704780898322106052146131050344223354172417918892290877128904693397579183166821072452886577300287605913021934410696419333315789150404571415571510150759313347067018155614134697544366299631877615175466702747992499691378170723240805893694504266094416954653431726471214487875228995333970243486471724362065194634035562833627383830413785525590101462126279287790122982746853775543354375554949279704170225740172092202881398339481"
4. Get Exponent from Public Key
$ openssl rsa -pubin -in $FILE -text | grep 'Exponent'
Gets the decimal exponent from the public key (example output below). Most people use 65537 as the exponent.
Exponent: 65537 (0x10001)

Signing 20-byte message with 256-bit RSA key working with openssl.exe but not in code

I have a 256-bit private key that I want to use to sign a SHA-1 digest (20 bytes). Using openssl directly it seems to work
echo doesntmatter | openssl dgst -sha1 -binary | openssl rsautl -sign -inkey 256bit_private_key.pem | openssl enc -base64
gives me a Base64 output as expected.
But doing it with the OpenSSL fails with "error:04075070:rsa routines:RSA_sign:digest too big for rsa key". As you can see below, I'm passing the 20-byte (SHA_DIGEST_LENGTH=20) SHA-1 digest as input to RSA_sign. Even with padding it shouldn't be more than the maximum of 32 bytes that I can encrypt with a 256 bit modulus key?!
unsigned char digest[SHA_DIGEST_LENGTH];
SHA1(message, messageSize, digest);
unsigned int privateKeySize = RSA_size(privateKey); // 256 bits = 32 bytes
unsigned char* signature = new unsigned char[privateKeySize];
unsigned int signatureSize;
int res = RSA_sign(NID_sha1, digest, SHA_DIGEST_LENGTH, signature, &signatureSize, privateKey);
if(res == 0)
{
int err = ERR_get_error(); // 67588208
char *s = ERR_error_string(err, 0); // error:04075070:lib(4):func(117):reason(112)
delete [] signature;
[...]
}
What am I doing wrong in the code?
check out this SO answer. rsautl is depreciated in favor of pkeyutl
Essentially, RSA signing should use RSA-PSS signature padding scheme to be secure, and the RSA-PSS will be strictly larger than the digest, because of the necessity to communicate a salt. additionally, RSA_sign does a digest as a part of the signing scheme, so your code is going to do a digest of a digest. you want to instead either pass the message in directly, or use RSA_private_encrypt combined with a suitable RSA_padding_add_x call.
and as I said in my comment, a 256-bit key is sufficiently secure for a symmetric algorithm, but it will be trivial to crack for an RSA private key (on the order of 4 minutes on a couple of year old machine). Using a bare minimum of a 1024 bit key (and probably a 2048 or 4096 bit key) will give you a security level roughly equivalent to a 128 bit symmetric algorithm.
Re Peter Elliott: not quite.
The PKCS1v1_5 standard sequence in RFC8017 section 8.2.1 (and 9.2, similar in rfc4347 and rfc3447) is hash, encode in ASN.1, pad ('type 1') and modexp d. (PSS omits the ASN.1 step and uses different padding.)
OpenSSL (low-level) RSA_sign does the last three (with v1.5 padding) but not the first -- i.e. it encodes the hash but it doesn't do the hash; even-lower RSA_private_encrypt does the last two.
The higher-level envelope module (original EVP_Sign{Init,Update,Final}*, enhanced EVP_DigestSign*) does all four.
It's the ASN.1 encode that made the value too large for a 256-bit key -- which as stated is breakable anyway.
rsautl does only the last two steps not the encode, so it runs but gives a nonstandard result.
pkeyutl for RSA does the same by default but can be told to do the encode.
Also, PSS has a better security proof, but as far as I've heard there's no actual attack on v1.5 and it's still widely used.
If you have the choice of PSS (both/all parties support it) choose it, but don't feel worried about using v1.5.
Guess I found the solution. openssl rsautl -sign uses RSA_private_encrypt instead of RSA_sign (what I would have expected). RSA_sign creates a longer structure than the 20-bytes message I provided, so it fails with the given error.

How does OpenSSL/Mozilla NSS RSA sign and verify (PK11_Sign, PK11_Verify, PK11_VerifyRecover functions) work?

I have my RSA public and private keys (all of p,q,e,n,d) in PEM format. I am curious to know:
How PK11_Sign(), PK11_Verify() and PK11_VerifyRecover() (from OpenSSL/Mozilla NSS library) work with RSA?
How the padding is applied to the input message to be signed?
The context of my question is: I have seen PK11_Sign() adds some padding to my input data during signing. For example (given the key size is 162 bits):
my input = 31323334353036373839
padded input = 1FFFFFFFFFFFFFFFF0031323334353036373839
I would like to know:
What is the name of this padding scheme and pointers on how it works?
What is the default padding scheme for the above mentioned OpenSSL functions? For example, if I perform " openssl rsautl -in input.txt -inkey mykey.pem -out signed.txt ", which padding scheme will be used?
PK11_Sign etc. uses PKCS#1 v.1.5 signatures, which includes the padding you mention.
The padding scheme is part of the algorithm called EMSA-PKCS1-V1_5-ENCODE. I do not believe it has a name, although it might be informally called "PKCS#1 v.1.5 signature padding". It is defined in the PKCS#1 standard.
According to the documentation the default for openssl rsautl is to use PKCS#1 v.1.5 signature, which implies this padding.