I try to hash a tokenId with a seed in my smart contract. For simplicity and to avoid other errors I leave the seed out for now. I basically just want to hash a number on my contract and hash the same number on my javascript code and receive the same output.
Code looks something like this on Solidity:
function _tokenURI(uint256 tokenId) internal view returns (string memory) {
string memory currentBaseURI = _baseURI();
bytes32 hashedToken = keccak256(abi.encodePacked(tokenId));
return
bytes(currentBaseURI).length > 0
? string(abi.encodePacked(currentBaseURI, hashedToken, baseExtension))
: "";
}
which also leads to an error on client side invalid codepoint at offset. To tackle this I tried to cast bit32 to string using these functions
function _bytes32ToString(bytes32 _bytes32)
private
pure
returns (string memory)
{
uint8 i = 0;
bytes memory bytesArray = new bytes(64);
for (i = 0; i < bytesArray.length; i++) {
uint8 _f = uint8(_bytes32[i / 2] & 0x0f);
uint8 _l = uint8(_bytes32[i / 2] >> 4);
bytesArray[i] = _toByte(_f);
i = i + 1;
bytesArray[i] = _toByte(_l);
}
return string(bytesArray);
}
function _toByte(uint8 _uint8) private pure returns (bytes1) {
if (_uint8 < 10) {
return bytes1(_uint8 + 48);
} else {
return bytes1(_uint8 + 87);
}
}
though I'm not sure if this is equivalent. Code on the frontend looks like:
const hashed = web3.utils.soliditySha3(
{ type: "uint256", value: tokenId}
);
What do I need to change in order to receive the exact same output? And what does
invalid codepoint at offset
mean?
Maybe issue is that tokenId is not uint256 or Web3, Solidity version? I did few tests with Remix IDE and I recieved the same results.
Solidity code:
// SPDX-License-Identifier: MIT
pragma solidity ^0.8.0;
contract Hash {
function getHashValue_1() public view returns(bytes32){
return keccak256(abi.encodePacked(uint256(234)));
}
// bytes32: 0x61c831beab28d67d1bb40b5ae1a11e2757fa842f031a2d0bc94a7867bc5d26c2
function getHashValue_3() public view returns(bytes32){
return keccak256(abi.encodePacked(uint256(10),string('StringSecretValue')));
}
// bytes32: 0x5938b4caf29ac4903ee34628c3dc1eb5c670a6bd392a006d0cb91f1fc5db3819
}
JS code:
(async () => {
try {
console.log('Web3 version is '+ Web3.version);
// Web3 version is 1.3.0
let theValueYouNeed = web3.utils.soliditySha3("234");
theValueYouNeed = web3.utils.soliditySha3({type: 'uint256', value: '234'});
theValueYouNeed = web3.utils.soliditySha3({t: 'uint256', v: '234'});
// above hashed value is 0x61c831beab28d67d1bb40b5ae1a11e2757fa842f031a2d0bc94a7867bc5d26c2
console.log('Hashed value 1 is '+theValueYouNeed);
theValueYouNeed = web3.utils.soliditySha3({t: 'uint256', v: '10'},{t: 'string', v: 'StringSecretValue'});
console.log('Hashed value 2 is '+theValueYouNeed);
// above hashed value is 0x5938b4caf29ac4903ee34628c3dc1eb5c670a6bd392a006d0cb91f1fc5db3819
} catch (e) {
console.log(e.message)
}
})()
I'm not sure but invalid codepoint at offset should mean that a designated value does not fall within the range or set of allowed values... So maybe there is something wrong with tokenId and you could do some tests with hardcoded values?
You get the invalid codepoint error because you mix string and byte data when you call abi.encodePacked(currentBaseURI, hashedToken, baseExtension)).
When Javascript gets the return value from the contract it expects a UTF8 string, but inside your hashedToken you have byte values that are not valid for a UTF8-encoded string.
This kind of error might be "intermittent". It might happen in just some cases. You're lucky to see it during development and not in production.
How to fix it?
You are on the right track converting the hash result to a string.
There is an alternative way to do it in this answer which uses less gas by using only bitwise operations.
To convert the hex value in Javascript you can use web3.utils.hexToNumberString().
So I have a code I previously wrote in C# that I am trying to convert to JS.
However I just encountered a strange issue while narrowing down my Number type to UInt32 Range in JS.
However, If I implement the same code in C# or Object Pascal (languages which have native UInt32 types), I get a different result.
Can someone please show me how I can get results that are consistent with the C# code in JS.
For simplicity sake, I am using defined constants to show the problem.
The result of my Code is
JS: 2444377276
C# and Object Pascal: 2444377275
function modulo(a, b) {
return a - Math.floor(a / b) * b;
}
function ToInteger(x) {
x = Number(x);
return x < 0 ? Math.ceil(x) : Math.floor(x);
}
function ToUint32(x) {
return modulo(ToInteger(x), Math.pow(2, 32));
}
let a = (1207988537 * 16777619);
console.log(a >>> 0);
let uu = new Uint32Array([a]);
console.log(uu[0]);
console.log(ToUint32(a));
// will only work in ES2020 upwards but produces same result as other JS solutions above
//console.log(BigInt.asUintN(32, BigInt(a)));
C# code
using System;
public class Test
{
public static void Main()
{
// your code goes here
unchecked
{
uint a = (uint)(1207988537 * 16777619);
Console.WriteLine(a);
}
}
}
Object Pascal Code
{$MODE DELPHI}
program ideone;
var
a: UInt32;
begin
(* your code goes here *)
a := UInt32(1207988537 * 16777619);
Writeln(a);
end.
I'm trying to fork PANCAKESWAP, everything seem to work well, I can add liquidity, but whenever I decided to add a liquidity that has to do with BNB (i.e calling the addLiquidityETH function) transaction gets reverted with that error message.
After a little digging, I found out it only appears in a single place in the whole router.
// given some amount of an asset and pair reserves, returns an equivalent amount of the other asset
function quote(uint amountA, uint reserveA, uint reserveB) internal pure returns (uint amountB) {
require(amountA > 0, 'PancakeLibrary: INSUFFICIENT_AMOUNT'); // THIS IS WHERE TRIGGERS THE REVERT!!
require(reserveA > 0 && reserveB > 0, 'PancakeLibrary: INSUFFICIENT_LIQUIDITY');
amountB = amountA.mul(reserveB) / reserveA;
}
and this quote function got called in the _addLiquidity function as seen below.
// **** ADD LIQUIDITY ****
function _addLiquidity(
address tokenA,
address tokenB,
uint amountADesired,
uint amountBDesired,
uint amountAMin,
uint amountBMin
) internal virtual returns (uint amountA, uint amountB) {
// create the pair if it doesn't exist yet
if (IPancakeFactory(factory).getPair(tokenA, tokenB) == address(0)) {
IPancakeFactory(factory).createPair(tokenA, tokenB);
}
(uint reserveA, uint reserveB) = PancakeLibrary.getReserves(factory, tokenA, tokenB);
if (reserveA == 0 && reserveB == 0) {
(amountA, amountB) = (amountADesired, amountBDesired);
} else {
uint amountBOptimal = PancakeLibrary.quote(amountADesired, reserveA, reserveB); // here calls the quote function
if (amountBOptimal <= amountBDesired) {
require(amountBOptimal >= amountBMin, 'PancakeRouter: INSUFFICIENT_B_AMOUNT');
(amountA, amountB) = (amountADesired, amountBOptimal);
} else {
uint amountAOptimal = PancakeLibrary.quote(amountBDesired, reserveB, reserveA); // and here too
assert(amountAOptimal <= amountADesired);
require(amountAOptimal >= amountAMin, 'PancakeRouter: INSUFFICIENT_A_AMOUNT');
(amountA, amountB) = (amountAOptimal, amountBDesired);
}
}
}
So far, these are the ONLY places that calls quote which will result to the transaction getting reverted, but I can't seem to find anything wrong I mean, AmountA is obviously > 0.
Any help would be really appreciated.
EDIT:
The error was from my end, I was debugging an old transaction where I didn't send BNB to the contract, and I assumed it was the same error I received for the future transaction, decided to check and found out the error I was receiving was INSUFFICIENT_A_AMOUNT, fixed it by setting amountETHMin and amountTokenMin to 0
The error was from my end, I was debugging an old transaction where I didn't send BNB to the contract, and I assumed it was the same error I received for the future transaction, decided to check and found out the error I was receiving was INSUFFICIENT_A_AMOUNT, fixed it by setting amountETHMin and amountTokenMin to 0
I've met this error, this is the demo java code to fix the bug.
raw = RawTransaction.createTransaction(
nonce,
gasProvider.getGasPrice(),
gasProvider.gasLimit,
contractAddress,
amountEth.toWei(), //>>> add amount of bnb here, when create rawTransaction
FunctionEncoder.encode(function)
)
rawTransactionManager.signAndSend(raw)
I am using the Scanner methods nextInt() and nextLine() for reading input.
It looks like this:
System.out.println("Enter numerical value");
int option;
option = input.nextInt(); // Read numerical value from input
System.out.println("Enter 1st string");
String string1 = input.nextLine(); // Read 1st string (this is skipped)
System.out.println("Enter 2nd string");
String string2 = input.nextLine(); // Read 2nd string (this appears right after reading numerical value)
The problem is that after entering the numerical value, the first input.nextLine() is skipped and the second input.nextLine() is executed, so that my output looks like this:
Enter numerical value
3 // This is my input
Enter 1st string // The program is supposed to stop here and wait for my input, but is skipped
Enter 2nd string // ...and this line is executed and waits for my input
I tested my application and it looks like the problem lies in using input.nextInt(). If I delete it, then both string1 = input.nextLine() and string2 = input.nextLine() are executed as I want them to be.
That's because the Scanner.nextInt method does not read the newline character in your input created by hitting "Enter," and so the call to Scanner.nextLine returns after reading that newline.
You will encounter the similar behaviour when you use Scanner.nextLine after Scanner.next() or any Scanner.nextFoo method (except nextLine itself).
Workaround:
Either put a Scanner.nextLine call after each Scanner.nextInt or Scanner.nextFoo to consume rest of that line including newline
int option = input.nextInt();
input.nextLine(); // Consume newline left-over
String str1 = input.nextLine();
Or, even better, read the input through Scanner.nextLine and convert your input to the proper format you need. For example, you may convert to an integer using Integer.parseInt(String) method.
int option = 0;
try {
option = Integer.parseInt(input.nextLine());
} catch (NumberFormatException e) {
e.printStackTrace();
}
String str1 = input.nextLine();
The problem is with the input.nextInt() method; it only reads the int value. So when you continue reading with input.nextLine() you receive the "\n" Enter key. So to skip this you have to add the input.nextLine().
Try it like this, instead:
System.out.print("Insert a number: ");
int number = input.nextInt();
input.nextLine(); // This line you have to add (It consumes the \n character)
System.out.print("Text1: ");
String text1 = input.nextLine();
System.out.print("Text2: ");
String text2 = input.nextLine();
It's because when you enter a number then press Enter, input.nextInt() consumes only the number, not the "end of line". When input.nextLine() executes, it consumes the "end of line" still in the buffer from the first input.
Instead, use input.nextLine() immediately after input.nextInt()
There seem to be many questions about this issue with java.util.Scanner. I think a more readable/idiomatic solution would be to call scanner.skip("[\r\n]+") to drop any newline characters after calling nextInt().
EDIT: as #PatrickParker noted below, this will cause an infinite loop if user inputs any whitespace after the number. See their answer for a better pattern to use with skip: https://stackoverflow.com/a/42471816/143585
TL;DR
nextLine() is safe to call when (a) it is first reading instruction, (b) previous reading instruction was also nextLine().
If you are not sure that either of above is true you can use scanner.skip("\\R?") before calling scanner.nextLine() since calls like next() nextInt() will leave potential line separator - created by return key which will affect result of nextLine(). The .skip("\\R?") will let us consume this unnecessary line separator.
skip uses regex where
\R represents line separators
? will make \R optional - which will prevent skip method from:
waiting for matching sequence
in case of reaching end of still opened source of data like System.in, input stream from socket, etc.
throwing java.util.NoSuchElementException in case of
terminated/closed source of data,
or when existing data doesn't match what we want to skip
Things you need to know:
text which represents few lines also contains non-printable characters between lines (we call them line separators) like
carriage return (CR - in String literals represented as "\r")
line feed (LF - in String literals represented as "\n")
when you are reading data from the console, it allows the user to type his response and when he is done he needs to somehow confirm that fact. To do so, the user is required to press "enter"/"return" key on the keyboard.
What is important is that this key beside ensuring placing user data to standard input (represented by System.in which is read by Scanner) also sends OS dependant line separators (like for Windows \r\n) after it.
So when you are asking the user for value like age, and user types 42 and presses enter, standard input will contain "42\r\n".
Problem
Scanner#nextInt (and other Scanner#nextType methods) doesn't allow Scanner to consume these line separators. It will read them from System.in (how else Scanner would know that there are no more digits from the user which represent age value than facing whitespace?) which will remove them from standard input, but it will also cache those line separators internally. What we need to remember, is that all of the Scanner methods are always scanning starting from the cached text.
Now Scanner#nextLine() simply collects and returns all characters until it finds line separators (or end of stream). But since line separators after reading the number from the console are found immediately in Scanner's cache, it returns empty String, meaning that Scanner was not able to find any character before those line separators (or end of stream).
BTW nextLine also consumes those line separators.
Solution
So when you want to ask for number and then for entire line while avoiding that empty string as result of nextLine, either
consume line separator left by nextInt from Scanners cache by
calling nextLine,
or IMO more readable way would be by calling skip("\\R") or skip("\r\n|\r|\n") to let Scanner skip part matched by line separator (more info about \R: https://stackoverflow.com/a/31060125)
don't use nextInt (nor next, or any nextTYPE methods) at all. Instead read entire data line-by-line using nextLine and parse numbers from each line (assuming one line contains only one number) to proper type like int via Integer.parseInt.
BTW: Scanner#nextType methods can skip delimiters (by default all whitespaces like tabs, line separators) including those cached by scanner, until they will find next non-delimiter value (token). Thanks to that for input like "42\r\n\r\n321\r\n\r\n\r\nfoobar" code
int num1 = sc.nextInt();
int num2 = sc.nextInt();
String name = sc.next();
will be able to properly assign num1=42 num2=321 name=foobar.
It does that because input.nextInt(); doesn't capture the newline. you could do like the others proposed by adding an input.nextLine(); underneath.
Alternatively you can do it C# style and parse a nextLine to an integer like so:
int number = Integer.parseInt(input.nextLine());
Doing this works just as well, and it saves you a line of code.
Instead of input.nextLine() use input.next(), that should solve the problem.
Modified code:
public static Scanner input = new Scanner(System.in);
public static void main(String[] args)
{
System.out.print("Insert a number: ");
int number = input.nextInt();
System.out.print("Text1: ");
String text1 = input.next();
System.out.print("Text2: ");
String text2 = input.next();
}
If you want to read both strings and ints, a solution is to use two Scanners:
Scanner stringScanner = new Scanner(System.in);
Scanner intScanner = new Scanner(System.in);
intScanner.nextInt();
String s = stringScanner.nextLine(); // unaffected by previous nextInt()
System.out.println(s);
intScanner.close();
stringScanner.close();
In order to avoid the issue, use nextLine(); immediately after nextInt(); as it helps in clearing out the buffer. When you press ENTER the nextInt(); does not capture the new line and hence, skips the Scanner code later.
Scanner scanner = new Scanner(System.in);
int option = scanner.nextInt();
scanner.nextLine(); //clearing the buffer
If you want to scan input fast without getting confused into Scanner class nextLine() method , Use Custom Input Scanner for it .
Code :
class ScanReader {
/**
* #author Nikunj Khokhar
*/
private byte[] buf = new byte[4 * 1024];
private int index;
private BufferedInputStream in;
private int total;
public ScanReader(InputStream inputStream) {
in = new BufferedInputStream(inputStream);
}
private int scan() throws IOException {
if (index >= total) {
index = 0;
total = in.read(buf);
if (total <= 0) return -1;
}
return buf[index++];
}
public char scanChar(){
int c=scan();
while (isWhiteSpace(c))c=scan();
return (char)c;
}
public int scanInt() throws IOException {
int integer = 0;
int n = scan();
while (isWhiteSpace(n)) n = scan();
int neg = 1;
if (n == '-') {
neg = -1;
n = scan();
}
while (!isWhiteSpace(n)) {
if (n >= '0' && n <= '9') {
integer *= 10;
integer += n - '0';
n = scan();
}
}
return neg * integer;
}
public String scanString() throws IOException {
int c = scan();
while (isWhiteSpace(c)) c = scan();
StringBuilder res = new StringBuilder();
do {
res.appendCodePoint(c);
c = scan();
} while (!isWhiteSpace(c));
return res.toString();
}
private boolean isWhiteSpace(int n) {
if (n == ' ' || n == '\n' || n == '\r' || n == '\t' || n == -1) return true;
else return false;
}
public long scanLong() throws IOException {
long integer = 0;
int n = scan();
while (isWhiteSpace(n)) n = scan();
int neg = 1;
if (n == '-') {
neg = -1;
n = scan();
}
while (!isWhiteSpace(n)) {
if (n >= '0' && n <= '9') {
integer *= 10;
integer += n - '0';
n = scan();
}
}
return neg * integer;
}
public void scanLong(long[] A) throws IOException {
for (int i = 0; i < A.length; i++) A[i] = scanLong();
}
public void scanInt(int[] A) throws IOException {
for (int i = 0; i < A.length; i++) A[i] = scanInt();
}
public double scanDouble() throws IOException {
int c = scan();
while (isWhiteSpace(c)) c = scan();
int sgn = 1;
if (c == '-') {
sgn = -1;
c = scan();
}
double res = 0;
while (!isWhiteSpace(c) && c != '.') {
if (c == 'e' || c == 'E') {
return res * Math.pow(10, scanInt());
}
res *= 10;
res += c - '0';
c = scan();
}
if (c == '.') {
c = scan();
double m = 1;
while (!isWhiteSpace(c)) {
if (c == 'e' || c == 'E') {
return res * Math.pow(10, scanInt());
}
m /= 10;
res += (c - '0') * m;
c = scan();
}
}
return res * sgn;
}
}
Advantages :
Scans Input faster than BufferReader
Reduces Time Complexity
Flushes Buffer for every next input
Methods :
scanChar() - scan single character
scanInt() - scan Integer value
scanLong() - scan Long value
scanString() - scan String value
scanDouble() - scan Double value
scanInt(int[] array) - scans complete Array(Integer)
scanLong(long[] array) - scans complete Array(Long)
Usage :
Copy the Given Code below your java code.
Initialise Object for Given Class
ScanReader sc = new ScanReader(System.in);
3. Import necessary Classes :
import java.io.BufferedInputStream;
import java.io.IOException;
import java.io.InputStream;
4. Throw IOException from your main method to handle Exception
5. Use Provided Methods.
6. Enjoy
Example :
import java.io.BufferedInputStream;
import java.io.IOException;
import java.io.InputStream;
class Main{
public static void main(String... as) throws IOException{
ScanReader sc = new ScanReader(System.in);
int a=sc.scanInt();
System.out.println(a);
}
}
class ScanReader....
sc.nextLine() is better as compared to parsing the input.
Because performance wise it will be good.
I guess I'm pretty late to the party..
As previously stated, calling input.nextLine() after getting your int value will solve your problem. The reason why your code didn't work was because there was nothing else to store from your input (where you inputted the int) into string1. I'll just shed a little more light to the entire topic.
Consider nextLine() as the odd one out among the nextFoo() methods in the Scanner class. Let's take a quick example.. Let's say we have two lines of code like the ones below:
int firstNumber = input.nextInt();
int secondNumber = input.nextInt();
If we input the value below (as a single line of input)
54 234
The value of our firstNumber and secondNumber variable become 54 and 234 respectively. The reason why this works this way is because a new line feed (i.e \n) IS NOT automatically generated when the nextInt() method takes in the values. It simply takes the "next int" and moves on. This is the same for the rest of the nextFoo() methods except nextLine().
nextLine() generates a new line feed immediately after taking a value; this is what #RohitJain means by saying the new line feed is "consumed".
Lastly, the next() method simply takes the nearest String without generating a new line; this makes this the preferential method for taking separate Strings within the same single line.
I hope this helps.. Merry coding!
public static void main(String[] args) {
Scanner scan = new Scanner(System.in);
int i = scan.nextInt();
scan.nextLine();
double d = scan.nextDouble();
scan.nextLine();
String s = scan.nextLine();
System.out.println("String: " + s);
System.out.println("Double: " + d);
System.out.println("Int: " + i);
}
if I expect a non-empty input
avoids:
– loss of data if the following input is eaten by an unchecked scan.nextLine() as workaround
– loss of data due to only partially read lines because scan.nextLine() was replaced by scan.next() (enter: "yippie ya yeah")
– Exceptions that are thrown when parsing input with Scanner methods (read first, parse afterwards)
public static Function<Scanner,String> scanLine = (scan -> {
String s = scan.nextLine();
return( s.length() == 0 ? scan.nextLine() : s );
});
used in above example:
System.out.println("Enter numerical value");
int option = input.nextInt(); // read numerical value from input
System.out.println("Enter 1st string");
String string1 = scanLine.apply( input ); // read 1st string
System.out.println("Enter 2nd string");
String string2 = scanLine.apply( input ); // read 2nd string
Use 2 scanner objects instead of one
Scanner input = new Scanner(System.in);
System.out.println("Enter numerical value");
int option;
Scanner input2 = new Scanner(System.in);
option = input2.nextInt();
In one of my usecase, I had the scenario of reading a string value preceded by a couple of integer values. I had to use a "for / while loop" to read the values. And none of the above suggestions worked in this case.
Using input.next() instead of input.nextLine() fixed the issue. Hope this might be helpful for those dealing with similar scenario.
As nextXXX() methods don't read newline, except nextLine(). We can skip the newline after reading any non-string value (int in this case) by using scanner.skip() as below:
Scanner sc = new Scanner(System.in);
int x = sc.nextInt();
sc.skip("(\r\n|[\n\r\u2028\u2029\u0085])?");
System.out.println(x);
double y = sc.nextDouble();
sc.skip("(\r\n|[\n\r\u2028\u2029\u0085])?");
System.out.println(y);
char z = sc.next().charAt(0);
sc.skip("(\r\n|[\n\r\u2028\u2029\u0085])?");
System.out.println(z);
String hello = sc.nextLine();
System.out.println(hello);
float tt = sc.nextFloat();
sc.skip("(\r\n|[\n\r\u2028\u2029\u0085])?");
System.out.println(tt);
Use this code it will fix your problem.
System.out.println("Enter numerical value");
int option;
option = input.nextInt(); // Read numerical value from input
input.nextLine();
System.out.println("Enter 1st string");
String string1 = input.nextLine(); // Read 1st string (this is skipped)
System.out.println("Enter 2nd string");
String string2 = input.nextLine(); // Read 2nd string (this appears right after reading numerical value)
To resolve this problem just make a scan.nextLine(), where scan is an instance of the Scanner object. For example, I am using a simple HackerRank Problem for the explanation.
package com.company;
import java.util.Scanner;
public class hackerrank {
public static void main(String[] args) {
Scanner scan = new Scanner(System.in);
int i = scan.nextInt();
double d = scan.nextDouble();
scan.nextLine(); // This line shall stop the skipping the nextLine()
String s = scan.nextLine();
scan.close();
// Write your code here.
System.out.println("String: " + s);
System.out.println("Double: " + d);
System.out.println("Int: " + i);
}
}
The nextLine() will read enter directly as an empty line without waiting for the text.
Simple solution by adding an extra scanner to consume the empty line:
System.out.println("Enter numerical value");
int option;
option = input.nextInt(); // Read numerical value from input
input.nextLine();
System.out.println("Enter 1st string");
String string1 = input.nextLine(); // Read 1st string (this is skipped)
System.out.println("Enter 2nd string");
String string2 = input.nextLine(); // Read 2nd string (this appears right after reading numerical value)
This is a very basic problem for beginner coders in java. The same problem I also have faced when I started java (Self Taught).
Actually, when we take an input of integer dataType, it reads only integer value and leaves the newLine(\n) character and this line(i.e. leaved new line by integer dynamic input )creates the problem when we try to take a new input.
eg. Like if we take the integer input and then after try to take an String input.
value1=sc.nextInt();
value2=sc.nextLine();
the value2 will auto read the newLine character and will not take the user input.
Solution:
just we need to add one line of code before taking the next user input i.e.
sc.nextLine();
or
value1=sc.nextInt();
sc.nextLine();
value2=sc.nextLine();
Note: don't forget to close the Scanner to prevent memory leak;
Scanner scan = new Scanner(System.in);
int i = scan.nextInt();
scan.nextLine();//to Ignore the rest of the line after (integer input)nextInt()
double d=scan.nextDouble();
scan.nextLine();
String s=scan.nextLine();
scan.close();
System.out.println("String: " + s);
System.out.println("Double: " + d);
System.out.println("Int: " + i);
Why not use a new Scanner for every reading? Like below. With this approach you will not confront your problem.
int i = new Scanner(System.in).nextInt();
The problem is with the input.nextInt() method - it only reads the int value. So when you continue reading with input.nextLine() you receive the "\n" Enter key. So to skip this you have to add the input.nextLine(). Hope this should be clear now.
Try it like that:
System.out.print("Insert a number: ");
int number = input.nextInt();
input.nextLine(); // This line you have to add (It consumes the \n character)
System.out.print("Text1: ");
String text1 = input.nextLine();
System.out.print("Text2: ");
String text2 = input.nextLine();
I'm trying to create signed PKCS#7 message for PKCS#10 certifacate request on client-side with javascript.
There are good examples on PKCS#10: http://blogs.msdn.com/b/alejacma/archive/2009/01/28/how-to-create-a-certificate-request-with-certenroll-javascript.aspx
But I need to create PKCS#7 and cannot figure out how to do it. There is a lack of examples (actually no at all) on official documentation for CertEnroll: http://msdn.microsoft.com/en-us/library/windows/desktop/aa374850(v=vs.85).aspx
I've ended up with this code:
var XCN_CRYPT_STRING_BASE64REQUESTHEADER = 3;
var XCN_CERT_NAME_STR_NONE = 0;
var _certEnrollClassFactory = new ActiveXObject("X509Enrollment.CX509EnrollmentWebClassFactory");
ComposePKCS10Request: function (containerName, subject)
{
// PKCS #10 certificate request
var objRequest = _certEnrollClassFactory.CreateObject("X509Enrollment.CX509CertificateRequestPkcs10");
var objCSP = objCertEnrollClassFactory.CreateObject("X509Enrollment.CCspInformation");
var objCSPs = objCertEnrollClassFactory.CreateObject("X509Enrollment.CCspInformations");
// Initialize the csp object using the desired Cryptograhic Service Provider (CSP)
objCSP.InitializeFromName("Microsoft Enhanced Cryptographic Provider v1.0");
// Add this CSP object to the CSP collection object
objCSPs.Add(objCSP);
// asymmetric private key that can be used for encryption, signing, and key agreement.
var objPrivateKey = _certEnrollClassFactory.CreateObject("X509Enrollment.CX509PrivateKey");
// Provide key container name, key length and key spec to the private key object
objPrivateKey.ContainerName = containerName;
//objPrivateKey.Length = 1024;
objPrivateKey.KeySpec = 1; // AT_KEYEXCHANGE = 1
// Provide the CSP collection object (in this case containing only 1 CSP object)
// to the private key object
objPrivateKey.CspInformations = objCSPs;
// Initialize P10 based on private key
objRequest.InitializeFromPrivateKey(1, objPrivateKey, ""); // context user = 1
// X.500 distinguished name (DN)
// The DN consists of a sequence of relative distinguished names (RDNs). Each RDN consists of a set of attributes,
// and each attribute consists of an object identifier (OID) and a value. The data type of the value is identified
// by the DirectoryString structure.
var objDn = _certEnrollClassFactory.CreateObject("X509Enrollment.CX500DistinguishedName");
// DN related stuff
objDn.Encode(subject, XCN_CERT_NAME_STR_NONE);
objRequest.Subject = objDn;
return objRequest;
}
CreatePKCS7: function (containerName, subject)
{
// PKCS #7 certificate request
var objPKCS7Request = _certEnrollClassFactory.CreateObject("X509Enrollment.CX509CertificateRequestPkcs7");
// initialize PKCS #7 certificate request by PKCS #10 certificate request
objPKCS7Request.InitializeFromInnerRequest(this.ComposePKCS10Request(containerName, subject));
var objSignerCert = _certEnrollClassFactory.CreateObject("X509Enrollment.CSignerCertificate");
var verifyType = 4; /* VerifyAllowUI, see typedef enum X509PrivateKeyVerify */
var encodingType = 0x3; /* see typedef enum EncodingType */
/**********************************************************************/
/* I have to provide certificate here??? How can I obtain it from UI? */
/**********************************************************************/
var strCertificate = '?????????????????????';
objSignerCert.Initialize(false, verifyType, encodingType, strCertificate);
/*****************************************************************************/
/* Also I'm not shure that SignerCertificate can be accessed via javascript. */
/*****************************************************************************/
objPKCS7Request.SignerCertificate = objSignerCert;
// represents the top level object and enables you to enroll in a certificate hierarchy and install a certificate response
var objEnroll = _certEnrollClassFactory.CreateObject("X509Enrollment.CX509Enrollment");
// Enroll
objEnroll.InitializeFromRequest(objPKCS7Request);
var pkcs7;
try
{
pkcs7 = objEnroll.CreateRequest(XCN_CRYPT_STRING_BASE64REQUESTHEADER);
}
catch (e)
{
...
}
return pkcs7;
}
Is there any way to create PKCS#7 message with javascript?
UPDATE: I've already had PKCS#10 cert request (see the first function in code sample) and need to create PKCS#7 signed message for it. Ok, I paraphrase my question. How to create signed PKCS#7 message with javascript? (Ideally, it should allow to specify proper cert with UI.)
As for javascript I understand that it's not the convenient way, but suitable because I must to deal with it on client-side (in browser). Moreover, cert enroll IX509CertificateRequestPkcs7 interface has methods marked as [WebEnabled], so I believe there must be the way to do what I state.
You can do PKCS#7 and PKCS#10 in pure JS using Forge (works in browser or node.js):
https://github.com/digitalbazaar/forge#pkcs7
https://github.com/digitalbazaar/forge#pkcs10
PKCS#7 format is defined in rfc2315 using ASN.1 notation.
ASN.1
ASN.1 is a notation for defining data structures. Additionaly, there is also DER - encoding rules that define how ASN.1 notation is encoded as binary data.
ASN1.js encoder
So the question boils down to "are there any JS-libraries that implements ASN1?"
There are some decoding libraries
But the only encoder is this one: https://github.com/indutny/asn1.js
ContentInfo and types
Let's dive deeper. The ASN.1 for PKCS#7 follows:
ContentInfo ::= SEQUENCE {
contentType ContentType,
content
[0] EXPLICIT ANY DEFINED BY contentType OPTIONAL }
ContentType ::= OBJECT IDENTIFIER
Here we need to understand what is OBJECT IDENTIFIER. It is a special sequence of numbers that defines some type. Valid object identifiers for PKCS#7 follow:
data OBJECT IDENTIFIER ::= { pkcs-7 1 }
signedData OBJECT IDENTIFIER ::= { pkcs-7 2 }
envelopedData OBJECT IDENTIFIER ::= { pkcs-7 3 }
signedAndEnvelopedData OBJECT IDENTIFIER ::= { pkcs-7 4 }
digestedData OBJECT IDENTIFIER ::= { pkcs-7 5 }
encryptedData OBJECT IDENTIFIER ::= { pkcs-7 6 }
pkcs-7 is an object identifier for PKCS#7 format.
pkcs-7 OBJECT IDENTIFIER ::=
{ iso(1) member-body(2) US(840) rsadsi(113549)
pkcs(1) 7 }
As you can see, there's ANY DEFINED BY contentType modifier for the content field. It means that this field can be any of 6 types depending on the value of contentType field.
It seems like you need signed message, so the content type would be "signedData"
ContentInfo encoding using JS
How this translates into ASN1.js code? Here you're:
var PKCS7_CONTENT_TYPES = {
"1 2 840 113549 1 7 1": "data",
"1 2 840 113549 1 7 2": "signedData",
"1 2 840 113549 1 7 3": "envelopedData",
"1 2 840 113549 1 7 4": "signedAndEnvelopedData",
"1 2 840 113549 1 7 5": "digestData",
"1 2 840 113549 1 7 6": "encryptedData",
};
var ContentInfo = asn1.define('ContentInfo', function() {
this.seq().obj(
this.key('contentType').objid(PKCS7_CONTENT_TYPES),
this.key('content').optional().explicit(0).any()
);
});
So, how to use this ContentInfo?
var signedData = ...
contentInfoEncoded = ContentInfo.encode({
'contentType': 'signedData',
'content': signedData
})
SignedData
To be continued...
VBScript method (it will prompt to choose certificate):
Function SignMessage(Message)
Dim oUtils
Set oUtils = CreateObject("CAPICOM.Utilities")
Dim cpcSigner
Set cpcSigner = CreateObject("CAPICOM.Signer")
cpcSigner.Options = 2 'CAPICOM_CERTIFICATE_INCLUDE_END_ENTITY_ONLY
Dim cpcSignedData
Set cpcSignedData = CreateObject("CAPICOM.SignedData")
cpcSignedData.Content = oUtils.Base64Decode(Message)
SignMessage = cpcSignedData.Sign(cpcSigner, False)
End function
Javascript method (be carefull due to encoding strings in JS you will get corrupted signature, so I suggest to use VBScript for signing data in your client scripts since it interoperable with JS):
var CAPICOM_CERTIFICATE_INCLUDE_END_ENTITY_ONLY = 2;
SignMessage: function (message) {
var cpcSigner = new ActiveXObject("CAPICOM.Signer");
cpcSigner.Options = CAPICOM_CERTIFICATE_INCLUDE_END_ENTITY_ONLY;
var cpcSignedData = new ActiveXObject("CAPICOM.SignedData");
var oUtils = new ActiveXObject("CAPICOM.Utilities");
cpcSignedData.Content = oUtils.Base64Decode(message);
return cpcSignedData.Sign(cpcSigner, false);
}