Im trying to reverse engineer a file format which is encrypted. Most likely it uses an XOR encryption. I can create encrypted files with known plaintext, which I analyzed:
enc 71 8d 7e 84 29 20 b8 cb 6c ed bb 8a 62 a1
dec 74 68 69 73 20 69 73 20 61 20 74 65 73 74
xor 05 e5 17 f7 09 49 cb eb 0d cd cf ef 11 d5
txt t h i s i s a t e s t
enc 61 ad be 84 29 20 b8 cb 6c ed bb 8a 62 a1
dec 64 68 69 73 20 69 73 20 61 20 74 65 73 74
xor 05 c5 d7 f7 09 49 cb eb 0d cd cf ef 11 d5
txt d h i s i s a t e s t
enc 62 a5 ae a4 e9 a0 b8 cb 6c ed bb 8a 62 a1
dec 67 68 69 73 20 69 73 20 61 20 74 65 73 74
xor 05 cd c7 d7 c9 c9 cb eb 0d cd cf ef 11 d5
txt g h i s i s a t e s t
It is obvious that the original text is part of the encryption. The first byte of the key is always 05. The second byte of the key can be calculated like this:
(enc1 + dec1) OR xor1
The rather low entropy of the key implies a similar rule for the other key-bytes.
Any ideas?
You almost got it!
The key's byte at the m position is given by :
km = [(en + dn) ^ kn] | secret
where :
en is the previous encrypted byte
dn is the previous plain text byte
kn is the previous key byte (k0 = 5)
secret is an arbitrary number starting at 5 and incremented by 2 every two turns
^ is the xor operator
| is the or operator
A simple C# key generator :
namespace Sample.CustomEncrypt {
using System.Collections.Generic;
using System.Text;
class Program {
static void Main() {
var key1 = GenerateKey("this is a test");
var key2 = GenerateKey("dhis is a test");
var key3 = GenerateKey("ghis is a test");
}
public static byte[] GenerateKey(string input) {
var plain = Encoding.UTF8.GetBytes(input);
var secret = 5;
var key = new List<byte> {
0x05
};
for (var i = 0; i < plain.Length - 1; i++) {
var dn = plain[i];
var kn = key[i];
var en = (byte)(dn ^ kn);
var km = (byte)(((dn + en) ^ kn) | secret);
key.Add(km);
if (i % 2 == 0) {
secret += 2;
}
}
return key.ToArray();
}
}
}
PS: As pointed out by Eugene you should post on Reverse Engineering or Cryptography next time.
Related
I have been trying to parse for a long time, but I just can't get it. I tried a lot of types and converters, but it didn't give any effect. Also i googled everywhere i can.
Here is full block of data:
3D 08 41 CF AC D9 59 64 44 44
Where is
3D - type of data (datetime of length),
08 - length of data and data (of datetime)
41 CF AC D9 59 64 44 44 = 8 October 2020 10:38:46 UTC
All other data was Big-endian and this block, maybe, same.
The closest binary data were Apple Absolute and Ole automation (current data was encoded in DCode and сompared with my data), but nevertheless they were not. Back-end is old and written on Java, maybe this will be some kind of clue.
Edit
Thanks to #GiacomoCatenazzi, business got off the ground. I think that start of time is 01.01.1970, but it is not unix epoch. And there is difference between mod by hour or by seconds (like seconds fracture, but without milliseconds).
The main problem I see at the moment is to find out datetime with or without seconds.
First byte 41 - may be a true value. And last 2 or 3 bytes is often equal.
Relative times to Zero
3C - 3D C2 3F BF -1m
Where 3C - type of data (datetime of 4 bytes)
3D 08 - 41 CE E1 1F E0 00 00 00 -1s
3D 08 - 41 CE E1 1F E0 02 22 22 1s
3D 08 - 41 CF AC EB BF A2 A2 A2 Current datetime (Oct 15 2020 02:59:15 GMT+0300)
3C - 3D C2 3F C0 0 - Center
3C - 3D C2 3F C1 1m
3C - 3D C2 3F FC 1 hour
3C - 3D C2 45 60 1 day
3C - 3D C2 F3 C0 1 month
3C - 3D CA 44 E0 1 Year
3C - 3E 8A BF E0 25 year (Dec 26 1994 03:00:00 GMT+0300)
3C - 3F 59 D6 CC Current date only (Oct 15 2020) (26711820 - minutes since 01.01.1970)
I have some asn.1 encoded data. I have some success decoding it with bouncycastle but I hit the wall with quite complex example. It must be doable, but can't get further, hope you could help me, here is the example data:
A1 81 A9 02 02 1C 1F 02 01 15 30 81 9F 55 02 01 14 A0 81 98 A4 81 95 6B 10 30 0E 80 04 00 00 01 1F A1 06 30 04 80 02 33 32 63 06 30 04 80 02 33 32 61 0E 30 0C 80 0A 30 32 32 33 37 38 33 36 31 30 62 06 30 04 80 02 33 32 64 02 87 00 6B 13 30 11 80 04 00 00 01 1F A1 09 30 07 80 05 23 23 37 30 30 4E 01 02 0A 01 16 67 12 30 10 A3 0E 81 0C 2B 34 38 32 32 33 37 38 33 36 31 30 68 0F 30 0D A3 0B 82 09 32 32 38 36 35 33 33 39 38 65 09 30 07 80 05 23 23 37 30 30 7E 12 A0 10 18 0E 32 30 31 36 30 33 30 32 32 32 30 31 33 36
CSTA Browser decoded:
rOSE.roiv-apdu
{ -- SEQUENCE --
invokeID = 7199,
operation-value = 21 (cSTAEventReport),
argument
{ -- SEQUENCE --
crossRefIdentifier = '01 14'H,
eventSpecificInfo.callControlEvents.delivered
{ -- SEQUENCE --
connection.both
{ -- SEQUENCE --
callID = '00 00 01 1F'H,
deviceID.staticID
{ -- SEQUENCE --
deviceIdentifier.dialingNumber = "32" '33 32'H
}
},
alertingDevice.deviceIdentifier
{ -- SEQUENCE --
deviceIdentifier.dialingNumber = "32" '33 32'H
},
callingDevice.deviceIdentifier
{ -- SEQUENCE --
deviceIdentifier.dialingNumber = "0223783610" '30 32 32 33 37 38 33 36 31 30'H
},
calledDevice.deviceIdentifier
{ -- SEQUENCE --
deviceIdentifier.dialingNumber = "32" '33 32'H
},
lastRedirectionDevice.notKnown NULL,
originatingNIDConnection.both
{ -- SEQUENCE --
callID = '00 00 01 1F'H,
deviceID.staticID
{ -- SEQUENCE --
deviceIdentifier.dialingNumber = "##700" '23 23 37 30 30'H
}
},
localConnectionInfo = 2 (alerting),
cause = 22 (newCall),
networkCallingDevice.deviceIdentifier
{ -- SEQUENCE --
deviceIdentifier.explicitPublic.international = "+48223783610" '2B 34 38 32 32 33 37 38 33 36 31 30'H
},
networkCalledDevice.deviceIdentifier
{ -- SEQUENCE --
deviceIdentifier.explicitPublic.national = "228653398" '32 32 38 36 35 33 33 39 38'H
},
associatedCallingDevice.deviceIdentifier
{ -- SEQUENCE --
deviceIdentifier.dialingNumber = "##700" '23 23 37 30 30'H
},
extensions
{ -- SEQUENCE --
security
{ -- SEQUENCE --
timestamp = "20160302220136" '32 30 31 36 30 33 30 32 32 32 30 31 33 36'H
}
}
}
}
}
bouncycastle's dump produces something like that:
00 AC A1 81 A9 02 02 5F B9 02 01 15 30 81 9F 55 02 01 91 A0 81 98 A4 81 95 6B 10 30 0E 80 04 00 00 03 98 A1 06 30 04 80 02 33 32 63 06 30 04 80 02 33 32 61 0E 30 0C 80 0A 30 32 32 33 37 38 33 36 31 30 62 06 30 04 80 02 33 32 64 02 87 00 6B 13 30 11 80 04 00 00 03 98 A1 09 30 07 80 05 23 23 37 30 30 4E 01 02 0A 01 16 67 12 30 10 A3 0E 81 0C 2B 34 38 32 32 33 37 38 33 36 31 30 68 0F 30 0D A3 0B 82 09 32 32 38 36 35 33 33 39 38 65 09 30 07 80 05 23 23 37 30 30 7E 12 A0 10 18 0E 32 30 31 36 30 33 30 34 31 35 32 32 34 30
buf:Tagged [1] IMPLICIT
Sequence
Integer(24505)
Integer(21)
DER Sequence
DER ApplicationSpecific[21] (0191)
Tagged [0]
Tagged [4] IMPLICIT
Sequence
DER ApplicationSpecific[11]
Sequence
Tagged [0] IMPLICIT
DER Octet String[4]
Tagged [1]
DER Sequence
Tagged [0] IMPLICIT
DER Octet String[2]
DER ApplicationSpecific[3]
Sequence
Tagged [0] IMPLICIT
DER Octet String[2]
DER ApplicationSpecific[1]
Sequence
Tagged [0] IMPLICIT
DER Octet String[10]
DER ApplicationSpecific[2]
Sequence
Tagged [0] IMPLICIT
DER Octet String[2]
DER ApplicationSpecific[4]
Tagged [7] IMPLICIT
DER Octet String[0]
DER ApplicationSpecific[11]
Sequence
Tagged [0] IMPLICIT
DER Octet String[4]
Tagged [1]
DER Sequence
Tagged [0] IMPLICIT
DER Octet String[5]
DER ApplicationSpecific[14] (02)
DER Enumerated(22)
DER ApplicationSpecific[7]
Sequence
Tagged [3]
Tagged [1] IMPLICIT
DER Octet String[12]
DER ApplicationSpecific[8]
Sequence
Tagged [3]
Tagged [2] IMPLICIT
DER Octet String[9]
DER ApplicationSpecific[5]
Sequence
Tagged [0] IMPLICIT
DER Octet String[5]
DER ApplicationSpecific[30]
Tagged [0]
GeneralizedTime(20160304152240GMT+01:00)
and I am trying to parse it:
protected void parse() {
logger.trace("Executing parse()");
try {
ASN1InputStream input = new ASN1InputStream(asn1Data);
ASN1Primitive p;
if ((p = input.readObject()) != null) {
ASN1TaggedObject o1 = ASN1TaggedObject.getInstance(p);
ASN1Sequence s1 = ASN1Sequence.getInstance(o1.getObject());
invokeID = Integer.parseInt(s1.getObjectAt(0).toString());
operationValue = Integer.parseInt(s1.getObjectAt(1).toString());
DERSequence ders = (DERSequence) DERSequence.getInstance(s1.getObjectAt(2));
DERApplicationSpecific das = (DERApplicationSpecific) ders.getObjectAt(0);
crossRefIdentifier = das.getContents();
//here are some experiments, but can't get the right objects I could parse / walk through
ASN1TaggedObject o2 = ASN1TaggedObject.getInstance(ders.getObjectAt(1));
DERTaggedObject dto = (DERTaggedObject) o2.getObject();
ASN1Sequence s2 = ASN1Sequence.getInstance(dto.getObject());
DERApplicationSpecific das1 = (DERApplicationSpecific) s2.getObjectAt(0);
ASN1Sequence s3 = (ASN1Sequence) das1.getObject();
}
} catch (Exception ex) {
logger.warn("exception while parsing ASN1 data", ex);
}
}
as you can see I've been able to decode some basic tags (ie. invokeID, operation-value and crossRefIdentifier) but can't get deeper in the tree (callId, callingNumber ...).
If you have experience in that matter I would appreciate your help.
It is a very complex ASN.1 object. There are several places where it could go wrong with another sample due to structure differences. Here is an example.
using System;
using System.Collections;
using System.IO;
using Org.BouncyCastle.Asn1;
namespace Asn1ParseBouncy
{
class Program
{
static void Main(string[] args)
{
var bytes2Parse = File.ReadAllBytes(#"c:\a.req");
// (0,169) CONTEXT SPECIFIC(1)
DerTaggedObject rootObj = (DerTaggedObject)Asn1Object.FromByteArray(bytes2Parse);
if (rootObj.TagNo == 1)
{
ParseAtRootLevel(rootObj);
}
else
{
throw new Exception("Expected Tag number to be 1");
}
}
private static void ParseAtRootLevel(DerTaggedObject rootObj)
{
// SEQUENCE under CONTEXT SPECIFIC(1)
var seq = (Asn1Sequence)rootObj.GetObject();
IEnumerator e = seq.GetEnumerator();
bool hasNext;
hasNext = e.MoveNext();
// (3,2) INTEGER -> invokeID
{
Asn1Encodable obj;
if (!hasNext)
{
throw new Exception("more entries expected in sequence");
}
obj = (Asn1Encodable)e.Current;
// TODO: put in a property of class that represents whole ASN.1 message
var invokeID = DerInteger.GetInstance(obj);
hasNext = e.MoveNext();
}
// (7,1) INTEGER -> operation-value
{
Asn1Encodable obj;
if (!hasNext)
{
throw new Exception("more entries expected in sequence");
}
obj = (Asn1Encodable)e.Current;
// TODO: put in a property of class that represents whole ASN.1 message
var operationValue = DerInteger.GetInstance(obj);
hasNext = e.MoveNext();
}
// (10,159) SEQUENCE -> argument
{
Asn1Encodable obj;
if (!hasNext)
{
throw new Exception("more entries expected in sequence");
}
obj = (Asn1Encodable)e.Current;
var argumentSeq = Asn1Sequence.GetInstance(obj);
// argumentData is parsed asn.1 object - argument
var argumentData = ParseArgumentData(argumentSeq);
hasNext = e.MoveNext();
}
if (hasNext)
{
throw new Exception("no more entries expected in sequence");
}
}
private static object ParseArgumentData(Asn1Sequence argumentSeq)
{
IEnumerator e = argumentSeq.GetEnumerator();
bool hasNext;
hasNext = e.MoveNext();
// (13,2) APPLICATION (21) -> crossRefIdentifier
{
Asn1Encodable obj;
if (!hasNext)
{
throw new Exception("more entries expected in sequence");
}
obj = (Asn1Encodable)e.Current;
var crossRefIdentifierAppSpecific = (DerApplicationSpecific)obj;
if (crossRefIdentifierAppSpecific.ApplicationTag != 21)
throw new Exception("Expected application tag 21");
// TODO: put in a property of class that represents whole ASN.1 message
var crossRefIdentifier = crossRefIdentifierAppSpecific.GetContents();
hasNext = e.MoveNext();
}
// (17,152) CONTEXT SPECIFIC (0) -> eventSpecificInfo.callControlEvents.delivered
{
Asn1Encodable obj;
if (!hasNext)
{
throw new Exception("more entries expected in sequence");
}
obj = (Asn1Encodable)e.Current;
var eventSpecificInfo = ((DerTaggedObject)obj);
if (eventSpecificInfo.TagNo != 0)
throw new Exception("Expected Context specific tag number to be 0");
// TODO: put in a property of class that represents whole ASN.1 message
var eventSpecificInfoData = ParseEventSpecificInfo(eventSpecificInfo);
hasNext = e.MoveNext();
}
if (hasNext)
{
throw new Exception("no more entries expected in sequence");
}
// TODO: return parsed values in some class
return null;
}
private static object ParseEventSpecificInfo(DerTaggedObject obj)
{
// still (17,152) CONTEXT SPECIFIC (0)
var connectionBothData = ParseConnectionBoth(obj);
return connectionBothData;
}
private static object ParseConnectionBoth(DerTaggedObject connectionBoth)
{
// (20,149) CONTEXT SPECIFIC (4)->connection.both
var connectionBothTagged = (DerTaggedObject)connectionBoth.GetObject();
if (connectionBothTagged.TagNo != 4)
throw new Exception("Expected Context specific tag number to be 4");
// Sequence under (20,149) CONTEXT SPECIFIC (4) -> connection.both
var connectionBothSeq = (DerSequence)connectionBothTagged.GetObject();
IEnumerator e = connectionBothSeq.GetEnumerator();
bool hasNext;
hasNext = e.MoveNext();
// callID
{
Asn1Encodable obj;
if (!hasNext)
{
throw new Exception("more entries expected in sequence");
}
obj = (Asn1Encodable)e.Current;
// (23,16) APPLICATION (11)
// TODO: put in a property of class that represents whole ASN.1 message
var callIDTagged = (DerApplicationSpecific)obj;
if (callIDTagged.ApplicationTag != 11)
throw new Exception("Expected tag number 11");
// (25,14) SEQUENCE
var callIdSeq = callIDTagged.GetObject().GetDerEncoded();
// TODO: parse CallIdSeq -> (27,4) ContextSpecific(0), (33,6) ContextSpecific(1)
hasNext = e.MoveNext();
}
// TODO: continue with (41,6) Application (3)
// TODO: continue with (49,14) Application (1)
// TODO: continue with (65,6) Application (2)
// etc.
// TOOD: return something useful
throw new NotImplementedException();
}
}
}
I placed notes in the code sample according to dump from ASN.1 Editor
You could make your life a lot easier if you had ASN.1 definition of the object. With help from Binary Notes you could generate classes to parse ASN.1 data object.
I am aware that most of the Base64 encoding has == at the end. Is there any other which uses does the same?
For example, I found this:
nijdRcCHIUnketWzFbcxmvqQKKDnFW05LSE3ttTjoqyBna7JT87AwxeKdoOszXYODMRm6UfA8jK97qgV8A==
But it is not a Base64 kind. What else can it be?
The string you have posted is a valid Base64 string.
A Base64 string will end with == if and only if the number of bytes it encodes, mod 3, equals 1.
>>> for i in range(10):
... print(i, base64.b64encode(b"\x00"*i))
...
0 b''
1 b'AA=='
2 b'AAA='
3 b'AAAA'
4 b'AAAAAA=='
5 b'AAAAAAA='
6 b'AAAAAAAA'
7 b'AAAAAAAAAA=='
8 b'AAAAAAAAAAA='
9 b'AAAAAAAAAAAA'
Do you see the pattern?
It happens that 16-byte (128-bit) encryption keys are very commonly encoded in Base64, and since 16 mod 3 = 1, their encoding will end with ==. But your string, decoded, is 61 bytes (488 bits) long. That is too big to be most sorts of encryption key, and too small to be an RSA key.
This is your string, decoded, and then hexdumped:
00000000 9e 28 dd 45 c0 87 21 49 e4 7a d5 b3 15 b7 31 9a |.(.E..!I.z....1.|
00000010 fa 90 28 a0 e7 15 6d 39 2d 21 37 b6 d4 e3 a2 ac |..(...m9-!7.....|
00000020 81 9d ae c9 4f ce c0 c3 17 8a 76 83 ac cd 76 0e |....O.....v...v.|
00000030 0c c4 66 e9 47 c0 f2 32 bd ee a8 15 f0 |..f.G..2.....|
0000003d
I don't see anything in there to tell me what it actually is, and file(1) is also stumped. It could be random enough to be encrypted, but I can't tell for sure by eye. (And if it is random, that doesn't mean it's encrypted! It could just be the output of a random number generator.)
It is important to understand that Base64 is not encryption, because it has no key. I didn't need to know or guess any piece of secret information to reverse the Base64 encoding of your string. (The term 'encoding' can be confusing — it is more general. UTF-8, Base64, and DEFLATE are all encodings, and so is AES-CBC, but of all of them, only AES-CBC is encryption.)
I'm trying to call an API using Network.HTTP.Client and am trying to figure out how to properly handle a TlsNotSupported exception and call the API over SSL. There are no examples in the documentation and there are not (surprisingly) any examples I can find elsewhere on the web.
Here is my existing code:
module Main where
import Network.URL
import qualified Network.URI as URI
import qualified Network.HTTP as HTTP
import qualified Data.ByteString as BS
import qualified Data.ByteString.Lazy as LBS
import qualified Data.ByteString.Base64 as B64
import qualified Network.HTTP.Client as HTTPClient
import qualified Network.HTTP.Types.Header as HTTPHeaders
import qualified Data.ByteString.Char8 as C
import qualified Network.HTTP.Types.Status as HTTPStatus
import qualified Data.Text as T
import qualified Control.Exception as E
import qualified Data.Text.Encoding as TE
import Data.Aeson
import Control.Applicative ((<*>), (<$>), pure)
import Control.Monad (mzero)
data Bookmark = Bookmark {
url :: T.Text,
title :: Maybe T.Text
} deriving Show
data Note = Note {
author :: T.Text,
text :: T.Text
} deriving Show
instance FromJSON Bookmark where
parseJSON (Object v) = Bookmark <$>
v .: T.pack "href" <*>
v .: T.pack "description"
parseJSON _ = mzero
b64Encode :: String -> String
b64Encode = T.unpack . TE.decodeUtf8 . B64.encode . TE.encodeUtf8 . T.pack
basicAuthHeader :: String -> String -> String
basicAuthHeader username password = "Authorization: " ++
b64Encode (username ++ ":" ++ username)
postsURL token = "https://api.pinboard.in/posts/all?format=json&auth_token=" ++ token
parse :: BS.ByteString -> Maybe [Bookmark]
parse response = decode (LBS.fromStrict response)
transform = LBS.fromStrict . C.pack
errorHandler :: HTTPClient.HttpException -> IO (Maybe a)
errorHandler (HTTPClient.StatusCodeException status _ _) = return Nothing
errorHandler (HTTPClient.InvalidUrlException _ _) = return Nothing
errorHandler (HTTPClient.HttpParserException _) = return Nothing
errorHandler e = do
case e of
HTTPClient.TlsNotSupported -> (putStrLn $ "Bummer. " ++ show e) >> return Nothing
main = do
putStrLn "Enter auth token: "
token <- getLine
manager <- HTTPClient.newManager HTTPClient.defaultManagerSettings
request <- HTTPClient.parseUrl $ postsURL token
putStrLn $ "Calling " ++ postsURL token
response <- (Just <$> HTTPClient.httpLbs request manager) `E.catch` errorHandler
return ()
Here's an example session:
$ runhaskell Pinboard.hs
Enter auth token:
blah
Calling https://api.pinboard.in/posts/all?format=json&auth_token=asd
Bummer. TlsNotSupported
Thanks in advance!
You need to use http-client-tls. In particular, replace your usage of defaultManagerSettings with tlsManagerSettings.
Calling https://api.pinboard.in/posts/all?format=json&auth_token=asd
Bummer. TlsNotSupported
I'm getting a different result. It appears TLS is supported.
Are you using Gandi Standard SSL CA or UTN-USERFirst-Hardware as a trust anchor?
$ echo -e "GET /posts/all?format=json&auth_token=asd HTTP/1.1\r\nHost:api.pinboard.in\r\n\r\n" | \
openssl s_client -tls1 -connect api.pinboard.in:443 -servername api.pinboard.in -ign_eof
CONNECTED(00000003)
depth=1 C = FR, O = GANDI SAS, CN = Gandi Standard SSL CA
verify error:num=20:unable to get local issuer certificate
verify return:0
---
Certificate chain
0 s:/OU=Domain Control Validated/OU=Gandi Standard Wildcard SSL/CN=*.pinboard.in
i:/C=FR/O=GANDI SAS/CN=Gandi Standard SSL CA
1 s:/C=FR/O=GANDI SAS/CN=Gandi Standard SSL CA
i:/C=US/ST=UT/L=Salt Lake City/O=The USERTRUST Network/OU=http://www.usertrust.com/CN=UTN-USERFirst-Hardware
---
Server certificate
-----BEGIN CERTIFICATE-----
MIIE4zCCA8ugAwIBAgIRAJhTQpn18jrbs6EACUBuCEEwDQYJKoZIhvcNAQEFBQAw
QTELMAkGA1UEBhMCRlIxEjAQBgNVBAoTCUdBTkRJIFNBUzEeMBwGA1UEAxMVR2Fu
ZGkgU3RhbmRhcmQgU1NMIENBMB4XDTEzMDgwNTAwMDAwMFoXDTE1MDkwMzIzNTk1
OVowYTEhMB8GA1UECxMYRG9tYWluIENvbnRyb2wgVmFsaWRhdGVkMSQwIgYDVQQL
ExtHYW5kaSBTdGFuZGFyZCBXaWxkY2FyZCBTU0wxFjAUBgNVBAMUDSoucGluYm9h
cmQuaW4wggEiMA0GCSqGSIb3DQEBAQUAA4IBDwAwggEKAoIBAQCmwELG8RLaC+PD
YRSORwf5dZ+OGDFNnot9It+nEJh3Y9e95y2xxfQQnMfGcrj4UdMNx6Vie2Baz3yD
hU6hweT5ZlpWA43u7xoF2DP5NsgktcTZzQ+IZ124uVvDs+Q5LAH6/aHUplzmdZDz
xDM9JSz8pxXmS9HSJmR1tamYi9B+d30/yxHPibe62Ku6FQ4yoa+f2GVGdvqqvvpZ
7gbwBgu6PKLVNRQPrhUdjdgEj0h44/4DZ/sUw3Jw2cti0yELh4eDLgXonvqCUrOQ
79NJTEzqBBHBUER0ltdUbCXczAm5IQk4pTzSUaI5rML/fcphBaWh0t0XSg9cMFAl
biNqpr9tAgMBAAGjggG0MIIBsDAfBgNVHSMEGDAWgBS2qP+iqC/Qps1LsWjz51AQ
Mad5ITAdBgNVHQ4EFgQUzuFnVq27SbN342v9mBiEc7tygJIwDgYDVR0PAQH/BAQD
AgWgMAwGA1UdEwEB/wQCMAAwHQYDVR0lBBYwFAYIKwYBBQUHAwEGCCsGAQUFBwMC
MGAGA1UdIARZMFcwSwYLKwYBBAGyMQECAhowPDA6BggrBgEFBQcCARYuaHR0cDov
L3d3dy5nYW5kaS5uZXQvY29udHJhY3RzL2ZyL3NzbC9jcHMvcGRmLzAIBgZngQwB
AgEwPAYDVR0fBDUwMzAxoC+gLYYraHR0cDovL2NybC5nYW5kaS5uZXQvR2FuZGlT
dGFuZGFyZFNTTENBLmNybDBqBggrBgEFBQcBAQReMFwwNwYIKwYBBQUHMAKGK2h0
dHA6Ly9jcnQuZ2FuZGkubmV0L0dhbmRpU3RhbmRhcmRTU0xDQS5jcnQwIQYIKwYB
BQUHMAGGFWh0dHA6Ly9vY3NwLmdhbmRpLm5ldDAlBgNVHREEHjAcgg0qLnBpbmJv
YXJkLmluggtwaW5ib2FyZC5pbjANBgkqhkiG9w0BAQUFAAOCAQEAn9i7ilujiOhL
QKMAAuS7xWTvERddqjnnOPBwUw7FCd+VaEnpNCCjqxwrTdWjm4MkjtN2HfDesw1c
IqpLVAMNn35m3aqu7fvyCbBKCkjXLnj1TuKsd/IIFJuqgHNjqyvfe6IIW/Mss+Qq
2TUmVF0HLF2+fyihsdYTlqcv5bR/X7dwbFi1xecoMaDf6K8TTiEKjmr2wNWuKRGy
TsWTqMPBPkyHBJPL589/ETBvqvx1cu/CU81hiadlVino/Buha0cDjNYZra2gOfRR
U2+vK9tsN9Ct0lfOYAamHSiIwGD1HfzdV8xItmZSNsLm3jBZQ1bsqN82+9n2CKWG
IS0WjmfyaA==
-----END CERTIFICATE-----
subject=/OU=Domain Control Validated/OU=Gandi Standard Wildcard SSL/CN=*.pinboard.in
issuer=/C=FR/O=GANDI SAS/CN=Gandi Standard SSL CA
---
No client certificate CA names sent
---
SSL handshake has read 3332 bytes and written 438 bytes
---
New, TLSv1/SSLv3, Cipher is DHE-RSA-AES256-SHA
Server public key is 2048 bit
Secure Renegotiation IS supported
Compression: NONE
Expansion: NONE
SSL-Session:
Protocol : TLSv1
Cipher : DHE-RSA-AES256-SHA
Session-ID: C4D0B1D4C4DA50734AFA09A3675A9A6828053B022A516F53E6C2BEA303C49AFC
Session-ID-ctx:
Master-Key: 34A2E6C6B1D17AE7214380462438E9C670CA1E8F9E719D0DEFB7EDE1EC87D847D1DF317523BAAE05278A10E1EDAE51C5
Key-Arg : None
PSK identity: None
PSK identity hint: None
SRP username: None
TLS session ticket lifetime hint: 300 (seconds)
TLS session ticket:
0000 - 15 99 b9 ce d4 d9 bc 6f-d5 4b 12 83 cd 6f eb b0 .......o.K...o..
0010 - f6 37 a3 66 21 ea ff d1-cf 73 56 fa 25 99 61 1c .7.f!....sV.%.a.
0020 - 38 15 a6 e9 e8 47 cc f8-2b df 8d 64 68 13 1c be 8....G..+..dh...
0030 - 8d 8a 32 a5 ca dd 79 d7-f6 d0 0c 1e e4 50 01 64 ..2...y......P.d
0040 - 73 3e 9f 34 42 3d 4d 56-a3 cc 09 d8 aa 7b 2a 82 s>.4B=MV.....{*.
0050 - 5d 96 c3 1f 3e 19 48 c7-90 c6 4c 07 75 15 e5 42 ]...>.H...L.u..B
0060 - 13 31 c1 fc b4 cc 5f 8e-0b a1 cd 5f bc 7a 16 9c .1...._...._.z..
0070 - 24 3c 5b e7 52 97 ce 15-4f b1 01 44 dc 72 35 82 $<[.R...O..D.r5.
0080 - 4e c9 f9 19 69 26 1c 82-44 f5 c0 6a 57 99 54 da N...i&..D..jW.T.
0090 - cf a8 f4 6f 6b ab c6 ec-98 c6 91 31 d1 20 5c 5c ...ok......1. \\
00a0 - 0f 94 42 5a 8f f5 f7 0d-cd 31 71 04 66 89 5f c1 ..BZ.....1q.f._.
00b0 - 00 84 cd 9e c1 99 52 4f-c0 1e 43 25 f2 36 b9 28 ......RO..C%.6.(
Start Time: 1408986495
Timeout : 7200 (sec)
Verify return code: 20 (unable to get local issuer certificate)
---
HTTP/1.1 403 Forbidden
Date: Mon, 25 Aug 2014 17:07:55 GMT
Server: Apache/2.2.22 (Ubuntu)
Vary: Accept-Encoding
Content-Length: 292
Content-Type: text/html; charset=iso-8859-1
<!DOCTYPE HTML PUBLIC "-//IETF//DTD HTML 2.0//EN">
<html><head>
<title>403 Forbidden</title>
</head><body>
<h1>Forbidden</h1>
<p>You don't have permission to access /posts/all
on this server.</p>
<hr>
<address>Apache/2.2.22 (Ubuntu) Server at api.pinboard.in Port 80</address>
</body></html>
read:errno=0
riemann::~$
I'm trying to implement certificate signature verification on a Microchip pic controller (certificates are generated and signed using OpenSSL). The Microchip PIC controller doesn't support OpenSSL libraries, but it does have an encryption/decryption function. I was successful in getting a SSL connection between PIC controller and a web server. My next step is to setup signature verification on the PIC controller.
After reading PKCS#1 V2.1 RSA Cryptography Standard (http://www.rsa.com/rsalabs/node.asp?id=2125)
I realized that encryption is essentially the same as signature verification and decryption is the same as signing. More specifically both encryption and verification uses the public key and the following formula:
m = s ^ e mod n
Where s is the signature or the message, e is the public exponent, n is the modulus and m is the encrypted message or decoded signature. Therefore, I'm trying to use the encryption algorithm provided to perform signature verification.
In order to verify the certificate, I generated the SHA1 hash of the certificate; Decoded signature using CA's public key and encryption algorithm. Remove the padding from the decoded signature, the result hash should be equal to the SHA1 hash of the certificate.
However, I cannot get the two hash values to be equal. I tried to verify my assumption and PIC controller results using OpenSSL command line.
This is the hash value I got from both OpenSSL command line and PIC controller
openssl rsautl -in signature.txt -verify -asn1parse -inkey pubkey.pem
-pubin
db e8 c6 cb 78 19 3c 0f-fd 96 1c 4f ed bd b2 34 45 60 bf 65
This is what I got from Signature verification using OpenSSL. After removing "ff" paddings I'll end up with asn1 format of the certificate hash.
openssl rsautl -verify -in signature.txt -inkey pubkey.pem -pubin
-raw -hexdump
00 01 ff ff ff ff ff ff-ff ff ff ff 00 30 21 30
09 06 05 2b 0e 03 02 1a-05 00 04 14 db e8 c6 cb
78 19 3c 0f fd 96 1c 4f-ed bd b2 34 45 60 bf 65
However this is what I got from the PIC controller which is much different from the above
8e fb 62 0e 09 c8 0b 49 40 1f 4d 2d a7 7d d6 8c
9b bc 95 e6 bc 98 4b 96 aa 74 e5 68 90 40 bf 43
b5 c5 02 6d ab e3 ad 7b e6 98 fd 10 22 af b9 fb
This is my signature
7951 9b3d 244a 37f6 86d7 dc02 dc18 3bb4
0f66 db3a a3c1 a254 5be5 11d3 a691 63ef
0cf2 ec59 c48b 25ad 8881 9ed2 5230 bcd6
This is my public key (I'm using a very small key just for testing, will make it larger once everything works)
96 FE CB 59 37 AE 8C 9C 6C 7A 01 50 0F D6 4F B4
E2 EC 45 D1 88 4E 1F 2D B7 1E 4B AD 76 4D 1F F1
B0 CD 09 6F E5 B7 43 CA F8 14 FE 31 B2 06 F8 7B
Exponent is 01 00 01
I'm wondering are my assumptions wrong that I cannot use encryption algorithm for decoding signature? or I'm doing something else wrong?
It turned out the method I described above is correct. I was able to get the matching result from hashing the certificate and unsigning the signature using encryption.
The problem that caused my previous failing attempts was the endianess used by Microchip Pic controller. They use small-endian instead of big-endian. I did not pay attention to the endianness of the exponent since 01 00 01 is the same in either format. However I was wrong, it turns out Microchip looks at a 4 byte value as the exponent (RSA standard??). So it pads 00 in the front resulting 00 01 00 01. Therefore, the endianness matters now since 00 01 00 01 is different from 01 00 01 00. And 01 00 01 00 is the small-endian format that Microchip Pic uses.