I've been using the http://www.youtube-mp3.org/ to obtain download links for mp3 songs for a "internet radio" project (download file stream and output it). Lately the http://www.youtube-mp3.org/ seemed to changed it's workflow. Till now I was able to obtain the proper format of the url:
http://www.youtube-mp3.org/get?ab=128&video_id=KMU0tzLwhbE&h=5671e7d33d4eccb5b89ea8f54d9911d4&r=1387110278658.1527580295
Whereas r=timestamp
I did the timestamp myself as epoch. This worked well without the .1527580295 suffix.
Unfortunetly this seems to be needed as the link without the suffix redirects back to the conversion site.
Up till now the algorithim was as mentioned:
Youtube mp3 org linq acquiring explained
http://www.youtube-mp3.org/api/itemInfo/?video_id=#{video_id}&ac=www&r=#{Time.now.to_i}
Does any one know how to obtain the suffix part?
//Hey,
I'm not much of an web programming guy ... but this is something I have surely overlooked. Thanks!
Added this to my small C# API.
<!-- language: c# -->
const int AM = 65521;
int GetCCSufix(string a)
{
int c = 1, b = 0, d, e;
var chars = a.ToCharArray();
for(e =0; e< chars.Length; e ++)
{
d = chars[e];
c = (c + d) % AM;
b = (b + c) % AM;
}
return b << 16 | c;
}
Works great ;)
I don't know what this suffix means but reading the client javascript file from youtube-mp3.org you can learn how to generate it.
var __AM=65521; // the largest prime less than 2^16...
function cc(a){
var c = 1, b = 0, d, e;
for(e = 0; e < a.length; e++){
d = a.charCodeAt(e);
c = (c+d)%__AM;
b = (b+c)%__AM;
}
return b<<16|c;
}
The generated media URL is :
"http://www.youtube-mp3.org/get?ab=128&video_id="+video_id+"&h="+info.h+"&r="+timeNow+"."+cc(video_id+timeNow)
with
var timeNow = Date.now().toString();
Related
Sorry to completely rewrite this post, but I was way off on my troubleshooting. Hopefully this prevents someone's headache in the future...
Now, for example code.
function compare(uint8 a, uint8 b)
private
requireUnpaused
returns(bool)
{
// increment a by 1
a = a++;
// if a + 1 = 3, then loop it around the cycle to be 0
if (a >= 3) {
a = 0;
}
// compare a to b. If a = b, a is the winner - return true
return a == b ? true : false;
}
This code works as expected if I replace "a = a++" with "a = ++a", just "a++" or "++a", or even "a = a + 1".
This drove me crazy for a few days. My question for someone more versed in this than I: Why does "a = a++" not work, when every other way of incrementing "a" by 1 does?
Hey guys I'm new to Game Maker Studio and new to the language. I'm making a game and have been working on the dialogue system.
This chunk of code was designed for characters respond to a set of choices, the dialogue starts by printing out the first element of the line_array, which it does, then give the player the choice of two responses from the response_array, which it insteads prints out the second element of the line_array and I don't understand why.
Does an argument only hold one element of an array? I'm initializing two arrays in an object oCivilian2 and pushing them through code DialogueCode which is linked to another object oRespond that supposed to allow me to sift through dialogue in game. Anything helps thanks
It's initialized here in create of oCivilian2
line_array = [3];
line_array[0] = "Ethan it's good to see you! \n I thought after the incident well.... \n well I thought we had lost you";
line_array[1] = "I've said too much";
line_array[2] = "You hit your head trying to saver her\n It was horrible";
response_array = [2];
response_array[0] = "What happened?";
response_array[1] = "I don't recall alot. How bad was it?";
counter = 0;
x1 = RESOLUTION_W / 2;
y1 = RESOLUTION_H -70;
x2 = RESOLUTION_W/2;
y2 = RESOLUTION_H;
_print = "";
responseSelected = 0;
Then the step which links it to DialogueCode when spacebar is pressed
keyActivate = keyboard_check_pressed(vk_space);
if (keyActivate)
{
var inst = collision_rectangle(oPlayer.x+3,oPlayer.y+3,oPlayer.x-3,oPlayer.y-3, oCivilian2, false, false);
if (inst != noone)
{
ScriptExecuteArray(DialogueCode, line_array);
ScriptExecuteArray(DialogueCode, response_array);
}
}
Then through to step in the object oRespond
lerpProgress += (1 - lerpProgress) / 50;
textProgress += global.textSpeed;
x1 = lerp(x1, x1Target,lerpProgress);
x2 = lerp(x2, x2Target,lerpProgress);
keyUp = (keyboard_check_pressed(vk_up)) || (keyboard_check_pressed(ord("W")))
keyDown = keyboard_check_pressed(vk_down) || keyboard_check_pressed(ord("S"));
responseSelected += (keyDown - keyUp);
var _max = 2;
var _min = 0;
if (responseSelected > _max) responseSelected = _min;
if (responseSelected < _min) responseSelected = _max;
for (var i = 0; i < 2; i++)
{
var _marker = string_pos(",", response);
if (string_pos(",",response))
{
responseScript[i] = string_copy(response,0,_marker);
string_delete(response,0,_marker);
var _marker = string_pos(",", response);
}
else
{
responseScript[i] = string_copy(response,0, string_length(response));
}
}
if (keyboard_check_pressed(vk_space))
{
counter++;
}
Then to print in oRespond
/// text
//response
NineSliceBoxStretched(sTextBox, x1,y1,x2,y2, 0);
draw_set_font(fText);
draw_set_halign(fa_center);
draw_set_valign(fa_top);
draw_set_color(c_black);
if (counter % 2 == 0)
{
var _i = 0;
var _print = string_copy(text,1,textProgress);
draw_text((x1+x2) / 2, y1 + 8, _print);
draw_set_color(c_white);
draw_text((x1+x2) / 2, y1 + 7, _print);
_i++;
}
else
{
if (array_length_1d(responseScript) > 0)
{
var _print = "";
for (var t = 0; t < array_length_1d(responseScript); t++)
{
_print += "\n";
if (t == responseSelected) _print += "--> "
_print += responseScript[t];
show_debug_message(responseScript[t]);
if (t == responseSelected) _print += " <-- "
}
draw_text((x1+x2) / 2, y1 + 8, _print);
draw_set_color(c_white);
draw_text((x1+x2) / 2, y1 + 7, _print);
}
}
Alright, i think to see many problems with your code.
First of all, since arrays in GM are dynamic declare them like
line_array[3]
is a bad practice (in my point of view)
I've never declared an array this way in GM so that could be the problem here.
Second, i don't really understand the logic of your code, always create objects, at least in the GM environment, that corresponds to "physical" entities, i would make an object for the Civilian but not for the "respond".
I've red your code a lot of times and since no one answered you in 3 months i can assume it's because no one can really understand your way of coding, and this way of coding will probably give you a lot of problems in future. The thing that you're trying to doing could be super-easy if done with a good hierarchy.
I would like to help u with this code, but i find it very chaotic.
If you've not resolved this problems, write a comment :)
I advice you to fully re-implement it even if resolved anyway.
I'm reading PDF by iTextSharp-5.5.7.0, PdfTextExtractor.GetTextFromPage() works well in most of files until this: sample PDF
I can't read any number from it, for example: only return 'ANEU' from 'A0NE8U', they are fine in Adobe Reader to copy out. Code is here:
public static string ExtractTextFromPdf(string path)
{
using (PdfReader reader = new PdfReader(path))
{
StringBuilder text = new StringBuilder();
for (int i = 1; i <= reader.NumberOfPages; i++)
{
text.Append(PdfTextExtractor.GetTextFromPage(reader, i));
}
return text.ToString();
}
}
The font in question has a ToUnicode map which is used for text extraction. Unfortunately, though, iText(Sharp) reads it only partially, and digits are located after the mappings read.
In detail:
The cause for the issue is the implementation of AbstractCMap.addRange (I'm showing the iText Java code as iText also has this issue and I'm more into the Java version):
void addRange(PdfString from, PdfString to, PdfObject code) {
byte[] a1 = decodeStringToByte(from);
byte[] a2 = decodeStringToByte(to);
if (a1.length != a2.length || a1.length == 0)
throw new IllegalArgumentException("Invalid map.");
byte[] sout = null;
if (code instanceof PdfString)
sout = decodeStringToByte((PdfString)code);
int start = a1[a1.length - 1] & 0xff;
int end = a2[a2.length - 1] & 0xff;
for (int k = start; k <= end; ++k) {
a1[a1.length - 1] = (byte)k;
PdfString s = new PdfString(a1);
s.setHexWriting(true);
if (code instanceof PdfArray) {
addChar(s, ((PdfArray)code).getPdfObject(k - start));
}
else if (code instanceof PdfNumber) {
int nn = ((PdfNumber)code).intValue() + k - start;
addChar(s, new PdfNumber(nn));
}
else if (code instanceof PdfString) {
PdfString s1 = new PdfString(sout);
s1.setHexWriting(true);
++sout[sout.length - 1];
addChar(s, s1);
}
}
}
The loop only considers the range in the least significant byte of from and to. Thus, for the range in question:
1 beginbfrange
<0000><01E1>[
<FFFD><FFFD><FFFD><0020><0041><0042><0043><0044>
<0045><0046><0047><0048><0049><004A><004B><004C>
...
<2248><003C><003E><2264><2265><00AC><0394><03A9>
<00B5><03C0><00B0><221E><2202><222B><221A><2211>
<220F><25CA>]
endbfrange
it only iterates from 0x00 to 0xE1, i.e. only the first 226 entries of the 482 mappings.
There actually are some peculiar restrictions in CMaps, e.g. there may only be up to 100 separate bfrange entries in the same section, and in the alternative bfrange entry syntax
n beginbfrange
srcCode1 srcCode2 dstString
endbfrange
which is handled by the same method addRange, there is the restriction
When defining ranges of this type, the value of the last byte in the string shall be less than or equal to 255 − (srcCode2 − srcCode1).
Probably a misunderstanding of this restriction made the developer believe, srcCode2 and srcCode1 also would merely differ in the least significant byte.
But maybe there are even more restrictions which I merely did not find...
Meanwhile (as of iText 5.5.9, tested against a development SNAPSHOT) this issue seems to have been fixed.
I am a newbie in programming and i start with Objective C as my first language.
I am messing around with some books and tutorials, at last programing a calculator...
Everything fine and i am getting into (programming makes really fun)
Now i am asking myself how I could translate arabic numbers to chinese numbers
(e.g. arabic 4 is in chinese 四 and 8 is 八 which means 四 + 四 = 八
The chinese number system is kind of different than arabic they have signs for 100, 1000, 10000 and ja kind of twisted, which screws up my brain ... anyway do anybody have some advice, hints, tips or solutions how i can tell the computer how to work with this numbers, or even how to calculate with them?
I think everything is possible so i wont ask "If its even possible?"
Considering the Chinese numerical system (Mandarin) as described by wikipedia http://en.wikipedia.org/wiki/Chinese_numerals, where for instance:
45 is interpreted as [4] [10] [5] and written 四十五
114 is interpreted as [1] [100] [1] [10] [4] and written 一百一十四
So the trick is to decompose a number as powers of 10:
x = c(k)*10^k + ... + c(1)*10 + c(0)
where k is the largest power of 10 that divides x such that the quotient is at least 1. In the 2nd example above, 114 = 1*10^2 + 1*10 + 4.
This x = c(k)*10^k + ... + c(1)*10 + c(0) becomes [c(k)][10^k]...[c(1)][10][c(0)]. In the 2nd example again, 114 = [1] [100] [1] [10] [4].
Then map each number within bracket to the corresponding sinogram:
0 = 〇
1 = 一
2 = 二
3 = 三
4 = 四
5 = 五
6 = 六
7 = 七
8 = 八
9 = 九
10 = 十
100 = 百
1000 = 千
10000 = 万
As long as you keep track of the [c(k)][10^k]...[c(1)][10][c(0)] form, it's easy to convert to an integer that the computer can handle or to the corresponding Chinese numeral. So it's this [c(k)][10^k]...[c(1)][10][c(0)] form that I'd store in an integer array of size k+2.
I'm not familiar with Objective-C, thus I can't help you with a solution for iOS.
Nonetheless, following is the Java code for Android...
I assumed it might help you, as well as it helped me.
double text2double(String text) {
String[] units = new String[] { "〇", "一", "二", "三", "四",
"五", "六", "七", "八", "九"};
String[] scales = new String[] { "十", "百", "千", "万",
"亿" };
HashMap<String, ScaleIncrementPair> numWord = new HashMap<String, ScaleIncrementPair>();
for (int i = 0; i < units.length; i++) {
numWord.put(units[i], new ScaleIncrementPair(1, i));
}
numWord.put("零", new ScaleIncrementPair(1, 0));
numWord.put("两", new ScaleIncrementPair(1, 2));
for (int i = 0; i < scales.length; i++) {
numWord.put(scales[i], new ScaleIncrementPair(Math.pow(10, (i + 1)), 0));
}
double current = 0;
double result = 0;
for (char character : text.toCharArray()) {
ScaleIncrementPair scaleIncrement = numWord.get(String.valueOf(character));
current = current * scaleIncrement.scale + scaleIncrement.increment;
if (scaleIncrement.scale > 10) {
result += current;
current = 0;
}
}
return result + current;
}
class ScaleIncrementPair {
public double scale;
public int increment;
public ScaleIncrementPair(double s, int i) {
scale = s;
increment = i;
}
}
You can make use of NSNumberFormatter.
Like below code, firstly get NSNumber from chinese characters, then combine them.
func getNumber(fromText text: String) -> NSNumber? {
let locale = Locale(identifier: "zh_Hans_CN")
let numberFormatter = NumberFormatter()
numberFormatter.locale = locale
numberFormatter.numberStyle = .spellOut
guard let number = numberFormatter.number(from: text) else { return nil }
print(number)
return number
}
I am creating a simple client app to experiment with Mega and I am having trouble wrapping my head around the way RSA is used. Let's take, for example, the decryption of the session ID - this is one of the first things that must be done in order to log in.
The API provides me the following RSA data:
p (1024 bits)
q (1024 bits)
d (2044 bits)
u (1024 bits)
To start with, I do not know what "u" stands for. I see from code that it is calculated by modinverse(p, q) - is this what is commonly referred to as qInverse?
This is considerably less RSA data for a private key than I have used previously, so I am not quite sure what to make of it. However, I am given to understand that some of the RSA data used by RSACryptoServiceProvider is just pre-calculated data for optimization purposes, so perhaps the rest is not needed?
Using this data, the site's JavaScript decrypts the session ID with the following function:
// Compute m**d mod p*q for RSA private key operations.
function RSAdecrypt(m, d, p, q, u)
{
var xp = bmodexp(bmod(m,p), bmod(d,bsub(p,[1])), p);
var xq = bmodexp(bmod(m,q), bmod(d,bsub(q,[1])), q);
var t=bsub(xq,xp);
if(t.length==0)
{
t=bsub(xp,xq);
t=bmod(bmul(t, u), q);
t=bsub(q,t);
}
else
{
t=bmod(bmul(t, u), q);
}
return badd(bmul(t,p), xp);
}
I would like to do this in .NET using RSACryptoServiceProvider but if I give it the 4 pieces of data I have (assuming u == qInverse), the key is rejected during import with a "Bad data" exception.
Should I be doing something more to the data? Is RSACryptoServiceProvider usable in this situation at all?
Example of the parameters and encrypted data I am testing with follows.
var p = Convert.FromBase64String("1AkMwy3SPbJtL/k2RUPNztBQKow0NX9LVr5/73+zR3cuwgUToYkVefKdzlTgeri9CAVUq/+jU6o+P7sUpPUN+V97quZa00m3GSIdonRMdaMrDDH5aHnkQgOsCjLJDWXU6+TQBqLumR3XMSat3VO09Dps+6NcMc+uMi5atC3tb+0=");
var q = Convert.FromBase64String("qtnlmPbATJajNdihw1K6cwSormySATp7g75vYfilYx6RXN3xpNCZR/i8zFbx/lDh+n1a2rdHy1nWyuaD3UmE26d1xUkmsPDfBc72WXt88UqWE/gF7NJjtgTxS2Ui+2GGKUCloi5UA/pOI7R5TBvGI8zna00SH78bctyE0dcAcwM=");
var d = Convert.FromBase64String("CFL4QPQ8zLzrf2bUzCVX8S2/eALzo/P2cvQsW9lft7uelHYfC1CvHP+z4RvQgXABpgT8YTdU+sgdMHrhHT1vxeUaDRkcQv9lV0IP6YtAcD+gk5jDQkXk4ruYztTUF3v4u8rlMuZ8kAKKWKw+JH6grLWD/vXjMv2RybxPqq3fKI6VJaj/Y/ZnDjD5HrQmJopnCbOrZrPysNb/rGrN3ad9ysaZwBvQtIE0/tQvmL+lsI+PfF9oGKeHkciIo0D4N2abOKT2fiazNm1U9LnrQih687ge0aeAlP2OO8c0h/nbEkMbNg83n1GGEt3DNojIWbT5uHaj12M6G81leS77mfLvSQ==");
var u = Convert.FromBase64String("CNlUzgCf6Ymd/qeWiv3ScCIXYCwjP3SNLHxRgozIbNg2JEKpJn2M3vO72qLI+FT34xckaAGIcKWMkmpoaKy6PYF4jsAz2atLEClLimbMEPvpWxK7b/I5yvXMT7i2r5hr0OjjplL0wFQYL1IS2M8DTrL99rd9zXCoCWg5Tax6zQM=");
var encryptedData = Convert.FromBase64String("CABt/Qp7ZODvweEk5RY9JNMXoyFfUwMnc53zbP5jB4jnwWXibLLvjc+Dv5CwQAtUYRme+vRd80++178BiWl0YSOKKhQaDQKoeOUONn3KbZVWyCtyWyQZNtASPoQfizay/Dw3yP5BKsJmDpEv47awdEZzh8IqTcTKeQbpHFL+3uL5EjIENpxMh15rJUsY9w+jq6Yax+379tq67EPMUON0aYkRQ3k1Rsp9fOL6qrgoqOPmOc0cIQgx76t6SFB9LmDySkyBhtK+vcEkdn9GwzZqc6n/Jqt9K8a+mbBv3K7eO3Pa37SDncsaxEzlyLwQ2om1+bK2QwauSQl+7QwQS1a9Ejb9");
var rsa = new RSACryptoServiceProvider();
// Throws exception saying "Bad data"
rsa.ImportParameters(new RSAParameters
{
D = d,
P = p,
Q = q,
InverseQ = u
});
Addendum February 2
I have dug around in the linked StackOverflow answers and have reached the point where I think I have determined how to generate the missing component. However, now I am getting a "Bad key" exception, which has me stumped.
I will write the code I am using to generate the missing components - perhaps you can spot an error somewhere?
I have also calculated InverseQ and D manually and the values match those in my input data. Below is my function for generating the required data based only on q, p and e.
private static RSAParameters CalculateRsaParameters(BigInteger p, BigInteger q, BigInteger e)
{
var modulus = BigInteger.Multiply(p, q);
var phi = BigInteger.Multiply(BigInteger.Subtract(p, BigInteger.One), BigInteger.Subtract(q, BigInteger.One));
BigInteger x, y;
// From http://www.codeproject.com/Articles/60108/BigInteger-Library
// Returns 1 with my test data.
ExtendedEuclidGcd(e, phi, out x, out y);
var d = BigInteger.Remainder(x, phi);
var dp = BigInteger.Remainder(d, BigInteger.Subtract(p, BigInteger.One));
var dq = BigInteger.Remainder(d, BigInteger.Subtract(q, BigInteger.One));
BigInteger x2, y2;
// Returns 1 with my test data.
ExtendedEuclidGcd(q, p, out x2, out y2);
// y2 since it matched the pre-generated inverseQ data I had and x2 was some negative value, so it did not seem to fit. I have no idea what the logic behind which to pick really is.
var qInverse = BigInteger.Remainder(y2, p);
return new RSAParameters
{
D = ToBigEndianByteArray(d, 256),
DP = ToBigEndianByteArray(dp, 128),
DQ = ToBigEndianByteArray(dq, 128),
InverseQ = ToBigEndianByteArray(qInverse, 128),
Exponent = ToBigEndianByteArray(e, 1),
Modulus = ToBigEndianByteArray(modulus, 256),
P = ToBigEndianByteArray(p, 128),
Q = ToBigEndianByteArray(q, 128)
};
}
My input data is:
e = 17
p = 148896287039501678969147386479458178246000691707699594019852371996225136011987881033904404601666619814302065310828663028471342954821076961960815187788626496609581811628527023262215778397482476920164511192915070597893567835708908996890192512834283979142025668876250608381744928577381330716218105191496818716653
q = 119975764355551220778509708561576785383941026741388506773912560292606151764383332427604710071170171329268379604135341015979284377183953677973647259809025842247294479469402755370769383988530082830904396657573472653613365794770434467132057189606171325505138499276437937752474437953713231209677228298628994462467
And here is how I make use of the generated structure:
var rsa = new RSACryptoServiceProvider(2048);
rsa.ImportParameters(CalculateRsaParameters(p, q, e));
The ImportParameters call throws an exception saying "Bad key". What am I doing wrong?
What happens if I switch Q and P?
Apparently, it makes RSACryptoServiceProvider accept the data! But what does this mean exactly?
I got the idea from the way I had to use ExtendedEuclidGcd in my generation code. Having to use different outputs for the two instances bothered me a lot, so I performed this experiment.
One thing is that u != qInverse - is this correct? I do not understand the math in the original JavaScript function, so I am not sure what the implications are. Am I right in guessing that the u value in the original is in fact some internal shortcut and not QInverse?
Further testing to follow (i.e. actual decryption of data). I will edit the question with any new developments once made.
Decryption fails with this parameter set
The encrypted test data I have is (base64-encoded):
/TYSvVZLEAztfglJrgZDtrL5tYnaELzI5UzEGsudg7Tf2nM73q7cb7CZvsYrfasm/6lzajbDRn92JMG9vtKGgUxK8mAufVBIeqvvMQghHM055uOoKLiq+uJ8fcpGNXlDEYlpdONQzEPsutr2++3HGqarow/3GEsla16HTJw2BDIS+eLe/lIc6QZ5ysRNKsKHc0Z0sLbjL5EOZsIqQf7INzz8sjaLH4Q+EtA2GSRbcivIVpVtyn02DuV4qAINGhQqiiNhdGmJAb/Xvk/zXfT6nhlhVAtAsJC/g8+N77Js4mXB54gHY/5s851zJwNTXyGjF9MkPRblJOHB7+Bkewr9bQ==
or
bf0Ke2Tg78HhJOUWPSTTF6MhX1MDJ3Od82z+YweI58Fl4myy743Pg7+QsEALVGEZnvr0XfNPvte/AYlpdGEjiioUGg0CqHjlDjZ9ym2VVsgrclskGTbQEj6EH4s2svw8N8j+QSrCZg6RL+O2sHRGc4fCKk3EynkG6RxS/t7i+RIyBDacTIdeayVLGPcPo6umGsft+/bauuxDzFDjdGmJEUN5NUbKfXzi+qq4KKjj5jnNHCEIMe+rekhQfS5g8kpMgYbSvr3BJHZ/RsM2anOp/yarfSvGvpmwb9yu3jtz2t+0g53LGsRM5ci8ENqJtfmytkMGrkkJfu0MEEtWvRI2/Q==
Two alternatives given since I am not sure of the byte order. It is the same data in both strings.
Decryption of both of these fails with an exception saying "Bad data" in the first case and "Not enough storage is available to process this command." in the second case (which MSDN claims might mean that the key does not match the encrypted data). I am telling RSACryptoServiceProvider that PKCS padding is used, though I also experimented with OAEP (which just gave an error about failing to decode padding).
The original JavaScript decrypts the data without a problem, though its "p" and "q" are switched around from mine.
Right now, my questions are:
Is the P and Q switch-around a valid operation to do?
Is my reasoning valid or have I made a mistake somewhere?
What should I do next to successfully decrypt my test data?
RsaParameters has eight fields. I think you need to initialize all of them when creating a private key.
Take a look at http://msdn.microsoft.com/en-us/library/system.security.cryptography.rsaparameters.aspx
There are a number of possible pitfalls that one can trip over in using .NET BigIntegers and RSA parameters. The two that are probably impacting you are endianness and leading zero suppression.
System.Numerics.BigInteger class, available starting in .NET 4.0, uses a litte-endian format for its ToByteArray() method and new BigInteger(byte []) constructor.
Unfortunately, the RSAParameters structure expects its byte array fields to be in big-endian order. There is also one other incompatibility that must be accounted for. A System.Numerics.BigInteger may be either positive or negative, and the ToByteArray() method accounts for this by using a variant of twos-complement representation. Effectively this means that a positive BigInteger whose byte array representation has a high-order byte >=
128 will have an additional zero byte placed in highest order position. However, the RSAParameter fields are assumed to be all positive, so a leading zero is gratuitously rejected with a "Bad Data" CryptographicException. You must delete these leading zeros where they occur.
The following is a simple example code fragment showing these operations:
static BigInteger ExtGCD(BigInteger a, BigInteger b, out BigInteger lastx, out BigInteger lasty)
{
var x = BigInteger.Zero;
lastx = BigInteger.One;
var y = BigInteger.One;
lasty = BigInteger.Zero;
while (!b.IsZero)
{
BigInteger remainder;
BigInteger q = BigInteger.DivRem(a, b, out remainder);
a = b;
b = remainder;
var t = x;
x = lastx - q * x;
lastx = t;
t = y;
y = lasty - q * y;
lasty = t;
}
return a;
}
static BigInteger inverse(BigInteger a, BigInteger n)
{
BigInteger d, x, y;
d = ExtGCD(a, n, out x, out y);
if (d.IsOne)
{
// Always return the least positive value
return (x + n) % n;
}
else
{
throw new ArgumentException("the arguments must be relatively prime, i.e. their gcd must be 1");
}
}
static byte[] ToByteArrayBE(BigInteger b)
{
var x = b.ToByteArray(); // x is little-endian
Array.Reverse(x); // now it is big-endian
if (x[0] == 0)
{
var newarray = new byte[x.Length - 1];
Array.Copy(x, 1, newarray, 0, newarray.Length);
return newarray;
} else
{
return x;
}
}
static RSAParameters CalculateRsaParameters(BigInteger p, BigInteger q, BigInteger e)
{
// Given p, q, and e (the RSA encryption exponent) compute the remaining parameters
var phi = (p - 1) * (q - 1);
var d = inverse(e, phi);
var dp = d % (p - 1);
var dq = d % (q - 1);
var qInv = inverse(q, p);
var RsaParams = new RSAParameters
{
Modulus = ToByteArrayBE(p * q),
Exponent = ToByteArrayBE(e),
P = ToByteArrayBE(p),
Q = ToByteArrayBE(q),
D = ToByteArrayBE(d),
DP = ToByteArrayBE(dp),
DQ = ToByteArrayBE(dq),
InverseQ = ToByteArrayBE(qInv)
};
return RsaParams;
}
static void Main(string[] args)
{
BigInteger p = BigInteger.Parse("148896287039501678969147386479458178246000691707699594019852371996225136011987881033904404601666619814302065310828663028471342954821076961960815187788626496609581811628527023262215778397482476920164511192915070597893567835708908996890192512834283979142025668876250608381744928577381330716218105191496818716653");
BigInteger q = BigInteger.Parse("119975764355551220778509708561576785383941026741388506773912560292606151764383332427604710071170171329268379604135341015979284377183953677973647259809025842247294479469402755370769383988530082830904396657573472653613365794770434467132057189606171325505138499276437937752474437953713231209677228298628994462467");
BigInteger e = new BigInteger(17);
RSAParameters RsaParams = CalculateRsaParameters(p, q, e);
var Rsa = new RSACryptoServiceProvider();
Rsa.ImportParameters(RsaParams);
}
}